From 2ace936a951c6562145b4dd359e104eca629bd05 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 20 Sep 2022 23:32:13 +0200 Subject: [PATCH 0001/1271] Draft to refactor fusion to new publisher and use as FusionHost --- openpype/hosts/fusion/api/__init__.py | 27 +-- openpype/hosts/fusion/api/menu.py | 2 +- openpype/hosts/fusion/api/pipeline.py | 174 ++++++++++++------ openpype/hosts/fusion/api/workio.py | 45 ----- .../deploy/MenuScripts/openpype_menu.py | 4 +- .../fusion/plugins/create/create_exr_saver.py | 44 +++-- 6 files changed, 160 insertions(+), 136 deletions(-) delete mode 100644 openpype/hosts/fusion/api/workio.py diff --git a/openpype/hosts/fusion/api/__init__.py b/openpype/hosts/fusion/api/__init__.py index ed70dbca50..495fe286d5 100644 --- a/openpype/hosts/fusion/api/__init__.py +++ b/openpype/hosts/fusion/api/__init__.py @@ -1,20 +1,11 @@ from .pipeline import ( - install, - uninstall, - + FusionHost, ls, imprint_container, - parse_container -) - -from .workio import ( - open_file, - save_file, - current_file, - has_unsaved_changes, - file_extensions, - work_root + parse_container, + list_instances, + remove_instance ) from .lib import ( @@ -30,21 +21,11 @@ from .menu import launch_openpype_menu __all__ = [ # pipeline - "install", - "uninstall", "ls", "imprint_container", "parse_container", - # workio - "open_file", - "save_file", - "current_file", - "has_unsaved_changes", - "file_extensions", - "work_root", - # lib "maintained_selection", "update_frame_range", diff --git a/openpype/hosts/fusion/api/menu.py b/openpype/hosts/fusion/api/menu.py index 7a6293807f..4e415cafba 100644 --- a/openpype/hosts/fusion/api/menu.py +++ b/openpype/hosts/fusion/api/menu.py @@ -144,7 +144,7 @@ class OpenPypeMenu(QtWidgets.QWidget): host_tools.show_creator() def on_publish_clicked(self): - host_tools.show_publish() + host_tools.show_publisher() def on_load_clicked(self): host_tools.show_loader(use_context=True) diff --git a/openpype/hosts/fusion/api/pipeline.py b/openpype/hosts/fusion/api/pipeline.py index c92d072ef7..b73759fee0 100644 --- a/openpype/hosts/fusion/api/pipeline.py +++ b/openpype/hosts/fusion/api/pipeline.py @@ -3,6 +3,7 @@ Basic avalon integration """ import os import logging +import contextlib import pyblish.api @@ -14,15 +15,14 @@ from openpype.pipeline import ( register_loader_plugin_path, register_creator_plugin_path, register_inventory_action_path, - deregister_loader_plugin_path, - deregister_creator_plugin_path, - deregister_inventory_action_path, AVALON_CONTAINER_ID, ) from openpype.pipeline.load import any_outdated_containers from openpype.hosts.fusion import FUSION_HOST_DIR +from openpype.host import HostBase, IWorkfileHost, ILoadHost, INewPublisher from openpype.tools.utils import host_tools + from .lib import ( get_current_comp, comp_lock_and_undo_chunk, @@ -47,71 +47,99 @@ class CompLogHandler(logging.Handler): comp.Print(entry) -def install(): - """Install fusion-specific functionality of OpenPype. +class FusionHost(HostBase, IWorkfileHost, ILoadHost, INewPublisher): + name = "fusion" - This is where you install menus and register families, data - and loaders into fusion. + def install(self): + """Install fusion-specific functionality of OpenPype. - It is called automatically when installing via - `openpype.pipeline.install_host(openpype.hosts.fusion.api)` + This is where you install menus and register families, data + and loaders into fusion. - See the Maya equivalent for inspiration on how to implement this. + It is called automatically when installing via + `openpype.pipeline.install_host(openpype.hosts.fusion.api)` - """ - # Remove all handlers associated with the root logger object, because - # that one always logs as "warnings" incorrectly. - for handler in logging.root.handlers[:]: - logging.root.removeHandler(handler) + See the Maya equivalent for inspiration on how to implement this. - # Attach default logging handler that prints to active comp - logger = logging.getLogger() - formatter = logging.Formatter(fmt="%(message)s\n") - handler = CompLogHandler() - handler.setFormatter(formatter) - logger.addHandler(handler) - logger.setLevel(logging.DEBUG) + """ + # Remove all handlers associated with the root logger object, because + # that one always logs as "warnings" incorrectly. + for handler in logging.root.handlers[:]: + logging.root.removeHandler(handler) - pyblish.api.register_host("fusion") - pyblish.api.register_plugin_path(PUBLISH_PATH) - log.info("Registering Fusion plug-ins..") + # Attach default logging handler that prints to active comp + logger = logging.getLogger() + formatter = logging.Formatter(fmt="%(message)s\n") + handler = CompLogHandler() + handler.setFormatter(formatter) + logger.addHandler(handler) + logger.setLevel(logging.DEBUG) - register_loader_plugin_path(LOAD_PATH) - register_creator_plugin_path(CREATE_PATH) - register_inventory_action_path(INVENTORY_PATH) + pyblish.api.register_host("fusion") + pyblish.api.register_plugin_path(PUBLISH_PATH) + log.info("Registering Fusion plug-ins..") - pyblish.api.register_callback( - "instanceToggled", on_pyblish_instance_toggled - ) + register_loader_plugin_path(LOAD_PATH) + register_creator_plugin_path(CREATE_PATH) + register_inventory_action_path(INVENTORY_PATH) - # Fusion integration currently does not attach to direct callbacks of - # the application. So we use workfile callbacks to allow similar behavior - # on save and open - register_event_callback("workfile.open.after", on_after_open) + pyblish.api.register_callback("instanceToggled", + on_pyblish_instance_toggled) + # Fusion integration currently does not attach to direct callbacks of + # the application. So we use workfile callbacks to allow similar + # behavior on save and open + register_event_callback("workfile.open.after", on_after_open) -def uninstall(): - """Uninstall all that was installed + # region workfile io api + def has_unsaved_changes(self): + comp = get_current_comp() + return comp.GetAttrs()["COMPB_Modified"] - This is where you undo everything that was done in `install()`. - That means, removing menus, deregistering families and data - and everything. It should be as though `install()` was never run, - because odds are calling this function means the user is interested - in re-installing shortly afterwards. If, for example, he has been - modifying the menu or registered families. + def get_workfile_extensions(self): + return [".comp"] - """ - pyblish.api.deregister_host("fusion") - pyblish.api.deregister_plugin_path(PUBLISH_PATH) - log.info("Deregistering Fusion plug-ins..") + def save_workfile(self, dst_path=None): + comp = get_current_comp() + comp.Save(dst_path) - deregister_loader_plugin_path(LOAD_PATH) - deregister_creator_plugin_path(CREATE_PATH) - deregister_inventory_action_path(INVENTORY_PATH) + def open_workfile(self, filepath): + # Hack to get fusion, see + # openpype.hosts.fusion.api.pipeline.get_current_comp() + fusion = getattr(sys.modules["__main__"], "fusion", None) - pyblish.api.deregister_callback( - "instanceToggled", on_pyblish_instance_toggled - ) + return fusion.LoadComp(filepath) + + def get_current_workfile(self): + comp = get_current_comp() + current_filepath = comp.GetAttrs()["COMPS_FileName"] + if not current_filepath: + return None + + return current_filepath + + def work_root(self, session): + work_dir = session["AVALON_WORKDIR"] + scene_dir = session.get("AVALON_SCENEDIR") + if scene_dir: + return os.path.join(work_dir, scene_dir) + else: + return work_dir + # endregion + + @contextlib.contextmanager + def maintained_selection(self): + from .lib import maintained_selection + return maintained_selection() + + def get_containers(self): + return ls() + + def update_context_data(self, data, changes): + print(data, changes) + + def get_context_data(self): + return {} def on_pyblish_instance_toggled(instance, old_value, new_value): @@ -254,3 +282,43 @@ def parse_container(tool): return container +def list_instances(creator_id=None): + """Return created instances in current workfile which will be published. + + Returns: + (list) of dictionaries matching instances format + """ + + comp = get_current_comp() + tools = comp.GetToolList(False, "Loader").values() + + instance_signature = { + "id": "pyblish.avalon.instance", + "identifier": creator_id + } + instances = [] + for tool in tools: + + data = tool.GetData('openpype') + if not isinstance(data, dict): + return + + if creator_id and data.get("identifier") != creator_id: + continue + + if data.get("id") != instance_signature["id"]: + continue + + instances.append(tool) + + return instances + + +def remove_instance(instance): + """Remove instance from current workfile. + + Args: + instance (dict): instance representation from subsetmanager model + """ + # Assume instance is a Fusion tool directly + instance.Delete() diff --git a/openpype/hosts/fusion/api/workio.py b/openpype/hosts/fusion/api/workio.py deleted file mode 100644 index 939b2ff4be..0000000000 --- a/openpype/hosts/fusion/api/workio.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Host API required Work Files tool""" -import sys -import os - -from .lib import get_current_comp - - -def file_extensions(): - return [".comp"] - - -def has_unsaved_changes(): - comp = get_current_comp() - return comp.GetAttrs()["COMPB_Modified"] - - -def save_file(filepath): - comp = get_current_comp() - comp.Save(filepath) - - -def open_file(filepath): - # Hack to get fusion, see - # openpype.hosts.fusion.api.pipeline.get_current_comp() - fusion = getattr(sys.modules["__main__"], "fusion", None) - - return fusion.LoadComp(filepath) - - -def current_file(): - comp = get_current_comp() - current_filepath = comp.GetAttrs()["COMPS_FileName"] - if not current_filepath: - return None - - return current_filepath - - -def work_root(session): - work_dir = session["AVALON_WORKDIR"] - scene_dir = session.get("AVALON_SCENEDIR") - if scene_dir: - return os.path.join(work_dir, scene_dir) - else: - return work_dir diff --git a/openpype/hosts/fusion/deploy/MenuScripts/openpype_menu.py b/openpype/hosts/fusion/deploy/MenuScripts/openpype_menu.py index 2918c552c8..685e58d58f 100644 --- a/openpype/hosts/fusion/deploy/MenuScripts/openpype_menu.py +++ b/openpype/hosts/fusion/deploy/MenuScripts/openpype_menu.py @@ -13,11 +13,11 @@ def main(env): # However the contents of that folder can conflict with Qt library dlls # so we make sure to move out of it to avoid DLL Load Failed errors. os.chdir("..") - from openpype.hosts.fusion import api + from openpype.hosts.fusion.api import FusionHost from openpype.hosts.fusion.api import menu # activate resolve from pype - install_host(api) + install_host(FusionHost()) log = Logger.get_logger(__name__) log.info(f"Registered host: {registered_host()}") diff --git a/openpype/hosts/fusion/plugins/create/create_exr_saver.py b/openpype/hosts/fusion/plugins/create/create_exr_saver.py index 6d93fe710a..74cd1cbea5 100644 --- a/openpype/hosts/fusion/plugins/create/create_exr_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_exr_saver.py @@ -1,24 +1,29 @@ import os -from openpype.pipeline import ( - LegacyCreator, - legacy_io -) from openpype.hosts.fusion.api import ( get_current_comp, - comp_lock_and_undo_chunk + comp_lock_and_undo_chunk, + remove_instance, + list_instances +) + +from openpype.pipeline import ( + legacy_io, + Creator, + CreatedInstance ) -class CreateOpenEXRSaver(LegacyCreator): - +class CreateOpenEXRSaver(Creator): + identifier = "io.openpype.creators.fusion.saver" name = "openexrDefault" label = "Create OpenEXR Saver" - hosts = ["fusion"] family = "render" - defaults = ["Main"] + default_variants = ["Main"] - def process(self): + selected_nodes = [] + + def create(self, subset_name, instance_data, pre_create_data): file_format = "OpenEXRFormat" @@ -26,13 +31,13 @@ class CreateOpenEXRSaver(LegacyCreator): workdir = os.path.normpath(legacy_io.Session["AVALON_WORKDIR"]) - filename = "{}..exr".format(self.name) + filename = "{}..exr".format(subset_name) filepath = os.path.join(workdir, "render", filename) with comp_lock_and_undo_chunk(comp): args = (-32768, -32768) # Magical position numbers saver = comp.AddTool("Saver", *args) - saver.SetAttrs({"TOOLS_Name": self.name}) + saver.SetAttrs({"TOOLS_Name": subset_name}) # Setting input attributes is different from basic attributes # Not confused with "MainInputAttributes" which @@ -47,3 +52,18 @@ class CreateOpenEXRSaver(LegacyCreator): # Set file format attributes saver[file_format]["Depth"] = 1 # int8 | int16 | float32 | other saver[file_format]["SaveAlpha"] = 0 + + def collect_instances(self): + for instance in list_instances(creator_id=self.identifier): + created_instance = CreatedInstance.from_existing(instance, self) + self._add_instance_to_context(created_instance) + + def update_instances(self, update_list): + print(update_list) + + def remove_instances(self, instances): + for instance in instances: + remove_instance(instance) + + def get_pre_create_attr_defs(self): + return [] \ No newline at end of file From 13cb1f4bc0756eae2cf1cf7d07726e137a03a37f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 20 Sep 2022 23:37:32 +0200 Subject: [PATCH 0002/1271] Ensure newline --- openpype/hosts/fusion/plugins/create/create_exr_saver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/create/create_exr_saver.py b/openpype/hosts/fusion/plugins/create/create_exr_saver.py index 74cd1cbea5..4809832cd0 100644 --- a/openpype/hosts/fusion/plugins/create/create_exr_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_exr_saver.py @@ -66,4 +66,4 @@ class CreateOpenEXRSaver(Creator): remove_instance(instance) def get_pre_create_attr_defs(self): - return [] \ No newline at end of file + return [] From 218c836026fcb99dd157fa56e6dfabc8bf3a8271 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 09:12:41 +0200 Subject: [PATCH 0003/1271] Reorder logic - makes more sense to check id first --- openpype/hosts/fusion/api/pipeline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/api/pipeline.py b/openpype/hosts/fusion/api/pipeline.py index b73759fee0..f5900a2dde 100644 --- a/openpype/hosts/fusion/api/pipeline.py +++ b/openpype/hosts/fusion/api/pipeline.py @@ -303,10 +303,10 @@ def list_instances(creator_id=None): if not isinstance(data, dict): return - if creator_id and data.get("identifier") != creator_id: + if data.get("id") != instance_signature["id"]: continue - if data.get("id") != instance_signature["id"]: + if creator_id and data.get("identifier") != creator_id: continue instances.append(tool) From 1cf86a68f7b1961118f127cadc068e6b37e62d77 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 14:19:56 +0200 Subject: [PATCH 0004/1271] Remove creator in menu in favor of new publisher --- openpype/hosts/fusion/api/menu.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/openpype/hosts/fusion/api/menu.py b/openpype/hosts/fusion/api/menu.py index 4e415cafba..ec8747298c 100644 --- a/openpype/hosts/fusion/api/menu.py +++ b/openpype/hosts/fusion/api/menu.py @@ -52,7 +52,6 @@ class OpenPypeMenu(QtWidgets.QWidget): asset_label.setAlignment(QtCore.Qt.AlignHCenter) workfiles_btn = QtWidgets.QPushButton("Workfiles...", self) - create_btn = QtWidgets.QPushButton("Create...", self) publish_btn = QtWidgets.QPushButton("Publish...", self) load_btn = QtWidgets.QPushButton("Load...", self) manager_btn = QtWidgets.QPushButton("Manage...", self) @@ -75,7 +74,6 @@ class OpenPypeMenu(QtWidgets.QWidget): layout.addSpacing(20) - layout.addWidget(create_btn) layout.addWidget(load_btn) layout.addWidget(publish_btn) layout.addWidget(manager_btn) @@ -100,7 +98,6 @@ class OpenPypeMenu(QtWidgets.QWidget): self.asset_label = asset_label workfiles_btn.clicked.connect(self.on_workfile_clicked) - create_btn.clicked.connect(self.on_create_clicked) publish_btn.clicked.connect(self.on_publish_clicked) load_btn.clicked.connect(self.on_load_clicked) manager_btn.clicked.connect(self.on_manager_clicked) @@ -140,9 +137,6 @@ class OpenPypeMenu(QtWidgets.QWidget): def on_workfile_clicked(self): host_tools.show_workfiles() - def on_create_clicked(self): - host_tools.show_creator() - def on_publish_clicked(self): host_tools.show_publisher() From ae5c565ab61609a3affcecc7b398ee9d0a6a874f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 14:20:50 +0200 Subject: [PATCH 0005/1271] Fix logic - add comments that these will remain unused however --- openpype/hosts/fusion/api/pipeline.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/fusion/api/pipeline.py b/openpype/hosts/fusion/api/pipeline.py index f5900a2dde..1587381b1a 100644 --- a/openpype/hosts/fusion/api/pipeline.py +++ b/openpype/hosts/fusion/api/pipeline.py @@ -282,6 +282,7 @@ def parse_container(tool): return container +# TODO: Function below is currently unused prototypes def list_instances(creator_id=None): """Return created instances in current workfile which will be published. @@ -290,7 +291,7 @@ def list_instances(creator_id=None): """ comp = get_current_comp() - tools = comp.GetToolList(False, "Loader").values() + tools = comp.GetToolList(False).values() instance_signature = { "id": "pyblish.avalon.instance", @@ -301,7 +302,7 @@ def list_instances(creator_id=None): data = tool.GetData('openpype') if not isinstance(data, dict): - return + continue if data.get("id") != instance_signature["id"]: continue @@ -314,6 +315,7 @@ def list_instances(creator_id=None): return instances +# TODO: Function below is currently unused prototypes def remove_instance(instance): """Remove instance from current workfile. @@ -321,4 +323,4 @@ def remove_instance(instance): instance (dict): instance representation from subsetmanager model """ # Assume instance is a Fusion tool directly - instance.Delete() + instance["tool"].Delete() From d62e1eef82bbb84902a96d1d48fa26538f9a2f81 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 14:21:28 +0200 Subject: [PATCH 0006/1271] Continue refactor to new publisher --- .../fusion/plugins/create/create_exr_saver.py | 74 ++++++++++++++++++- .../plugins/publish/collect_instances.py | 48 ++++-------- 2 files changed, 86 insertions(+), 36 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_exr_saver.py b/openpype/hosts/fusion/plugins/create/create_exr_saver.py index 4809832cd0..c2adb48bac 100644 --- a/openpype/hosts/fusion/plugins/create/create_exr_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_exr_saver.py @@ -1,5 +1,7 @@ import os +import qtawesome + from openpype.hosts.fusion.api import ( get_current_comp, comp_lock_and_undo_chunk, @@ -19,7 +21,9 @@ class CreateOpenEXRSaver(Creator): name = "openexrDefault" label = "Create OpenEXR Saver" family = "render" - default_variants = ["Main"] + default_variants = ["Main"] + + description = "Fusion Saver to generate EXR image sequence" selected_nodes = [] @@ -27,6 +31,10 @@ class CreateOpenEXRSaver(Creator): file_format = "OpenEXRFormat" + print(subset_name) + print(instance_data) + print(pre_create_data) + comp = get_current_comp() workdir = os.path.normpath(legacy_io.Session["AVALON_WORKDIR"]) @@ -53,17 +61,79 @@ class CreateOpenEXRSaver(Creator): saver[file_format]["Depth"] = 1 # int8 | int16 | float32 | other saver[file_format]["SaveAlpha"] = 0 + # Save all data in a "openpype.{key}" = value data + for key, value in instance_data.items(): + saver.SetData("openpype.{}".format(key), value) + def collect_instances(self): - for instance in list_instances(creator_id=self.identifier): + + comp = get_current_comp() + tools = comp.GetToolList(False, "Saver").values() + + # Allow regular non-managed savers to also be picked up + project = legacy_io.Session["AVALON_PROJECT"] + asset = legacy_io.Session["AVALON_ASSET"] + task = legacy_io.Session["AVALON_TASK"] + + for tool in tools: + + path = tool["Clip"][comp.TIME_UNDEFINED] + fname = os.path.basename(path) + fname, _ext = os.path.splitext(fname) + subset = fname.rstrip(".") + + attrs = tool.GetAttrs() + passthrough = attrs["TOOLB_PassThrough"] + variant = subset[len("render"):] + + # TODO: this should not be done this way - this should actually + # get the data as stored on the tool explicitly (however) + # that would disallow any 'regular saver' to be collected + # unless the instance data is stored on it to begin with + instance = { + # Required data + "project": project, + "asset": asset, + "subset": subset, + "task": task, + "variant": variant, + "active": not passthrough, + "family": self.family, + + # Fusion data + "tool_name": tool.Name + } + + # Use the explicit data on the saver (if any) + data = tool.GetData("openpype") + if data: + instance.update(data) + + # Add instance created_instance = CreatedInstance.from_existing(instance, self) + + # TODO: move this to lifetime data or alike + # (Doing this before CreatedInstance.from_existing wouldn't + # work because `tool` isn't JSON serializable) + created_instance["tool"] = tool + self._add_instance_to_context(created_instance) + def get_icon(self): + return qtawesome.icon("fa.eye", color="white") + def update_instances(self, update_list): + # TODO: Not sure what to do here? print(update_list) def remove_instances(self, instances): for instance in instances: + + # Remove the tool from the scene remove_instance(instance) + # Remove the collected CreatedInstance to remove from UI directly + self._remove_instance_from_context(instance) + def get_pre_create_attr_defs(self): return [] diff --git a/openpype/hosts/fusion/plugins/publish/collect_instances.py b/openpype/hosts/fusion/plugins/publish/collect_instances.py index fe60b83827..e42e7b5f70 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_instances.py +++ b/openpype/hosts/fusion/plugins/publish/collect_instances.py @@ -30,7 +30,7 @@ class CollectInstances(pyblish.api.ContextPlugin): """ order = pyblish.api.CollectorOrder - label = "Collect Instances" + label = "Collect Instances Data" hosts = ["fusion"] def process(self, context): @@ -39,67 +39,47 @@ class CollectInstances(pyblish.api.ContextPlugin): from openpype.hosts.fusion.api.lib import get_frame_path comp = context.data["currentComp"] - - # Get all savers in the comp - tools = comp.GetToolList(False).values() - savers = [tool for tool in tools if tool.ID == "Saver"] - start, end, global_start, global_end = get_comp_render_range(comp) context.data["frameStart"] = int(start) context.data["frameEnd"] = int(end) context.data["frameStartHandle"] = int(global_start) context.data["frameEndHandle"] = int(global_end) - for tool in savers: + # Comp tools by name + tools = {tool.Name: tool for tool in comp.GetToolList(False).values()} + + for instance in context: + + tool_name = instance.data["tool_name"] + tool = tools[tool_name] + path = tool["Clip"][comp.TIME_UNDEFINED] - - tool_attrs = tool.GetAttrs() - active = not tool_attrs["TOOLB_PassThrough"] - - if not path: - self.log.warning("Skipping saver because it " - "has no path set: {}".format(tool.Name)) - continue - filename = os.path.basename(path) head, padding, tail = get_frame_path(filename) ext = os.path.splitext(path)[1] assert tail == ext, ("Tail does not match %s" % ext) - subset = head.rstrip("_. ") # subset is head of the filename # Include start and end render frame in label + subset = instance.data["subset"] label = "{subset} ({start}-{end})".format(subset=subset, start=int(start), end=int(end)) - - instance = context.create_instance(subset) instance.data.update({ - "asset": os.environ["AVALON_ASSET"], # todo: not a constant - "subset": subset, "path": path, "outputDir": os.path.dirname(path), - "ext": ext, # todo: should be redundant + "ext": ext, # todo: should be redundant? "label": label, + # todo: Allow custom frame range per instance "frameStart": context.data["frameStart"], "frameEnd": context.data["frameEnd"], "frameStartHandle": context.data["frameStartHandle"], "frameEndHandle": context.data["frameStartHandle"], "fps": context.data["fps"], "families": ["render", "review"], - "family": "render", - "active": active, - "publish": active # backwards compatibility + "family": "render" }) + # Add tool itself as member instance.append(tool) self.log.info("Found: \"%s\" " % path) - - # Sort/grouped by family (preserving local index) - context[:] = sorted(context, key=self.sort_by_family) - - return context - - def sort_by_family(self, instance): - """Sort by family""" - return instance.data.get("families", instance.data.get("family")) From 33cf1c3089cfa91601e9a10afc8df926ef5db95a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 19:57:45 +0200 Subject: [PATCH 0007/1271] Cleanup and fixes --- .../fusion/plugins/create/create_exr_saver.py | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_exr_saver.py b/openpype/hosts/fusion/plugins/create/create_exr_saver.py index c2adb48bac..e0366c6532 100644 --- a/openpype/hosts/fusion/plugins/create/create_exr_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_exr_saver.py @@ -4,9 +4,7 @@ import qtawesome from openpype.hosts.fusion.api import ( get_current_comp, - comp_lock_and_undo_chunk, - remove_instance, - list_instances + comp_lock_and_undo_chunk ) from openpype.pipeline import ( @@ -16,25 +14,20 @@ from openpype.pipeline import ( ) -class CreateOpenEXRSaver(Creator): +class CreateSaver(Creator): identifier = "io.openpype.creators.fusion.saver" - name = "openexrDefault" - label = "Create OpenEXR Saver" + name = "saver" + label = "Create Saver" family = "render" default_variants = ["Main"] - description = "Fusion Saver to generate EXR image sequence" - - selected_nodes = [] + description = "Fusion Saver to generate image sequence" def create(self, subset_name, instance_data, pre_create_data): + # TODO: Add pre_create attributes to choose file format? file_format = "OpenEXRFormat" - print(subset_name) - print(instance_data) - print(pre_create_data) - comp = get_current_comp() workdir = os.path.normpath(legacy_io.Session["AVALON_WORKDIR"]) @@ -61,9 +54,7 @@ class CreateOpenEXRSaver(Creator): saver[file_format]["Depth"] = 1 # int8 | int16 | float32 | other saver[file_format]["SaveAlpha"] = 0 - # Save all data in a "openpype.{key}" = value data - for key, value in instance_data.items(): - saver.SetData("openpype.{}".format(key), value) + self._imprint(saver, instance_data) def collect_instances(self): @@ -112,28 +103,42 @@ class CreateOpenEXRSaver(Creator): # Add instance created_instance = CreatedInstance.from_existing(instance, self) - # TODO: move this to lifetime data or alike - # (Doing this before CreatedInstance.from_existing wouldn't - # work because `tool` isn't JSON serializable) - created_instance["tool"] = tool - self._add_instance_to_context(created_instance) def get_icon(self): return qtawesome.icon("fa.eye", color="white") def update_instances(self, update_list): - # TODO: Not sure what to do here? - print(update_list) + for update in update_list: + instance = update.instance + changes = update.changes + tool = self._get_instance_tool(instance) + self._imprint(tool, changes) def remove_instances(self, instances): for instance in instances: - # Remove the tool from the scene - remove_instance(instance) + tool = self._get_instance_tool(instance) + if tool: + tool.Delete() # Remove the collected CreatedInstance to remove from UI directly self._remove_instance_from_context(instance) - def get_pre_create_attr_defs(self): - return [] + def _imprint(self, tool, data): + + # Save all data in a "openpype.{key}" = value data + for key, value in data.items(): + tool.SetData("openpype.{}".format(key), value) + + def _get_instance_tool(self, instance): + # finds tool name of instance in currently active comp + # TODO: assign `tool` as some sort of lifetime data or alike so that + # the actual tool can be retrieved in current session. We can't store + # it in the instance itself since instance needs to be serializable + comp = get_current_comp() + tool_name = instance["tool_name"] + print(tool_name) + return { + tool.Name: tool for tool in comp.GetToolList(False).values() + }.get(tool_name) From 450a471c42a13d670eaf96a4323c92629c0b20a5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 19:58:42 +0200 Subject: [PATCH 0008/1271] Rename `create_exr_saver.py` -> `create_saver.py` --- .../plugins/create/{create_exr_saver.py => create_saver.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename openpype/hosts/fusion/plugins/create/{create_exr_saver.py => create_saver.py} (100%) diff --git a/openpype/hosts/fusion/plugins/create/create_exr_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py similarity index 100% rename from openpype/hosts/fusion/plugins/create/create_exr_saver.py rename to openpype/hosts/fusion/plugins/create/create_saver.py From 7a046dd446bef43606317df47c487b7809701a81 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 20:02:58 +0200 Subject: [PATCH 0009/1271] Register the saver directly on create --- openpype/hosts/fusion/plugins/create/create_saver.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index e0366c6532..a0ab1c1fcf 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -56,6 +56,16 @@ class CreateSaver(Creator): self._imprint(saver, instance_data) + # Register the CreatedInstance + instance = CreatedInstance( + family=self.family, + subset_name=subset_name, + instance_data=instance_data, + creator=self) + self._add_instance_to_context(instance) + + return instance + def collect_instances(self): comp = get_current_comp() From cd0825756e753de983c651413affdcaad110120a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 20:04:17 +0200 Subject: [PATCH 0010/1271] Fix keyword --- openpype/hosts/fusion/plugins/create/create_saver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index a0ab1c1fcf..c2c9ad1cb7 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -60,7 +60,7 @@ class CreateSaver(Creator): instance = CreatedInstance( family=self.family, subset_name=subset_name, - instance_data=instance_data, + data=instance_data, creator=self) self._add_instance_to_context(instance) From 01167e84caf8e1e047ecffc93d7350b34d8ada46 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 23:11:05 +0200 Subject: [PATCH 0011/1271] Barebones refactor of validators to raise PublishValidationError --- .../publish/validate_background_depth.py | 7 +++- .../plugins/publish/validate_comp_saved.py | 7 +++- .../publish/validate_create_folder_checked.py | 6 ++- .../validate_filename_has_extension.py | 4 +- .../publish/validate_saver_has_input.py | 8 +++- .../publish/validate_saver_passthrough.py | 7 ++-- .../publish/validate_unique_subsets.py | 41 ++++++++++++++----- 7 files changed, 57 insertions(+), 23 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/validate_background_depth.py b/openpype/hosts/fusion/plugins/publish/validate_background_depth.py index 4268fab528..f057989535 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_background_depth.py +++ b/openpype/hosts/fusion/plugins/publish/validate_background_depth.py @@ -1,6 +1,7 @@ import pyblish.api from openpype.pipeline.publish import RepairAction +from openpype.pipeline import PublishValidationError class ValidateBackgroundDepth(pyblish.api.InstancePlugin): @@ -29,8 +30,10 @@ class ValidateBackgroundDepth(pyblish.api.InstancePlugin): def process(self, instance): invalid = self.get_invalid(instance) if invalid: - raise RuntimeError("Found %i nodes which are not set to float32" - % len(invalid)) + raise PublishValidationError( + "Found {} Backgrounds tools which" + " are not set to float32".format(len(invalid)), + title=self.label) @classmethod def repair(cls, instance): diff --git a/openpype/hosts/fusion/plugins/publish/validate_comp_saved.py b/openpype/hosts/fusion/plugins/publish/validate_comp_saved.py index cabe65af6e..748047e8cf 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_comp_saved.py +++ b/openpype/hosts/fusion/plugins/publish/validate_comp_saved.py @@ -1,6 +1,7 @@ import os import pyblish.api +from openpype.pipeline import PublishValidationError class ValidateFusionCompSaved(pyblish.api.ContextPlugin): @@ -19,10 +20,12 @@ class ValidateFusionCompSaved(pyblish.api.ContextPlugin): filename = attrs["COMPS_FileName"] if not filename: - raise RuntimeError("Comp is not saved.") + raise PublishValidationError("Comp is not saved.", + title=self.label) if not os.path.exists(filename): - raise RuntimeError("Comp file does not exist: %s" % filename) + raise PublishValidationError( + "Comp file does not exist: %s" % filename, title=self.label) if attrs["COMPB_Modified"]: self.log.warning("Comp is modified. Save your comp to ensure your " diff --git a/openpype/hosts/fusion/plugins/publish/validate_create_folder_checked.py b/openpype/hosts/fusion/plugins/publish/validate_create_folder_checked.py index f6beefefc1..3674b33644 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_create_folder_checked.py +++ b/openpype/hosts/fusion/plugins/publish/validate_create_folder_checked.py @@ -1,6 +1,7 @@ import pyblish.api from openpype.pipeline.publish import RepairAction +from openpype.pipeline import PublishValidationError class ValidateCreateFolderChecked(pyblish.api.InstancePlugin): @@ -31,8 +32,9 @@ class ValidateCreateFolderChecked(pyblish.api.InstancePlugin): def process(self, instance): invalid = self.get_invalid(instance) if invalid: - raise RuntimeError("Found Saver with Create Folder During " - "Render checked off") + raise PublishValidationError( + "Found Saver with Create Folder During Render checked off", + title=self.label) @classmethod def repair(cls, instance): diff --git a/openpype/hosts/fusion/plugins/publish/validate_filename_has_extension.py b/openpype/hosts/fusion/plugins/publish/validate_filename_has_extension.py index 4795a2aa05..22f1db809c 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_filename_has_extension.py +++ b/openpype/hosts/fusion/plugins/publish/validate_filename_has_extension.py @@ -1,6 +1,7 @@ import os import pyblish.api +from openpype.pipeline import PublishValidationError class ValidateFilenameHasExtension(pyblish.api.InstancePlugin): @@ -20,7 +21,8 @@ class ValidateFilenameHasExtension(pyblish.api.InstancePlugin): def process(self, instance): invalid = self.get_invalid(instance) if invalid: - raise RuntimeError("Found Saver without an extension") + raise PublishValidationError("Found Saver without an extension", + title=self.label) @classmethod def get_invalid(cls, instance): diff --git a/openpype/hosts/fusion/plugins/publish/validate_saver_has_input.py b/openpype/hosts/fusion/plugins/publish/validate_saver_has_input.py index 7243b44a3e..8d961525f0 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_saver_has_input.py +++ b/openpype/hosts/fusion/plugins/publish/validate_saver_has_input.py @@ -1,4 +1,5 @@ import pyblish.api +from openpype.pipeline import PublishValidationError class ValidateSaverHasInput(pyblish.api.InstancePlugin): @@ -25,5 +26,8 @@ class ValidateSaverHasInput(pyblish.api.InstancePlugin): def process(self, instance): invalid = self.get_invalid(instance) if invalid: - raise RuntimeError("Saver has no incoming connection: " - "{} ({})".format(instance, invalid[0].Name)) + saver_name = invalid[0].Name + raise PublishValidationError( + "Saver has no incoming connection: {} ({})".format(instance, + saver_name), + title=self.label) diff --git a/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py b/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py index aed3835de3..c191d6669c 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py +++ b/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py @@ -1,4 +1,5 @@ import pyblish.api +from openpype.pipeline import PublishValidationError class ValidateSaverPassthrough(pyblish.api.ContextPlugin): @@ -27,8 +28,8 @@ class ValidateSaverPassthrough(pyblish.api.ContextPlugin): if invalid_instances: self.log.info("Reset pyblish to collect your current scene state, " "that should fix error.") - raise RuntimeError("Invalid instances: " - "{0}".format(invalid_instances)) + raise PublishValidationError( + "Invalid instances: {0}".format(invalid_instances)) def is_invalid(self, instance): @@ -36,7 +37,7 @@ class ValidateSaverPassthrough(pyblish.api.ContextPlugin): attr = saver.GetAttrs() active = not attr["TOOLB_PassThrough"] - if active != instance.data["publish"]: + if active != instance.data.get("publish", True): self.log.info("Saver has different passthrough state than " "Pyblish: {} ({})".format(instance, saver.Name)) return [saver] diff --git a/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py b/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py index b218a311ba..b78f185a3a 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py +++ b/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py @@ -1,7 +1,10 @@ +from collections import defaultdict + import pyblish.api +from openpype.pipeline import PublishValidationError -class ValidateUniqueSubsets(pyblish.api.InstancePlugin): +class ValidateUniqueSubsets(pyblish.api.ContextPlugin): """Ensure all instances have a unique subset name""" order = pyblish.api.ValidatorOrder @@ -10,20 +13,36 @@ class ValidateUniqueSubsets(pyblish.api.InstancePlugin): hosts = ["fusion"] @classmethod - def get_invalid(cls, instance): + def get_invalid(cls, context): - context = instance.context - subset = instance.data["subset"] - for other_instance in context: - if other_instance == instance: - continue + # Collect instances per subset per asset + instances_per_subset_asset = defaultdict(lambda: defaultdict(list)) + for instance in context: + asset = instance.data.get("asset", context.data.get("asset")) + subset = instance.data.get("subset", context.data.get("subset")) + instances_per_subset_asset[asset][subset].append(instance) - if other_instance.data["subset"] == subset: - return [instance] # current instance is invalid + # Find which asset + subset combination has more than one instance + # Those are considered invalid because they'd integrate to the same + # destination. + invalid = [] + for asset, instances_per_subset in instances_per_subset_asset.items(): + for subset, instances in instances_per_subset.items(): + if len(instances) > 1: + cls.log.warning( + "{asset} > {subset} used by more than " + "one instance: {instances}".format( + asset=asset, + subset=subset, + instances=instances + )) + invalid.extend(instances) - return [] + return invalid def process(self, instance): invalid = self.get_invalid(instance) if invalid: - raise RuntimeError("Animation content is invalid. See log.") + raise PublishValidationError("Multiple instances are set to " + "the same asset > subset.", + title=self.label) From ce954651182b2f4c526f981fcd5c086989f23b36 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 23:17:31 +0200 Subject: [PATCH 0012/1271] Fix bugs in Creator --- .../fusion/plugins/create/create_saver.py | 154 +++++++++++------- 1 file changed, 99 insertions(+), 55 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index c2c9ad1cb7..347aaaf497 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -29,20 +29,12 @@ class CreateSaver(Creator): file_format = "OpenEXRFormat" comp = get_current_comp() - - workdir = os.path.normpath(legacy_io.Session["AVALON_WORKDIR"]) - - filename = "{}..exr".format(subset_name) - filepath = os.path.join(workdir, "render", filename) - with comp_lock_and_undo_chunk(comp): args = (-32768, -32768) # Magical position numbers saver = comp.AddTool("Saver", *args) - saver.SetAttrs({"TOOLS_Name": subset_name}) - # Setting input attributes is different from basic attributes - # Not confused with "MainInputAttributes" which - saver["Clip"] = filepath + self._update_tool_with_data(saver, data=instance_data) + saver["OutputFormat"] = file_format # Check file format settings are available @@ -54,6 +46,9 @@ class CreateSaver(Creator): saver[file_format]["Depth"] = 1 # int8 | int16 | float32 | other saver[file_format]["SaveAlpha"] = 0 + # Fusion data for the instance data + instance_data["tool_name"] = saver.Name + self._imprint(saver, instance_data) # Register the CreatedInstance @@ -70,49 +65,17 @@ class CreateSaver(Creator): comp = get_current_comp() tools = comp.GetToolList(False, "Saver").values() - - # Allow regular non-managed savers to also be picked up - project = legacy_io.Session["AVALON_PROJECT"] - asset = legacy_io.Session["AVALON_ASSET"] - task = legacy_io.Session["AVALON_TASK"] - for tool in tools: - path = tool["Clip"][comp.TIME_UNDEFINED] - fname = os.path.basename(path) - fname, _ext = os.path.splitext(fname) - subset = fname.rstrip(".") + data = self.get_managed_tool_data(tool) + if not data: + data = self._collect_unmanaged_saver(tool) - attrs = tool.GetAttrs() - passthrough = attrs["TOOLB_PassThrough"] - variant = subset[len("render"):] - - # TODO: this should not be done this way - this should actually - # get the data as stored on the tool explicitly (however) - # that would disallow any 'regular saver' to be collected - # unless the instance data is stored on it to begin with - instance = { - # Required data - "project": project, - "asset": asset, - "subset": subset, - "task": task, - "variant": variant, - "active": not passthrough, - "family": self.family, - - # Fusion data - "tool_name": tool.Name - } - - # Use the explicit data on the saver (if any) - data = tool.GetData("openpype") - if data: - instance.update(data) + # Collect non-stored data + data["tool_name"] = tool.Name # Add instance - created_instance = CreatedInstance.from_existing(instance, self) - + created_instance = CreatedInstance.from_existing(data, self) self._add_instance_to_context(created_instance) def get_icon(self): @@ -121,9 +84,18 @@ class CreateSaver(Creator): def update_instances(self, update_list): for update in update_list: instance = update.instance - changes = update.changes + + # Get the new values after the changes by key, ignore old value + new_data = { + key: new for key, (_old, new) in update.changes.items() + } + tool = self._get_instance_tool(instance) - self._imprint(tool, changes) + self._update_tool_with_data(tool, new_data) + self._imprint(tool, new_data) + + # Ensure tool name is up-to-date + instance["tool_name"] = tool.Name def remove_instances(self, instances): for instance in instances: @@ -136,19 +108,91 @@ class CreateSaver(Creator): self._remove_instance_from_context(instance) def _imprint(self, tool, data): - # Save all data in a "openpype.{key}" = value data for key, value in data.items(): tool.SetData("openpype.{}".format(key), value) def _get_instance_tool(self, instance): # finds tool name of instance in currently active comp - # TODO: assign `tool` as some sort of lifetime data or alike so that - # the actual tool can be retrieved in current session. We can't store - # it in the instance itself since instance needs to be serializable + # TODO: assign `tool` as 'lifetime' data instead of name so the + # tool can be retrieved in current session. We can't store currently + # in the CreatedInstance data because it needs to be serializable comp = get_current_comp() tool_name = instance["tool_name"] - print(tool_name) return { tool.Name: tool for tool in comp.GetToolList(False).values() }.get(tool_name) + + def _update_tool_with_data(self, tool, data): + """Update tool node name and output path based on subset data""" + if "subset" not in data: + return + + original_subset = tool.GetData("openpype.subset") + subset = data["subset"] + if original_subset != subset: + # Subset change detected + # Update output filepath + workdir = os.path.normpath(legacy_io.Session["AVALON_WORKDIR"]) + filename = "{}..exr".format(subset) + filepath = os.path.join(workdir, "render", subset, filename) + tool["Clip"] = filepath + + # Rename tool + if tool.Name != subset: + print(f"Renaming {tool.Name} -> {subset}") + tool.SetAttrs({"TOOLS_Name": subset}) + + def _collect_unmanaged_saver(self, tool): + + # TODO: this should not be done this way - this should actually + # get the data as stored on the tool explicitly (however) + # that would disallow any 'regular saver' to be collected + # unless the instance data is stored on it to begin with + + print("Collecting unmanaged saver..") + comp = tool.Comp() + + # Allow regular non-managed savers to also be picked up + project = legacy_io.Session["AVALON_PROJECT"] + asset = legacy_io.Session["AVALON_ASSET"] + task = legacy_io.Session["AVALON_TASK"] + + path = tool["Clip"][comp.TIME_UNDEFINED] + fname = os.path.basename(path) + fname, _ext = os.path.splitext(fname) + subset = fname.rstrip(".") + + attrs = tool.GetAttrs() + passthrough = attrs["TOOLB_PassThrough"] + variant = subset[len("render"):] + return { + # Required data + "project": project, + "asset": asset, + "subset": subset, + "task": task, + "variant": variant, + "active": not passthrough, + "family": self.family, + + # Unique identifier for instance and this creator + "id": "pyblish.avalon.instance", + "creator_identifier": self.identifier + } + + def get_managed_tool_data(self, tool): + """Return data of the tool if it matches creator identifier""" + data = tool.GetData('openpype') + if not isinstance(data, dict): + return + + required = { + "id": "pyblish.avalon.instance", + "creator_identifier": self.identifier + } + for key, value in required.items(): + if key not in data or data[key] != value: + return + + return data From af8662c87525012a8c7fd9915dcc1f2ce725c967 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 23:24:28 +0200 Subject: [PATCH 0013/1271] Fix refactor to ContextPlugin --- .../hosts/fusion/plugins/publish/validate_unique_subsets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py b/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py index b78f185a3a..5f0f93f764 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py +++ b/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py @@ -40,8 +40,8 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin): return invalid - def process(self, instance): - invalid = self.get_invalid(instance) + def process(self, context): + invalid = self.get_invalid(context) if invalid: raise PublishValidationError("Multiple instances are set to " "the same asset > subset.", From fe857b84429cda78235084d16c32ba3e58701aba Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 23:26:03 +0200 Subject: [PATCH 0014/1271] Add title to error --- .../hosts/fusion/plugins/publish/validate_saver_passthrough.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py b/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py index c191d6669c..bbafd8949e 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py +++ b/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py @@ -29,7 +29,8 @@ class ValidateSaverPassthrough(pyblish.api.ContextPlugin): self.log.info("Reset pyblish to collect your current scene state, " "that should fix error.") raise PublishValidationError( - "Invalid instances: {0}".format(invalid_instances)) + "Invalid instances: {0}".format(invalid_instances), + title=self.label) def is_invalid(self, instance): From 6abfabed4057292419059fa193bf5ffcbc8b9d21 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 21 Sep 2022 23:33:13 +0200 Subject: [PATCH 0015/1271] Fix missing import --- openpype/hosts/fusion/api/pipeline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/fusion/api/pipeline.py b/openpype/hosts/fusion/api/pipeline.py index 1587381b1a..6fc3902949 100644 --- a/openpype/hosts/fusion/api/pipeline.py +++ b/openpype/hosts/fusion/api/pipeline.py @@ -2,6 +2,7 @@ Basic avalon integration """ import os +import sys import logging import contextlib From bdfe2414583480c06f0ee35113b0deea3fced1c6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 23 Sep 2022 17:31:35 +0200 Subject: [PATCH 0016/1271] Refactor new publish logic to make use of "transientData" on the Creator --- .../fusion/plugins/create/create_saver.py | 33 +++++++------------ .../plugins/publish/collect_instances.py | 11 +++---- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index 347aaaf497..b3a912c56a 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -46,9 +46,6 @@ class CreateSaver(Creator): saver[file_format]["Depth"] = 1 # int8 | int16 | float32 | other saver[file_format]["SaveAlpha"] = 0 - # Fusion data for the instance data - instance_data["tool_name"] = saver.Name - self._imprint(saver, instance_data) # Register the CreatedInstance @@ -57,6 +54,10 @@ class CreateSaver(Creator): subset_name=subset_name, data=instance_data, creator=self) + + # Insert the transient data + instance.transient_data["tool"] = saver + self._add_instance_to_context(instance) return instance @@ -71,11 +72,12 @@ class CreateSaver(Creator): if not data: data = self._collect_unmanaged_saver(tool) - # Collect non-stored data - data["tool_name"] = tool.Name - # Add instance created_instance = CreatedInstance.from_existing(data, self) + + # Collect transient data + created_instance.transient_data["tool"] = tool + self._add_instance_to_context(created_instance) def get_icon(self): @@ -90,17 +92,15 @@ class CreateSaver(Creator): key: new for key, (_old, new) in update.changes.items() } - tool = self._get_instance_tool(instance) + tool = instance.transient_data["tool"] self._update_tool_with_data(tool, new_data) self._imprint(tool, new_data) - # Ensure tool name is up-to-date - instance["tool_name"] = tool.Name - def remove_instances(self, instances): for instance in instances: # Remove the tool from the scene - tool = self._get_instance_tool(instance) + + tool = instance.transient_data["tool"] if tool: tool.Delete() @@ -112,17 +112,6 @@ class CreateSaver(Creator): for key, value in data.items(): tool.SetData("openpype.{}".format(key), value) - def _get_instance_tool(self, instance): - # finds tool name of instance in currently active comp - # TODO: assign `tool` as 'lifetime' data instead of name so the - # tool can be retrieved in current session. We can't store currently - # in the CreatedInstance data because it needs to be serializable - comp = get_current_comp() - tool_name = instance["tool_name"] - return { - tool.Name: tool for tool in comp.GetToolList(False).values() - }.get(tool_name) - def _update_tool_with_data(self, tool, data): """Update tool node name and output path based on subset data""" if "subset" not in data: diff --git a/openpype/hosts/fusion/plugins/publish/collect_instances.py b/openpype/hosts/fusion/plugins/publish/collect_instances.py index e42e7b5f70..2f3e82fded 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_instances.py +++ b/openpype/hosts/fusion/plugins/publish/collect_instances.py @@ -45,13 +45,9 @@ class CollectInstances(pyblish.api.ContextPlugin): context.data["frameStartHandle"] = int(global_start) context.data["frameEndHandle"] = int(global_end) - # Comp tools by name - tools = {tool.Name: tool for tool in comp.GetToolList(False).values()} - for instance in context: - tool_name = instance.data["tool_name"] - tool = tools[tool_name] + tool = instance.data["transientData"]["tool"] path = tool["Clip"][comp.TIME_UNDEFINED] filename = os.path.basename(path) @@ -76,7 +72,10 @@ class CollectInstances(pyblish.api.ContextPlugin): "frameEndHandle": context.data["frameStartHandle"], "fps": context.data["fps"], "families": ["render", "review"], - "family": "render" + "family": "render", + + # Backwards compatibility: embed tool in instance.data + "tool": tool }) # Add tool itself as member From 87621c14f5d47de295ae476879c00fbbe7efcf14 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 23 Sep 2022 17:34:47 +0200 Subject: [PATCH 0017/1271] Refactor `INewPublisher` to `IPublishHost` --- openpype/hosts/fusion/api/pipeline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/api/pipeline.py b/openpype/hosts/fusion/api/pipeline.py index 6fc3902949..3e30c5eaf5 100644 --- a/openpype/hosts/fusion/api/pipeline.py +++ b/openpype/hosts/fusion/api/pipeline.py @@ -20,7 +20,7 @@ from openpype.pipeline import ( ) from openpype.pipeline.load import any_outdated_containers from openpype.hosts.fusion import FUSION_HOST_DIR -from openpype.host import HostBase, IWorkfileHost, ILoadHost, INewPublisher +from openpype.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost from openpype.tools.utils import host_tools @@ -48,7 +48,7 @@ class CompLogHandler(logging.Handler): comp.Print(entry) -class FusionHost(HostBase, IWorkfileHost, ILoadHost, INewPublisher): +class FusionHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): name = "fusion" def install(self): From 11bd9e8bb1f5a62c501f4fe4388c79fe82d8bc8a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 23 Sep 2022 17:44:05 +0200 Subject: [PATCH 0018/1271] Add Select Invalid Action --- openpype/hosts/fusion/api/action.py | 54 +++++++++++++++++++ .../publish/validate_saver_has_input.py | 3 ++ 2 files changed, 57 insertions(+) create mode 100644 openpype/hosts/fusion/api/action.py diff --git a/openpype/hosts/fusion/api/action.py b/openpype/hosts/fusion/api/action.py new file mode 100644 index 0000000000..1750920950 --- /dev/null +++ b/openpype/hosts/fusion/api/action.py @@ -0,0 +1,54 @@ +import pyblish.api + + +from openpype.hosts.fusion.api.lib import get_current_comp +from openpype.pipeline.publish import get_errored_instances_from_context + + +class SelectInvalidAction(pyblish.api.Action): + """Select invalid nodes in Maya when plug-in failed. + + To retrieve the invalid nodes this assumes a static `get_invalid()` + method is available on the plugin. + + """ + label = "Select invalid" + on = "failed" # This action is only available on a failed plug-in + icon = "search" # Icon from Awesome Icon + + def process(self, context, plugin): + errored_instances = get_errored_instances_from_context(context) + + # Apply pyblish.logic to get the instances for the plug-in + instances = pyblish.api.instances_by_plugin(errored_instances, plugin) + + # Get the invalid nodes for the plug-ins + self.log.info("Finding invalid nodes..") + invalid = list() + for instance in instances: + invalid_nodes = plugin.get_invalid(instance) + if invalid_nodes: + if isinstance(invalid_nodes, (list, tuple)): + invalid.extend(invalid_nodes) + else: + self.log.warning("Plug-in returned to be invalid, " + "but has no selectable nodes.") + + if not invalid: + # Assume relevant comp is current comp and clear selection + self.log.info("No invalid tools found.") + comp = get_current_comp() + flow = comp.CurrentFrame.FlowView + flow.Select() # No args equals clearing selection + return + + # Assume a single comp + first_tool = invalid[0] + comp = first_tool.Comp() + flow = comp.CurrentFrame.FlowView + flow.Select() # No args equals clearing selection + names = set() + for tool in invalid: + flow.Select(tool, True) + names.add(tool.Name) + self.log.info("Selecting invalid tools: %s" % ", ".join(sorted(names))) diff --git a/openpype/hosts/fusion/plugins/publish/validate_saver_has_input.py b/openpype/hosts/fusion/plugins/publish/validate_saver_has_input.py index 8d961525f0..e02125f531 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_saver_has_input.py +++ b/openpype/hosts/fusion/plugins/publish/validate_saver_has_input.py @@ -1,6 +1,8 @@ import pyblish.api from openpype.pipeline import PublishValidationError +from openpype.hosts.fusion.api.action import SelectInvalidAction + class ValidateSaverHasInput(pyblish.api.InstancePlugin): """Validate saver has incoming connection @@ -13,6 +15,7 @@ class ValidateSaverHasInput(pyblish.api.InstancePlugin): label = "Validate Saver Has Input" families = ["render"] hosts = ["fusion"] + actions = [SelectInvalidAction] @classmethod def get_invalid(cls, instance): From 0f1ed036231d91ba6f8105d85b0c6e341e5b6487 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 23 Sep 2022 17:47:19 +0200 Subject: [PATCH 0019/1271] Add Select Invalid action to more validators --- .../fusion/plugins/publish/validate_background_depth.py | 4 ++++ .../plugins/publish/validate_create_folder_checked.py | 3 +++ .../plugins/publish/validate_filename_has_extension.py | 3 +++ .../fusion/plugins/publish/validate_saver_passthrough.py | 3 +++ .../hosts/fusion/plugins/publish/validate_unique_subsets.py | 6 ++++++ 5 files changed, 19 insertions(+) diff --git a/openpype/hosts/fusion/plugins/publish/validate_background_depth.py b/openpype/hosts/fusion/plugins/publish/validate_background_depth.py index f057989535..261533de01 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_background_depth.py +++ b/openpype/hosts/fusion/plugins/publish/validate_background_depth.py @@ -3,6 +3,8 @@ import pyblish.api from openpype.pipeline.publish import RepairAction from openpype.pipeline import PublishValidationError +from openpype.hosts.fusion.api.action import SelectInvalidAction + class ValidateBackgroundDepth(pyblish.api.InstancePlugin): """Validate if all Background tool are set to float32 bit""" @@ -14,6 +16,8 @@ class ValidateBackgroundDepth(pyblish.api.InstancePlugin): families = ["render"] optional = True + actions = [SelectInvalidAction] + @classmethod def get_invalid(cls, instance): diff --git a/openpype/hosts/fusion/plugins/publish/validate_create_folder_checked.py b/openpype/hosts/fusion/plugins/publish/validate_create_folder_checked.py index 3674b33644..ba943abacb 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_create_folder_checked.py +++ b/openpype/hosts/fusion/plugins/publish/validate_create_folder_checked.py @@ -3,6 +3,8 @@ import pyblish.api from openpype.pipeline.publish import RepairAction from openpype.pipeline import PublishValidationError +from openpype.hosts.fusion.api.action import SelectInvalidAction + class ValidateCreateFolderChecked(pyblish.api.InstancePlugin): """Valid if all savers have the input attribute CreateDir checked on @@ -16,6 +18,7 @@ class ValidateCreateFolderChecked(pyblish.api.InstancePlugin): label = "Validate Create Folder Checked" families = ["render"] hosts = ["fusion"] + actions = [SelectInvalidAction] @classmethod def get_invalid(cls, instance): diff --git a/openpype/hosts/fusion/plugins/publish/validate_filename_has_extension.py b/openpype/hosts/fusion/plugins/publish/validate_filename_has_extension.py index 22f1db809c..bbba2dde6e 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_filename_has_extension.py +++ b/openpype/hosts/fusion/plugins/publish/validate_filename_has_extension.py @@ -3,6 +3,8 @@ import os import pyblish.api from openpype.pipeline import PublishValidationError +from openpype.hosts.fusion.api.action import SelectInvalidAction + class ValidateFilenameHasExtension(pyblish.api.InstancePlugin): """Ensure the Saver has an extension in the filename path @@ -17,6 +19,7 @@ class ValidateFilenameHasExtension(pyblish.api.InstancePlugin): label = "Validate Filename Has Extension" families = ["render"] hosts = ["fusion"] + actions = [SelectInvalidAction] def process(self, instance): invalid = self.get_invalid(instance) diff --git a/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py b/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py index bbafd8949e..56f2e7e6b8 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py +++ b/openpype/hosts/fusion/plugins/publish/validate_saver_passthrough.py @@ -1,6 +1,8 @@ import pyblish.api from openpype.pipeline import PublishValidationError +from openpype.hosts.fusion.api.action import SelectInvalidAction + class ValidateSaverPassthrough(pyblish.api.ContextPlugin): """Validate saver passthrough is similar to Pyblish publish state""" @@ -9,6 +11,7 @@ class ValidateSaverPassthrough(pyblish.api.ContextPlugin): label = "Validate Saver Passthrough" families = ["render"] hosts = ["fusion"] + actions = [SelectInvalidAction] def process(self, context): diff --git a/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py b/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py index 5f0f93f764..28bab59949 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py +++ b/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py @@ -3,6 +3,8 @@ from collections import defaultdict import pyblish.api from openpype.pipeline import PublishValidationError +from openpype.hosts.fusion.api.action import SelectInvalidAction + class ValidateUniqueSubsets(pyblish.api.ContextPlugin): """Ensure all instances have a unique subset name""" @@ -11,6 +13,7 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin): label = "Validate Unique Subsets" families = ["render"] hosts = ["fusion"] + actions = [SelectInvalidAction] @classmethod def get_invalid(cls, context): @@ -38,6 +41,9 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin): )) invalid.extend(instances) + # Return tools for the invalid instances so they can be selected + invalid = [instance.data["tool"] for instance in invalid] + return invalid def process(self, context): From b823f3d80e0c898c53ca8aeda2536f8f114f3243 Mon Sep 17 00:00:00 2001 From: Allan Ihsan Date: Mon, 26 Sep 2022 08:22:41 +0300 Subject: [PATCH 0020/1271] 8c4e93cfc Merge branch 'develop' into enhancement/OP-3783_Maya-Playblast-Options --- .../defaults/project_settings/maya.json | 5 ++++ .../schemas/schema_maya_capture.json | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 76ef0a7338..daaa8da8e6 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -712,6 +712,11 @@ "motionBlurEnable": false, "motionBlurSampleCount": 8, "motionBlurShutterOpenFraction": 0.2, + "xray": false, + "udm": false, + "wos": false, + "jointXray": false, + "backfaceCulling": false, "cameras": false, "clipGhosts": false, "deformers": false, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json index 62c33f55fc..02c25e8a03 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json @@ -348,6 +348,31 @@ "type": "label", "label": "Show" }, + { + "type": "boolean", + "key": "xray", + "label": "Toggle X-Ray Mode" + }, + { + "type": "boolean", + "key": "udm", + "label": "Use Default Material" + }, + { + "type": "boolean", + "key": "wos", + "label": "Wireframe On Shaded" + }, + { + "type": "boolean", + "key": "jointXray", + "label": "Toggle Joint X-Ray Mode" + }, + { + "type": "boolean", + "key": "backfaceCulling", + "label": "Enable Back Face Culling" + }, { "type": "boolean", "key": "cameras", From 486589ef3479af1907f813b08a20c319e5f60141 Mon Sep 17 00:00:00 2001 From: Allan Ihsan Date: Mon, 26 Sep 2022 08:24:50 +0300 Subject: [PATCH 0021/1271] 8c4e93cfc Merge branch 'develop' into enhancement/OP-3783_Maya-Playblast-Options --- openpype/settings/defaults/project_anatomy/attributes.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_anatomy/attributes.json b/openpype/settings/defaults/project_anatomy/attributes.json index 983ac603f9..bf8bbef8de 100644 --- a/openpype/settings/defaults/project_anatomy/attributes.json +++ b/openpype/settings/defaults/project_anatomy/attributes.json @@ -19,8 +19,7 @@ "blender/2-91", "harmony/20", "photoshop/2021", - "aftereffects/2021", - "unreal/4-26" + "aftereffects/2021" ], "tools_env": [], "active": true From 45732d884b89b9336bf8bb4e0387ac91cc916539 Mon Sep 17 00:00:00 2001 From: Allan Ihsan Date: Mon, 26 Sep 2022 08:25:45 +0300 Subject: [PATCH 0022/1271] 8c4e93cfc Merge branch 'develop' into enhancement/OP-3783_Maya-Playblast-Options --- .../schemas/schema_maya_capture.json | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json index 02c25e8a03..c3d051b56e 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json @@ -352,6 +352,34 @@ "type": "boolean", "key": "xray", "label": "Toggle X-Ray Mode" +<<<<<<< Updated upstream +======= + }, + { + "type": "boolean", + "key": "udm", + "label": "Use Default Material" + }, + { + "type": "boolean", + "key": "wos", + "label": "Wireframe On Shaded" + }, + { + "type": "boolean", + "key": "jointXray", + "label": "Toggle Joint X-Ray Mode" + }, + { + "type": "boolean", + "key": "backfaceCulling", + "label": "Enable Back Face Culling" + }, + { + "type": "boolean", + "key": "cameras", + "label": "cameras" +>>>>>>> Stashed changes }, { "type": "boolean", From 94cb7bee1f4b8c54e26b0364c3f272734305bba0 Mon Sep 17 00:00:00 2001 From: "Allan I. A" <76656700+Allan-I@users.noreply.github.com> Date: Mon, 26 Sep 2022 14:11:11 +0300 Subject: [PATCH 0023/1271] Move schema to a higher location. Co-authored-by: Roy Nieterau --- .../schemas/schema_maya_capture.json | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json index 02c25e8a03..88c994ca8f 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json @@ -349,10 +349,9 @@ "label": "Show" }, { - "type": "boolean", - "key": "xray", - "label": "Toggle X-Ray Mode" - }, + "type": "label", + "label": "Shading" + }, { "type": "boolean", "key": "udm", @@ -363,15 +362,20 @@ "key": "wos", "label": "Wireframe On Shaded" }, + { + "type": "boolean", + "key": "xray", + "label": "X-Ray" + }, { "type": "boolean", "key": "jointXray", - "label": "Toggle Joint X-Ray Mode" + "label": "X-Ray Joints" }, { "type": "boolean", "key": "backfaceCulling", - "label": "Enable Back Face Culling" + "label": "Backface Culling" }, { "type": "boolean", From cfddc9f9fe2d96b88b91c9a368af2a79c0a00550 Mon Sep 17 00:00:00 2001 From: "Allan I. A" <76656700+Allan-I@users.noreply.github.com> Date: Mon, 26 Sep 2022 14:25:06 +0300 Subject: [PATCH 0024/1271] Update openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json Co-authored-by: Roy Nieterau --- .../schemas/projects_schema/schemas/schema_maya_capture.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json index 88c994ca8f..1b9ef2c30b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json @@ -354,12 +354,12 @@ }, { "type": "boolean", - "key": "udm", + "key": "useDefaultMaterial", "label": "Use Default Material" }, { "type": "boolean", - "key": "wos", + "key": "wireframeOnShaded", "label": "Wireframe On Shaded" }, { From 6e7309ea2dd077843e54456cd9b263651d73aa65 Mon Sep 17 00:00:00 2001 From: "Allan I. A" <76656700+Allan-I@users.noreply.github.com> Date: Mon, 26 Sep 2022 14:25:23 +0300 Subject: [PATCH 0025/1271] Update openpype/settings/defaults/project_settings/maya.json Co-authored-by: Roy Nieterau --- openpype/settings/defaults/project_settings/maya.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index daaa8da8e6..b873827df0 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -713,8 +713,8 @@ "motionBlurSampleCount": 8, "motionBlurShutterOpenFraction": 0.2, "xray": false, - "udm": false, - "wos": false, + "useDefaultMaterial": false, + "wireframeOnShaded": false, "jointXray": false, "backfaceCulling": false, "cameras": false, From a64a551fa19a472c4efe36528051394866f37cf1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 27 Sep 2022 10:56:34 +0200 Subject: [PATCH 0026/1271] Tweak label --- openpype/hosts/fusion/plugins/create/create_saver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index b3a912c56a..99aa7583f1 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -17,7 +17,7 @@ from openpype.pipeline import ( class CreateSaver(Creator): identifier = "io.openpype.creators.fusion.saver" name = "saver" - label = "Create Saver" + label = "Saver" family = "render" default_variants = ["Main"] From 85cb398b6ee14b66ece629fb446c97ee9fd2347a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 27 Sep 2022 10:59:41 +0200 Subject: [PATCH 0027/1271] Implement draft for Create Workfile --- .../fusion/plugins/create/create_workfile.py | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 openpype/hosts/fusion/plugins/create/create_workfile.py diff --git a/openpype/hosts/fusion/plugins/create/create_workfile.py b/openpype/hosts/fusion/plugins/create/create_workfile.py new file mode 100644 index 0000000000..26a73abb64 --- /dev/null +++ b/openpype/hosts/fusion/plugins/create/create_workfile.py @@ -0,0 +1,129 @@ +import collections + +import qtawesome + +import openpype.hosts.fusion.api as api +from openpype.client import get_asset_by_name +from openpype.pipeline import ( + AutoCreator, + CreatedInstance, + legacy_io, +) + + +def flatten_dict(d, parent_key=None, separator="."): + items = [] + for key, v in d.items(): + new_key = parent_key + separator + key if parent_key else key + if isinstance(v, collections.MutableMapping): + items.extend(flatten_dict(v, new_key, separator=separator).items()) + else: + items.append((new_key, v)) + return dict(items) + + +class FusionWorkfileCreator(AutoCreator): + identifier = "workfile" + family = "workfile" + label = "Workfile" + + default_variant = "Main" + + create_allow_context_change = False + + data_key = "openpype.workfile" + + def collect_instances(self): + + comp = api.get_current_comp() + data = comp.GetData(self.data_key) + if not data: + return + + instance = CreatedInstance( + family=self.family, + subset_name=data["subset"], + data=data, + creator=self + ) + instance.transient_data["comp"] = comp + instance.transient_data["tool"] = None + + self._add_instance_to_context(instance) + + def update_instances(self, update_list): + for update in update_list: + instance = update.instance + comp = instance.transient_data["comp"] + if not hasattr(comp, "SetData"): + # Comp is not alive anymore, likely closed by the user + self.log.error("Workfile comp not found for existing instance." + " Comp might have been closed in the meantime.") + continue + + # TODO: It appears sometimes this could be 'nested' + # Get the new values after the changes by key, ignore old value + new_data = { + key: new for key, (_old, new) in update.changes.items() + } + self._imprint(comp, new_data) + + def create(self, options=None): + + comp = api.get_current_comp() + if not comp: + self.log.error("Unable to find current comp") + return + + # TODO: Is this really necessary? + # Force kill any existing "workfile" instances + for instance in self.create_context.instances: + if instance.family == self.family: + self.log.debug(f"Removing instance: {instance}") + self._remove_instance_from_context(instance) + + project_name = legacy_io.Session["AVALON_PROJECT"] + asset_name = legacy_io.Session["AVALON_ASSET"] + task_name = legacy_io.Session["AVALON_TASK"] + host_name = legacy_io.Session["AVALON_APP"] + + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + self.default_variant, task_name, asset_doc, + project_name, host_name + ) + data = { + "asset": asset_name, + "task": task_name, + "variant": self.default_variant + } + data.update(self.get_dynamic_data( + self.default_variant, task_name, asset_doc, + project_name, host_name + )) + + instance = CreatedInstance( + self.family, subset_name, data, self + ) + instance.transient_data["comp"] = comp + instance.transient_data["tool"] = None + self._add_instance_to_context(instance) + + self._imprint(comp, data) + + def get_icon(self): + return qtawesome.icon("fa.file-o", color="white") + + def _imprint(self, comp, data): + + # TODO: Should this keys persist or not? I'd prefer not + # Do not persist the current context for the Workfile + for key in ["variant", "subset", "asset", "task"]: + data.pop(key, None) + + # Flatten any potential nested dicts + data = flatten_dict(data, separator=".") + + # Prefix with data key openpype.workfile + data = {f"{self.data_key}.{key}" for key, value in data.items()} + comp.SetData(data) From f555c1bf9a11200d8a48a9a68bac6f5e4695e347 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 27 Sep 2022 11:00:35 +0200 Subject: [PATCH 0028/1271] Shush hound --- .../hosts/fusion/plugins/publish/validate_unique_subsets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py b/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py index 28bab59949..5b6ceb2fdb 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py +++ b/openpype/hosts/fusion/plugins/publish/validate_unique_subsets.py @@ -38,7 +38,8 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin): asset=asset, subset=subset, instances=instances - )) + ) + ) invalid.extend(instances) # Return tools for the invalid instances so they can be selected From b8f4a0a3969b841f50cb66f15e08717d3d3cd890 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 27 Sep 2022 11:41:55 +0200 Subject: [PATCH 0029/1271] Specifiy families explicitly (to avoid issues with workfile family not having a `tool`) --- openpype/hosts/fusion/plugins/publish/collect_inputs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/fusion/plugins/publish/collect_inputs.py b/openpype/hosts/fusion/plugins/publish/collect_inputs.py index 8f9857b02f..e06649da99 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_inputs.py +++ b/openpype/hosts/fusion/plugins/publish/collect_inputs.py @@ -97,10 +97,15 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin): label = "Collect Inputs" order = pyblish.api.CollectorOrder + 0.2 hosts = ["fusion"] + families = ["render"] def process(self, instance): # Get all upstream and include itself + if not any(instance[:]): + self.log.debug("No tool found in instance, skipping..") + return + tool = instance[0] nodes = list(iter_upstream(tool)) nodes.append(tool) From 2afc8ba573ee06ed790146eb113d1a74cbe705b3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 27 Sep 2022 11:42:38 +0200 Subject: [PATCH 0030/1271] Fix collector with workfile present --- .../plugins/publish/collect_instances.py | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/collect_instances.py b/openpype/hosts/fusion/plugins/publish/collect_instances.py index 2f3e82fded..ad264f0478 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_instances.py +++ b/openpype/hosts/fusion/plugins/publish/collect_instances.py @@ -46,24 +46,12 @@ class CollectInstances(pyblish.api.ContextPlugin): context.data["frameEndHandle"] = int(global_end) for instance in context: - - tool = instance.data["transientData"]["tool"] - - path = tool["Clip"][comp.TIME_UNDEFINED] - filename = os.path.basename(path) - head, padding, tail = get_frame_path(filename) - ext = os.path.splitext(path)[1] - assert tail == ext, ("Tail does not match %s" % ext) - # Include start and end render frame in label subset = instance.data["subset"] label = "{subset} ({start}-{end})".format(subset=subset, start=int(start), end=int(end)) instance.data.update({ - "path": path, - "outputDir": os.path.dirname(path), - "ext": ext, # todo: should be redundant? "label": label, # todo: Allow custom frame range per instance "frameStart": context.data["frameStart"], @@ -71,14 +59,32 @@ class CollectInstances(pyblish.api.ContextPlugin): "frameStartHandle": context.data["frameStartHandle"], "frameEndHandle": context.data["frameStartHandle"], "fps": context.data["fps"], - "families": ["render", "review"], - "family": "render", - - # Backwards compatibility: embed tool in instance.data - "tool": tool }) - # Add tool itself as member - instance.append(tool) + if instance.data["family"] == "render": + # TODO: This should probably move into a collector of + # its own for the "render" family + # This is only the case for savers currently but not + # for workfile instances. So we assume saver here. + tool = instance.data["transientData"]["tool"] + path = tool["Clip"][comp.TIME_UNDEFINED] - self.log.info("Found: \"%s\" " % path) + filename = os.path.basename(path) + head, padding, tail = get_frame_path(filename) + ext = os.path.splitext(path)[1] + assert tail == ext, ("Tail does not match %s" % ext) + + instance.data.update({ + "path": path, + "outputDir": os.path.dirname(path), + "ext": ext, # todo: should be redundant? + + "families": ["render", "review"], + "family": "render", + + # Backwards compatibility: embed tool in instance.data + "tool": tool + }) + + # Add tool itself as member + instance.append(tool) From d0ea9171b3cbd2c52ab3e027f496f75e6bb36768 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 27 Sep 2022 11:43:25 +0200 Subject: [PATCH 0031/1271] Remove storage of empty tool placeholder value --- openpype/hosts/fusion/plugins/create/create_workfile.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_workfile.py b/openpype/hosts/fusion/plugins/create/create_workfile.py index 26a73abb64..19ad04f572 100644 --- a/openpype/hosts/fusion/plugins/create/create_workfile.py +++ b/openpype/hosts/fusion/plugins/create/create_workfile.py @@ -47,7 +47,6 @@ class FusionWorkfileCreator(AutoCreator): creator=self ) instance.transient_data["comp"] = comp - instance.transient_data["tool"] = None self._add_instance_to_context(instance) @@ -106,7 +105,6 @@ class FusionWorkfileCreator(AutoCreator): self.family, subset_name, data, self ) instance.transient_data["comp"] = comp - instance.transient_data["tool"] = None self._add_instance_to_context(instance) self._imprint(comp, data) From 3a952d5038b51e7d05e64d70e10054a8c9b5bd4c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 27 Sep 2022 11:43:37 +0200 Subject: [PATCH 0032/1271] Cosmetics/readability --- openpype/hosts/fusion/plugins/create/create_workfile.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/create/create_workfile.py b/openpype/hosts/fusion/plugins/create/create_workfile.py index 19ad04f572..783d3a147a 100644 --- a/openpype/hosts/fusion/plugins/create/create_workfile.py +++ b/openpype/hosts/fusion/plugins/create/create_workfile.py @@ -102,7 +102,10 @@ class FusionWorkfileCreator(AutoCreator): )) instance = CreatedInstance( - self.family, subset_name, data, self + family=self.family, + subset_name=subset_name, + data=data, + creator=self ) instance.transient_data["comp"] = comp self._add_instance_to_context(instance) From 00d9aa216bd8689862a189eb32fcd1dd692303fe Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 27 Sep 2022 12:01:11 +0200 Subject: [PATCH 0033/1271] Apply to workfile family - like other hosts do --- .../fusion/plugins/publish/increment_current_file_deadline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/publish/increment_current_file_deadline.py b/openpype/hosts/fusion/plugins/publish/increment_current_file_deadline.py index 5c595638e9..42891446f7 100644 --- a/openpype/hosts/fusion/plugins/publish/increment_current_file_deadline.py +++ b/openpype/hosts/fusion/plugins/publish/increment_current_file_deadline.py @@ -11,7 +11,7 @@ class FusionIncrementCurrentFile(pyblish.api.ContextPlugin): label = "Increment current file" order = pyblish.api.IntegratorOrder + 9.0 hosts = ["fusion"] - families = ["render.farm"] + families = ["workfile"] optional = True def process(self, context): From 34c1346961a621726dfc4f9352606c038d650ec4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 27 Sep 2022 12:01:28 +0200 Subject: [PATCH 0034/1271] Refactor filename --- ...crement_current_file_deadline.py => increment_current_file.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename openpype/hosts/fusion/plugins/publish/{increment_current_file_deadline.py => increment_current_file.py} (100%) diff --git a/openpype/hosts/fusion/plugins/publish/increment_current_file_deadline.py b/openpype/hosts/fusion/plugins/publish/increment_current_file.py similarity index 100% rename from openpype/hosts/fusion/plugins/publish/increment_current_file_deadline.py rename to openpype/hosts/fusion/plugins/publish/increment_current_file.py From d6080593e3213df882b5e4b6dc112223531d109e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 27 Sep 2022 12:02:16 +0200 Subject: [PATCH 0035/1271] Include workfile family --- openpype/hosts/fusion/plugins/publish/save_scene.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/publish/save_scene.py b/openpype/hosts/fusion/plugins/publish/save_scene.py index 0cdfafa095..a249c453d8 100644 --- a/openpype/hosts/fusion/plugins/publish/save_scene.py +++ b/openpype/hosts/fusion/plugins/publish/save_scene.py @@ -7,7 +7,7 @@ class FusionSaveComp(pyblish.api.ContextPlugin): label = "Save current file" order = pyblish.api.ExtractorOrder - 0.49 hosts = ["fusion"] - families = ["render"] + families = ["render", "workfile"] def process(self, context): From eed65758e651acf372d6814b4d7061990d6ad993 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 27 Sep 2022 12:21:31 +0200 Subject: [PATCH 0036/1271] Move Submit Fusion Deadline to Deadline Module --- .../deadline/plugins/publish/submit_fusion_deadline.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename openpype/{hosts/fusion/plugins/publish/submit_deadline.py => modules/deadline/plugins/publish/submit_fusion_deadline.py} (100%) diff --git a/openpype/hosts/fusion/plugins/publish/submit_deadline.py b/openpype/modules/deadline/plugins/publish/submit_fusion_deadline.py similarity index 100% rename from openpype/hosts/fusion/plugins/publish/submit_deadline.py rename to openpype/modules/deadline/plugins/publish/submit_fusion_deadline.py From aeef0a484e257e868c9f5ec334e98bf4ad013f39 Mon Sep 17 00:00:00 2001 From: Allan Ihsan Date: Wed, 28 Sep 2022 15:06:20 +0300 Subject: [PATCH 0037/1271] Fix bug --- .../schemas/projects_schema/schemas/schema_maya_capture.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json index 0a28cc2552..aba6a020f8 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json @@ -348,8 +348,6 @@ "type": "label", "label": "Show" }, - { - "type": "label", { "type": "boolean", "key": "useDefaultMaterial", @@ -379,7 +377,6 @@ "type": "boolean", "key": "cameras", "label": "cameras" ->>>>>>> Stashed changes }, { "type": "boolean", From 3d7af9e434e283b0606814b75589ef4643e150cd Mon Sep 17 00:00:00 2001 From: Allan Ihsan Date: Wed, 28 Sep 2022 15:27:01 +0300 Subject: [PATCH 0038/1271] Remove redundant, move playblast options. --- .../schemas/schema_maya_capture.json | 75 +++++++------------ 1 file changed, 25 insertions(+), 50 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json index aba6a020f8..ff3c5a79f4 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json @@ -221,6 +221,31 @@ { "type": "splitter" }, + { + "type": "boolean", + "key": "useDefaultMaterial", + "label": "Use Default Material" + }, + { + "type": "boolean", + "key": "wireframeOnShaded", + "label": "Wireframe On Shaded" + }, + { + "type": "boolean", + "key": "xray", + "label": "X-Ray" + }, + { + "type": "boolean", + "key": "jointXray", + "label": "X-Ray Joints" + }, + { + "type": "boolean", + "key": "backfaceCulling", + "label": "Backface Culling" + }, { "type": "boolean", "key": "ssaoEnable", @@ -348,56 +373,6 @@ "type": "label", "label": "Show" }, - { - "type": "boolean", - "key": "useDefaultMaterial", - "label": "Use Default Material" - }, - { - "type": "boolean", - "key": "wireframeOnShaded", - "label": "Wireframe On Shaded" - }, - { - "type": "boolean", - "key": "xray", - "label": "X-Ray" - }, - { - "type": "boolean", - "key": "jointXray", - "label": "X-Ray Joints" - }, - { - "type": "boolean", - "key": "backfaceCulling", - "label": "Backface Culling" - }, - { - "type": "boolean", - "key": "cameras", - "label": "cameras" - }, - { - "type": "boolean", - "key": "udm", - "label": "Use Default Material" - }, - { - "type": "boolean", - "key": "wos", - "label": "Wireframe On Shaded" - }, - { - "type": "boolean", - "key": "jointXray", - "label": "Toggle Joint X-Ray Mode" - }, - { - "type": "boolean", - "key": "backfaceCulling", - "label": "Enable Back Face Culling" - }, { "type": "boolean", "key": "cameras", From 4d15535163119fbe2989ff068740477418eb28cf Mon Sep 17 00:00:00 2001 From: Allan Ihsan Date: Mon, 3 Oct 2022 13:43:26 +0300 Subject: [PATCH 0039/1271] remove unnecessary line --- openpype/settings/defaults/project_anatomy/attributes.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/settings/defaults/project_anatomy/attributes.json b/openpype/settings/defaults/project_anatomy/attributes.json index bf8bbef8de..c2d2aa9b15 100644 --- a/openpype/settings/defaults/project_anatomy/attributes.json +++ b/openpype/settings/defaults/project_anatomy/attributes.json @@ -18,9 +18,8 @@ "houdini/18-5", "blender/2-91", "harmony/20", - "photoshop/2021", - "aftereffects/2021" + "photoshop/2021" ], "tools_env": [], "active": true -} \ No newline at end of file +} From f6f3abf33811d875cce9a9f11829996d38645a61 Mon Sep 17 00:00:00 2001 From: Allan Ihsan Date: Tue, 4 Oct 2022 10:58:26 +0300 Subject: [PATCH 0040/1271] Add label below splitter. --- .../schemas/projects_schema/schemas/schema_maya_capture.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json index ff3c5a79f4..1f0e4eeffb 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json @@ -184,6 +184,10 @@ { "type": "splitter" }, + { + "type": "label", + "label": "Display" + }, { "type":"boolean", "key": "renderDepthOfField", From 3c7fa6faa5af05e96fc3a42c3ca5d5175b910301 Mon Sep 17 00:00:00 2001 From: Allan Ihsan Date: Tue, 4 Oct 2022 10:58:37 +0300 Subject: [PATCH 0041/1271] Reorder defaults --- openpype/settings/defaults/project_settings/maya.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index b873827df0..e2a3357cfe 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -695,6 +695,11 @@ "twoSidedLighting": true, "lineAAEnable": true, "multiSample": 8, + "useDefaultMaterial": false, + "wireframeOnShaded": false, + "xray": false, + "jointXray": false, + "backfaceCulling": false, "ssaoEnable": false, "ssaoAmount": 1, "ssaoRadius": 16, @@ -712,11 +717,6 @@ "motionBlurEnable": false, "motionBlurSampleCount": 8, "motionBlurShutterOpenFraction": 0.2, - "xray": false, - "useDefaultMaterial": false, - "wireframeOnShaded": false, - "jointXray": false, - "backfaceCulling": false, "cameras": false, "clipGhosts": false, "deformers": false, From 62a6f4c4ad77b6c75799bd6b52b5452e111d6ebe Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 19 Oct 2022 22:24:28 +0800 Subject: [PATCH 0042/1271] Renderman setup for sample and display filters --- openpype/hosts/maya/api/lib_rendersettings.py | 45 +++++++++++- .../defaults/project_settings/maya.json | 6 ++ .../schemas/schema_maya_render_settings.json | 69 ++++++++++++++++++- 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 777a6ffbc9..dad9dde114 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -4,6 +4,7 @@ from maya import cmds # noqa import maya.mel as mel import six import sys +import os from openpype.lib import Logger from openpype.api import ( @@ -29,7 +30,7 @@ class RenderSettings(object): _image_prefixes = { 'vray': get_current_project_settings()["maya"]["RenderSettings"]["vray_renderer"]["image_prefix"], # noqa 'arnold': get_current_project_settings()["maya"]["RenderSettings"]["arnold_renderer"]["image_prefix"], # noqa - 'renderman': 'maya///{aov_separator}', + 'renderman': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["image_prefix"], # noqa 'redshift': get_current_project_settings()["maya"]["RenderSettings"]["redshift_renderer"]["image_prefix"] # noqa } @@ -96,6 +97,9 @@ class RenderSettings(object): if renderer == "redshift": self._set_redshift_settings(width, height) + if renderer == "renderman": + self._set_renderman_settings(width, height) + def _set_arnold_settings(self, width, height): """Sets settings for Arnold.""" from mtoa.core import createOptions # noqa @@ -158,6 +162,45 @@ class RenderSettings(object): cmds.setAttr("defaultResolution.height", height) self._additional_attribs_setter(additional_options) + def _set_renderman_settings(self, width, height): + """Sets settings for Renderman""" + rman_render_presets = ( + self._project_settings + ["maya"] + ["RenderSettings"] + ["renderman_renderer"] + ) + display_filters = rman_render_presets["display_filters"] + d_filters_number = len(display_filters) + for i in range(d_filters_number): + d_node = cmds.ls(typ=display_filters[i]) + if len(d_node) > 0: + filter_nodes = d_node[0] + else: + filter_nodes = cmds.createNode(display_filters[i]) + cmds.connectAttr(filter_nodes+".message", + "rmanGlobals.displayFilters[%i]"% i, + force=True) + + sample_filters = rman_render_presets["sample_filters"] + s_filters_number = len(sample_filters) + for n in range(s_filters_number): + s_node = cmds.ls(typ=sample_filters[n]) + if len(s_node) > 0: + filter_nodes = s_node[0] + else: + filter_nodes = cmds.createNode(sample_filters[n]) + cmds.connectAttr(filter_nodes+".message", + "rmanGlobals.sampleFilters[%i]"% n, + force=True) + + additional_options = rman_render_presets["additional_options"] + + self._set_global_output_settings() + cmds.setAttr("defaultResolution.width", width) + cmds.setAttr("defaultResolution.height", height) + self._additional_attribs_setter(additional_options) + def _set_vray_settings(self, aov_separator, width, height): # type: (str, int, int) -> None """Sets important settings for Vray.""" diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 76ef0a7338..777a0bccb2 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -62,6 +62,12 @@ "force_combine": true, "aov_list": [], "additional_options": [] + }, + "renderman_renderer": { + "image_prefix": "maya///{aov_separator}", + "display_filters": [], + "sample_filters": [], + "additional_options": [] } }, "create": { diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json index 6ee02ca78f..a0b38906be 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json @@ -418,6 +418,73 @@ } } ] - } + }, + { + "type": "dict", + "collapsible": true, + "key": "renderman_renderer", + "label": "Renderman Renderer", + "is_group": true, + "children": [ + { + "key": "image_prefix", + "label": "Image prefix template", + "type": "text" + }, + { + "key": "display_filters", + "label": "Display Filters", + "type": "enum", + "multiselection": true, + "defaults": "empty", + "enum_items": [ + {"PxrBackgroundDisplayFilter": "PxrBackgroundDisplayFilter"}, + {"PxrCopyAOVDisplayFilter": "PxrCopyAOVDisplayFilter"}, + {"PxrEdgeDetect":"PxrEdgeDetect"}, + {"PxrFilmicTonemapperDisplayFilter": "PxrFilmicTonemapperDisplayFilter"}, + {"PxrGradeDisplayFilter": "PxrGradeDisplayFilter"}, + {"PxrHalfBufferErrorFilter": "PxrHalfBufferErrorFilter"}, + {"PxrImageDisplayFilter": "PxrImageDisplayFilter"}, + {"PxrLightSaturation": "PxrLightSaturation"}, + {"PxrShadowDisplayFilter": "PxrShadowDisplayFilter"}, + {"PxrStylizedHatching": "PxrStylizedHatching"}, + {"PxrStylizedLines": "PxrStylizedLines"}, + {"PxrStylizedToon": "PxrStylizedToon"}, + {"PxrWhitePointDisplayFilter": "PxrWhitePointDisplayFilter"} + ] + }, + { + "key": "sample_filters", + "label": "Sample Filters", + "type": "enum", + "multiselection": true, + "defaults": "empty", + "enum_items": [ + {"PxrBackgroundSampleFilter": "PxrBackgroundSampleFilter"}, + {"PxrCopyAOVSampleFilter": "PxrCopyAOVSampleFilter"}, + {"PxrCryptomatte": "PxrCryptomatte"}, + {"PxrFilmicTonemapperSampleFilter": "PxrFilmicTonemapperSampleFilter"}, + {"PxrGradeSampleFilter": "PxrGradeSampleFilter"}, + {"PxrShadowFilter": "PxrShadowFilter"}, + {"PxrWatermarkFilter": "PxrWatermarkFilter"}, + {"PxrWhitePointSampleFilter": "PxrWhitePointSampleFilter"} + ] + }, + { + "type": "label", + "label": "Add additional options - put attribute and value, like Ci" + }, + { + "type": "dict-modifiable", + "store_as_list": true, + "key": "additional_options", + "label": "Additional Renderer Options", + "use_label_wrap": true, + "object_type": { + "type": "text" + } + } + ] + } ] } From 28dfc7b9f659e21189fe43faa1ca95b30546f005 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 19 Oct 2022 22:33:17 +0800 Subject: [PATCH 0043/1271] Renderman setup for sample and display filters --- openpype/hosts/maya/api/lib_rendersettings.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index dad9dde114..ac3a68ca29 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -4,7 +4,6 @@ from maya import cmds # noqa import maya.mel as mel import six import sys -import os from openpype.lib import Logger from openpype.api import ( @@ -178,7 +177,8 @@ class RenderSettings(object): filter_nodes = d_node[0] else: filter_nodes = cmds.createNode(display_filters[i]) - cmds.connectAttr(filter_nodes+".message", + + cmds.connectAttr(filter_nodes + ".message", "rmanGlobals.displayFilters[%i]"% i, force=True) @@ -190,7 +190,8 @@ class RenderSettings(object): filter_nodes = s_node[0] else: filter_nodes = cmds.createNode(sample_filters[n]) - cmds.connectAttr(filter_nodes+".message", + + cmds.connectAttr(filter_nodes + ".message", "rmanGlobals.sampleFilters[%i]"% n, force=True) From bf5213a780892b42524d169068e04e6416eb066f Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 19 Oct 2022 22:34:35 +0800 Subject: [PATCH 0044/1271] Renderman setup for sample and display filters --- openpype/hosts/maya/api/lib_rendersettings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index ac3a68ca29..c56d4aa903 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -179,7 +179,7 @@ class RenderSettings(object): filter_nodes = cmds.createNode(display_filters[i]) cmds.connectAttr(filter_nodes + ".message", - "rmanGlobals.displayFilters[%i]"% i, + "rmanGlobals.displayFilters[%i]" % i, force=True) sample_filters = rman_render_presets["sample_filters"] @@ -192,7 +192,7 @@ class RenderSettings(object): filter_nodes = cmds.createNode(sample_filters[n]) cmds.connectAttr(filter_nodes + ".message", - "rmanGlobals.sampleFilters[%i]"% n, + "rmanGlobals.sampleFilters[%i]" % n, force=True) additional_options = rman_render_presets["additional_options"] From 769763c4751a41c47fd570b94767353fc1f296b1 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 26 Oct 2022 20:00:22 +0800 Subject: [PATCH 0045/1271] Renderman setup for sample and display filters --- openpype/hosts/maya/api/lib_rendersettings.py | 2 +- .../defaults/project_settings/maya.json | 60 ++++++++++++---- .../projects_schema/schema_project_maya.json | 70 +++++++++++++++++++ 3 files changed, 116 insertions(+), 16 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index c56d4aa903..91dde16db3 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -6,7 +6,7 @@ import six import sys from openpype.lib import Logger -from openpype.api import ( +from openpype.settings import ( get_project_settings, get_current_project_settings ) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 777a0bccb2..3b03bbe2dd 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1,5 +1,27 @@ { - "mel_workspace": "workspace -fr \"shaders\" \"renderData/shaders\";\nworkspace -fr \"images\" \"renders\";\nworkspace -fr \"particles\" \"particles\";\nworkspace -fr \"mayaAscii\" \"\";\nworkspace -fr \"mayaBinary\" \"\";\nworkspace -fr \"scene\" \"\";\nworkspace -fr \"alembicCache\" \"cache/alembic\";\nworkspace -fr \"renderData\" \"renderData\";\nworkspace -fr \"sourceImages\" \"sourceimages\";\nworkspace -fr \"fileCache\" \"cache/nCache\";\n", + "imageio": { + "colorManagementPreference_v2": { + "enabled": true, + "configFilePath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "renderSpace": "ACEScg", + "displayName": "sRGB", + "viewName": "ACES 1.0 SDR-video" + }, + "colorManagementPreference": { + "configFilePath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "renderSpace": "scene-linear Rec 709/sRGB", + "viewTransform": "sRGB gamma" + } + }, + "mel_workspace": "workspace -fr \"shaders\" \"renderData/shaders\";\nworkspace -fr \"images\" \"renders/maya\";\nworkspace -fr \"particles\" \"particles\";\nworkspace -fr \"mayaAscii\" \"\";\nworkspace -fr \"mayaBinary\" \"\";\nworkspace -fr \"scene\" \"\";\nworkspace -fr \"alembicCache\" \"cache/alembic\";\nworkspace -fr \"renderData\" \"renderData\";\nworkspace -fr \"sourceImages\" \"sourceimages\";\nworkspace -fr \"fileCache\" \"cache/nCache\";\n", "ext_mapping": { "model": "ma", "mayaAscii": "ma", @@ -34,12 +56,12 @@ }, "RenderSettings": { "apply_render_settings": true, - "default_render_image_folder": "renders", - "enable_all_lights": false, + "default_render_image_folder": "renders/maya", + "enable_all_lights": true, "aov_separator": "underscore", "reset_current_frame": false, "arnold_renderer": { - "image_prefix": "maya///_", + "image_prefix": "//_", "image_format": "exr", "multilayer_exr": true, "tiled": true, @@ -47,14 +69,14 @@ "additional_options": [] }, "vray_renderer": { - "image_prefix": "maya///", + "image_prefix": "//", "engine": "1", "image_format": "exr", "aov_list": [], "additional_options": [] }, "redshift_renderer": { - "image_prefix": "maya///", + "image_prefix": "//", "primary_gi_engine": "0", "secondary_gi_engine": "0", "image_format": "exr", @@ -64,7 +86,7 @@ "additional_options": [] }, "renderman_renderer": { - "image_prefix": "maya///{aov_separator}", + "image_prefix": "//{aov_separator}", "display_filters": [], "sample_filters": [], "additional_options": [] @@ -110,13 +132,25 @@ "CreateAnimation": { "enabled": true, "write_color_sets": false, + "write_face_sets": false, "defaults": [ "Main" ] }, + "CreateModel": { + "enabled": true, + "write_color_sets": false, + "write_face_sets": false, + "defaults": [ + "Main", + "Proxy", + "Sculpt" + ] + }, "CreatePointCache": { "enabled": true, "write_color_sets": false, + "write_face_sets": false, "defaults": [ "Main" ] @@ -169,14 +203,6 @@ "Main" ] }, - "CreateModel": { - "enabled": true, - "defaults": [ - "Main", - "Proxy", - "Sculpt" - ] - }, "CreateRenderSetup": { "enabled": true, "defaults": [ @@ -557,6 +583,10 @@ "vrayproxy" ] }, + "ExtractObj": { + "enabled": false, + "optional": true + }, "ValidateRigContents": { "enabled": false, "optional": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json index d7a2b086d9..b2d79797a3 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json @@ -5,6 +5,76 @@ "label": "Maya", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "collapsible": true, + "is_group": true, + "children": [ + { + "key": "colorManagementPreference_v2", + "type": "dict", + "label": "Color Management Preference v2 (Maya 2022+)", + "collapsible": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Use Color Management Preference v2" + }, + { + "type": "path", + "key": "configFilePath", + "label": "OCIO Config File Path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "renderSpace", + "label": "Rendering Space" + }, + { + "type": "text", + "key": "displayName", + "label": "Display" + }, + { + "type": "text", + "key": "viewName", + "label": "View" + } + ] + }, + { + "key": "colorManagementPreference", + "type": "dict", + "label": "Color Management Preference (legacy)", + "collapsible": true, + "children": [ + { + "type": "path", + "key": "configFilePath", + "label": "OCIO Config File Path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "renderSpace", + "label": "Rendering Space" + }, + { + "type": "text", + "key": "viewTransform", + "label": "Viewer Transform" + } + ] + } + ] + }, { "type": "text", "multiline" : true, From fed2a212a8de6d2fe31d6818c23c4923cd9c12da Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 26 Oct 2022 20:04:36 +0800 Subject: [PATCH 0046/1271] Renderman setup for sample and display filters --- .../schemas/schema_maya_render_settings.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json index a0b38906be..a97c58f45d 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json @@ -140,7 +140,7 @@ }, { "type": "label", - "label": "Add additional options - put attribute and value, like AASamples" + "label": "Add additional options - put attribute and value, like defaultArnoldRenderOptions.AASamples = 4" }, { "type": "dict-modifiable", @@ -276,7 +276,7 @@ }, { "type": "label", - "label": "Add additional options - put attribute and value, like aaFilterSize" + "label": "Add additional options - put attribute and value, like vraySettings.aaFilterSize = 1.5" }, { "type": "dict-modifiable", @@ -405,7 +405,7 @@ }, { "type": "label", - "label": "Add additional options - put attribute and value, like reflectionMaxTraceDepth" + "label": "Add additional options - put attribute and value, like redshiftOptions.reflectionMaxTraceDepth = 3" }, { "type": "dict-modifiable", From 8a9399f93be72d40b5b25da9711c140ffcb5fc9b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 28 Oct 2022 20:03:45 +0800 Subject: [PATCH 0047/1271] Renderman setup for sample and display filters --- openpype/hosts/maya/api/lib_renderproducts.py | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 1e883ea43f..cd204445b7 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -80,7 +80,7 @@ IMAGE_PREFIXES = { "mayahardware2": "defaultRenderGlobals.imageFilePrefix" } -RENDERMAN_IMAGE_DIR = "maya//" +RENDERMAN_IMAGE_DIR = "/" def has_tokens(string, tokens): @@ -260,20 +260,20 @@ class ARenderProducts: """ try: - file_prefix_attr = IMAGE_PREFIXES[self.renderer] + prefix_attr = IMAGE_PREFIXES[self.renderer] except KeyError: raise UnsupportedRendererException( "Unsupported renderer {}".format(self.renderer) ) - file_prefix = self._get_attr(file_prefix_attr) + prefix = self._get_attr(prefix_attr) - if not file_prefix: + if not prefix: # Fall back to scene name by default log.debug("Image prefix not set, using ") file_prefix = "" - return file_prefix + return prefix def get_render_attribute(self, attribute): """Get attribute from render options. @@ -730,13 +730,16 @@ class RenderProductsVray(ARenderProducts): """Get image prefix for V-Ray. This overrides :func:`ARenderProducts.get_renderer_prefix()` as - we must add `` token manually. + we must add `` token manually. This is done only for + non-multipart outputs, where `` token doesn't make sense. See also: :func:`ARenderProducts.get_renderer_prefix()` """ prefix = super(RenderProductsVray, self).get_renderer_prefix() + if self.multipart: + return prefix aov_separator = self._get_aov_separator() prefix = "{}{}".format(prefix, aov_separator) return prefix @@ -974,15 +977,18 @@ class RenderProductsRedshift(ARenderProducts): """Get image prefix for Redshift. This overrides :func:`ARenderProducts.get_renderer_prefix()` as - we must add `` token manually. + we must add `` token manually. This is done only for + non-multipart outputs, where `` token doesn't make sense. See also: :func:`ARenderProducts.get_renderer_prefix()` """ - file_prefix = super(RenderProductsRedshift, self).get_renderer_prefix() - separator = self.extract_separator(file_prefix) - prefix = "{}{}".format(file_prefix, separator or "_") + prefix = super(RenderProductsRedshift, self).get_renderer_prefix() + if self.multipart: + return prefix + separator = self.extract_separator(prefix) + prefix = "{}{}".format(prefix, separator or "_") return prefix def get_render_products(self): From 7cf4578d411a4500d908ff4f5102f29c1d5bccb9 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 8 Nov 2022 14:22:36 +0800 Subject: [PATCH 0048/1271] renderman-support-for-sample-and-display --- openpype/hosts/maya/api/lib_rendersettings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 91dde16db3..10b94c324f 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -29,7 +29,11 @@ class RenderSettings(object): _image_prefixes = { 'vray': get_current_project_settings()["maya"]["RenderSettings"]["vray_renderer"]["image_prefix"], # noqa 'arnold': get_current_project_settings()["maya"]["RenderSettings"]["arnold_renderer"]["image_prefix"], # noqa +<<<<<<< HEAD 'renderman': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["image_prefix"], # noqa +======= + 'renderman': '//{aov_separator}', +>>>>>>> develop 'redshift': get_current_project_settings()["maya"]["RenderSettings"]["redshift_renderer"]["image_prefix"] # noqa } From 64668576c4ff950542e3e0eadf651146b109ab90 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 8 Nov 2022 14:29:07 +0800 Subject: [PATCH 0049/1271] renderman --- openpype/hosts/maya/api/lib_rendersettings.py | 4 ---- vendor/configs/OpenColorIO-Configs | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) create mode 160000 vendor/configs/OpenColorIO-Configs diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 10b94c324f..91dde16db3 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -29,11 +29,7 @@ class RenderSettings(object): _image_prefixes = { 'vray': get_current_project_settings()["maya"]["RenderSettings"]["vray_renderer"]["image_prefix"], # noqa 'arnold': get_current_project_settings()["maya"]["RenderSettings"]["arnold_renderer"]["image_prefix"], # noqa -<<<<<<< HEAD 'renderman': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["image_prefix"], # noqa -======= - 'renderman': '//{aov_separator}', ->>>>>>> develop 'redshift': get_current_project_settings()["maya"]["RenderSettings"]["redshift_renderer"]["image_prefix"] # noqa } diff --git a/vendor/configs/OpenColorIO-Configs b/vendor/configs/OpenColorIO-Configs new file mode 160000 index 0000000000..0bb079c08b --- /dev/null +++ b/vendor/configs/OpenColorIO-Configs @@ -0,0 +1 @@ +Subproject commit 0bb079c08be410030669cbf5f19ff869b88af953 From 8dc7a17a79932d479e105f09d66e9b04823b4e14 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 8 Nov 2022 14:30:11 +0800 Subject: [PATCH 0050/1271] Renderman support for sample and deisplay filter --- openpype/hosts/maya/api/lib_rendersettings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 91dde16db3..61e2a4b2ca 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -6,7 +6,7 @@ import six import sys from openpype.lib import Logger -from openpype.settings import ( +from openpype.api import ( get_project_settings, get_current_project_settings ) @@ -29,7 +29,7 @@ class RenderSettings(object): _image_prefixes = { 'vray': get_current_project_settings()["maya"]["RenderSettings"]["vray_renderer"]["image_prefix"], # noqa 'arnold': get_current_project_settings()["maya"]["RenderSettings"]["arnold_renderer"]["image_prefix"], # noqa - 'renderman': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["image_prefix"], # noqa + 'renderman': 'maya///{aov_separator}', 'redshift': get_current_project_settings()["maya"]["RenderSettings"]["redshift_renderer"]["image_prefix"] # noqa } From d3f0ab475406232bb50cca8ed2800e60a7ae52cd Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 8 Nov 2022 14:31:24 +0800 Subject: [PATCH 0051/1271] Renderman support for sample and deisplay filter --- openpype/hosts/maya/api/lib_rendersettings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 61e2a4b2ca..c56d4aa903 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -29,7 +29,7 @@ class RenderSettings(object): _image_prefixes = { 'vray': get_current_project_settings()["maya"]["RenderSettings"]["vray_renderer"]["image_prefix"], # noqa 'arnold': get_current_project_settings()["maya"]["RenderSettings"]["arnold_renderer"]["image_prefix"], # noqa - 'renderman': 'maya///{aov_separator}', + 'renderman': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["image_prefix"], # noqa 'redshift': get_current_project_settings()["maya"]["RenderSettings"]["redshift_renderer"]["image_prefix"] # noqa } From 7885a7b10c87a5387ac276a9d303e6801ff0ce6e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 8 Nov 2022 14:32:35 +0800 Subject: [PATCH 0052/1271] Renderman support for sample and deisplay filter --- openpype/hosts/maya/api/lib_rendersettings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index c56d4aa903..61e2a4b2ca 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -29,7 +29,7 @@ class RenderSettings(object): _image_prefixes = { 'vray': get_current_project_settings()["maya"]["RenderSettings"]["vray_renderer"]["image_prefix"], # noqa 'arnold': get_current_project_settings()["maya"]["RenderSettings"]["arnold_renderer"]["image_prefix"], # noqa - 'renderman': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["image_prefix"], # noqa + 'renderman': 'maya///{aov_separator}', 'redshift': get_current_project_settings()["maya"]["RenderSettings"]["redshift_renderer"]["image_prefix"] # noqa } From 59cab53c68908227f60629157c8181646d7c93d0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 8 Nov 2022 14:33:50 +0800 Subject: [PATCH 0053/1271] Renderman support for sample and deisplay filter --- openpype/hosts/maya/api/lib_rendersettings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 61e2a4b2ca..33685818e3 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -29,7 +29,7 @@ class RenderSettings(object): _image_prefixes = { 'vray': get_current_project_settings()["maya"]["RenderSettings"]["vray_renderer"]["image_prefix"], # noqa 'arnold': get_current_project_settings()["maya"]["RenderSettings"]["arnold_renderer"]["image_prefix"], # noqa - 'renderman': 'maya///{aov_separator}', + 'renderman': '//{aov_separator}', 'redshift': get_current_project_settings()["maya"]["RenderSettings"]["redshift_renderer"]["image_prefix"] # noqa } From 81e31576163c9fe63dd31997a64356a1628b28e8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 8 Nov 2022 14:35:19 +0800 Subject: [PATCH 0054/1271] Renderman support for sample and deisplay filter --- openpype/hosts/maya/api/lib_rendersettings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 33685818e3..bfd24a5608 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -29,7 +29,7 @@ class RenderSettings(object): _image_prefixes = { 'vray': get_current_project_settings()["maya"]["RenderSettings"]["vray_renderer"]["image_prefix"], # noqa 'arnold': get_current_project_settings()["maya"]["RenderSettings"]["arnold_renderer"]["image_prefix"], # noqa - 'renderman': '//{aov_separator}', + 'renderman': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["image_prefix"], # noqa 'redshift': get_current_project_settings()["maya"]["RenderSettings"]["redshift_renderer"]["image_prefix"] # noqa } From c3c7288803da3babe334c30411024bfedf9e7d79 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 8 Nov 2022 14:36:17 +0800 Subject: [PATCH 0055/1271] Renderman support for sample and deisplay filter --- openpype/hosts/maya/api/lib_rendersettings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index bfd24a5608..33685818e3 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -29,7 +29,7 @@ class RenderSettings(object): _image_prefixes = { 'vray': get_current_project_settings()["maya"]["RenderSettings"]["vray_renderer"]["image_prefix"], # noqa 'arnold': get_current_project_settings()["maya"]["RenderSettings"]["arnold_renderer"]["image_prefix"], # noqa - 'renderman': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["image_prefix"], # noqa + 'renderman': '//{aov_separator}', 'redshift': get_current_project_settings()["maya"]["RenderSettings"]["redshift_renderer"]["image_prefix"] # noqa } From ddcb21362ad4a19c48d3e321beeaaa456c80fa48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 23 Nov 2022 14:54:55 +0100 Subject: [PATCH 0056/1271] Removed submodule vendor/configs/OpenColorIO-Configs --- vendor/configs/OpenColorIO-Configs | 1 - 1 file changed, 1 deletion(-) delete mode 160000 vendor/configs/OpenColorIO-Configs diff --git a/vendor/configs/OpenColorIO-Configs b/vendor/configs/OpenColorIO-Configs deleted file mode 160000 index 0bb079c08b..0000000000 --- a/vendor/configs/OpenColorIO-Configs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0bb079c08be410030669cbf5f19ff869b88af953 From 474958f7fa7d7fff65605a58e71b090ce5bd658a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Nov 2022 15:35:28 +0800 Subject: [PATCH 0057/1271] set the correct path for filters, image ouptut and image directory --- openpype/hosts/maya/api/lib_rendersettings.py | 29 +++++++++++++++++-- .../defaults/project_settings/maya.json | 6 +++- .../schemas/schema_maya_render_settings.json | 20 +++++++++++++ 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 43c6cd6e36..2da444c3fe 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -22,17 +22,25 @@ class RenderSettings(object): _image_prefix_nodes = { 'vray': 'vraySettings.fileNamePrefix', 'arnold': 'defaultRenderGlobals.imageFilePrefix', - 'renderman': 'defaultRenderGlobals.imageFilePrefix', + 'renderman': 'rmanGlobals.imageFileFormat', 'redshift': 'defaultRenderGlobals.imageFilePrefix' } _image_prefixes = { 'vray': get_current_project_settings()["maya"]["RenderSettings"]["vray_renderer"]["image_prefix"], # noqa 'arnold': get_current_project_settings()["maya"]["RenderSettings"]["arnold_renderer"]["image_prefix"], # noqa - 'renderman': '//{aov_separator}', + 'renderman': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["image_prefix"], # noqa 'redshift': get_current_project_settings()["maya"]["RenderSettings"]["redshift_renderer"]["image_prefix"] # noqa } + # Renderman only + _image_dir ={ + 'renderman': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["image_dir"], # noqa + 'cryptomatte': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["cryptomatte_dir"], # noqa + 'imageDisplay': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["imageDisplay_dir"], # noqa + "watermark": get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["watermark_dir"] # noqa + } + _aov_chars = { "dot": ".", "dash": "-", @@ -81,7 +89,6 @@ class RenderSettings(object): prefix, type="string") # noqa else: print("{0} isn't a supported renderer to autoset settings.".format(renderer)) # noqa - # TODO: handle not having res values in the doc width = asset_doc["data"].get("resolutionWidth") height = asset_doc["data"].get("resolutionHeight") @@ -97,6 +104,9 @@ class RenderSettings(object): self._set_redshift_settings(width, height) if renderer == "renderman": + image_dir = self._image_dir["renderman"] + cmds.setAttr("rmanGlobals.imageOutputDir", + image_dir, type= "string") self._set_renderman_settings(width, height) def _set_arnold_settings(self, width, height): @@ -181,6 +191,10 @@ class RenderSettings(object): cmds.connectAttr(filter_nodes + ".message", "rmanGlobals.displayFilters[%i]" % i, force=True) + if filter_nodes.startswith("PxrImageDisplayFilter"): + imageDisplay_dir = self._image_dir["imageDisplay"] + cmds.setAttr(filter_nodes + ".filename", + imageDisplay_dir, type="string") sample_filters = rman_render_presets["sample_filters"] s_filters_number = len(sample_filters) @@ -195,6 +209,15 @@ class RenderSettings(object): "rmanGlobals.sampleFilters[%i]" % n, force=True) + if filter_nodes.startswith("PxrCryptomatte"): + matte_dir = self._image_dir["cryptomatte"] + cmds.setAttr(filter_nodes + ".filename", + matte_dir, type="string") + elif filter_nodes.startswith("PxrWatermarkFilter"): + watermark_dir = self._image_dir["watermark"] + cmds.setAttr(filter_nodes + ".filename", + watermark_dir, type="string") + additional_options = rman_render_presets["additional_options"] self._set_global_output_settings() diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 3b03bbe2dd..e14a87af86 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -86,9 +86,13 @@ "additional_options": [] }, "renderman_renderer": { - "image_prefix": "//{aov_separator}", + "image_prefix": "{aov_separator}..", + "image_dir": "/", "display_filters": [], + "imageDisplay_dir": "/imageDisplayFilter..", "sample_filters": [], + "cryptomatte_dir": "/cryptomatte..", + "watermark_dir": "/watermarkFilter..", "additional_options": [] } }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json index a97c58f45d..1edf1dbc77 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json @@ -431,6 +431,11 @@ "label": "Image prefix template", "type": "text" }, + { + "key": "image_dir", + "label": "Image Output Directory", + "type": "text" + }, { "key": "display_filters", "label": "Display Filters", @@ -453,6 +458,11 @@ {"PxrWhitePointDisplayFilter": "PxrWhitePointDisplayFilter"} ] }, + { + "key": "imageDisplay_dir", + "label": "Image Display Filter Directory", + "type": "text" + }, { "key": "sample_filters", "label": "Sample Filters", @@ -470,6 +480,16 @@ {"PxrWhitePointSampleFilter": "PxrWhitePointSampleFilter"} ] }, + { + "key": "cryptomatte_dir", + "label": "Cryptomatte Output Directory", + "type": "text" + }, + { + "key": "watermark_dir", + "label": "Watermark Filter Directory", + "type": "text" + }, { "type": "label", "label": "Add additional options - put attribute and value, like Ci" From 6e3c75f0f0c3d01845828382a8daf72f5605aa17 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Nov 2022 15:38:11 +0800 Subject: [PATCH 0058/1271] set the correct path for filters, image ouptut and image directory --- openpype/hosts/maya/api/lib_rendersettings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 2da444c3fe..cd7bbfea4d 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -34,7 +34,7 @@ class RenderSettings(object): } # Renderman only - _image_dir ={ + _image_dir = { 'renderman': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["image_dir"], # noqa 'cryptomatte': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["cryptomatte_dir"], # noqa 'imageDisplay': get_current_project_settings()["maya"]["RenderSettings"]["renderman_renderer"]["imageDisplay_dir"], # noqa @@ -105,8 +105,8 @@ class RenderSettings(object): if renderer == "renderman": image_dir = self._image_dir["renderman"] - cmds.setAttr("rmanGlobals.imageOutputDir", - image_dir, type= "string") + cmds.setAttr("rmanGlobals.imageOutputDir", image_dir, + type= "string") self._set_renderman_settings(width, height) def _set_arnold_settings(self, width, height): From 8b5a7c4061a0eb68a60271023ddc8a404b9a7fec Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Nov 2022 15:40:56 +0800 Subject: [PATCH 0059/1271] set the correct path for filters, image ouptut and image directory --- openpype/hosts/maya/api/lib_rendersettings.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index cd7bbfea4d..847e57ffa0 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -105,8 +105,9 @@ class RenderSettings(object): if renderer == "renderman": image_dir = self._image_dir["renderman"] - cmds.setAttr("rmanGlobals.imageOutputDir", image_dir, - type= "string") + rman_image_dir_attr = "rmanGlobals.imageOutputDir" + cmds.setAttr(rman_image_dir_attr, + image_dir,type= "string") self._set_renderman_settings(width, height) def _set_arnold_settings(self, width, height): From f63c28fd5b76c9e64be909c1b33b484c69f059df Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Nov 2022 15:42:40 +0800 Subject: [PATCH 0060/1271] set the correct path for filters, image ouptut and image directory --- openpype/hosts/maya/api/lib_rendersettings.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 847e57ffa0..5ec1756506 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -106,8 +106,7 @@ class RenderSettings(object): if renderer == "renderman": image_dir = self._image_dir["renderman"] rman_image_dir_attr = "rmanGlobals.imageOutputDir" - cmds.setAttr(rman_image_dir_attr, - image_dir,type= "string") + cmds.setAttr(rman_image_dir_attr, image_dir, type= "string") self._set_renderman_settings(width, height) def _set_arnold_settings(self, width, height): From 7e65eb24018795df68b794166d21f4e0f515a9d7 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Nov 2022 15:44:01 +0800 Subject: [PATCH 0061/1271] set the correct path for filters, image ouptut and image directory --- openpype/hosts/maya/api/lib_rendersettings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 5ec1756506..0a3828787c 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -105,8 +105,8 @@ class RenderSettings(object): if renderer == "renderman": image_dir = self._image_dir["renderman"] - rman_image_dir_attr = "rmanGlobals.imageOutputDir" - cmds.setAttr(rman_image_dir_attr, image_dir, type= "string") + cmds.setAttr("rmanGlobals.imageOutputDir", + image_dir, type="string") self._set_renderman_settings(width, height) def _set_arnold_settings(self, width, height): From 7dc9734a1e1a3f60348b96fc9314bdfde2c99c87 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Nov 2022 18:49:18 +0800 Subject: [PATCH 0062/1271] add the correct image prefix format for Renderman --- openpype/hosts/maya/api/lib_rendersettings.py | 11 +++++++++-- openpype/settings/defaults/project_settings/maya.json | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 0a3828787c..a99ae057e2 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -107,7 +107,8 @@ class RenderSettings(object): image_dir = self._image_dir["renderman"] cmds.setAttr("rmanGlobals.imageOutputDir", image_dir, type="string") - self._set_renderman_settings(width, height) + self._set_renderman_settings(width, height, + aov_separator) def _set_arnold_settings(self, width, height): """Sets settings for Arnold.""" @@ -171,7 +172,7 @@ class RenderSettings(object): cmds.setAttr("defaultResolution.height", height) self._additional_attribs_setter(additional_options) - def _set_renderman_settings(self, width, height): + def _set_renderman_settings(self, width, height, aov_separator): """Sets settings for Renderman""" rman_render_presets = ( self._project_settings @@ -193,6 +194,8 @@ class RenderSettings(object): force=True) if filter_nodes.startswith("PxrImageDisplayFilter"): imageDisplay_dir = self._image_dir["imageDisplay"] + imageDisplay_dir = imageDisplay_dir.replace("{aov_separator}", + aov_separator) cmds.setAttr(filter_nodes + ".filename", imageDisplay_dir, type="string") @@ -211,10 +214,14 @@ class RenderSettings(object): if filter_nodes.startswith("PxrCryptomatte"): matte_dir = self._image_dir["cryptomatte"] + matte_dir = matte_dir.replace("{aov_separator}", + aov_separator) cmds.setAttr(filter_nodes + ".filename", matte_dir, type="string") elif filter_nodes.startswith("PxrWatermarkFilter"): watermark_dir = self._image_dir["watermark"] + watermark_dir = watermark_dir.replace("{aov_separator}", + aov_separator) cmds.setAttr(filter_nodes + ".filename", watermark_dir, type="string") diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index e14a87af86..9c33d19878 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -89,10 +89,10 @@ "image_prefix": "{aov_separator}..", "image_dir": "/", "display_filters": [], - "imageDisplay_dir": "/imageDisplayFilter..", + "imageDisplay_dir": "/{aov_separator}imageDisplayFilter..", "sample_filters": [], - "cryptomatte_dir": "/cryptomatte..", - "watermark_dir": "/watermarkFilter..", + "cryptomatte_dir": "/{aov_separator}cryptomatte..", + "watermark_dir": "/{aov_separator}watermarkFilter..", "additional_options": [] } }, From 8ad8aa9e52dc44fec91b85ed2764c6ab645a7d51 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Nov 2022 18:50:16 +0800 Subject: [PATCH 0063/1271] add the correct image prefix format for Renderman --- openpype/hosts/maya/api/lib_rendersettings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index a99ae057e2..da0eb44da2 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -221,7 +221,7 @@ class RenderSettings(object): elif filter_nodes.startswith("PxrWatermarkFilter"): watermark_dir = self._image_dir["watermark"] watermark_dir = watermark_dir.replace("{aov_separator}", - aov_separator) + aov_separator) cmds.setAttr(filter_nodes + ".filename", watermark_dir, type="string") From 6af973fc8fc0c1ad5a5adf0996ef64c413629fce Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 14 Dec 2022 08:59:14 +0000 Subject: [PATCH 0064/1271] Adjust xgen creation --- openpype/hosts/maya/plugins/create/create_xgen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_xgen.py b/openpype/hosts/maya/plugins/create/create_xgen.py index 8672c06a1e..70e23cf47b 100644 --- a/openpype/hosts/maya/plugins/create/create_xgen.py +++ b/openpype/hosts/maya/plugins/create/create_xgen.py @@ -2,9 +2,9 @@ from openpype.hosts.maya.api import plugin class CreateXgen(plugin.Creator): - """Xgen interactive export""" + """Xgen""" name = "xgen" - label = "Xgen Interactive" + label = "Xgen" family = "xgen" icon = "pagelines" From 91d935c948c18ddf85dbfa54db924907b0067fcb Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 14 Dec 2022 08:59:26 +0000 Subject: [PATCH 0065/1271] Validate xgen --- .../maya/plugins/publish/validate_xgen.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 openpype/hosts/maya/plugins/publish/validate_xgen.py diff --git a/openpype/hosts/maya/plugins/publish/validate_xgen.py b/openpype/hosts/maya/plugins/publish/validate_xgen.py new file mode 100644 index 0000000000..7ccd02bb07 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_xgen.py @@ -0,0 +1,28 @@ +import maya.cmds as cmds + +import pyblish.api + + +class ValidateXgen(pyblish.api.InstancePlugin): + """Ensure Xgen objectset only contains collections.""" + + label = "Xgen" + order = pyblish.api.ValidatorOrder + host = ["maya"] + families = ["xgen"] + + def process(self, instance): + nodes = ( + cmds.ls(instance, type="xgmPalette", long=True) + + cmds.ls(instance, type="transform", long=True) + + cmds.ls(instance, type="xgmDescription", long=True) + + cmds.ls(instance, type="xgmSubdPatch", long=True) + ) + remainder_nodes = [] + for node in instance: + if node in nodes: + continue + remainder_nodes.append(node) + + msg = "Invalid nodes in the objectset:\n{}".format(remainder_nodes) + assert not remainder_nodes, msg From 94f0f773f865857da894e824630fdacec8666b4c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sun, 18 Dec 2022 21:08:25 +0000 Subject: [PATCH 0066/1271] Exclude xgen from default Maya loading. --- openpype/hosts/maya/plugins/load/actions.py | 15 ++++++++++++++- .../hosts/maya/plugins/load/load_reference.py | 1 - 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/actions.py b/openpype/hosts/maya/plugins/load/actions.py index 9cc9180d6e..ced0f7ccbb 100644 --- a/openpype/hosts/maya/plugins/load/actions.py +++ b/openpype/hosts/maya/plugins/load/actions.py @@ -93,7 +93,20 @@ class ImportMayaLoader(load.LoaderPlugin): """ representations = ["ma", "mb", "obj"] - families = ["*"] + families = [ + "model", + "pointcache", + "proxyAbc", + "animation", + "mayaAscii", + "mayaScene", + "setdress", + "layout", + "camera", + "rig", + "camerarig", + "staticMesh" + ] label = "Import" order = 10 diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index c6b07b036d..31a36a587c 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -25,7 +25,6 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): "camera", "rig", "camerarig", - "xgen", "staticMesh"] representations = ["ma", "abc", "fbx", "mb"] From bf2d4ee323eb3df631cdbb93fabeb58242da0886 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sun, 18 Dec 2022 21:09:55 +0000 Subject: [PATCH 0067/1271] Initial working publish of xgen --- .../maya/plugins/publish/collect_xgen.py | 39 +++++ .../plugins/publish/extract_maya_scene_raw.py | 3 +- .../maya/plugins/publish/extract_xgen.py | 138 ++++++++++++++++++ .../maya/plugins/publish/validate_xgen.py | 11 +- .../plugins/publish/collect_resources_path.py | 3 +- 5 files changed, 185 insertions(+), 9 deletions(-) create mode 100644 openpype/hosts/maya/plugins/publish/collect_xgen.py create mode 100644 openpype/hosts/maya/plugins/publish/extract_xgen.py diff --git a/openpype/hosts/maya/plugins/publish/collect_xgen.py b/openpype/hosts/maya/plugins/publish/collect_xgen.py new file mode 100644 index 0000000000..fa7ec3776d --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/collect_xgen.py @@ -0,0 +1,39 @@ +from maya import cmds + +import pyblish.api +from openpype.hosts.maya.api.lib import get_attribute_input + + +class CollectXgen(pyblish.api.InstancePlugin): + """Collect Xgen""" + + order = pyblish.api.CollectorOrder + 0.499999 + label = "Collect Xgen" + families = ["xgen"] + + def process(self, instance): + data = { + "xgenPalettes": cmds.ls(instance, type="xgmPalette", long=True), + "xgmDescriptions": cmds.ls( + instance, type="xgmDescription", long=True + ), + "xgmSubdPatches": cmds.ls(instance, type="xgmSubdPatch", long=True) + } + data["xgenNodes"] = ( + data["xgenPalettes"] + + data["xgmDescriptions"] + + data["xgmSubdPatches"] + ) + + if data["xgenPalettes"]: + data["xgenPalette"] = data["xgenPalettes"][0] + + data["xgenConnections"] = {} + for node in data["xgmSubdPatches"]: + data["xgenConnections"][node] = {} + for attr in ["transform", "geometry"]: + input = get_attribute_input("{}.{}".format(node, attr)) + data["xgenConnections"][node][attr] = input + + self.log.info(data) + instance.data.update(data) diff --git a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py index 3769ec3605..c2411ca651 100644 --- a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py +++ b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py @@ -20,8 +20,7 @@ class ExtractMayaSceneRaw(publish.Extractor): "mayaScene", "setdress", "layout", - "camerarig", - "xgen"] + "camerarig"] scene_type = "ma" def process(self, instance): diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py new file mode 100644 index 0000000000..69d1d3db40 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -0,0 +1,138 @@ +import os +import copy + +from maya import cmds +import pymel.core as pc +import xgenm + +from openpype.pipeline import publish +from openpype.hosts.maya.api.lib import maintained_selection, attribute_values +from openpype.lib import StringTemplate + + +class ExtractXgenCache(publish.Extractor): + """Extract Xgen""" + + label = "Extract Xgen" + hosts = ["maya"] + families = ["xgen"] + scene_type = "ma" + + def process(self, instance): + if "representations" not in instance.data: + instance.data["representations"] = [] + + staging_dir = self.staging_dir(instance) + maya_filename = "{}.{}".format(instance.data["name"], self.scene_type) + maya_filepath = os.path.join(staging_dir, maya_filename) + + # Get published xgen file name. + template_data = copy.deepcopy(instance.data["anatomyData"]) + template_data.update({"ext": "xgen"}) + templates = instance.context.data["anatomy"].templates["publish"] + xgen_filename = StringTemplate(templates["file"]).format(template_data) + name = instance.data["xgenPalette"].replace(":", "__").replace("|", "") + value = xgen_filename.replace(".xgen", "__" + name + ".xgen") + attribute_data = { + "{}.xgFileName".format(instance.data["xgenPalette"]): xgen_filename + } + + # Export xgen palette files. + xgen_path = os.path.join(staging_dir, value).replace("\\", "/") + xgenm.exportPalette( + instance.data["xgenPalette"].replace("|", ""), xgen_path + ) + self.log.info("Extracted to {}".format(xgen_path)) + + representation = { + "name": name, + "ext": "xgen", + "files": value, + "stagingDir": staging_dir, + } + instance.data["representations"].append(representation) + + # Collect nodes to export. + duplicate_nodes = [] + for node, connections in instance.data["xgenConnections"].items(): + transform_name = connections["transform"].split(".")[0] + + # Duplicate_transform subd patch geometry. + duplicate_transform = pc.duplicate(transform_name)[0] + pc.parent(duplicate_transform, world=True) + duplicate_transform.name(stripNamespace=True) + duplicate_shape = pc.listRelatives( + duplicate_transform, shapes=True + )[0] + + pc.connectAttr( + "{}.matrix".format(duplicate_transform), + "{}.transform".format(node), + force=True + ) + pc.connectAttr( + "{}.worldMesh".format(duplicate_shape), + "{}.geometry".format(node), + force=True + ) + + duplicate_nodes.append(duplicate_transform) + + # Import xgen onto the duplicate. + with maintained_selection(): + cmds.select(duplicate_nodes) + collection = xgenm.importPalette(xgen_path, []) + + # Export Maya file. + type = "mayaAscii" if self.scene_type == "ma" else "mayaBinary" + with attribute_values(attribute_data): + with maintained_selection(): + cmds.select(duplicate_nodes + [collection]) + cmds.file( + maya_filepath, + force=True, + type=type, + exportSelected=True, + preserveReferences=True, + constructionHistory=True, + shader=True, + constraints=True, + expressions=True + ) + + self.log.info("Extracted to {}".format(maya_filepath)) + + representation = { + "name": self.scene_type, + "ext": self.scene_type, + "files": maya_filename, + "stagingDir": staging_dir, + "data": {"xgenName": collection} + } + instance.data["representations"].append(representation) + + # Revert to original xgen connections. + for node, connections in instance.data["xgenConnections"].items(): + for attr, src in connections.items(): + cmds.connectAttr( + src, "{}.{}".format(node, attr), force=True + ) + + cmds.delete(duplicate_nodes + [collection]) + + # Setup transfers. + #needs to reduce resources to only what is used for the collections in + #the objectset + xgen_dir = os.path.join( + os.path.dirname(instance.context.data["currentFile"]), "xgen" + ) + transfers = [] + for root, dirs, files in os.walk(xgen_dir): + for file in files: + source = os.path.join(root, file) + destination = source.replace( + xgen_dir, instance.data["resourcesDir"] + ) + transfers.append((source, destination)) + + instance.data["transfers"] = transfers diff --git a/openpype/hosts/maya/plugins/publish/validate_xgen.py b/openpype/hosts/maya/plugins/publish/validate_xgen.py index 7ccd02bb07..fc93f62846 100644 --- a/openpype/hosts/maya/plugins/publish/validate_xgen.py +++ b/openpype/hosts/maya/plugins/publish/validate_xgen.py @@ -4,19 +4,18 @@ import pyblish.api class ValidateXgen(pyblish.api.InstancePlugin): - """Ensure Xgen objectset only contains collections.""" + """Validate Xgen data.""" - label = "Xgen" + label = "Validate Xgen" order = pyblish.api.ValidatorOrder host = ["maya"] families = ["xgen"] def process(self, instance): + # Validate only xgen collections are in objectset. nodes = ( - cmds.ls(instance, type="xgmPalette", long=True) + - cmds.ls(instance, type="transform", long=True) + - cmds.ls(instance, type="xgmDescription", long=True) + - cmds.ls(instance, type="xgmSubdPatch", long=True) + instance.data["xgenNodes"] + + cmds.ls(instance, type="transform", long=True) ) remainder_nodes = [] for node in instance: diff --git a/openpype/plugins/publish/collect_resources_path.py b/openpype/plugins/publish/collect_resources_path.py index a2d5b95ab2..52aba0eca3 100644 --- a/openpype/plugins/publish/collect_resources_path.py +++ b/openpype/plugins/publish/collect_resources_path.py @@ -57,7 +57,8 @@ class CollectResourcesPath(pyblish.api.InstancePlugin): "background", "effect", "staticMesh", - "skeletalMesh" + "skeletalMesh", + "xgen" ] def process(self, instance): From a1890ee04d9b06031a9c136a276ffba70b2f67b3 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sun, 18 Dec 2022 21:10:07 +0000 Subject: [PATCH 0068/1271] Initial working loading of xgen --- openpype/hosts/maya/plugins/load/load_xgen.py | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 openpype/hosts/maya/plugins/load/load_xgen.py diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py new file mode 100644 index 0000000000..3b7451f95c --- /dev/null +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -0,0 +1,90 @@ +import os +import shutil + +import maya.cmds as cmds +import pymel.core as pm + +import openpype.hosts.maya.api.plugin +from openpype.hosts.maya.api.lib import maintained_selection +from openpype.hosts.maya.api import current_file + + +class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): + """Load Xgen as reference""" + + families = ["xgen"] + representations = ["ma", "mb"] + + label = "Reference Xgen" + icon = "code-fork" + color = "orange" + + def process_reference(self, context, name, namespace, options): + maya_filepath = self.prepare_root_value( + self.fname, context["project"]["name"] + ) + project_path = os.path.dirname(current_file()) + + # Setup xgen palette file. + # Copy the xgen palette file from published version. + _, maya_extension = os.path.splitext(maya_filepath) + source = maya_filepath.replace(maya_extension, ".xgen") + destination = os.path.join( + project_path, + "{basename}__{namespace}__{name}.xgen".format( + basename=os.path.splitext(os.path.basename(current_file()))[0], + namespace=namespace, + name=context["representation"]["data"]["xgenName"] + ) + ) + shutil.copy(source, destination) + + # Modify xgDataPath and xgProjectPath to have current workspace first + # and published version directory second. This ensure that any newly + # created xgen files are created in the current workspace. + resources_path = os.path.join(os.path.dirname(source), "resources") + lines = [] + with open(destination, "r") as f: + for line in [line.rstrip() for line in f]: + if line.startswith("\txgDataPath"): + data_path = line.split("\t")[-1] + line = "\txgDataPath\t\t{}{}{}".format( + data_path, + os.pathsep, + data_path.replace( + "${PROJECT}xgen", resources_path.replace("\\", "/") + ) + ) + + if line.startswith("\txgProjectPath"): + line = "\txgProjectPath\t\t{}/".format( + project_path.replace("\\", "/") + ) + + lines.append(line) + + with open(destination, "w") as f: + f.write("\n".join(lines)) + + # Reference xgen. Xgen does not like being referenced in under a group. + new_nodes = [] + + with maintained_selection(): + nodes = cmds.file( + maya_filepath, + namespace=namespace, + sharedReferenceFile=False, + reference=True, + returnNewNodes=True + ) + + shapes = cmds.ls(nodes, shapes=True, long=True) + + new_nodes = (list(set(nodes) - set(shapes))) + + self[:] = new_nodes + + return new_nodes + + def update(self, container, representation): + pass From 2a7d05e090b6b3a5d30f52f7006f57f9137603bd Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 20 Dec 2022 07:25:00 +0000 Subject: [PATCH 0069/1271] Working destructive updating --- openpype/hosts/maya/plugins/load/load_xgen.py | 63 ++++++++++++++++--- .../maya/plugins/publish/extract_xgen.py | 13 ++-- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 3b7451f95c..2d58550bc7 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -2,11 +2,15 @@ import os import shutil import maya.cmds as cmds -import pymel.core as pm +import xgenm import openpype.hosts.maya.api.plugin -from openpype.hosts.maya.api.lib import maintained_selection +from openpype.hosts.maya.api.lib import ( + maintained_selection, get_container_members +) from openpype.hosts.maya.api import current_file +from openpype.hosts.maya.api.plugin import get_reference_node +from openpype.pipeline import get_representation_path class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): @@ -19,13 +23,10 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): icon = "code-fork" color = "orange" - def process_reference(self, context, name, namespace, options): - maya_filepath = self.prepare_root_value( - self.fname, context["project"]["name"] - ) + def setup_xgen_palette_file(self, maya_filepath, namespace, name): + # Setup xgen palette file. project_path = os.path.dirname(current_file()) - # Setup xgen palette file. # Copy the xgen palette file from published version. _, maya_extension = os.path.splitext(maya_filepath) source = maya_filepath.replace(maya_extension, ".xgen") @@ -34,9 +35,10 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): "{basename}__{namespace}__{name}.xgen".format( basename=os.path.splitext(os.path.basename(current_file()))[0], namespace=namespace, - name=context["representation"]["data"]["xgenName"] + name=name ) - ) + ).replace("\\", "/") + self.log.info("Copying {} to {}".format(source, destination)) shutil.copy(source, destination) # Modify xgDataPath and xgProjectPath to have current workspace first @@ -66,6 +68,18 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): with open(destination, "w") as f: f.write("\n".join(lines)) + return destination + + def process_reference(self, context, name, namespace, options): + maya_filepath = self.prepare_root_value( + self.fname, context["project"]["name"] + ) + + name = context["representation"]["data"]["xgenName"] + xgen_file = self.setup_xgen_palette_file( + maya_filepath, namespace, name + ) + # Reference xgen. Xgen does not like being referenced in under a group. new_nodes = [] @@ -78,6 +92,13 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): returnNewNodes=True ) + xgen_palette = cmds.ls(nodes, type="xgmPalette", long=True)[0] + cmds.setAttr( + "{}.xgFileName".format(xgen_palette), + os.path.basename(xgen_file), + type="string" + ) + shapes = cmds.ls(nodes, shapes=True, long=True) new_nodes = (list(set(nodes) - set(shapes))) @@ -87,4 +108,26 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): return new_nodes def update(self, container, representation): - pass + super().update(container, representation) + + # Get reference node from container members + node = container["objectName"] + members = get_container_members(node) + reference_node = get_reference_node(members, self.log) + namespace = cmds.referenceQuery(reference_node, namespace=True)[1:] + + xgen_file = self.setup_xgen_palette_file( + get_representation_path(representation), + namespace, + representation["data"]["xgenName"] + ) + + xgen_palette = cmds.ls(members, type="xgmPalette", long=True)[0] + cmds.setAttr( + "{}.xgFileName".format(xgen_palette), + os.path.basename(xgen_file), + type="string" + ) + + # Reload reference to update the xgen. + cmds.file(loadReference=reference_node, force=True) diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 69d1d3db40..2daa6e238d 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -32,13 +32,10 @@ class ExtractXgenCache(publish.Extractor): templates = instance.context.data["anatomy"].templates["publish"] xgen_filename = StringTemplate(templates["file"]).format(template_data) name = instance.data["xgenPalette"].replace(":", "__").replace("|", "") - value = xgen_filename.replace(".xgen", "__" + name + ".xgen") - attribute_data = { - "{}.xgFileName".format(instance.data["xgenPalette"]): xgen_filename - } + xgen_filename = xgen_filename.replace(".xgen", "__" + name + ".xgen") # Export xgen palette files. - xgen_path = os.path.join(staging_dir, value).replace("\\", "/") + xgen_path = os.path.join(staging_dir, xgen_filename).replace("\\", "/") xgenm.exportPalette( instance.data["xgenPalette"].replace("|", ""), xgen_path ) @@ -47,7 +44,7 @@ class ExtractXgenCache(publish.Extractor): representation = { "name": name, "ext": "xgen", - "files": value, + "files": xgen_filename, "stagingDir": staging_dir, } instance.data["representations"].append(representation) @@ -83,6 +80,10 @@ class ExtractXgenCache(publish.Extractor): cmds.select(duplicate_nodes) collection = xgenm.importPalette(xgen_path, []) + attribute_data = { + "{}.xgFileName".format(collection): xgen_filename + } + # Export Maya file. type = "mayaAscii" if self.scene_type == "ma" else "mayaBinary" with attribute_values(attribute_data): From 89ea46189e3f8fe6df6006804ec84227003641ad Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 20 Dec 2022 11:46:16 +0000 Subject: [PATCH 0070/1271] Initial non-destructive updating --- openpype/hosts/maya/plugins/load/load_xgen.py | 56 ++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 2d58550bc7..99673440fd 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -79,6 +79,22 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): xgen_file = self.setup_xgen_palette_file( maya_filepath, namespace, name ) + xgd_file = xgen_file.replace(".xgen", ".xgd") + + # Create a placeholder xgen delta file. + #change author and date + xgd_template = """ +# XGen Delta File +# +# Version: C:/Program Files/Autodesk/Maya2022/plug-ins/xgen/ +# Author: tokejepsen +# Date: Tue Dec 20 09:03:29 2022 + +FileVersion 18 + +""" + with open(xgd_file, "w") as f: + f.write(xgd_template) # Reference xgen. Xgen does not like being referenced in under a group. new_nodes = [] @@ -94,10 +110,16 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): xgen_palette = cmds.ls(nodes, type="xgmPalette", long=True)[0] cmds.setAttr( - "{}.xgFileName".format(xgen_palette), + "{}.xgBaseFile".format(xgen_palette), os.path.basename(xgen_file), type="string" ) + cmds.setAttr( + "{}.xgFileName".format(xgen_palette), + os.path.basename(xgd_file), + type="string" + ) + cmds.setAttr("{}.xgExportAsDelta".format(xgen_palette), True) shapes = cmds.ls(nodes, shapes=True, long=True) @@ -108,11 +130,9 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): return new_nodes def update(self, container, representation): - super().update(container, representation) - # Get reference node from container members - node = container["objectName"] - members = get_container_members(node) + container_node = container["objectName"] + members = get_container_members(container_node) reference_node = get_reference_node(members, self.log) namespace = cmds.referenceQuery(reference_node, namespace=True)[1:] @@ -121,13 +141,35 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): namespace, representation["data"]["xgenName"] ) + xgd_file = xgen_file.replace(".xgen", ".xgd") xgen_palette = cmds.ls(members, type="xgmPalette", long=True)[0] + cmds.setAttr( "{}.xgFileName".format(xgen_palette), os.path.basename(xgen_file), type="string" ) + cmds.setAttr( + "{}.xgBaseFile".format(xgen_palette), + "", + type="string" + ) + cmds.setAttr("{}.xgExportAsDelta".format(xgen_palette), False) - # Reload reference to update the xgen. - cmds.file(loadReference=reference_node, force=True) + super().update(container, representation) + + # Apply xgen delta. + xgenm.applyDelta(xgen_palette.replace("|", ""), xgd_file) + + cmds.setAttr( + "{}.xgBaseFile".format(xgen_palette), + os.path.basename(xgen_file), + type="string" + ) + cmds.setAttr( + "{}.xgFileName".format(xgen_palette), + os.path.basename(xgd_file), + type="string" + ) + cmds.setAttr("{}.xgExportAsDelta".format(xgen_palette), True) From c58c0d98784d3579b72bfa721d0dcb35442bf296 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 20 Dec 2022 11:52:39 +0000 Subject: [PATCH 0071/1271] Refactor --- openpype/hosts/maya/plugins/load/load_xgen.py | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 99673440fd..61204522e6 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -6,7 +6,7 @@ import xgenm import openpype.hosts.maya.api.plugin from openpype.hosts.maya.api.lib import ( - maintained_selection, get_container_members + maintained_selection, get_container_members, attribute_values ) from openpype.hosts.maya.api import current_file from openpype.hosts.maya.api.plugin import get_reference_node @@ -145,31 +145,14 @@ FileVersion 18 xgen_palette = cmds.ls(members, type="xgmPalette", long=True)[0] - cmds.setAttr( - "{}.xgFileName".format(xgen_palette), - os.path.basename(xgen_file), - type="string" - ) - cmds.setAttr( - "{}.xgBaseFile".format(xgen_palette), - "", - type="string" - ) - cmds.setAttr("{}.xgExportAsDelta".format(xgen_palette), False) + attribute_data = { + "{}.xgFileName".format(xgen_palette): os.path.basename(xgen_file), + "{}.xgBaseFile".format(xgen_palette): "", + "{}.xgExportAsDelta".format(xgen_palette): False + } - super().update(container, representation) + with attribute_values(attribute_data): + super().update(container, representation) - # Apply xgen delta. - xgenm.applyDelta(xgen_palette.replace("|", ""), xgd_file) - - cmds.setAttr( - "{}.xgBaseFile".format(xgen_palette), - os.path.basename(xgen_file), - type="string" - ) - cmds.setAttr( - "{}.xgFileName".format(xgen_palette), - os.path.basename(xgd_file), - type="string" - ) - cmds.setAttr("{}.xgExportAsDelta".format(xgen_palette), True) + # Apply xgen delta. + xgenm.applyDelta(xgen_palette.replace("|", ""), xgd_file) From 55e3ae2729248c5d9db380fa2f52c20624f3c892 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Dec 2022 09:23:57 +0000 Subject: [PATCH 0072/1271] Fix xgd missing file --- openpype/hosts/maya/plugins/load/load_xgen.py | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 61204522e6..f6b487e7e8 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -81,8 +81,12 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): ) xgd_file = xgen_file.replace(".xgen", ".xgd") - # Create a placeholder xgen delta file. - #change author and date + # Create a placeholder xgen delta file. This create an expression + # attribute of float. If we did not add any changes to the delta file, + # then Xgen deletes the xgd file on save to clean up (?). This gives + # errors when launching the workfile again due to trying to find the + # xgd file. + #change author, date and version path xgd_template = """ # XGen Delta File # @@ -92,6 +96,8 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): FileVersion 18 +Palette custom_float_ignore 0 + """ with open(xgd_file, "w") as f: f.write(xgd_template) @@ -130,7 +136,19 @@ FileVersion 18 return new_nodes def update(self, container, representation): - # Get reference node from container members + """Workflow for updating Xgen. + + - Copy and overwrite the workspace .xgen file. + - Set collection attributes to not include delta files. + - Update xgen maya file reference. + - Apply the delta file changes. + - Reset collection attributes to include delta files. + + We have to do this workflow because when using referencing of the xgen + collection, Maya implicitly imports the Xgen data from the xgen file so + we dont have any control over added the delta file changes. + """ + container_node = container["objectName"] members = get_container_members(container_node) reference_node = get_reference_node(members, self.log) @@ -150,9 +168,7 @@ FileVersion 18 "{}.xgBaseFile".format(xgen_palette): "", "{}.xgExportAsDelta".format(xgen_palette): False } - with attribute_values(attribute_data): super().update(container, representation) - # Apply xgen delta. xgenm.applyDelta(xgen_palette.replace("|", ""), xgd_file) From 87e72491898df9a29c0b44bc044c9f6e08de40fc Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Dec 2022 11:03:50 +0000 Subject: [PATCH 0073/1271] Avoid making placeholder xgd file --- openpype/hosts/maya/plugins/load/load_xgen.py | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index f6b487e7e8..23a22127e9 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -81,27 +81,6 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): ) xgd_file = xgen_file.replace(".xgen", ".xgd") - # Create a placeholder xgen delta file. This create an expression - # attribute of float. If we did not add any changes to the delta file, - # then Xgen deletes the xgd file on save to clean up (?). This gives - # errors when launching the workfile again due to trying to find the - # xgd file. - #change author, date and version path - xgd_template = """ -# XGen Delta File -# -# Version: C:/Program Files/Autodesk/Maya2022/plug-ins/xgen/ -# Author: tokejepsen -# Date: Tue Dec 20 09:03:29 2022 - -FileVersion 18 - -Palette custom_float_ignore 0 - -""" - with open(xgd_file, "w") as f: - f.write(xgd_template) - # Reference xgen. Xgen does not like being referenced in under a group. new_nodes = [] @@ -127,6 +106,14 @@ Palette custom_float_ignore 0 ) cmds.setAttr("{}.xgExportAsDelta".format(xgen_palette), True) + # This create an expression attribute of float. If we did not add + # any changes to collection, then Xgen does not create an xgd file + # on save. This gives errors when launching the workfile again due + # to trying to find the xgd file. + xgenm.addCustomAttr( + "custom_float_ignore", xgen_palette.replace("|", "") + ) + shapes = cmds.ls(nodes, shapes=True, long=True) new_nodes = (list(set(nodes) - set(shapes))) From 2c9613b55e67e2854e149849dc3a057fc6583591 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Dec 2022 11:38:22 +0000 Subject: [PATCH 0074/1271] Validate against deactive modifiers --- .../maya/plugins/publish/validate_xgen.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/validate_xgen.py b/openpype/hosts/maya/plugins/publish/validate_xgen.py index fc93f62846..9393520d6d 100644 --- a/openpype/hosts/maya/plugins/publish/validate_xgen.py +++ b/openpype/hosts/maya/plugins/publish/validate_xgen.py @@ -1,4 +1,7 @@ +import json + import maya.cmds as cmds +import xgenm import pyblish.api @@ -25,3 +28,27 @@ class ValidateXgen(pyblish.api.InstancePlugin): msg = "Invalid nodes in the objectset:\n{}".format(remainder_nodes) assert not remainder_nodes, msg + + # Cant have deactive modifiers in collection cause Xgen will try and + # look for them when loading. + palette = instance.data["xgenPalette"].replace("|", "") + deactive_modifiers = {} + for description in instance.data["xgmDescriptions"]: + description = description.split("|")[-2] + modifier_names = xgenm.fxModules(palette, description) + for name in modifier_names: + attr = xgenm.getAttr("active", palette, description, name) + # Attribute value are lowercase strings of false/true. + if attr == "false": + try: + deactive_modifiers[description].append(name) + except KeyError: + deactive_modifiers[description] = [name] + + msg = ( + "There are deactive modifiers on the collection. " + "Please delete these:\n{}".format( + json.dumps(deactive_modifiers, indent=4, sort_keys=True) + ) + ) + assert not deactive_modifiers, msg From 0fc1e357a35c32bf505f4f1661eb631ffe340585 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Dec 2022 12:19:23 +0000 Subject: [PATCH 0075/1271] Validate against multiple collections per instance. --- openpype/hosts/maya/plugins/publish/validate_xgen.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/validate_xgen.py b/openpype/hosts/maya/plugins/publish/validate_xgen.py index 9393520d6d..1ef85c4cc6 100644 --- a/openpype/hosts/maya/plugins/publish/validate_xgen.py +++ b/openpype/hosts/maya/plugins/publish/validate_xgen.py @@ -29,6 +29,13 @@ class ValidateXgen(pyblish.api.InstancePlugin): msg = "Invalid nodes in the objectset:\n{}".format(remainder_nodes) assert not remainder_nodes, msg + # Only one collection per instance. + palette_amount = len(instance.data["xgenPalettes"]) + msg = "Only one collection per instance allow. Found {}:\n{}".format( + palette_amount, instance.data["xgenPalettes"] + ) + assert palette_amount == 1, msg + # Cant have deactive modifiers in collection cause Xgen will try and # look for them when loading. palette = instance.data["xgenPalette"].replace("|", "") From 1bb6d77e4eab876b354f14637f70ad3a2872c777 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Dec 2022 12:37:24 +0000 Subject: [PATCH 0076/1271] Hound --- openpype/hosts/maya/plugins/publish/extract_xgen.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 2daa6e238d..4979f583b4 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -122,13 +122,11 @@ class ExtractXgenCache(publish.Extractor): cmds.delete(duplicate_nodes + [collection]) # Setup transfers. - #needs to reduce resources to only what is used for the collections in - #the objectset xgen_dir = os.path.join( os.path.dirname(instance.context.data["currentFile"]), "xgen" ) transfers = [] - for root, dirs, files in os.walk(xgen_dir): + for root, _, files in os.walk(xgen_dir): for file in files: source = os.path.join(root, file) destination = source.replace( From 2875bb81fbe98d9504f582b05ae75033360ba140 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Wed, 21 Dec 2022 12:51:49 +0000 Subject: [PATCH 0077/1271] Update openpype/hosts/maya/plugins/publish/extract_xgen.py Co-authored-by: Roy Nieterau --- .../hosts/maya/plugins/publish/extract_xgen.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 4979f583b4..003b6aaa85 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -55,24 +55,26 @@ class ExtractXgenCache(publish.Extractor): transform_name = connections["transform"].split(".")[0] # Duplicate_transform subd patch geometry. - duplicate_transform = pc.duplicate(transform_name)[0] - pc.parent(duplicate_transform, world=True) - duplicate_transform.name(stripNamespace=True) - duplicate_shape = pc.listRelatives( - duplicate_transform, shapes=True + duplicate_transform = cmds.duplicate(transform_name)[0] + duplicate_shape = cmds.listRelatives( + duplicate_transform, + shapes=True, + fullPath=True )[0] - pc.connectAttr( + cmds.connectAttr( "{}.matrix".format(duplicate_transform), "{}.transform".format(node), force=True ) - pc.connectAttr( + cmds.connectAttr( "{}.worldMesh".format(duplicate_shape), "{}.geometry".format(node), force=True ) + duplicate_transform = cmds.parent(duplicate_transform, world=True)[0] + duplicate_nodes.append(duplicate_transform) # Import xgen onto the duplicate. From b1bef86858d92239868cfcaffa8e55d5319cfecc Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Dec 2022 12:56:23 +0000 Subject: [PATCH 0078/1271] @BigRoy suggestion --- openpype/hosts/maya/plugins/publish/extract_xgen.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 003b6aaa85..d8045a90e7 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -131,9 +131,7 @@ class ExtractXgenCache(publish.Extractor): for root, _, files in os.walk(xgen_dir): for file in files: source = os.path.join(root, file) - destination = source.replace( - xgen_dir, instance.data["resourcesDir"] - ) + destination = os.path.join(instance.data["resourcesDir"], file) transfers.append((source, destination)) instance.data["transfers"] = transfers From 9fdd402fec2a6b84da8da64753bd03bb7e101ecd Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Dec 2022 12:56:52 +0000 Subject: [PATCH 0079/1271] Hound --- openpype/hosts/maya/plugins/publish/extract_xgen.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index d8045a90e7..89b73a2d56 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -2,7 +2,6 @@ import os import copy from maya import cmds -import pymel.core as pc import xgenm from openpype.pipeline import publish @@ -57,8 +56,8 @@ class ExtractXgenCache(publish.Extractor): # Duplicate_transform subd patch geometry. duplicate_transform = cmds.duplicate(transform_name)[0] duplicate_shape = cmds.listRelatives( - duplicate_transform, - shapes=True, + duplicate_transform, + shapes=True, fullPath=True )[0] @@ -73,7 +72,9 @@ class ExtractXgenCache(publish.Extractor): force=True ) - duplicate_transform = cmds.parent(duplicate_transform, world=True)[0] + duplicate_transform = cmds.parent( + duplicate_transform, world=True + )[0] duplicate_nodes.append(duplicate_transform) From 2609508ced1e5076a91288d290204ac30f146c6d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Dec 2022 13:01:16 +0000 Subject: [PATCH 0080/1271] deactive > inactive --- .../hosts/maya/plugins/publish/validate_xgen.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_xgen.py b/openpype/hosts/maya/plugins/publish/validate_xgen.py index 1ef85c4cc6..442af794cd 100644 --- a/openpype/hosts/maya/plugins/publish/validate_xgen.py +++ b/openpype/hosts/maya/plugins/publish/validate_xgen.py @@ -36,10 +36,10 @@ class ValidateXgen(pyblish.api.InstancePlugin): ) assert palette_amount == 1, msg - # Cant have deactive modifiers in collection cause Xgen will try and + # Cant have inactive modifiers in collection cause Xgen will try and # look for them when loading. palette = instance.data["xgenPalette"].replace("|", "") - deactive_modifiers = {} + inactive_modifiers = {} for description in instance.data["xgmDescriptions"]: description = description.split("|")[-2] modifier_names = xgenm.fxModules(palette, description) @@ -48,14 +48,14 @@ class ValidateXgen(pyblish.api.InstancePlugin): # Attribute value are lowercase strings of false/true. if attr == "false": try: - deactive_modifiers[description].append(name) + inactive_modifiers[description].append(name) except KeyError: - deactive_modifiers[description] = [name] + inactive_modifiers[description] = [name] msg = ( - "There are deactive modifiers on the collection. " + "There are inactive modifiers on the collection. " "Please delete these:\n{}".format( - json.dumps(deactive_modifiers, indent=4, sort_keys=True) + json.dumps(inactive_modifiers, indent=4, sort_keys=True) ) ) - assert not deactive_modifiers, msg + assert not inactive_modifiers, msg From cbbb3dc8041994912ed6bf5d638ee9f06d36556d Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Wed, 21 Dec 2022 13:04:01 +0000 Subject: [PATCH 0081/1271] Update openpype/hosts/maya/plugins/publish/validate_xgen.py Co-authored-by: Roy Nieterau --- openpype/hosts/maya/plugins/publish/validate_xgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_xgen.py b/openpype/hosts/maya/plugins/publish/validate_xgen.py index 442af794cd..bc25082aa8 100644 --- a/openpype/hosts/maya/plugins/publish/validate_xgen.py +++ b/openpype/hosts/maya/plugins/publish/validate_xgen.py @@ -16,7 +16,7 @@ class ValidateXgen(pyblish.api.InstancePlugin): def process(self, instance): # Validate only xgen collections are in objectset. - nodes = ( + nodes = set( instance.data["xgenNodes"] + cmds.ls(instance, type="transform", long=True) ) From 0e80eff9349fb61125a459f7993dba0a7e8eb5f0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 22 Dec 2022 21:32:53 +0800 Subject: [PATCH 0082/1271] maya gltf texture convertor and validator --- .../plugins/publish/convert_gltf_shader.py | 83 ++++++++++++ .../publish/validate_gltf_textures_names.py | 125 ++++++++++++++++++ .../defaults/project_settings/maya.json | 5 + .../schemas/schema_maya_publish.json | 4 + 4 files changed, 217 insertions(+) create mode 100644 openpype/hosts/maya/plugins/publish/convert_gltf_shader.py create mode 100644 openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py diff --git a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py new file mode 100644 index 0000000000..f62fb92f81 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py @@ -0,0 +1,83 @@ +import os +from maya import cmds +import pyblish.api + +from openpype.pipeline import publish + + +class ConvertGLSLShader(publish.Extractor): + """ + Converting StingrayPBS material to GLSL Shaders + specially for the glb export through Maya2GLTF plugin + + """ + order = pyblish.api.ExtractorOrder - 0.1 + hosts = ["maya"] + label = "Convert StingrayPBS to GLTF" + families = ["gltf"] + optional = True + + def process(self, instance): + meshes = cmds.ls(instance, type="mesh", long=True) + self.log.info("meshes: {}".format(meshes)) + # load the glsl shader plugin + cmds.loadPlugin("glslShader.mll", quiet=True) + + for mesh in meshes: + + # create glsl shader + glsl_shader = cmds.createNode('GLSLShader') + glsl_shadingGrp = cmds.sets(name=glsl_shader + "SG", empty=True, + renderable=True, noSurfaceShader=True) + cmds.connectAttr(glsl_shader + ".outColor", glsl_shadingGrp + ".surfaceShader") + + # load the maya2gltf shader + maya_dir = os.getenv("MAYA_APP_DIR") + ogsfx = maya_dir + "/maya2glTF/PBR/shaders/glTF_PBR.ogsfx" + cmds.setAttr(glsl_shader + ".shader", ogsfx, typ="string") + + # list the materials used for the assets + shading_grp = cmds.listConnections( + mesh, destination=True, type="shadingEngine" + ) + + # get the materials related to the selected assets + for material in shading_grp: + main_shader = cmds.listConnections( + material, destination=True, type="StingrayPBS" + ) + for shader in main_shader: + # get the file textures related to the PBS Shader + albedo = cmds.listConnections(shader + ".TEX_color_map")[0] + dif_output = albedo + ".outColor" + + orm_packed = cmds.listConnections(shader + ".TEX_ao_mapX")[0] + ao_output = orm_packed + ".outColorR" + rough_output = orm_packed + ".outColorG" + metallic_output = orm_packed + ".outColorB" + + nrm = cmds.listConnections(shader + ".TEX_normal_map")[0] + nrm_output = nrm + ".outColor" + + # get the glsl_shader input + # reconnect the file nodes to maya2gltf shader + glsl_dif = glsl_shader + ".u_BaseColorTexture" + glsl_nrm = glsl_shader + ".u_NormalTexture" + cmds.connectAttr(dif_output, glsl_dif) + cmds.connectAttr(nrm_output, glsl_nrm) + + rgb_list = ["R", "G", "B"] + for ch in rgb_list: + mtl = ".u_MetallicTexture.u_MetallicTexture{}".format(ch) + mtl = glsl_shader + mtl + ao = ".u_OcclusionTexture.u_OcclusionTexture{}".format(ch) + ao= glsl_shader + ao + rough = ".u_RoughnessTexture.u_RoughnessTexture{}".format(ch) + rough= glsl_shader + rough + + cmds.connectAttr(metallic_output, mtl) + cmds.connectAttr(ao_output, ao) + cmds.connectAttr(rough_output, rough) + + # assign the shader to the asset + cmds.sets(mesh, forceElement = str(glsl_shadingGrp)) diff --git a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py new file mode 100644 index 0000000000..2b99defd1b --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py @@ -0,0 +1,125 @@ +from maya import cmds + +import pyblish.api +import openpype.hosts.maya.api.action +from openpype.pipeline.publish import ValidateContentsOrder + + +class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): + """ + Validate if the asset uses StingrayPBS material before conversion + of the GLSL Shader + Validate if the names of GLTF Textures follow + the packed ORM/ORMS standard. + + The texture naming conventions follows the UE5-style-guides: + https://github.com/Allar/ue5-style-guide#anc-textures-packing + + ORM: Occulsion Roughness Metallic + ORMS: Occulsion Roughness Metallic Specular + + Texture Naming Style: + + Albedo/Diffuse: {Name}_D.{imageExtension} or + {Name}_D..{imageExtension} + + Normal: {Name}_N.{imageExtension} or + {Name}_N..{imageExtension} + ORM: {Name}_ORM.{imageExtension} or + {Name}_ORM..{imageExtension} + + """ + + order = ValidateContentsOrder + families = ['gltf'] + hosts = ['maya'] + label = 'GLTF Textures Name' + actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + + def process(self, instance): + """Process all the nodes in the instance""" + invalid = self.get_texture_shader_invalid(instance) + if invalid: + raise RuntimeError("Non PBS material found in " + "{0}".format(invalid)) + invalid = self.get_texture_node_invalid(instance) + if invalid: + raise RuntimeError("Related texture file " \ + "nodes not connected") + invalid = self.get_texture_name_invalid(instance) + if invalid: + raise RuntimeError("Invalid texture name(s) found: " + "{0}".format(invalid)) + + + def get_texture_name_invalid(self, instance): + + invalid = set() + shading_grp = self.shader_selection(instance) + + # get the materials related to the selected assets + # get the file textures related to the PBS Shader + # validate the names of the textures + for material in shading_grp: + main_shader = cmds.listConnections( + material, destination=True, type="StingrayPBS" + ) + for shader in main_shader: + albedo = cmds.listConnections(shader + ".TEX_color_map")[0] + dif_path = cmds.getAttr(albedo + ".fileTextureName") + dif = dif_path.split(".")[0] + # "_D" + if not dif.endswith("_D"): + invalid.add(dif_path) + orm_packed = cmds.listConnections(shader + ".TEX_ao_mapX")[0] + # "_ORM" + orm_path = cmds.getAttr(orm_packed + ".fileTextureName") + orm = orm_path.split(".")[0] + if not orm.endswith("_ORM"): + invalid.add(orm_path) + nrm = cmds.listConnections(shader + ".TEX_normal_map")[0] + nrm_path = cmds.getAttr(nrm + ".fileTextureName") + nrm_map = nrm_path.split(".")[0] + # "_N" + if not nrm_map.endswith("_N"): + invalid.add(nrm_path) + + return list(invalid) + + def get_texture_node_invalid(self, instance): + invalid = set() + shading_grp = self.shader_selection(instance) + for material in shading_grp: + main_shader = cmds.listConnections( + material, destination=True, type="StingrayPBS" + ) + for shader in main_shader: + # diffuse texture file node + albedo = cmds.listConnections(shader + ".TEX_color_map") + if not albedo: + invalid.add(albedo) + orm_packed = cmds.listConnections(shader + ".TEX_ao_mapX") + if not orm_packed: + invalid.add(orm_packed) + nrm = cmds.listConnections(shader + ".TEX_normal_map") + if not nrm: + invalid.add(nrm) + return list(invalid) + + def get_texture_shader_invalid(self, instance): + + invalid = set() + shading_grp = self.shader_selection(instance) + for shader in shading_grp: + if "StingrayPBS" not in shader: + invalid.add(shader) + return list(invalid) + + def shader_selection(self, instance): + shapes = cmds.ls(instance, type="mesh", long=True) + for shape in shapes: + shading_grp = cmds.listConnections( + shape, destination=True, type="shadingEngine" + ) + + return shading_grp diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 5f40c2a10c..db64f388c8 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -349,6 +349,11 @@ "optional": false, "active": true }, + "ValidateGLTFTexturesNames": { + "enabled": true, + "optional": false, + "active": true + }, "ValidateRenderImageRule": { "enabled": true, "optional": false, 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 9aaff248ab..32280d1934 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 @@ -369,6 +369,10 @@ "key": "ValidateCurrentRenderLayerIsRenderable", "label": "Validate Current Render Layer Has Renderable Camera" }, + { + "key": "ValidateGLTFTexturesNames", + "label": "Validate GLTF Textures Names" + }, { "key": "ValidateRenderImageRule", "label": "Validate Images File Rule (Workspace)" From 92fd7658d985fcf8208048de458636c8792b805f Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 22 Dec 2022 21:59:24 +0800 Subject: [PATCH 0083/1271] maya gltf texture convertor and validator --- .../plugins/publish/convert_gltf_shader.py | 34 +++++++++---------- .../publish/validate_gltf_textures_names.py | 12 +++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py index f62fb92f81..8223b80c3c 100644 --- a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py +++ b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py @@ -26,26 +26,26 @@ class ConvertGLSLShader(publish.Extractor): for mesh in meshes: # create glsl shader - glsl_shader = cmds.createNode('GLSLShader') - glsl_shadingGrp = cmds.sets(name=glsl_shader + "SG", empty=True, + glsl = cmds.createNode('GLSLShader') + glsl_shadingGrp = cmds.sets(name=glsl + "SG", empty=True, renderable=True, noSurfaceShader=True) - cmds.connectAttr(glsl_shader + ".outColor", glsl_shadingGrp + ".surfaceShader") + cmds.connectAttr(glsl + ".outColor", glsl_shadingGrp + ".surfaceShader") # load the maya2gltf shader maya_dir = os.getenv("MAYA_APP_DIR") ogsfx = maya_dir + "/maya2glTF/PBR/shaders/glTF_PBR.ogsfx" - cmds.setAttr(glsl_shader + ".shader", ogsfx, typ="string") + cmds.setAttr(glsl + ".shader", ogsfx, typ="string") # list the materials used for the assets shading_grp = cmds.listConnections( - mesh, destination=True, type="shadingEngine" - ) + mesh, destination=True, + type="shadingEngine") # get the materials related to the selected assets for material in shading_grp: main_shader = cmds.listConnections( - material, destination=True, type="StingrayPBS" - ) + material, destination=True, + type="StingrayPBS") for shader in main_shader: # get the file textures related to the PBS Shader albedo = cmds.listConnections(shader + ".TEX_color_map")[0] @@ -61,23 +61,23 @@ class ConvertGLSLShader(publish.Extractor): # get the glsl_shader input # reconnect the file nodes to maya2gltf shader - glsl_dif = glsl_shader + ".u_BaseColorTexture" - glsl_nrm = glsl_shader + ".u_NormalTexture" + glsl_dif = glsl + ".u_BaseColorTexture" + glsl_nrm = glsl + ".u_NormalTexture" cmds.connectAttr(dif_output, glsl_dif) cmds.connectAttr(nrm_output, glsl_nrm) rgb_list = ["R", "G", "B"] for ch in rgb_list: - mtl = ".u_MetallicTexture.u_MetallicTexture{}".format(ch) - mtl = glsl_shader + mtl - ao = ".u_OcclusionTexture.u_OcclusionTexture{}".format(ch) - ao= glsl_shader + ao - rough = ".u_RoughnessTexture.u_RoughnessTexture{}".format(ch) - rough= glsl_shader + rough + mtl = ".u_MetallicTexture.u_MetallicTexture{}".format(ch) # noqa + mtl = glsl + mtl + ao = ".u_OcclusionTexture.u_OcclusionTexture{}".format(ch) # noqa + ao = glsl + ao + rough = ".u_RoughnessTexture.u_RoughnessTexture{}".format(ch) # noqa + rough = glsl + rough cmds.connectAttr(metallic_output, mtl) cmds.connectAttr(ao_output, ao) cmds.connectAttr(rough_output, rough) # assign the shader to the asset - cmds.sets(mesh, forceElement = str(glsl_shadingGrp)) + cmds.sets(mesh, forceElement=str(glsl_shadingGrp)) diff --git a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py index 2b99defd1b..6ff8aba623 100644 --- a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py +++ b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py @@ -62,8 +62,8 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): # validate the names of the textures for material in shading_grp: main_shader = cmds.listConnections( - material, destination=True, type="StingrayPBS" - ) + material, destination=True, + type="StingrayPBS") for shader in main_shader: albedo = cmds.listConnections(shader + ".TEX_color_map")[0] dif_path = cmds.getAttr(albedo + ".fileTextureName") @@ -91,8 +91,8 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): shading_grp = self.shader_selection(instance) for material in shading_grp: main_shader = cmds.listConnections( - material, destination=True, type="StingrayPBS" - ) + material, destination=True, + type="StingrayPBS") for shader in main_shader: # diffuse texture file node albedo = cmds.listConnections(shader + ".TEX_color_map") @@ -119,7 +119,7 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): shapes = cmds.ls(instance, type="mesh", long=True) for shape in shapes: shading_grp = cmds.listConnections( - shape, destination=True, type="shadingEngine" - ) + shape, destination=True, + type="shadingEngine") return shading_grp From 96c80299975dae5e9418144c7f106a2461ef90e7 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 22 Dec 2022 22:05:09 +0800 Subject: [PATCH 0084/1271] maya gltf texture convertor and validator --- .../plugins/publish/convert_gltf_shader.py | 15 ++++++------- .../publish/validate_gltf_textures_names.py | 21 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py index 8223b80c3c..6c1a7346e7 100644 --- a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py +++ b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py @@ -29,7 +29,8 @@ class ConvertGLSLShader(publish.Extractor): glsl = cmds.createNode('GLSLShader') glsl_shadingGrp = cmds.sets(name=glsl + "SG", empty=True, renderable=True, noSurfaceShader=True) - cmds.connectAttr(glsl + ".outColor", glsl_shadingGrp + ".surfaceShader") + cmds.connectAttr(glsl + ".outColor", + glsl_shadingGrp + ".surfaceShader") # load the maya2gltf shader maya_dir = os.getenv("MAYA_APP_DIR") @@ -37,15 +38,15 @@ class ConvertGLSLShader(publish.Extractor): cmds.setAttr(glsl + ".shader", ogsfx, typ="string") # list the materials used for the assets - shading_grp = cmds.listConnections( - mesh, destination=True, - type="shadingEngine") + shading_grp = cmds.listConnections(mesh, + destination=True, + type="shadingEngine") # get the materials related to the selected assets for material in shading_grp: - main_shader = cmds.listConnections( - material, destination=True, - type="StingrayPBS") + main_shader = cmds.listConnections(material, + destination=True, + type="StingrayPBS") for shader in main_shader: # get the file textures related to the PBS Shader albedo = cmds.listConnections(shader + ".TEX_color_map")[0] diff --git a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py index 6ff8aba623..6e5e1a832d 100644 --- a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py +++ b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py @@ -44,14 +44,13 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): "{0}".format(invalid)) invalid = self.get_texture_node_invalid(instance) if invalid: - raise RuntimeError("Related texture file " \ + raise RuntimeError("Related texture file " "nodes not connected") invalid = self.get_texture_name_invalid(instance) if invalid: raise RuntimeError("Invalid texture name(s) found: " "{0}".format(invalid)) - def get_texture_name_invalid(self, instance): invalid = set() @@ -61,9 +60,9 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): # get the file textures related to the PBS Shader # validate the names of the textures for material in shading_grp: - main_shader = cmds.listConnections( - material, destination=True, - type="StingrayPBS") + main_shader = cmds.listConnections(material, + destination=True, + type="StingrayPBS") for shader in main_shader: albedo = cmds.listConnections(shader + ".TEX_color_map")[0] dif_path = cmds.getAttr(albedo + ".fileTextureName") @@ -90,9 +89,9 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): invalid = set() shading_grp = self.shader_selection(instance) for material in shading_grp: - main_shader = cmds.listConnections( - material, destination=True, - type="StingrayPBS") + main_shader = cmds.listConnections(material, + destination=True, + type="StingrayPBS") for shader in main_shader: # diffuse texture file node albedo = cmds.listConnections(shader + ".TEX_color_map") @@ -118,8 +117,8 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): def shader_selection(self, instance): shapes = cmds.ls(instance, type="mesh", long=True) for shape in shapes: - shading_grp = cmds.listConnections( - shape, destination=True, - type="shadingEngine") + shading_grp = cmds.listConnections(shape, + destination=True, + type="shadingEngine") return shading_grp From 7f02e8c72b89344076a7ca816daa4aacb027b349 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 22 Dec 2022 22:06:23 +0800 Subject: [PATCH 0085/1271] maya gltf texture convertor and validator --- openpype/hosts/maya/plugins/publish/convert_gltf_shader.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py index 6c1a7346e7..bbeeb38ef3 100644 --- a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py +++ b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py @@ -52,7 +52,8 @@ class ConvertGLSLShader(publish.Extractor): albedo = cmds.listConnections(shader + ".TEX_color_map")[0] dif_output = albedo + ".outColor" - orm_packed = cmds.listConnections(shader + ".TEX_ao_mapX")[0] + orm_packed = cmds.listConnections(shader + + ".TEX_ao_mapX")[0] ao_output = orm_packed + ".outColorR" rough_output = orm_packed + ".outColorG" metallic_output = orm_packed + ".outColorB" From dc2baf29069afddad2c1c3a9acff64c85b7d8968 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 4 Jan 2023 14:56:40 +0800 Subject: [PATCH 0086/1271] check whether the ocio config enabled in maya for colorspace in maya --- .../hosts/maya/plugins/publish/extract_look.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index df07a674dc..efe6c3c062 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -367,10 +367,19 @@ class ExtractLook(publish.Extractor): for filepath in files_metadata: linearize = False - if do_maketx and files_metadata[filepath]["color_space"].lower() == "srgb": # noqa: E501 - linearize = True - # set its file node to 'raw' as tx will be linearized - files_metadata[filepath]["color_space"] = "Raw" + # if OCIO color management enabled + # it wont take the condition of the files_metadata + + # TODO: if do_maketx: linearize=False + ocio_maya = cmds.colorManagementPrefs(q=True, + cmConfigFileEnabled=True, + cmEnabled=True) + + if do_maketx and not ocio_maya: + if files_metadata[filepath]["color_space"].lower() == "srgb": # noqa: E501 + linearize = True + # set its file node to 'raw' as tx will be linearized + files_metadata[filepath]["color_space"] = "Raw" # if do_maketx: # color_space = "Raw" @@ -421,6 +430,7 @@ class ExtractLook(publish.Extractor): color_space_attr = resource["node"] + ".colorSpace" try: color_space = cmds.getAttr(color_space_attr) + self.log.info("current colorspace: {0}".format(color_space)) except ValueError: # node doesn't have color space attribute color_space = "Raw" From e045cc95be4d9dc242536bfa5388a002171659b5 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 4 Jan 2023 15:10:43 +0800 Subject: [PATCH 0087/1271] check whether the ocio config enabled in maya for colorspace in maya --- openpype/hosts/maya/plugins/publish/extract_look.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index efe6c3c062..878c2dceae 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -370,7 +370,6 @@ class ExtractLook(publish.Extractor): # if OCIO color management enabled # it wont take the condition of the files_metadata - # TODO: if do_maketx: linearize=False ocio_maya = cmds.colorManagementPrefs(q=True, cmConfigFileEnabled=True, cmEnabled=True) @@ -430,7 +429,6 @@ class ExtractLook(publish.Extractor): color_space_attr = resource["node"] + ".colorSpace" try: color_space = cmds.getAttr(color_space_attr) - self.log.info("current colorspace: {0}".format(color_space)) except ValueError: # node doesn't have color space attribute color_space = "Raw" From 2a748047ed777067ada7e3b78eca8b6df0f65678 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 4 Jan 2023 12:41:12 +0000 Subject: [PATCH 0088/1271] Only collect resource files from instance collection --- .../maya/plugins/publish/extract_xgen.py | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 89b73a2d56..5b4c6b29f2 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -124,15 +124,26 @@ class ExtractXgenCache(publish.Extractor): cmds.delete(duplicate_nodes + [collection]) - # Setup transfers. - xgen_dir = os.path.join( - os.path.dirname(instance.context.data["currentFile"]), "xgen" + # Collect all files under palette root as resources. + data_path = xgenm.getAttr( + "xgDataPath", instance.data["xgenPalette"].replace("|", "") + ).split(os.pathsep)[0] + data_path = data_path.replace( + "${PROJECT}", + xgenm.getAttr( + "xgProjectPath", instance.data["xgenPalette"].replace("|", "") + ) ) transfers = [] - for root, _, files in os.walk(xgen_dir): + for root, _, files in os.walk(data_path): for file in files: - source = os.path.join(root, file) - destination = os.path.join(instance.data["resourcesDir"], file) - transfers.append((source, destination)) + source = os.path.join(root, file).replace("\\", "/") + destination = os.path.join( + instance.data["resourcesDir"], + "collections", + os.path.basename(data_path), + source.replace(data_path, "")[1:] + ) + transfers.append((source, destination.replace("\\", "/"))) instance.data["transfers"] = transfers From 370bb96408c88e68273593a8ae7aabcbfff93f99 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 4 Jan 2023 15:03:30 +0000 Subject: [PATCH 0089/1271] Improve validation error message --- openpype/hosts/maya/plugins/publish/validate_xgen.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_xgen.py b/openpype/hosts/maya/plugins/publish/validate_xgen.py index bc25082aa8..5ba9ddad52 100644 --- a/openpype/hosts/maya/plugins/publish/validate_xgen.py +++ b/openpype/hosts/maya/plugins/publish/validate_xgen.py @@ -26,7 +26,10 @@ class ValidateXgen(pyblish.api.InstancePlugin): continue remainder_nodes.append(node) - msg = "Invalid nodes in the objectset:\n{}".format(remainder_nodes) + msg = ( + "Only the collection is used when publishing. Found these invalid" + " nodes in the objectset:\n{}".format(remainder_nodes) + ) assert not remainder_nodes, msg # Only one collection per instance. From 1ba5223801fa9fa6695fa62a59dd53eaca7be27c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 4 Jan 2023 23:49:07 +0800 Subject: [PATCH 0090/1271] adding optional validator for maya color space --- .../publish/validate_look_color_space.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 openpype/hosts/maya/plugins/publish/validate_look_color_space.py diff --git a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py new file mode 100644 index 0000000000..6839df1d72 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py @@ -0,0 +1,28 @@ +from maya import cmds + +import pyblish.api +import openpype.hosts.maya.api.action +from openpype.pipeline.publish import ValidateContentsOrder + + +class ValidateMayaColorSpace(pyblish.api.InstancePlugin): + """ + Check if the OCIO Color Management and maketx options + enabled at the same time + + """ + + order = ValidateContentsOrder + families = ['look'] + hosts = ['maya'] + label = 'Maya Color Space' + optional = True + + def process(self, instance): + ocio_maya = cmds.colorManagementPrefs(q=True, + cmConfigFileEnabled=True, + cmEnabled=True) + maketx = instance.data["maketx"] + + if ocio_maya and maketx: + raise Exception("Maya is color managed and maketx option is on. OpenPype doesn't support this combination yet.") # noqa From e9b149ee6a62372daf903b0f0060b58e566eaa9a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 4 Jan 2023 23:50:05 +0800 Subject: [PATCH 0091/1271] adding optional validator for maya color space --- openpype/hosts/maya/plugins/publish/validate_look_color_space.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py index 6839df1d72..e309e25da9 100644 --- a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py +++ b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py @@ -1,7 +1,6 @@ from maya import cmds import pyblish.api -import openpype.hosts.maya.api.action from openpype.pipeline.publish import ValidateContentsOrder From 8e233c46c8c1fceea3b0c05f0b31cf20172eb410 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Wed, 4 Jan 2023 16:54:25 +0000 Subject: [PATCH 0092/1271] Update openpype/hosts/maya/plugins/publish/validate_xgen.py Co-authored-by: Roy Nieterau --- .../maya/plugins/publish/validate_xgen.py | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_xgen.py b/openpype/hosts/maya/plugins/publish/validate_xgen.py index 5ba9ddad52..b9d67dad25 100644 --- a/openpype/hosts/maya/plugins/publish/validate_xgen.py +++ b/openpype/hosts/maya/plugins/publish/validate_xgen.py @@ -16,21 +16,17 @@ class ValidateXgen(pyblish.api.InstancePlugin): def process(self, instance): # Validate only xgen collections are in objectset. - nodes = set( + valid_nodes = set( instance.data["xgenNodes"] + cmds.ls(instance, type="transform", long=True) ) - remainder_nodes = [] - for node in instance: - if node in nodes: - continue - remainder_nodes.append(node) - - msg = ( - "Only the collection is used when publishing. Found these invalid" - " nodes in the objectset:\n{}".format(remainder_nodes) - ) - assert not remainder_nodes, msg + invalid_nodes = [node for node in instance if node not in valid_nodes] + + if invalid_nodes: + raise KnownPublishError( + "Only the collection is used when publishing. Found these invalid" + " nodes in the objectset:\n{}".format(invalid_nodes) + ) # Only one collection per instance. palette_amount = len(instance.data["xgenPalettes"]) From 8918f8965c7d591aaec1b3da608dd8be77d68ea9 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 4 Jan 2023 16:56:32 +0000 Subject: [PATCH 0093/1271] Fix suggestion --- openpype/hosts/maya/plugins/publish/validate_xgen.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_xgen.py b/openpype/hosts/maya/plugins/publish/validate_xgen.py index b9d67dad25..b5817d4920 100644 --- a/openpype/hosts/maya/plugins/publish/validate_xgen.py +++ b/openpype/hosts/maya/plugins/publish/validate_xgen.py @@ -4,6 +4,7 @@ import maya.cmds as cmds import xgenm import pyblish.api +from openpype.pipeline.publish import KnownPublishError class ValidateXgen(pyblish.api.InstancePlugin): @@ -21,11 +22,11 @@ class ValidateXgen(pyblish.api.InstancePlugin): cmds.ls(instance, type="transform", long=True) ) invalid_nodes = [node for node in instance if node not in valid_nodes] - + if invalid_nodes: raise KnownPublishError( - "Only the collection is used when publishing. Found these invalid" - " nodes in the objectset:\n{}".format(invalid_nodes) + "Only the collection is used when publishing. Found these " + "invalid nodes in the objectset:\n{}".format(invalid_nodes) ) # Only one collection per instance. From f47b52aea7da19ca19c1dc6602a7390b9575a91a Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Wed, 4 Jan 2023 18:22:48 +0100 Subject: [PATCH 0094/1271] fix features for gizmo menu --- openpype/hosts/nuke/api/gizmo_menu.py | 10 ++++++++-- .../schemas/schema_nuke_scriptsgizmo.json | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/api/gizmo_menu.py b/openpype/hosts/nuke/api/gizmo_menu.py index 9edfc62e3b..5838ee8a8a 100644 --- a/openpype/hosts/nuke/api/gizmo_menu.py +++ b/openpype/hosts/nuke/api/gizmo_menu.py @@ -53,12 +53,18 @@ class GizmoMenu(): item_type = item.get("sourcetype") - if item_type == ("python" or "file"): + if item_type == "python": parent.addCommand( item["title"], command=str(item["command"]), icon=item.get("icon"), - shortcut=item.get("hotkey") + shortcut=item.get("shortcut") + ) + elif item_type == "file": + parent.addCommand( + item['title'], + "nuke.createNode('{}')".format(item.get('file_name')), + shortcut=item.get('shortcut') ) # add separator diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_scriptsgizmo.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_scriptsgizmo.json index abe14970c5..e4c65177a7 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_scriptsgizmo.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_scriptsgizmo.json @@ -72,6 +72,11 @@ "key": "command", "label": "Python command" }, + { + "type": "text", + "key": "icon", + "label": "Icon Path" + }, { "type": "text", "key": "shortcut", From e436dbb57e86948b54cc4f54591376f5f6ff42ca Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 5 Jan 2023 11:53:17 +0800 Subject: [PATCH 0095/1271] add the validator into the settings --- .../hosts/maya/plugins/publish/validate_look_color_space.py | 1 - openpype/settings/defaults/project_settings/maya.json | 5 +++++ .../schemas/projects_schema/schemas/schema_maya_publish.json | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py index e309e25da9..933951501c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py +++ b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py @@ -15,7 +15,6 @@ class ValidateMayaColorSpace(pyblish.api.InstancePlugin): families = ['look'] hosts = ['maya'] label = 'Maya Color Space' - optional = True def process(self, instance): ocio_maya = cmds.colorManagementPrefs(q=True, diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 5f40c2a10c..21815278a8 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -306,6 +306,11 @@ "optional": true, "active": true }, + "ValidateMayaColorSpace": { + "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 9aaff248ab..a920d5aeae 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 @@ -144,6 +144,10 @@ { "key": "ValidateShadingEngine", "label": "Validate Look Shading Engine Naming" + }, + { + "key": "ValidateMayaColorSpace", + "label": "ValidateMayaColorSpace" } ] }, From 6575e304b8a8127e4fa0d0989efcacb3d773be46 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 5 Jan 2023 18:20:03 +0000 Subject: [PATCH 0096/1271] Connect Geometry action --- .../plugins/inventory/connect_geometry.py | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 openpype/hosts/maya/plugins/inventory/connect_geometry.py diff --git a/openpype/hosts/maya/plugins/inventory/connect_geometry.py b/openpype/hosts/maya/plugins/inventory/connect_geometry.py new file mode 100644 index 0000000000..375ad29ef0 --- /dev/null +++ b/openpype/hosts/maya/plugins/inventory/connect_geometry.py @@ -0,0 +1,139 @@ +from maya import cmds + +from openpype.pipeline import InventoryAction, get_representation_context + + +class ConnectGeometry(InventoryAction): + """Connect geometries within containers. + + Source container will connect to the target containers, by searching for + matching geometry IDs (cbid). + Source containers are of family; "animation" and "pointcache". + The connection with be done with a live world space blendshape. + """ + + label = "Connect Geometry" + icon = "link" + color = "white" + + def process(self, containers): + # Validate selection is more than 1. + message = ( + "Only 1 container selected. 2+ containers needed for this action." + ) + if len(containers) == 1: + self.display_warning(message) + return + + # Categorize containers by family. + containers_by_family = {} + for container in containers: + family = get_representation_context( + container["representation"] + )["subset"]["data"]["family"] + try: + containers_by_family[family].append(container) + except KeyError: + containers_by_family[family] = [container] + + # Validate to only 1 source container. + source_containers = containers_by_family.get("animation", []) + source_containers += containers_by_family.get("pointcache", []) + source_container_namespaces = [ + x["namespace"] for x in source_containers + ] + message = ( + "{} animation containers selected:\n\n{}\n\nOnly select 1 of type " + "\"animation\" or \"pointcache\".".format( + len(source_containers), source_container_namespaces + ) + ) + if len(source_containers) != 1: + self.display_warning(message) + return + + source_container = source_containers[0] + + # Collect matching geometry transforms based cbId attribute. + target_containers = [] + for family, containers in containers_by_family.items(): + if family in ["animation", "pointcache"]: + continue + + target_containers.extend(containers) + + source_data = self.get_container_data(source_container["objectName"]) + matches = [] + node_types = [] + for target_container in target_containers: + target_data = self.get_container_data( + target_container["objectName"] + ) + node_types.extend(target_data["node_types"]) + for id, transform in target_data["ids"].items(): + source_match = source_data["ids"].get(id) + if source_match: + matches.append([source_match, transform]) + + # Message user about what is about to happen. + if not matches: + self.display_warning("No matching geometries found.") + return + + message = "Linking geometries:\n\n" + for match in matches: + message += "{} > {}\n".format(match[0], match[1]) + + choice = self.display_warning(message, show_cancel=True) + if choice is False: + return + + # Setup live worldspace blendshape connection. + for match in matches: + source = match[0] + target = match[1] + blendshape = cmds.blendShape(source, target)[0] + cmds.setAttr(blendshape + ".origin", 0) + cmds.setAttr(blendshape + "." + target.split(":")[-1], 1) + + # Update Xgen if in any of the containers. + if "xgmPalette" in node_types: + cmds.xgmPreview() + + def get_container_data(self, container): + data = {"node_types": [], "ids": {}} + ref_node = cmds.sets(container, query=True, nodesOnly=True)[0] + for node in cmds.referenceQuery(ref_node, nodes=True): + node_type = cmds.nodeType(node) + data["node_types"].append(node_type) + if node_type == "mesh": + transform = cmds.listRelatives(node, parent=True)[0] + id = cmds.getAttr(transform + ".cbId") + data["ids"][id] = transform + + return data + + def display_warning(self, message, show_cancel=False): + """Show feedback to user. + + Returns: + bool + """ + + from Qt import QtWidgets + + accept = QtWidgets.QMessageBox.Ok + if show_cancel: + buttons = accept | QtWidgets.QMessageBox.Cancel + else: + buttons = accept + + state = QtWidgets.QMessageBox.warning( + None, + "", + message, + buttons=buttons, + defaultButton=accept + ) + + return state == accept From 2facf4d49eb3c18f3bfe39a3d50bb2bac51abca7 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 6 Jan 2023 10:19:21 +0000 Subject: [PATCH 0097/1271] Open workfile post initialization --- openpype/hooks/pre_add_last_workfile_arg.py | 12 ++++++++++ openpype/hosts/maya/startup/userSetup.py | 22 +++++++++++++++---- .../defaults/project_settings/maya.json | 1 + .../projects_schema/schema_project_maya.json | 5 +++++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/openpype/hooks/pre_add_last_workfile_arg.py b/openpype/hooks/pre_add_last_workfile_arg.py index 3609620917..ffb116d2e4 100644 --- a/openpype/hooks/pre_add_last_workfile_arg.py +++ b/openpype/hooks/pre_add_last_workfile_arg.py @@ -1,5 +1,7 @@ import os + from openpype.lib import PreLaunchHook +from openpype.settings import get_project_settings class AddLastWorkfileToLaunchArgs(PreLaunchHook): @@ -40,5 +42,15 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook): self.log.info("Current context does not have any workfile yet.") return + # Determine whether to open workfile post initialization. + if self.data["app"].host_name == "maya": + project_name = self.data["project_name"] + settings = get_project_settings(project_name) + key = "open_workfile_post_initialization" + if settings["maya"][key]: + self.log.debug("Opening workfile post initialization.") + self.data["env"]["OPENPYPE_" + key.upper()] = "1" + return + # Add path to workfile to arguments self.launch_context.launch_args.append(last_workfile) diff --git a/openpype/hosts/maya/startup/userSetup.py b/openpype/hosts/maya/startup/userSetup.py index 40cd51f2d8..b64ed93000 100644 --- a/openpype/hosts/maya/startup/userSetup.py +++ b/openpype/hosts/maya/startup/userSetup.py @@ -1,16 +1,27 @@ import os + from openpype.settings import get_project_settings from openpype.pipeline import install_host from openpype.hosts.maya.api import MayaHost + from maya import cmds host = MayaHost() install_host(host) -print("starting OpenPype usersetup") +print("Starting OpenPype usersetup...") -# build a shelf +# Open Workfile Post Initialization. +key = "OPENPYPE_OPEN_WORKFILE_POST_INITIALIZATION" +if bool(int(os.environ.get(key, "0"))): + cmds.evalDeferred( + "cmds.file(os.environ[\"AVALON_LAST_WORKFILE\"], open=True," + " force=True)", + lowestPriority=True + ) + +# Build a shelf. settings = get_project_settings(os.environ['AVALON_PROJECT']) shelf_preset = settings['maya'].get('project_shelf') @@ -26,7 +37,10 @@ if shelf_preset: print(import_string) exec(import_string) - cmds.evalDeferred("mlib.shelf(name=shelf_preset['name'], iconPath=icon_path, preset=shelf_preset)") + cmds.evalDeferred( + "mlib.shelf(name=shelf_preset['name'], iconPath=icon_path," + " preset=shelf_preset)" + ) -print("finished OpenPype usersetup") +print("Finished OpenPype usersetup.") diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 5f40c2a10c..dbc3282ce8 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1,4 +1,5 @@ { + "open_workfile_post_initialization": false, "imageio": { "colorManagementPreference_v2": { "enabled": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json index b2d79797a3..b668d74afd 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json @@ -5,6 +5,11 @@ "label": "Maya", "is_file": true, "children": [ + { + "type": "boolean", + "key": "open_workfile_post_initialization", + "label": "Open Workfile Post Initialization" + }, { "key": "imageio", "type": "dict", From edc10cd85f232d132266b00dae6392ab33945b3f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:57:51 +0100 Subject: [PATCH 0098/1271] OP-4643 - added Settings for ExtractColorTranscode --- .../defaults/project_settings/global.json | 8 +- .../schemas/schema_global_publish.json | 73 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 0b4e4c74e6..167f7611ce 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -70,6 +70,10 @@ "output": [] } }, + "ExtractColorTranscode": { + "enabled": true, + "profiles": [] + }, "ExtractReview": { "enabled": true, "profiles": [ @@ -442,7 +446,9 @@ "template": "{family}{Task}" }, { - "families": ["render"], + "families": [ + "render" + ], "hosts": [ "aftereffects" ], diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5388d04bc9..46ae6ba554 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -197,6 +197,79 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractColorTranscode", + "label": "ExtractColorTranscode", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "key": "hosts", + "label": "Host names", + "type": "hosts-enum", + "multiselection": true + }, + { + "key": "task_types", + "label": "Task types", + "type": "task-types-enum" + }, + { + "key": "task_names", + "label": "Task names", + "type": "list", + "object_type": "text" + }, + { + "key": "subsets", + "label": "Subset names", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "ext", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } + } + ] + }, { "type": "dict", "collapsible": true, From c9996bb0c00b2fdb3d4db964efe1933bc99438e1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:58:51 +0100 Subject: [PATCH 0099/1271] OP-4643 - added ExtractColorTranscode Added method to convert from one colorspace to another to transcoding lib --- openpype/lib/transcoding.py | 53 ++++++++ .../publish/extract_color_transcode.py | 124 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 openpype/plugins/publish/extract_color_transcode.py diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 57279d0380..6899811ed5 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1037,3 +1037,56 @@ def convert_ffprobe_fps_to_float(value): if divisor == 0.0: return 0.0 return dividend / divisor + + +def convert_colorspace_for_input_paths( + input_paths, + output_dir, + source_color_space, + target_color_space, + logger=None +): + """Convert source files from one color space to another. + + Filenames of input files are kept so make sure that output directory + is not the same directory as input files have. + - This way it can handle gaps and can keep input filenames without handling + frame template + + Args: + input_paths (str): Paths that should be converted. It is expected that + contains single file or image sequence of samy type. + output_dir (str): Path to directory where output will be rendered. + Must not be same as input's directory. + source_color_space (str): ocio valid color space of source files + target_color_space (str): ocio valid target color space + logger (logging.Logger): Logger used for logging. + + """ + if logger is None: + logger = logging.getLogger(__name__) + + input_arg = "-i" + oiio_cmd = [ + get_oiio_tools_path(), + + # Don't add any additional attributes + "--nosoftwareattrib", + "--colorconvert", source_color_space, target_color_space + ] + for input_path in input_paths: + # Prepare subprocess arguments + + oiio_cmd.extend([ + input_arg, input_path, + ]) + + # Add last argument - path to output + base_filename = os.path.basename(input_path) + output_path = os.path.join(output_dir, base_filename) + oiio_cmd.extend([ + "-o", output_path + ]) + + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py new file mode 100644 index 0000000000..58508ab18f --- /dev/null +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -0,0 +1,124 @@ +import pyblish.api + +from openpype.pipeline import publish +from openpype.lib import ( + + is_oiio_supported, +) + +from openpype.lib.transcoding import ( + convert_colorspace_for_input_paths, + get_transcode_temp_directory, +) + +from openpype.lib.profiles_filtering import filter_profiles + + +class ExtractColorTranscode(publish.Extractor): + """ + Extractor to convert colors from one colorspace to different. + """ + + label = "Transcode color spaces" + order = pyblish.api.ExtractorOrder + 0.01 + + optional = True + + # Configurable by Settings + profiles = None + options = None + + def process(self, instance): + if not self.profiles: + self.log.warning("No profiles present for create burnin") + return + + if "representations" not in instance.data: + self.log.warning("No representations, skipping.") + return + + if not is_oiio_supported(): + self.log.warning("OIIO not supported, no transcoding possible.") + return + + colorspace_data = instance.data.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Instance has not colorspace data, skipping") + return + source_color_space = colorspace_data["colorspace"] + + host_name = instance.context.data["hostName"] + family = instance.data["family"] + task_data = instance.data["anatomyData"].get("task", {}) + task_name = task_data.get("name") + task_type = task_data.get("type") + subset = instance.data["subset"] + + filtering_criteria = { + "hosts": host_name, + "families": family, + "task_names": task_name, + "task_types": task_type, + "subset": subset + } + profile = filter_profiles(self.profiles, filtering_criteria, + logger=self.log) + + if not profile: + self.log.info(( + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) + return + + self.log.debug("profile: {}".format(profile)) + + target_colorspace = profile["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(repres): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self.repre_is_valid(repre): + continue + + new_staging_dir = get_transcode_temp_directory() + repre["stagingDir"] = new_staging_dir + files_to_remove = repre["files"] + if not isinstance(files_to_remove, list): + files_to_remove = [files_to_remove] + instance.context.data["cleanupFullPaths"].extend(files_to_remove) + + convert_colorspace_for_input_paths( + repre["files"], + new_staging_dir, + source_color_space, + target_colorspace, + self.log + ) + + def repre_is_valid(self, repre): + """Validation if representation should be processed. + + Args: + repre (dict): Representation which should be checked. + + Returns: + bool: False if can't be processed else True. + """ + + if "review" not in (repre.get("tags") or []): + self.log.info(( + "Representation \"{}\" don't have \"review\" tag. Skipped." + ).format(repre["name"])) + return False + + if not repre.get("files"): + self.log.warning(( + "Representation \"{}\" have empty files. Skipped." + ).format(repre["name"])) + return False + return True From 23a5f21f6b54f4f01d40c73ef5bdb032a2c7440f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 12:05:57 +0100 Subject: [PATCH 0100/1271] OP-4643 - extractor must run just before ExtractReview Nuke render local is set to 0.01 --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 58508ab18f..5163cd4045 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -20,7 +20,7 @@ class ExtractColorTranscode(publish.Extractor): """ label = "Transcode color spaces" - order = pyblish.api.ExtractorOrder + 0.01 + order = pyblish.api.ExtractorOrder + 0.019 optional = True From 9f8107df36dd8d281482d93fb6a0e3530d91fe91 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:03:22 +0100 Subject: [PATCH 0101/1271] OP-4643 - fix for full file paths --- .../publish/extract_color_transcode.py | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 5163cd4045..6ad7599f2c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,3 +1,4 @@ +import os import pyblish.api from openpype.pipeline import publish @@ -41,13 +42,6 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return - colorspace_data = instance.data.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Instance has not colorspace data, skipping") - return - source_color_space = colorspace_data["colorspace"] - host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) @@ -82,18 +76,32 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self.repre_is_valid(repre): + # if not self.repre_is_valid(repre): + # continue + + colorspace_data = repre.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Repre has not colorspace data, skipping") + continue + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") continue new_staging_dir = get_transcode_temp_directory() + original_staging_dir = repre["stagingDir"] repre["stagingDir"] = new_staging_dir - files_to_remove = repre["files"] - if not isinstance(files_to_remove, list): - files_to_remove = [files_to_remove] - instance.context.data["cleanupFullPaths"].extend(files_to_remove) + files_to_convert = repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + instance.context.data["cleanupFullPaths"].extend(files_to_convert) convert_colorspace_for_input_paths( - repre["files"], + files_to_convert, new_staging_dir, source_color_space, target_colorspace, From d588ee7ed967e34adfd4017824d9f323e2ab7ad4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:04:06 +0100 Subject: [PATCH 0102/1271] OP-4643 - pass path for ocio config --- openpype/lib/transcoding.py | 3 +++ openpype/plugins/publish/extract_color_transcode.py | 1 + 2 files changed, 4 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 6899811ed5..792e8ddd1e 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1042,6 +1042,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace_for_input_paths( input_paths, output_dir, + config_path, source_color_space, target_color_space, logger=None @@ -1058,6 +1059,7 @@ def convert_colorspace_for_input_paths( contains single file or image sequence of samy type. output_dir (str): Path to directory where output will be rendered. Must not be same as input's directory. + config_path (str): path to OCIO config file source_color_space (str): ocio valid color space of source files target_color_space (str): ocio valid target color space logger (logging.Logger): Logger used for logging. @@ -1072,6 +1074,7 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", + "--colorconfig", config_path, "--colorconvert", source_color_space, target_color_space ] for input_path in input_paths: diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 6ad7599f2c..fdb13a47e8 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -103,6 +103,7 @@ class ExtractColorTranscode(publish.Extractor): convert_colorspace_for_input_paths( files_to_convert, new_staging_dir, + config_path, source_color_space, target_colorspace, self.log From 5ca4f825006d28bed4d0c01df71af3b3ffe21deb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:15:33 +0100 Subject: [PATCH 0103/1271] OP-4643 - add custom_tags --- openpype/plugins/publish/extract_color_transcode.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index fdb13a47e8..ab932b2476 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -72,6 +72,7 @@ class ExtractColorTranscode(publish.Extractor): target_colorspace = profile["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") + custom_tags = profile["custom_tags"] repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): @@ -109,6 +110,11 @@ class ExtractColorTranscode(publish.Extractor): self.log ) + if custom_tags: + if not repre.get("custom_tags"): + repre["custom_tags"] = [] + repre["custom_tags"].extend(custom_tags) + def repre_is_valid(self, repre): """Validation if representation should be processed. From 4854b521d408d326f741cc73cda0ddc3e67795ce Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:18:38 +0100 Subject: [PATCH 0104/1271] OP-4643 - added docstring --- openpype/plugins/publish/extract_color_transcode.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index ab932b2476..88e2eed90f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -18,6 +18,17 @@ from openpype.lib.profiles_filtering import filter_profiles class ExtractColorTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. + + Expects "colorspaceData" on representation. This dictionary is collected + previously and denotes that representation files should be converted. + This dict contains source colorspace information, collected by hosts. + + Target colorspace is selected by profiles in the Settings, based on: + - families + - host + - task types + - task names + - subset names """ label = "Transcode color spaces" From 39b8c111cfd418b74e0cf915590bcae2547c30ae Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:15:44 +0100 Subject: [PATCH 0105/1271] OP-4643 - updated Settings schema --- .../schemas/schema_global_publish.json | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 46ae6ba554..c2c911d7d6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -246,24 +246,44 @@ "type": "list", "object_type": "text" }, + { + "type": "boolean", + "key": "delete_original", + "label": "Delete Original Representation" + }, { "type": "splitter" }, { - "key": "ext", - "label": "Output extension", - "type": "text" - }, - { - "key": "output_colorspace", - "label": "Output colorspace", - "type": "text" - }, - { - "key": "custom_tags", - "label": "Custom Tags", - "type": "list", - "object_type": "text" + "key": "outputs", + "label": "Output Definitions", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": { + "type": "dict", + "children": [ + { + "key": "output_extension", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "type": "schema", + "name": "schema_representation_tags" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } } ] } From a63ddebc19c7078c33b49029e5de3059b0fcdd9d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:17:25 +0100 Subject: [PATCH 0106/1271] OP-4643 - skip video files Only frames currently supported. --- .../plugins/publish/extract_color_transcode.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 88e2eed90f..a0714c9a33 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -36,6 +36,9 @@ class ExtractColorTranscode(publish.Extractor): optional = True + # Supported extensions + supported_exts = ["exr", "jpg", "jpeg", "png", "dpx"] + # Configurable by Settings profiles = None options = None @@ -88,13 +91,7 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - # if not self.repre_is_valid(repre): - # continue - - colorspace_data = repre.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Repre has not colorspace data, skipping") + if not self._repre_is_valid(repre): continue source_color_space = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") @@ -136,9 +133,9 @@ class ExtractColorTranscode(publish.Extractor): bool: False if can't be processed else True. """ - if "review" not in (repre.get("tags") or []): - self.log.info(( - "Representation \"{}\" don't have \"review\" tag. Skipped." + if repre.get("ext") not in self.supported_exts: + self.log.warning(( + "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False From eced917d1a06fb6c973396c22c37db70bf71e4d4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:19:08 +0100 Subject: [PATCH 0107/1271] OP-4643 - refactored profile, delete of original Implemented multiple outputs from single input representation --- .../publish/extract_color_transcode.py | 156 ++++++++++++------ 1 file changed, 109 insertions(+), 47 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index a0714c9a33..b0c851d5f4 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,4 +1,6 @@ import os +import copy + import pyblish.api from openpype.pipeline import publish @@ -56,13 +58,94 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return + profile = self._get_profile(instance) + if not profile: + return + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(list(repres)): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self._repre_is_valid(repre): + continue + + colorspace_data = repre["colorspaceData"] + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") + continue + + repre = self._handle_original_repre(repre, profile) + + for _, output_def in profile.get("outputs", {}).items(): + new_repre = copy.deepcopy(repre) + + new_staging_dir = get_transcode_temp_directory() + original_staging_dir = new_repre["stagingDir"] + new_repre["stagingDir"] = new_staging_dir + files_to_convert = new_repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + + files_to_delete = copy.deepcopy(files_to_convert) + + output_extension = output_def["output_extension"] + files_to_convert = self._rename_output_files(files_to_convert, + output_extension) + + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + + target_colorspace = output_def["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + convert_colorspace_for_input_paths( + files_to_convert, + new_staging_dir, + config_path, + source_color_space, + target_colorspace, + self.log + ) + + instance.context.data["cleanupFullPaths"].extend( + files_to_delete) + + custom_tags = output_def.get("custom_tags") + if custom_tags: + if not new_repre.get("custom_tags"): + new_repre["custom_tags"] = [] + new_repre["custom_tags"].extend(custom_tags) + + # Add additional tags from output definition to representation + for tag in output_def["tags"]: + if tag not in new_repre["tags"]: + new_repre["tags"].append(tag) + + instance.data["representations"].append(new_repre) + + def _rename_output_files(self, files_to_convert, output_extension): + """Change extension of converted files.""" + if output_extension: + output_extension = output_extension.replace('.', '') + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files + return files_to_convert + + def _get_profile(self, instance): + """Returns profile if and how repre should be color transcoded.""" host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) task_name = task_data.get("name") task_type = task_data.get("type") subset = instance.data["subset"] - filtering_criteria = { "hosts": host_name, "families": family, @@ -75,55 +158,15 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) - return + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) + return profile - target_colorspace = profile["output_colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") - custom_tags = profile["custom_tags"] - - repres = instance.data.get("representations") or [] - for idx, repre in enumerate(repres): - self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self._repre_is_valid(repre): - continue - source_color_space = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): - self.log.warning("Config file doesn't exist, skipping") - continue - - new_staging_dir = get_transcode_temp_directory() - original_staging_dir = repre["stagingDir"] - repre["stagingDir"] = new_staging_dir - files_to_convert = repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - instance.context.data["cleanupFullPaths"].extend(files_to_convert) - - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) - - if custom_tags: - if not repre.get("custom_tags"): - repre["custom_tags"] = [] - repre["custom_tags"].extend(custom_tags) - - def repre_is_valid(self, repre): + def _repre_is_valid(self, repre): """Validation if representation should be processed. Args: @@ -144,4 +187,23 @@ class ExtractColorTranscode(publish.Extractor): "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False + + if not repre.get("colorspaceData"): + self.log.warning("Repre has not colorspace data, skipping") + return False + return True + + def _handle_original_repre(self, repre, profile): + delete_original = profile["delete_original"] + + if delete_original: + if not repre.get("tags"): + repre["tags"] = [] + + if "review" in repre["tags"]: + repre["tags"].remove("review") + if "delete" not in repre["tags"]: + repre["tags"].append("delete") + + return repre From 82a8fda4d6fd319be4f570d0731881d0effb3dac Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:23:01 +0100 Subject: [PATCH 0108/1271] OP-4643 - switched logging levels Do not use warning unnecessary. --- openpype/plugins/publish/extract_color_transcode.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index b0c851d5f4..4d38514b8b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -47,11 +47,11 @@ class ExtractColorTranscode(publish.Extractor): def process(self, instance): if not self.profiles: - self.log.warning("No profiles present for create burnin") + self.log.debug("No profiles present for color transcode") return if "representations" not in instance.data: - self.log.warning("No representations, skipping.") + self.log.debug("No representations, skipping.") return if not is_oiio_supported(): @@ -177,19 +177,19 @@ class ExtractColorTranscode(publish.Extractor): """ if repre.get("ext") not in self.supported_exts: - self.log.warning(( + self.log.debug(( "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): - self.log.warning(( + self.log.debug(( "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.warning("Repre has not colorspace data, skipping") + self.log.debug("Repre has no colorspace data. Skipped.") return False return True From 6f7e1c3cb49fe0162220919d398e86cddf68d0fa Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:14 +0100 Subject: [PATCH 0109/1271] OP-4643 - propagate new extension to representation --- .../publish/extract_color_transcode.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4d38514b8b..62cf8f0dee 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -90,8 +90,13 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) output_extension = output_def["output_extension"] - files_to_convert = self._rename_output_files(files_to_convert, - output_extension) + output_extension = output_extension.replace('.', '') + if output_extension: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + files_to_convert = self._rename_output_files( + files_to_convert, output_extension) files_to_convert = [os.path.join(original_staging_dir, path) for path in files_to_convert] @@ -127,15 +132,13 @@ class ExtractColorTranscode(publish.Extractor): def _rename_output_files(self, files_to_convert, output_extension): """Change extension of converted files.""" - if output_extension: - output_extension = output_extension.replace('.', '') - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files return files_to_convert def _get_profile(self, instance): From d27dab7c970903e483aa6335ebee62ffffbab191 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:35 +0100 Subject: [PATCH 0110/1271] OP-4643 - added label to Settings --- .../projects_schema/schemas/schema_global_publish.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index c2c911d7d6..7155510fef 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -201,10 +201,14 @@ "type": "dict", "collapsible": true, "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode", + "label": "ExtractColorTranscode (ImageIO)", "checkbox_key": "enabled", "is_group": true, "children": [ + { + "type": "label", + "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + }, { "type": "boolean", "key": "enabled", From c0da2c8afc711657b0b46eeba34b1742b9b5afa8 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sat, 7 Jan 2023 09:33:38 +0000 Subject: [PATCH 0111/1271] Simplify validation --- .../maya/plugins/publish/validate_xgen.py | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_xgen.py b/openpype/hosts/maya/plugins/publish/validate_xgen.py index b5817d4920..19cf612848 100644 --- a/openpype/hosts/maya/plugins/publish/validate_xgen.py +++ b/openpype/hosts/maya/plugins/publish/validate_xgen.py @@ -16,29 +16,27 @@ class ValidateXgen(pyblish.api.InstancePlugin): families = ["xgen"] def process(self, instance): - # Validate only xgen collections are in objectset. - valid_nodes = set( - instance.data["xgenNodes"] + - cmds.ls(instance, type="transform", long=True) - ) - invalid_nodes = [node for node in instance if node not in valid_nodes] + set_members = instance.data.get("setMembers") - if invalid_nodes: + # Only 1 collection/node per instance. + if len(set_members) != 1: raise KnownPublishError( - "Only the collection is used when publishing. Found these " - "invalid nodes in the objectset:\n{}".format(invalid_nodes) + "Only one collection per instance is allowed." + " Found:\n{}".format(set_members) ) - # Only one collection per instance. - palette_amount = len(instance.data["xgenPalettes"]) - msg = "Only one collection per instance allow. Found {}:\n{}".format( - palette_amount, instance.data["xgenPalettes"] - ) - assert palette_amount == 1, msg + # Only xgen palette node is allowed. + node_type = cmds.nodeType(set_members[0]) + if node_type != "xgmPalette": + raise KnownPublishError( + "Only node of type \"xgmPalette\" are allowed. Referred to as" + " \"collection\" in the Maya UI." + " Node type found: {}".format(node_type) + ) # Cant have inactive modifiers in collection cause Xgen will try and # look for them when loading. - palette = instance.data["xgenPalette"].replace("|", "") + palette = instance.data["xgmPalette"].replace("|", "") inactive_modifiers = {} for description in instance.data["xgmDescriptions"]: description = description.split("|")[-2] From 239995f715ea3df72af6cc9416152d8b043a3938 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sat, 7 Jan 2023 09:34:20 +0000 Subject: [PATCH 0112/1271] Account for references and children of patches --- openpype/hosts/maya/plugins/publish/extract_xgen.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 5b4c6b29f2..99efcb34ce 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -61,6 +61,12 @@ class ExtractXgenCache(publish.Extractor): fullPath=True )[0] + # Discard the children. + shapes = cmds.listRelatives(duplicate_transform, shapes=True) + children = cmds.listRelatives(duplicate_transform, children=True) + cmds.delete(set(children) - set(shapes)) + + # Connect attributes. cmds.connectAttr( "{}.matrix".format(duplicate_transform), "{}.transform".format(node), @@ -97,7 +103,7 @@ class ExtractXgenCache(publish.Extractor): force=True, type=type, exportSelected=True, - preserveReferences=True, + preserveReferences=False, constructionHistory=True, shader=True, constraints=True, From efb7d42a7fa9d267b1f7e284bc1d659f3cd41583 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sat, 7 Jan 2023 09:34:34 +0000 Subject: [PATCH 0113/1271] Code cosmetics --- openpype/hosts/maya/plugins/publish/collect_xgen.py | 8 ++++---- openpype/hosts/maya/plugins/publish/extract_xgen.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_xgen.py b/openpype/hosts/maya/plugins/publish/collect_xgen.py index fa7ec3776d..5a48b1d221 100644 --- a/openpype/hosts/maya/plugins/publish/collect_xgen.py +++ b/openpype/hosts/maya/plugins/publish/collect_xgen.py @@ -13,20 +13,20 @@ class CollectXgen(pyblish.api.InstancePlugin): def process(self, instance): data = { - "xgenPalettes": cmds.ls(instance, type="xgmPalette", long=True), + "xgmPalettes": cmds.ls(instance, type="xgmPalette", long=True), "xgmDescriptions": cmds.ls( instance, type="xgmDescription", long=True ), "xgmSubdPatches": cmds.ls(instance, type="xgmSubdPatch", long=True) } data["xgenNodes"] = ( - data["xgenPalettes"] + + data["xgmPalettes"] + data["xgmDescriptions"] + data["xgmSubdPatches"] ) - if data["xgenPalettes"]: - data["xgenPalette"] = data["xgenPalettes"][0] + if data["xgmPalettes"]: + data["xgmPalette"] = data["xgmPalettes"][0] data["xgenConnections"] = {} for node in data["xgmSubdPatches"]: diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 99efcb34ce..22260df2c7 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -30,13 +30,13 @@ class ExtractXgenCache(publish.Extractor): template_data.update({"ext": "xgen"}) templates = instance.context.data["anatomy"].templates["publish"] xgen_filename = StringTemplate(templates["file"]).format(template_data) - name = instance.data["xgenPalette"].replace(":", "__").replace("|", "") + name = instance.data["xgmPalette"].replace(":", "__").replace("|", "") xgen_filename = xgen_filename.replace(".xgen", "__" + name + ".xgen") # Export xgen palette files. xgen_path = os.path.join(staging_dir, xgen_filename).replace("\\", "/") xgenm.exportPalette( - instance.data["xgenPalette"].replace("|", ""), xgen_path + instance.data["xgmPalette"].replace("|", ""), xgen_path ) self.log.info("Extracted to {}".format(xgen_path)) @@ -132,12 +132,12 @@ class ExtractXgenCache(publish.Extractor): # Collect all files under palette root as resources. data_path = xgenm.getAttr( - "xgDataPath", instance.data["xgenPalette"].replace("|", "") + "xgDataPath", instance.data["xgmPalette"].replace("|", "") ).split(os.pathsep)[0] data_path = data_path.replace( "${PROJECT}", xgenm.getAttr( - "xgProjectPath", instance.data["xgenPalette"].replace("|", "") + "xgProjectPath", instance.data["xgmPalette"].replace("|", "") ) ) transfers = [] From 9ed90e67f8a2fbdc62d3c2a15cc89ed79111301d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sun, 8 Jan 2023 18:03:34 +0000 Subject: [PATCH 0114/1271] Code cosmetics --- openpype/hosts/maya/plugins/inventory/connect_geometry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/inventory/connect_geometry.py b/openpype/hosts/maya/plugins/inventory/connect_geometry.py index 375ad29ef0..9fe4f9e195 100644 --- a/openpype/hosts/maya/plugins/inventory/connect_geometry.py +++ b/openpype/hosts/maya/plugins/inventory/connect_geometry.py @@ -52,7 +52,7 @@ class ConnectGeometry(InventoryAction): self.display_warning(message) return - source_container = source_containers[0] + source_object = source_containers[0]["objectName"] # Collect matching geometry transforms based cbId attribute. target_containers = [] @@ -62,7 +62,7 @@ class ConnectGeometry(InventoryAction): target_containers.extend(containers) - source_data = self.get_container_data(source_container["objectName"]) + source_data = self.get_container_data(source_object) matches = [] node_types = [] for target_container in target_containers: @@ -80,7 +80,7 @@ class ConnectGeometry(InventoryAction): self.display_warning("No matching geometries found.") return - message = "Linking geometries:\n\n" + message = "Connecting geometries:\n\n" for match in matches: message += "{} > {}\n".format(match[0], match[1]) From 0382ef29f5e9b02186d2c207063a3a15b66b3397 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sun, 8 Jan 2023 18:04:01 +0000 Subject: [PATCH 0115/1271] Connect and update xgen animation. --- openpype/hosts/maya/api/plugin.py | 31 ++++ .../maya/plugins/inventory/connect_xgen.py | 168 ++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 openpype/hosts/maya/plugins/inventory/connect_xgen.py diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 82df85a8be..f11757b2ab 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -300,6 +300,37 @@ class ReferenceLoader(Loader): str(representation["_id"]), type="string") + # Update any xgen containers. + compound_name = "xgenContainers" + object = "SplinePrimitive" + if cmds.objExists("{}.{}".format(node, compound_name)): + import xgenm + container_amount = cmds.getAttr( + "{}.{}".format(node, compound_name), size=True + ) + # loop through all compound children + for i in range(container_amount): + attr = "{}.{}[{}].container".format(node, compound_name, i) + objectset = cmds.listConnections(attr)[0] + reference_node = cmds.sets(objectset, query=True)[0] + palettes = cmds.ls( + cmds.referenceQuery(reference_node, nodes=True), + type="xgmPalette" + ) + for palette in palettes: + for description in xgenm.descriptions(palette): + xgenm.setAttr( + "cacheFileName", + path.replace("\\", "/"), + palette, + description, + object + ) + + # Refresh UI and viewport. + de = xgenm.xgGlobal.DescriptionEditor + de.refresh("Full") + def remove(self, container): """Remove an existing `container` from Maya scene diff --git a/openpype/hosts/maya/plugins/inventory/connect_xgen.py b/openpype/hosts/maya/plugins/inventory/connect_xgen.py new file mode 100644 index 0000000000..933a1b4025 --- /dev/null +++ b/openpype/hosts/maya/plugins/inventory/connect_xgen.py @@ -0,0 +1,168 @@ +from maya import cmds +import xgenm + +from openpype.pipeline import ( + InventoryAction, get_representation_context, get_representation_path +) + + +class ConnectXgen(InventoryAction): + """Connect Xgen with an animation or pointcache. + """ + + label = "Connect Xgen" + icon = "link" + color = "white" + + def process(self, containers): + # Validate selection is more than 1. + message = ( + "Only 1 container selected. 2+ containers needed for this action." + ) + if len(containers) == 1: + self.display_warning(message) + return + + # Categorize containers by family. + containers_by_family = {} + for container in containers: + family = get_representation_context( + container["representation"] + )["subset"]["data"]["family"] + try: + containers_by_family[family].append(container) + except KeyError: + containers_by_family[family] = [container] + + # Validate to only 1 source container. + source_containers = containers_by_family.get("animation", []) + source_containers += containers_by_family.get("pointcache", []) + source_container_namespaces = [ + x["namespace"] for x in source_containers + ] + message = ( + "{} animation containers selected:\n\n{}\n\nOnly select 1 of type " + "\"animation\" or \"pointcache\".".format( + len(source_containers), source_container_namespaces + ) + ) + if len(source_containers) != 1: + self.display_warning(message) + return + + source_container = source_containers[0] + source_object = source_container["objectName"] + + # Validate source representation is an alembic. + source_path = get_representation_path( + get_representation_context( + source_container["representation"] + )["representation"] + ).replace("\\", "/") + message = "Animation container \"{}\" is not an alembic:\n{}".format( + source_container["namespace"], source_path + ) + if not source_path.endswith(".abc"): + self.display_warning(message) + return + + # Target containers. + target_containers = [] + for family, containers in containers_by_family.items(): + if family in ["animation", "pointcache"]: + continue + + target_containers.extend(containers) + + # Inform user of connections from source representation to target + # descriptions. + descriptions_data = [] + connections_msg = "" + for target_container in target_containers: + reference_node = cmds.sets( + target_container["objectName"], query=True + )[0] + palettes = cmds.ls( + cmds.referenceQuery(reference_node, nodes=True), + type="xgmPalette" + ) + for palette in palettes: + for description in xgenm.descriptions(palette): + descriptions_data.append([palette, description]) + connections_msg += "\n{}/{}".format(palette, description) + + message = "Connecting \"{}\" to:\n".format( + source_container["namespace"] + ) + message += connections_msg + choice = self.display_warning(message, show_cancel=True) + if choice is False: + return + + # Recreate "xgenContainers" attribute to reset. + compound_name = "xgenContainers" + attr = "{}.{}".format(source_object, compound_name) + if cmds.objExists(attr): + cmds.deleteAttr(attr) + + cmds.addAttr( + source_object, + longName=compound_name, + attributeType="compound", + numberOfChildren=1, + multi=True + ) + + # Connect target containers. + for target_container in target_containers: + cmds.addAttr( + source_object, + longName="container", + attributeType="message", + parent=compound_name + ) + index = target_containers.index(target_container) + cmds.connectAttr( + target_container["objectName"] + ".message", + source_object + ".{}[{}].container".format( + compound_name, index + ) + ) + + # Setup cache on Xgen + object = "SplinePrimitive" + for palette, description in descriptions_data: + xgenm.setAttr("useCache", "true", palette, description, object) + xgenm.setAttr("liveMode", "false", palette, description, object) + xgenm.setAttr( + "cacheFileName", source_path, palette, description, object + ) + + # Refresh UI and viewport. + de = xgenm.xgGlobal.DescriptionEditor + de.refresh("Full") + + def display_warning(self, message, show_cancel=False): + """Show feedback to user. + + Returns: + bool + """ + + from Qt import QtWidgets + + accept = QtWidgets.QMessageBox.Ok + if show_cancel: + buttons = accept | QtWidgets.QMessageBox.Cancel + else: + buttons = accept + + state = QtWidgets.QMessageBox.warning( + None, + "", + message, + buttons=buttons, + defaultButton=accept + ) + + return state == accept From baca6a93c3d1208960b68d95dbc3b19bd58d5725 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 9 Jan 2023 16:44:40 +0800 Subject: [PATCH 0116/1271] remove the validator for color-space --- .../publish/validate_look_color_space.py | 26 ------------------- .../defaults/project_settings/maya.json | 5 ---- .../schemas/schema_maya_publish.json | 4 --- 3 files changed, 35 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/publish/validate_look_color_space.py diff --git a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py deleted file mode 100644 index 933951501c..0000000000 --- a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py +++ /dev/null @@ -1,26 +0,0 @@ -from maya import cmds - -import pyblish.api -from openpype.pipeline.publish import ValidateContentsOrder - - -class ValidateMayaColorSpace(pyblish.api.InstancePlugin): - """ - Check if the OCIO Color Management and maketx options - enabled at the same time - - """ - - order = ValidateContentsOrder - families = ['look'] - hosts = ['maya'] - label = 'Maya Color Space' - - def process(self, instance): - ocio_maya = cmds.colorManagementPrefs(q=True, - cmConfigFileEnabled=True, - cmEnabled=True) - maketx = instance.data["maketx"] - - if ocio_maya and maketx: - raise Exception("Maya is color managed and maketx option is on. OpenPype doesn't support this combination yet.") # noqa diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 21815278a8..5f40c2a10c 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -306,11 +306,6 @@ "optional": true, "active": true }, - "ValidateMayaColorSpace": { - "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 a920d5aeae..9aaff248ab 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 @@ -144,10 +144,6 @@ { "key": "ValidateShadingEngine", "label": "Validate Look Shading Engine Naming" - }, - { - "key": "ValidateMayaColorSpace", - "label": "ValidateMayaColorSpace" } ] }, From 0dd54b0a5c767a02bcb998e6740b235695f996c6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 9 Jan 2023 15:40:50 +0000 Subject: [PATCH 0117/1271] Validate workfile has been saved. --- openpype/hosts/maya/plugins/load/load_xgen.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 23a22127e9..565d1306b4 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -4,6 +4,8 @@ import shutil import maya.cmds as cmds import xgenm +from Qt import QtWidgets + import openpype.hosts.maya.api.plugin from openpype.hosts.maya.api.lib import ( maintained_selection, get_container_members, attribute_values @@ -71,6 +73,15 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): return destination def process_reference(self, context, name, namespace, options): + # Validate workfile has a path. + if current_file() is None: + QtWidgets.QMessageBox.warning( + None, + "", + "Current workfile has not been saved." + ) + return + maya_filepath = self.prepare_root_value( self.fname, context["project"]["name"] ) From ce0a6d8ba4ba2ceae2c32cf61e4f8ec52c5f9700 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 10 Jan 2023 09:21:09 +0000 Subject: [PATCH 0118/1271] Fix --- openpype/hosts/maya/api/lib_renderproducts.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index c54e3ab3e0..fcc844d5d6 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -797,6 +797,11 @@ class RenderProductsVray(ARenderProducts): if default_ext in {"exr (multichannel)", "exr (deep)"}: default_ext = "exr" + # Define multipart. + multipart = False + if image_format_str == "exr (multichannel)": + multipart = True + products = [] # add beauty as default when not disabled @@ -804,23 +809,28 @@ class RenderProductsVray(ARenderProducts): if not dont_save_rgb: for camera in cameras: products.append( - RenderProduct(productName="", - ext=default_ext, - camera=camera)) + RenderProduct( + productName="", + ext=default_ext, + camera=camera, + multipart=multipart + ) + ) # separate alpha file separate_alpha = self._get_attr("vraySettings.separateAlpha") if separate_alpha: for camera in cameras: products.append( - RenderProduct(productName="Alpha", - ext=default_ext, - camera=camera) + RenderProduct( + productName="Alpha", + ext=default_ext, + camera=camera, + multipart=multipart + ) ) - - if image_format_str == "exr (multichannel)": + if multipart: # AOVs are merged in m-channel file, only main layer is rendered - self.multipart = True return products # handle aovs from references From 5bb1405a5506ceec98f885237da9a372c210e91a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 10 Jan 2023 09:21:17 +0000 Subject: [PATCH 0119/1271] Code cosmetics --- openpype/hosts/maya/plugins/publish/collect_render.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index b1ad3ca58e..23fcd730d5 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -42,7 +42,6 @@ Provides: import re import os import platform -import json from maya import cmds import maya.app.renderSetup.model.renderSetup as renderSetup From 6f404ed450f2141a2f1efba13c87918ee74948c1 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 10 Jan 2023 09:21:38 +0000 Subject: [PATCH 0120/1271] Improve debug logging. --- .../deadline/plugins/publish/submit_publish_job.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 5c5c54febb..4d98cabe25 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -503,6 +503,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # toggle preview on if multipart is on if instance_data.get("multipartExr"): + self.log.debug("Adding preview tag because its multipartExr") preview = True self.log.debug("preview:{}".format(preview)) new_instance = deepcopy(instance_data) @@ -582,6 +583,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): if instance["useSequenceForReview"]: # toggle preview on if multipart is on if instance.get("multipartExr", False): + self.log.debug("Adding preview tag because its multipartExr") preview = True else: render_file_name = list(collection)[0] @@ -689,8 +691,14 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): if preview: if "ftrack" not in families: if os.environ.get("FTRACK_SERVER"): + self.log.debug( + "Adding \"ftrack\" to families because of preview tag." + ) families.append("ftrack") if "review" not in families: + self.log.debug( + "Adding \"review\" to families because of preview tag." + ) families.append("review") instance["families"] = families From a09667670a3c84afb87f95427500a9e1dc4461b5 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 10 Jan 2023 09:29:23 +0000 Subject: [PATCH 0121/1271] Hound --- .../modules/deadline/plugins/publish/submit_publish_job.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 4d98cabe25..45463ead1b 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -583,7 +583,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): if instance["useSequenceForReview"]: # toggle preview on if multipart is on if instance.get("multipartExr", False): - self.log.debug("Adding preview tag because its multipartExr") + self.log.debug( + "Adding preview tag because its multipartExr" + ) preview = True else: render_file_name = list(collection)[0] From 3021fea4762ec84e53091f1cb954883be6a53634 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 11 Jan 2023 10:04:17 +0000 Subject: [PATCH 0122/1271] Increment xgen files with workfile. --- openpype/hosts/maya/startup/userSetup.py | 42 +++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/startup/userSetup.py b/openpype/hosts/maya/startup/userSetup.py index b64ed93000..5cd27500dc 100644 --- a/openpype/hosts/maya/startup/userSetup.py +++ b/openpype/hosts/maya/startup/userSetup.py @@ -1,8 +1,10 @@ import os +import shutil from openpype.settings import get_project_settings from openpype.pipeline import install_host -from openpype.hosts.maya.api import MayaHost +from openpype.hosts.maya.api import MayaHost, current_file +from openpype.lib import register_event_callback from maya import cmds @@ -21,6 +23,44 @@ if bool(int(os.environ.get(key, "0"))): lowestPriority=True ) + +# Setup Xgen save callback. +def xgen_on_save(): + """Increments the xgen side car files .xgen and .xgd + + Only works when incrementing to the same directory. + """ + + file_path = current_file() + current_dir = os.path.dirname(file_path) + basename = os.path.basename(file_path).split(".")[0] + attrs = ["xgFileName", "xgBaseFile"] + for palette in cmds.ls(type="xgmPalette"): + for attr in attrs: + source = os.path.join( + current_dir, cmds.getAttr(palette + "." + attr) + ) + if not os.path.exists(source): + continue + + destination_basename = "{}__{}{}".format( + basename, + palette.replace(":", "_"), + os.path.splitext(source)[1] + ) + destination = os.path.join(current_dir, destination_basename) + + if source == destination: + continue + + shutil.copy(source, destination) + cmds.setAttr( + palette + "." + attr, destination_basename, type="string" + ) + + +register_event_callback("save", xgen_on_save) + # Build a shelf. settings = get_project_settings(os.environ['AVALON_PROJECT']) shelf_preset = settings['maya'].get('project_shelf') From 331ec6bf9d0eea58c12d7f1cfbca7cf67a1796a8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 12 Jan 2023 11:01:53 +0100 Subject: [PATCH 0123/1271] OP-4617 - added new plugin to expose field for frames to fix Collector should get triggered for render family and offer field to input frames that should be re-rendered and replaced ones in latest version --- .../plugins/publish/collect_frames_fix.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 openpype/plugins/publish/collect_frames_fix.py diff --git a/openpype/plugins/publish/collect_frames_fix.py b/openpype/plugins/publish/collect_frames_fix.py new file mode 100644 index 0000000000..1e73f2caaa --- /dev/null +++ b/openpype/plugins/publish/collect_frames_fix.py @@ -0,0 +1,29 @@ +import pyblish.api +from openpype.lib.attribute_definitions import TextDef +from openpype.pipeline.publish import OpenPypePyblishPluginMixin + + +class CollectFramesFixDef( + pyblish.api.ContextPlugin, + OpenPypePyblishPluginMixin +): + label = "Collect frames to fix" + targets = ["local"] + # Disable plugin by default + families = ["render"] + enabled = True + + def process(self, instance): + attribute_values = self.get_attr_values_from_data(instance.data) + frames_to_fix = attribute_values.get("frames_to_fix") + if frames_to_fix: + instance.data["frames_to_fix"] = frames_to_fix + + @classmethod + def get_attribute_defs(cls): + return [ + TextDef("frames_to_fix", label="Frames to fix", + placeholder="5,10-15", + regex="[0-9,-]+") + ] + From 0b65779698d7f820ef8dea25be2c9ba9f65e0e85 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 12 Jan 2023 11:04:14 +0100 Subject: [PATCH 0124/1271] OP-4617 - added possibility to rerender frames locally in Nuke WIP --- .../plugins/publish/extract_render_local.py | 87 ++++++++++++------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_render_local.py b/openpype/hosts/nuke/plugins/publish/extract_render_local.py index 811b2d4ffb..7271c8b45b 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_render_local.py +++ b/openpype/hosts/nuke/plugins/publish/extract_render_local.py @@ -5,6 +5,11 @@ import clique import nuke from openpype.pipeline import publish +from openpype.client import ( + get_version_by_id, + get_last_version_by_subset_id, +) + class NukeRenderLocal(publish.Extractor): @@ -35,42 +40,64 @@ class NukeRenderLocal(publish.Extractor): self.log.debug("instance collected: {}".format(instance.data)) - first_frame = instance.data.get("frameStartHandle", None) - - last_frame = instance.data.get("frameEndHandle", None) node_subset_name = instance.data.get("name", None) + frames_to_fix = instance.data.get("frames_to_fix") + frames_to_render = [] + if not frames_to_fix: + first_frame = instance.data.get("frameStartHandle", None) + last_frame = instance.data.get("frameEndHandle", None) + frames_to_render.append((first_frame, last_frame)) + else: + for frame_range in frames_to_fix.split(","): + if isinstance(frame_range, int): + first_frame = frame_range + last_frame = frame_range + elif '-' in frame_range: + frames = frame_range.split('-') + first_frame = int(frames[0]) + last_frame = int(frames[1]) + else: + raise ValueError("Wrong format of frames to fix {}" + .format(frames_to_fix)) + frames_to_render.append((first_frame, last_frame)) - self.log.info("Starting render") - self.log.info("Start frame: {}".format(first_frame)) - self.log.info("End frame: {}".format(last_frame)) + filenames = [] + for first_frame, last_frame in frames_to_render: - node_file = node["file"] - # Collecte expected filepaths for each frame - # - for cases that output is still image is first created set of - # paths which is then sorted and converted to list - expected_paths = list(sorted({ - node_file.evaluate(frame) - for frame in range(first_frame, last_frame + 1) - })) - # Extract only filenames for representation - filenames = [ - os.path.basename(filepath) - for filepath in expected_paths - ] + self.log.info("Starting render") + self.log.info("Start frame: {}".format(first_frame)) + self.log.info("End frame: {}".format(last_frame)) - # Ensure output directory exists. - out_dir = os.path.dirname(expected_paths[0]) - if not os.path.exists(out_dir): - os.makedirs(out_dir) + node_file = node["file"] + # Collecte expected filepaths for each frame + # - for cases that output is still image is first created set of + # paths which is then sorted and converted to list + expected_paths = list(sorted({ + node_file.evaluate(frame) + for frame in range(first_frame, last_frame + 1) + })) + # Extract only filenames for representation + filenames.extend([ + os.path.basename(filepath) + for filepath in expected_paths + ]) - # Render frames - nuke.execute( - node_subset_name, - int(first_frame), - int(last_frame) - ) + # Ensure output directory exists. + out_dir = os.path.dirname(expected_paths[0]) + if not os.path.exists(out_dir): + os.makedirs(out_dir) - ext = node["file_type"].value() + # Render frames + nuke.execute( + node_subset_name, + int(first_frame), + int(last_frame) + ) + + ext = node["file_type"].value() + + if frames_to_fix: + pass if "representations" not in instance.data: instance.data["representations"] = [] From 4dc155d95fb89ccea3310721afcd534f91e07a10 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 12 Jan 2023 15:51:51 +0000 Subject: [PATCH 0125/1271] Support for rendering Opening the workfile in its published location will load the xgen correctly. --- .../plugins/publish/extract_workfile_xgen.py | 131 ++++++++++++++++++ .../plugins/publish/reset_xgen_attributes.py | 21 +++ 2 files changed, 152 insertions(+) create mode 100644 openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py create mode 100644 openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py diff --git a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py new file mode 100644 index 0000000000..e12a870eaf --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -0,0 +1,131 @@ +import os +import shutil + +from maya import cmds + +import pyblish.api +from openpype.hosts.maya.api import current_file +from openpype.pipeline import publish + + +class ExtractWorkfileXgen(publish.Extractor): + """Extract Workfile Xgen.""" + + # Offset to run before workfile scene save. + order = pyblish.api.ExtractorOrder - 0.499 + label = "Extract Workfile Xgen" + families = ["workfile"] + hosts = ["maya"] + + def process(self, instance): + # Validate to extract only when we are publishing a renderlayer as + # well. + renderlayer = False + for i in instance.context: + is_renderlayer = ( + "renderlayer" in i.data.get("families", []) or + i.data["family"] == "renderlayer" + ) + if is_renderlayer and i.data["publish"]: + renderlayer = True + break + + if not renderlayer: + self.log.debug( + "No publishable renderlayers found in context. Abort Xgen" + " extraction." + ) + return + + # Collect Xgen and Delta files. + xgen_files = [] + sources = [] + file_path = current_file() + current_dir = os.path.dirname(file_path) + attrs = ["xgFileName", "xgBaseFile"] + for palette in cmds.ls(type="xgmPalette"): + for attr in attrs: + source = os.path.join( + current_dir, cmds.getAttr(palette + "." + attr) + ) + if not os.path.exists(source): + continue + + ext = os.path.splitext(source)[1] + if ext == ".xgen": + xgen_files.append(source) + if ext == ".xgd": + sources.append(source) + + # Copy .xgen file to temporary location and modify. + staging_dir = self.staging_dir(instance) + for source in xgen_files: + destination = os.path.join(staging_dir, os.path.basename(source)) + shutil.copy(source, destination) + + lines = [] + with open(destination, "r") as f: + for line in [line.rstrip() for line in f]: + if line.startswith("\txgProjectPath"): + line = "\txgProjectPath\t\t{}/".format( + instance.data["resourcesDir"].replace("\\", "/") + ) + + lines.append(line) + + with open(destination, "w") as f: + f.write("\n".join(lines)) + + sources.append(destination) + + # Add resource files to workfile instance. + transfers = [] + for source in sources: + basename = os.path.basename(source) + destination = os.path.join(instance.data["resourcesDir"], basename) + transfers.append((source, destination)) + + import xgenm + for palette in cmds.ls(type="xgmPalette"): + relative_data_path = xgenm.getAttr( + "xgDataPath", palette.replace("|", "") + ).split(os.pathsep)[0] + absolute_data_path = relative_data_path.replace( + "${PROJECT}", + xgenm.getAttr("xgProjectPath", palette.replace("|", "")) + ) + + for root, _, files in os.walk(absolute_data_path): + for file in files: + source = os.path.join(root, file).replace("\\", "/") + destination = os.path.join( + instance.data["resourcesDir"], + relative_data_path.replace("${PROJECT}", ""), + source.replace(absolute_data_path, "")[1:] + ) + transfers.append((source, destination.replace("\\", "/"))) + + for source, destination in transfers: + self.log.debug("Transfer: {} > {}".format(source, destination)) + + instance.data["transfers"] = transfers + + # Set palette attributes in preparation for workfile publish. + attrs = ["xgFileName", "xgBaseFile"] + data = {} + for palette in cmds.ls(type="xgmPalette"): + for attr in attrs: + value = cmds.getAttr(palette + "." + attr) + if value: + new_value = "resources/{}".format(value) + node_attr = "{}.{}".format(palette, attr) + self.log.info( + "Setting \"{}\" on \"{}\"".format(new_value, node_attr) + ) + cmds.setAttr(node_attr, new_value, type="string") + try: + data[palette][attr] = value + except KeyError: + data[palette] = {attr: value} + + instance.data["xgenAttributes"] = data diff --git a/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py b/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py new file mode 100644 index 0000000000..9397d479d2 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py @@ -0,0 +1,21 @@ +from maya import cmds + +import pyblish.api + + +class ResetXgenAttributes(pyblish.api.InstancePlugin): + """Reset Xgen attributes.""" + + label = "Reset Xgen Attributes." + # Offset to run after global integrator. + order = pyblish.api.IntegratorOrder + 1.0 + families = ["workfile"] + + def process(self, instance): + for palette, data in instance.data.get("xgenAttributes", []).items(): + for attr, value in data.items(): + node_attr = "{}.{}".format(palette, attr) + self.log.info( + "Setting \"{}\" on \"{}\"".format(value, node_attr) + ) + cmds.setAttr(node_attr, value, type="string") From 05a8ef36f7472143bffa36837e3cf56c9593733e Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 13 Jan 2023 12:59:25 +0000 Subject: [PATCH 0126/1271] Xgen documentation --- website/docs/artist_hosts_maya.md | 17 +++++ website/docs/artist_hosts_maya_xgen.md | 94 ++++++++++++++++++++++---- 2 files changed, 97 insertions(+), 14 deletions(-) diff --git a/website/docs/artist_hosts_maya.md b/website/docs/artist_hosts_maya.md index fb1af765d2..ced6887d08 100644 --- a/website/docs/artist_hosts_maya.md +++ b/website/docs/artist_hosts_maya.md @@ -601,3 +601,20 @@ about customizing review process refer to [admin section](project_settings/setti If you don't move `modelMain` into `reviewMain`, review will be generated but it will be published as separate entity. + + +## Inventory Actions + +### Connect Geometry + +This action will connect geometries between containers. + +#### Usage + +Select 1 container of type `animation` or `pointcache`, then 1+ container of any type. + +#### Details + +The action searches the selected containers for 1 animation container of type `animation` or `pointcache`. This animation container will be connected to the rest of the selected containers. Matching geometries between containers is done by comparing the attribute `cbId`. + +The connection between geometries is done with a live blendshape. diff --git a/website/docs/artist_hosts_maya_xgen.md b/website/docs/artist_hosts_maya_xgen.md index 8b0174a29f..629b600458 100644 --- a/website/docs/artist_hosts_maya_xgen.md +++ b/website/docs/artist_hosts_maya_xgen.md @@ -4,26 +4,92 @@ title: Xgen for Maya sidebar_label: Xgen --- -## Working with Xgen in OpenPype +## Setup -OpenPype support publishing and loading of Xgen interactive grooms. You can publish -them as mayaAscii files with scalps that can be loaded into another maya scene, or as -alembic caches. +### Settings -### Publishing Xgen Grooms +Go to project settings > Maya > enable "Open Workfile Post Initialization"; -To prepare xgen for publishing just select all the descriptions that should be published together and the create Xgen Subset in the scene using - **OpenPype menu** → **Create**... and select **Xgen Interactive**. Leave Use selection checked. +`project_settings/maya/open_workfile_post_initialization` -For actual publishing of your groom to go **OpenPype → Publish** and then press ▶ to publish. This will export `.ma` file containing your grooms with any geometries they are attached to and also a baked cache in `.abc` format +This is due to two errors occurring when opening workfile contaiing referenced xgen nodes on launch of Maya; +- ``Critical``: Duplicate collection errors on launching workfile. This is because Maya first imports Xgen when referencing in external Maya files, then imports Xgen again when the reference edits are applied. +``` +Importing XGen Collections... +# Error: XGen: Failed to find description ball_xgenMain_01_:parent in collection ball_xgenMain_01_:collection. Abort applying delta: P:/PROJECTS/OP01_CG_demo/shots/sh040/work/Lighting/cg_sh040_Lighting_v001__ball_xgenMain_01___collection.xgen # +# Error: XGen: Tried to import a duplicate collection, ball_xgenMain_02_:collection, from file P:/PROJECTS/OP01_CG_demo/shots/sh040/work/Lighting/cg_sh040_Lighting_v001__ball_xgenMain_02___collection.xgen. Aborting import. # +``` +- ``Non-critical``: Errors on opening workfile and failed opening of published xgen. This is because Maya imports Xgen when referencing in external Maya files but the reference edits that ensure the location of the Xgen files are correct, has not been applied yet. +``` +Importing XGen Collections... +# Error: XGen: Failed to open file: P:/PROJECTS/OP01_CG_demo/shots/sh040/work/Lighting/cg_ball_xgenMain_v035__ball_rigMain_01___collection.xgen # +# Error: XGen: Failed to import collection from file P:/PROJECTS/OP01_CG_demo/shots/sh040/work/Lighting/cg_ball_xgenMain_v035__ball_rigMain_01___collection.xgen # +``` -:::tip adding more descriptions -You can add multiple xgen description into the subset you are about to publish, simply by -adding them to the maya set that was created for you. Please make sure that only xgen description nodes are present inside of the set and not the scalp geometry. -::: +### Workfile Incremental Save -### Loading Xgen +When you increment the Maya workfile to a new version, all `.xgen` and `.xgd` files referenced by the Xgen collection in the workspace is incremented as well. -You can use published xgens by loading them using OpenPype Publisher. You can choose to reference or import xgen. We don't have any automatic mesh linking at the moment and it is expected, that groom is published with a scalp, that can then be manually attached to your animated mesh for example. +## Create -The alembic representation can be loaded too and it contains the groom converted to curves. Keep in mind that the density of the alembic directly depends on your viewport xgen density at the point of export. +Create an Xgen instance to publish. This needs to contain 1 Xgen collection only, but validation will check for this: + +`OpenPype > Create... > Xgen` + +You can create multiple Xgen instances if you have multiple collections to publish. + +### Publish + +The publishing process will grab geometry used for Xgen along with any external files used in the collection's descriptions. This creates an isolated Maya file with just the Xgen collection's dependencies, so you can use any nested geometry when creating the Xgen description. An Xgen version will consist of: + +- Maya file (`.ma`) - this contains the geometry and the connections to the Xgen collection and descriptions. +- Xgen file (`.xgen`) - this contains the Xgen collection and description. +- Resource files (`.ptx`, `.xuv`) - this contains Xgen side car files used in the collection and descriptions. + +## Load + +Open the Loader tool, `OpenPype > Loader...`, and navigate to the published Xgen version. On right-click you'll get the option `Reference Xgen (ma)` +When loading an Xgen version the following happens: + +- References in the Maya file. +- Copies the Xgen file (`.xgen`) to the current workspace. +- Modifies the Xgen file copy to load the current workspace first then the published Xgen collection. +- Makes a custom attribute on the Xgen collection, `float_ignore`, which can be seen under the `Expressions` tab of the `Xgen` UI. This is done to initialize the Xgen delta file workflow. +- Setup an Xgen delta file (`.xgd`) to store any workspace changes of the published Xgen version. + +When the loading is done, Xgen collection will be in the Xgen delta file workflow which means any changes done in the Maya workfile will be stored in the current workspace. The published Xgen collection will remain intact, even if the user assigns maps to any attributes or otherwise modifies any attribute. + +### Updating + +When there are changes to the Xgen version, the user will be notified when opening the workfile or publishing. Since the Xgen is referenced, it follows the standard Maya referencing system and overrides. + +For example publishing `xgenMain` version 1 with the attribute `renderer` set to `None`, then version 2 has `renderer` set to `Arnold Renderer`. When updating from version 1 to 2, the `renderer` attribute will be updated to `Arnold Renderer` unless there is an override. + +### Connect Patches + +When loading in an Xgen version, it does not have any connections to anything in the workfile, so its static in the position it was published in. Use the [Connect Geometry](artist_hosts_maya#connect-geometry) action to connect Xgen to any matching loaded animated geometry. + +### Connect Guides + +Along with patches you can also connect the Xgen guides to an Alembic cache. + +#### Usage + +Select 1 animation container, of family `animation` or `pointcache`, then the Xgen containers to connect to. Right-click > `Actions` > `Connect Xgen`. + +***Note: Only alembic (`.abc`) representations are allowed.*** + +#### Details + +Connecting the guide will make Xgen use the Alembic directly, setting the attributes under `Guide Animation`, so the Alembic needs to contain the same amount of curves as guides in the Xgen. + +The animation container gets connected with the Xgen container, so if the animation container is updated so will the Xgen container's attribute. + +## Rendering + +To render with Xgen, follow the [Rendering With OpenPype](artist_hosts_maya#rendering-with-openpype) guide. + +### Details + +When submitting a workfile with Xgen, all Xgen related files will be collected and published as the workfiles resources. This means the published workfile is no longer referencing the workspace Xgen files. From 1f45f8c5b37a3d42936ff068467ba78725fad84b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 13 Jan 2023 23:08:23 +0800 Subject: [PATCH 0127/1271] add validator to the look publish --- .../publish/validate_look_color_space.py | 25 +++++++++++++++++++ .../defaults/project_settings/maya.json | 5 ++++ .../schemas/schema_maya_publish.json | 4 +++ 3 files changed, 34 insertions(+) create mode 100644 openpype/hosts/maya/plugins/publish/validate_look_color_space.py diff --git a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py new file mode 100644 index 0000000000..05a10fd9f6 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py @@ -0,0 +1,25 @@ +from maya import cmds + +import pyblish.api +from openpype.pipeline.publish import ValidateContentsOrder + + +class ValidateMayaColorSpace(pyblish.api.InstancePlugin): + """ + Check if the OCIO Color Management and maketx options + enabled at the same time + """ + + order = ValidateContentsOrder + families = ['look'] + hosts = ['maya'] + label = 'Maya Color Space' + + def process(self, instance): + ocio_maya = cmds.colorManagementPrefs(q=True, + cmConfigFileEnabled=True, + cmEnabled=True) + maketx = instance.data["maketx"] + + if ocio_maya and maketx: + raise Exception("Maya is color managed and maketx option is on. OpenPype doesn't support this combination yet.") # noqa diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 5f40c2a10c..21815278a8 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -306,6 +306,11 @@ "optional": true, "active": true }, + "ValidateMayaColorSpace": { + "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 9aaff248ab..a920d5aeae 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 @@ -144,6 +144,10 @@ { "key": "ValidateShadingEngine", "label": "Validate Look Shading Engine Naming" + }, + { + "key": "ValidateMayaColorSpace", + "label": "ValidateMayaColorSpace" } ] }, From d206a64e936569c6b0b3719279dc82716ff9ba60 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Sat, 14 Jan 2023 00:09:16 +0800 Subject: [PATCH 0128/1271] adding info to tell user the tx conversion based on ocio.config --- openpype/hosts/maya/plugins/publish/extract_look.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 878c2dceae..860872c3a6 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -535,6 +535,9 @@ class ExtractLook(publish.Extractor): self.log.info("tx: converting sRGB -> linear") additional_args.extend(["--colorconvert", "sRGB", "linear"]) + self.log.info("Using nuke-default ocio config instead of maya ocio config!") # noqa + self.log.info("The tx conversion is different from the maya tx conversion!") # noqa + config_path = get_ocio_config_path("nuke-default") additional_args.extend(["--colorconfig", config_path]) # Ensure folder exists From 27fc690209493147ef965ce0c364465fe5acf223 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 16 Jan 2023 10:04:55 +0000 Subject: [PATCH 0129/1271] Update website/docs/artist_hosts_maya_xgen.md Co-authored-by: Roy Nieterau --- website/docs/artist_hosts_maya_xgen.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/artist_hosts_maya_xgen.md b/website/docs/artist_hosts_maya_xgen.md index 629b600458..cde56888d4 100644 --- a/website/docs/artist_hosts_maya_xgen.md +++ b/website/docs/artist_hosts_maya_xgen.md @@ -12,7 +12,7 @@ Go to project settings > Maya > enable "Open Workfile Post Initialization"; `project_settings/maya/open_workfile_post_initialization` -This is due to two errors occurring when opening workfile contaiing referenced xgen nodes on launch of Maya; +This is due to two errors occurring when opening workfile containing referenced xgen nodes on launch of Maya, specifically: - ``Critical``: Duplicate collection errors on launching workfile. This is because Maya first imports Xgen when referencing in external Maya files, then imports Xgen again when the reference edits are applied. ``` From 1b1cd149065a561741b5783823aceb88a82e5f62 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 16 Jan 2023 10:12:32 +0000 Subject: [PATCH 0130/1271] Update website/docs/artist_hosts_maya_xgen.md Co-authored-by: Roy Nieterau --- website/docs/artist_hosts_maya_xgen.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/artist_hosts_maya_xgen.md b/website/docs/artist_hosts_maya_xgen.md index cde56888d4..e0c008929c 100644 --- a/website/docs/artist_hosts_maya_xgen.md +++ b/website/docs/artist_hosts_maya_xgen.md @@ -33,7 +33,7 @@ When you increment the Maya workfile to a new version, all `.xgen` and `.xgd` fi ## Create -Create an Xgen instance to publish. This needs to contain 1 Xgen collection only, but validation will check for this: +Create an Xgen instance to publish. This needs to contain only **one Xgen collection**. `OpenPype > Create... > Xgen` From 86012906f753e48dd1015516c0d795ca3ccd556f Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 16 Jan 2023 10:13:30 +0000 Subject: [PATCH 0131/1271] Update website/docs/artist_hosts_maya_xgen.md Co-authored-by: Roy Nieterau --- website/docs/artist_hosts_maya_xgen.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/artist_hosts_maya_xgen.md b/website/docs/artist_hosts_maya_xgen.md index e0c008929c..3f47428abe 100644 --- a/website/docs/artist_hosts_maya_xgen.md +++ b/website/docs/artist_hosts_maya_xgen.md @@ -64,7 +64,7 @@ When the loading is done, Xgen collection will be in the Xgen delta file workflo When there are changes to the Xgen version, the user will be notified when opening the workfile or publishing. Since the Xgen is referenced, it follows the standard Maya referencing system and overrides. -For example publishing `xgenMain` version 1 with the attribute `renderer` set to `None`, then version 2 has `renderer` set to `Arnold Renderer`. When updating from version 1 to 2, the `renderer` attribute will be updated to `Arnold Renderer` unless there is an override. +For example publishing `xgenMain` version 1 with the attribute `renderer` set to `None`, then version 2 has `renderer` set to `Arnold Renderer`. When updating from version 1 to 2, the `renderer` attribute will be updated to `Arnold Renderer` unless there is a local override. ### Connect Patches From 58808104280eba59fed182af0849a66e366450f3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jan 2023 13:44:57 +0100 Subject: [PATCH 0132/1271] OP-4642 - refactor weird assignment Co-authored-by: Roy Nieterau --- openpype/plugins/publish/extract_color_transcode.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 62cf8f0dee..835b64b685 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -138,8 +138,7 @@ class ExtractColorTranscode(publish.Extractor): new_file_name = '{}.{}'.format(file_name, output_extension) renamed_files.append(new_file_name) - files_to_convert = renamed_files - return files_to_convert + return renamed_files def _get_profile(self, instance): """Returns profile if and how repre should be color transcoded.""" From 93c5d507a3fa6d8ea8592e1ad5217cc31fa0fbb9 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 16 Jan 2023 22:25:54 +0800 Subject: [PATCH 0133/1271] use RuntimeError instead of Exception and rename the validator --- .../hosts/maya/plugins/publish/validate_look_color_space.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py index 05a10fd9f6..9225feda24 100644 --- a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py +++ b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py @@ -1,3 +1,4 @@ +import os from maya import cmds import pyblish.api @@ -13,7 +14,7 @@ class ValidateMayaColorSpace(pyblish.api.InstancePlugin): order = ValidateContentsOrder families = ['look'] hosts = ['maya'] - label = 'Maya Color Space' + label = 'Color Management with maketx' def process(self, instance): ocio_maya = cmds.colorManagementPrefs(q=True, @@ -22,4 +23,4 @@ class ValidateMayaColorSpace(pyblish.api.InstancePlugin): maketx = instance.data["maketx"] if ocio_maya and maketx: - raise Exception("Maya is color managed and maketx option is on. OpenPype doesn't support this combination yet.") # noqa + raise RuntimeError("Maya is color managed and maketx option is on. OpenPype doesn't support this combination yet.") # noqa From d57a7c67cd7ded6eae21ebf0145acf2642b54e63 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 16 Jan 2023 22:26:59 +0800 Subject: [PATCH 0134/1271] use RuntimeError instead of Exception and rename the validator --- openpype/hosts/maya/plugins/publish/validate_look_color_space.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py index 9225feda24..b354d51fef 100644 --- a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py +++ b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py @@ -1,4 +1,3 @@ -import os from maya import cmds import pyblish.api From b35ee739bcfcc158daa9ef2d9261f3bda328d4cc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:57:51 +0100 Subject: [PATCH 0135/1271] OP-4643 - added Settings for ExtractColorTranscode --- .../defaults/project_settings/global.json | 4 + .../schemas/schema_global_publish.json | 73 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 0e078dc157..474e878cb6 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -68,6 +68,10 @@ "output": [] } }, + "ExtractColorTranscode": { + "enabled": true, + "profiles": [] + }, "ExtractReview": { "enabled": true, "profiles": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5388d04bc9..46ae6ba554 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -197,6 +197,79 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractColorTranscode", + "label": "ExtractColorTranscode", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "key": "hosts", + "label": "Host names", + "type": "hosts-enum", + "multiselection": true + }, + { + "key": "task_types", + "label": "Task types", + "type": "task-types-enum" + }, + { + "key": "task_names", + "label": "Task names", + "type": "list", + "object_type": "text" + }, + { + "key": "subsets", + "label": "Subset names", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "ext", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } + } + ] + }, { "type": "dict", "collapsible": true, From 687d6dbf2811db3b8c6f46ee10726b26c1120772 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:58:51 +0100 Subject: [PATCH 0136/1271] OP-4643 - added ExtractColorTranscode Added method to convert from one colorspace to another to transcoding lib --- openpype/lib/transcoding.py | 53 ++++++++ .../publish/extract_color_transcode.py | 124 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 openpype/plugins/publish/extract_color_transcode.py diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 57279d0380..6899811ed5 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1037,3 +1037,56 @@ def convert_ffprobe_fps_to_float(value): if divisor == 0.0: return 0.0 return dividend / divisor + + +def convert_colorspace_for_input_paths( + input_paths, + output_dir, + source_color_space, + target_color_space, + logger=None +): + """Convert source files from one color space to another. + + Filenames of input files are kept so make sure that output directory + is not the same directory as input files have. + - This way it can handle gaps and can keep input filenames without handling + frame template + + Args: + input_paths (str): Paths that should be converted. It is expected that + contains single file or image sequence of samy type. + output_dir (str): Path to directory where output will be rendered. + Must not be same as input's directory. + source_color_space (str): ocio valid color space of source files + target_color_space (str): ocio valid target color space + logger (logging.Logger): Logger used for logging. + + """ + if logger is None: + logger = logging.getLogger(__name__) + + input_arg = "-i" + oiio_cmd = [ + get_oiio_tools_path(), + + # Don't add any additional attributes + "--nosoftwareattrib", + "--colorconvert", source_color_space, target_color_space + ] + for input_path in input_paths: + # Prepare subprocess arguments + + oiio_cmd.extend([ + input_arg, input_path, + ]) + + # Add last argument - path to output + base_filename = os.path.basename(input_path) + output_path = os.path.join(output_dir, base_filename) + oiio_cmd.extend([ + "-o", output_path + ]) + + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py new file mode 100644 index 0000000000..58508ab18f --- /dev/null +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -0,0 +1,124 @@ +import pyblish.api + +from openpype.pipeline import publish +from openpype.lib import ( + + is_oiio_supported, +) + +from openpype.lib.transcoding import ( + convert_colorspace_for_input_paths, + get_transcode_temp_directory, +) + +from openpype.lib.profiles_filtering import filter_profiles + + +class ExtractColorTranscode(publish.Extractor): + """ + Extractor to convert colors from one colorspace to different. + """ + + label = "Transcode color spaces" + order = pyblish.api.ExtractorOrder + 0.01 + + optional = True + + # Configurable by Settings + profiles = None + options = None + + def process(self, instance): + if not self.profiles: + self.log.warning("No profiles present for create burnin") + return + + if "representations" not in instance.data: + self.log.warning("No representations, skipping.") + return + + if not is_oiio_supported(): + self.log.warning("OIIO not supported, no transcoding possible.") + return + + colorspace_data = instance.data.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Instance has not colorspace data, skipping") + return + source_color_space = colorspace_data["colorspace"] + + host_name = instance.context.data["hostName"] + family = instance.data["family"] + task_data = instance.data["anatomyData"].get("task", {}) + task_name = task_data.get("name") + task_type = task_data.get("type") + subset = instance.data["subset"] + + filtering_criteria = { + "hosts": host_name, + "families": family, + "task_names": task_name, + "task_types": task_type, + "subset": subset + } + profile = filter_profiles(self.profiles, filtering_criteria, + logger=self.log) + + if not profile: + self.log.info(( + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) + return + + self.log.debug("profile: {}".format(profile)) + + target_colorspace = profile["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(repres): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self.repre_is_valid(repre): + continue + + new_staging_dir = get_transcode_temp_directory() + repre["stagingDir"] = new_staging_dir + files_to_remove = repre["files"] + if not isinstance(files_to_remove, list): + files_to_remove = [files_to_remove] + instance.context.data["cleanupFullPaths"].extend(files_to_remove) + + convert_colorspace_for_input_paths( + repre["files"], + new_staging_dir, + source_color_space, + target_colorspace, + self.log + ) + + def repre_is_valid(self, repre): + """Validation if representation should be processed. + + Args: + repre (dict): Representation which should be checked. + + Returns: + bool: False if can't be processed else True. + """ + + if "review" not in (repre.get("tags") or []): + self.log.info(( + "Representation \"{}\" don't have \"review\" tag. Skipped." + ).format(repre["name"])) + return False + + if not repre.get("files"): + self.log.warning(( + "Representation \"{}\" have empty files. Skipped." + ).format(repre["name"])) + return False + return True From e36cf8004706a7d70133d79ea5e0ddd1f208f4c3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 12:05:57 +0100 Subject: [PATCH 0137/1271] OP-4643 - extractor must run just before ExtractReview Nuke render local is set to 0.01 --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 58508ab18f..5163cd4045 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -20,7 +20,7 @@ class ExtractColorTranscode(publish.Extractor): """ label = "Transcode color spaces" - order = pyblish.api.ExtractorOrder + 0.01 + order = pyblish.api.ExtractorOrder + 0.019 optional = True From cb27e5a4d6b512dfa193b031ab266340975245f0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:03:22 +0100 Subject: [PATCH 0138/1271] OP-4643 - fix for full file paths --- .../publish/extract_color_transcode.py | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 5163cd4045..6ad7599f2c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,3 +1,4 @@ +import os import pyblish.api from openpype.pipeline import publish @@ -41,13 +42,6 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return - colorspace_data = instance.data.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Instance has not colorspace data, skipping") - return - source_color_space = colorspace_data["colorspace"] - host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) @@ -82,18 +76,32 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self.repre_is_valid(repre): + # if not self.repre_is_valid(repre): + # continue + + colorspace_data = repre.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Repre has not colorspace data, skipping") + continue + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") continue new_staging_dir = get_transcode_temp_directory() + original_staging_dir = repre["stagingDir"] repre["stagingDir"] = new_staging_dir - files_to_remove = repre["files"] - if not isinstance(files_to_remove, list): - files_to_remove = [files_to_remove] - instance.context.data["cleanupFullPaths"].extend(files_to_remove) + files_to_convert = repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + instance.context.data["cleanupFullPaths"].extend(files_to_convert) convert_colorspace_for_input_paths( - repre["files"], + files_to_convert, new_staging_dir, source_color_space, target_colorspace, From 7201c57ddc71cac47f6ea38fdbf7d6c1d2d03577 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:04:06 +0100 Subject: [PATCH 0139/1271] OP-4643 - pass path for ocio config --- openpype/lib/transcoding.py | 3 +++ openpype/plugins/publish/extract_color_transcode.py | 1 + 2 files changed, 4 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 6899811ed5..792e8ddd1e 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1042,6 +1042,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace_for_input_paths( input_paths, output_dir, + config_path, source_color_space, target_color_space, logger=None @@ -1058,6 +1059,7 @@ def convert_colorspace_for_input_paths( contains single file or image sequence of samy type. output_dir (str): Path to directory where output will be rendered. Must not be same as input's directory. + config_path (str): path to OCIO config file source_color_space (str): ocio valid color space of source files target_color_space (str): ocio valid target color space logger (logging.Logger): Logger used for logging. @@ -1072,6 +1074,7 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", + "--colorconfig", config_path, "--colorconvert", source_color_space, target_color_space ] for input_path in input_paths: diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 6ad7599f2c..fdb13a47e8 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -103,6 +103,7 @@ class ExtractColorTranscode(publish.Extractor): convert_colorspace_for_input_paths( files_to_convert, new_staging_dir, + config_path, source_color_space, target_colorspace, self.log From dc796b71b4c50d2bb0240667cf9d0f19d85ad5dc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:15:33 +0100 Subject: [PATCH 0140/1271] OP-4643 - add custom_tags --- openpype/plugins/publish/extract_color_transcode.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index fdb13a47e8..ab932b2476 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -72,6 +72,7 @@ class ExtractColorTranscode(publish.Extractor): target_colorspace = profile["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") + custom_tags = profile["custom_tags"] repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): @@ -109,6 +110,11 @@ class ExtractColorTranscode(publish.Extractor): self.log ) + if custom_tags: + if not repre.get("custom_tags"): + repre["custom_tags"] = [] + repre["custom_tags"].extend(custom_tags) + def repre_is_valid(self, repre): """Validation if representation should be processed. From 50ff228070729e87dc0a846aa82461fdcc8b08b7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:18:38 +0100 Subject: [PATCH 0141/1271] OP-4643 - added docstring --- openpype/plugins/publish/extract_color_transcode.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index ab932b2476..88e2eed90f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -18,6 +18,17 @@ from openpype.lib.profiles_filtering import filter_profiles class ExtractColorTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. + + Expects "colorspaceData" on representation. This dictionary is collected + previously and denotes that representation files should be converted. + This dict contains source colorspace information, collected by hosts. + + Target colorspace is selected by profiles in the Settings, based on: + - families + - host + - task types + - task names + - subset names """ label = "Transcode color spaces" From 7a162f9dba79e1c829b9a01338917033607ec074 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:15:44 +0100 Subject: [PATCH 0142/1271] OP-4643 - updated Settings schema --- .../schemas/schema_global_publish.json | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 46ae6ba554..c2c911d7d6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -246,24 +246,44 @@ "type": "list", "object_type": "text" }, + { + "type": "boolean", + "key": "delete_original", + "label": "Delete Original Representation" + }, { "type": "splitter" }, { - "key": "ext", - "label": "Output extension", - "type": "text" - }, - { - "key": "output_colorspace", - "label": "Output colorspace", - "type": "text" - }, - { - "key": "custom_tags", - "label": "Custom Tags", - "type": "list", - "object_type": "text" + "key": "outputs", + "label": "Output Definitions", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": { + "type": "dict", + "children": [ + { + "key": "output_extension", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "type": "schema", + "name": "schema_representation_tags" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } } ] } From 171af695c3ebfdb5a946af8897002e853f34af81 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:17:25 +0100 Subject: [PATCH 0143/1271] OP-4643 - skip video files Only frames currently supported. --- .../plugins/publish/extract_color_transcode.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 88e2eed90f..a0714c9a33 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -36,6 +36,9 @@ class ExtractColorTranscode(publish.Extractor): optional = True + # Supported extensions + supported_exts = ["exr", "jpg", "jpeg", "png", "dpx"] + # Configurable by Settings profiles = None options = None @@ -88,13 +91,7 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - # if not self.repre_is_valid(repre): - # continue - - colorspace_data = repre.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Repre has not colorspace data, skipping") + if not self._repre_is_valid(repre): continue source_color_space = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") @@ -136,9 +133,9 @@ class ExtractColorTranscode(publish.Extractor): bool: False if can't be processed else True. """ - if "review" not in (repre.get("tags") or []): - self.log.info(( - "Representation \"{}\" don't have \"review\" tag. Skipped." + if repre.get("ext") not in self.supported_exts: + self.log.warning(( + "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False From c7b443519e220ca14dc32ee135c6dcc8085c7d88 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:19:08 +0100 Subject: [PATCH 0144/1271] OP-4643 - refactored profile, delete of original Implemented multiple outputs from single input representation --- .../publish/extract_color_transcode.py | 156 ++++++++++++------ 1 file changed, 109 insertions(+), 47 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index a0714c9a33..b0c851d5f4 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,4 +1,6 @@ import os +import copy + import pyblish.api from openpype.pipeline import publish @@ -56,13 +58,94 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return + profile = self._get_profile(instance) + if not profile: + return + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(list(repres)): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self._repre_is_valid(repre): + continue + + colorspace_data = repre["colorspaceData"] + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") + continue + + repre = self._handle_original_repre(repre, profile) + + for _, output_def in profile.get("outputs", {}).items(): + new_repre = copy.deepcopy(repre) + + new_staging_dir = get_transcode_temp_directory() + original_staging_dir = new_repre["stagingDir"] + new_repre["stagingDir"] = new_staging_dir + files_to_convert = new_repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + + files_to_delete = copy.deepcopy(files_to_convert) + + output_extension = output_def["output_extension"] + files_to_convert = self._rename_output_files(files_to_convert, + output_extension) + + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + + target_colorspace = output_def["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + convert_colorspace_for_input_paths( + files_to_convert, + new_staging_dir, + config_path, + source_color_space, + target_colorspace, + self.log + ) + + instance.context.data["cleanupFullPaths"].extend( + files_to_delete) + + custom_tags = output_def.get("custom_tags") + if custom_tags: + if not new_repre.get("custom_tags"): + new_repre["custom_tags"] = [] + new_repre["custom_tags"].extend(custom_tags) + + # Add additional tags from output definition to representation + for tag in output_def["tags"]: + if tag not in new_repre["tags"]: + new_repre["tags"].append(tag) + + instance.data["representations"].append(new_repre) + + def _rename_output_files(self, files_to_convert, output_extension): + """Change extension of converted files.""" + if output_extension: + output_extension = output_extension.replace('.', '') + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files + return files_to_convert + + def _get_profile(self, instance): + """Returns profile if and how repre should be color transcoded.""" host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) task_name = task_data.get("name") task_type = task_data.get("type") subset = instance.data["subset"] - filtering_criteria = { "hosts": host_name, "families": family, @@ -75,55 +158,15 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) - return + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) + return profile - target_colorspace = profile["output_colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") - custom_tags = profile["custom_tags"] - - repres = instance.data.get("representations") or [] - for idx, repre in enumerate(repres): - self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self._repre_is_valid(repre): - continue - source_color_space = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): - self.log.warning("Config file doesn't exist, skipping") - continue - - new_staging_dir = get_transcode_temp_directory() - original_staging_dir = repre["stagingDir"] - repre["stagingDir"] = new_staging_dir - files_to_convert = repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - instance.context.data["cleanupFullPaths"].extend(files_to_convert) - - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) - - if custom_tags: - if not repre.get("custom_tags"): - repre["custom_tags"] = [] - repre["custom_tags"].extend(custom_tags) - - def repre_is_valid(self, repre): + def _repre_is_valid(self, repre): """Validation if representation should be processed. Args: @@ -144,4 +187,23 @@ class ExtractColorTranscode(publish.Extractor): "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False + + if not repre.get("colorspaceData"): + self.log.warning("Repre has not colorspace data, skipping") + return False + return True + + def _handle_original_repre(self, repre, profile): + delete_original = profile["delete_original"] + + if delete_original: + if not repre.get("tags"): + repre["tags"] = [] + + if "review" in repre["tags"]: + repre["tags"].remove("review") + if "delete" not in repre["tags"]: + repre["tags"].append("delete") + + return repre From 69c04bb01dc7bc220318a3d6f63e4ed568bddff2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:23:01 +0100 Subject: [PATCH 0145/1271] OP-4643 - switched logging levels Do not use warning unnecessary. --- openpype/plugins/publish/extract_color_transcode.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index b0c851d5f4..4d38514b8b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -47,11 +47,11 @@ class ExtractColorTranscode(publish.Extractor): def process(self, instance): if not self.profiles: - self.log.warning("No profiles present for create burnin") + self.log.debug("No profiles present for color transcode") return if "representations" not in instance.data: - self.log.warning("No representations, skipping.") + self.log.debug("No representations, skipping.") return if not is_oiio_supported(): @@ -177,19 +177,19 @@ class ExtractColorTranscode(publish.Extractor): """ if repre.get("ext") not in self.supported_exts: - self.log.warning(( + self.log.debug(( "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): - self.log.warning(( + self.log.debug(( "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.warning("Repre has not colorspace data, skipping") + self.log.debug("Repre has no colorspace data. Skipped.") return False return True From 44e12b05b95db1f0b1a43ad506850b5578705942 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:14 +0100 Subject: [PATCH 0146/1271] OP-4643 - propagate new extension to representation --- .../publish/extract_color_transcode.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4d38514b8b..62cf8f0dee 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -90,8 +90,13 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) output_extension = output_def["output_extension"] - files_to_convert = self._rename_output_files(files_to_convert, - output_extension) + output_extension = output_extension.replace('.', '') + if output_extension: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + files_to_convert = self._rename_output_files( + files_to_convert, output_extension) files_to_convert = [os.path.join(original_staging_dir, path) for path in files_to_convert] @@ -127,15 +132,13 @@ class ExtractColorTranscode(publish.Extractor): def _rename_output_files(self, files_to_convert, output_extension): """Change extension of converted files.""" - if output_extension: - output_extension = output_extension.replace('.', '') - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files return files_to_convert def _get_profile(self, instance): From 85fc41bd7427a395fbf1454b12a6ee6fea34e594 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:35 +0100 Subject: [PATCH 0147/1271] OP-4643 - added label to Settings --- .../projects_schema/schemas/schema_global_publish.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index c2c911d7d6..7155510fef 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -201,10 +201,14 @@ "type": "dict", "collapsible": true, "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode", + "label": "ExtractColorTranscode (ImageIO)", "checkbox_key": "enabled", "is_group": true, "children": [ + { + "type": "label", + "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + }, { "type": "boolean", "key": "enabled", From 24abe69437801ea4f022d3a0816664e93b2072f4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jan 2023 18:22:08 +0100 Subject: [PATCH 0148/1271] OP-4643 - refactored according to review Function turned into single filepath input. --- openpype/lib/transcoding.py | 43 ++++++----- .../publish/extract_color_transcode.py | 72 ++++++++++--------- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 792e8ddd1e..8e3432e0e9 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1039,12 +1039,12 @@ def convert_ffprobe_fps_to_float(value): return dividend / divisor -def convert_colorspace_for_input_paths( - input_paths, - output_dir, +def convert_colorspace( + input_path, + out_filepath, config_path, - source_color_space, - target_color_space, + source_colorspace, + target_colorspace, logger=None ): """Convert source files from one color space to another. @@ -1055,13 +1055,13 @@ def convert_colorspace_for_input_paths( frame template Args: - input_paths (str): Paths that should be converted. It is expected that + input_path (str): Paths that should be converted. It is expected that contains single file or image sequence of samy type. - output_dir (str): Path to directory where output will be rendered. + out_filepath (str): Path to directory where output will be rendered. Must not be same as input's directory. config_path (str): path to OCIO config file - source_color_space (str): ocio valid color space of source files - target_color_space (str): ocio valid target color space + source_colorspace (str): ocio valid color space of source files + target_colorspace (str): ocio valid target color space logger (logging.Logger): Logger used for logging. """ @@ -1075,21 +1075,18 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_color_space, target_color_space + "--colorconvert", source_colorspace, target_colorspace ] - for input_path in input_paths: - # Prepare subprocess arguments + # Prepare subprocess arguments - oiio_cmd.extend([ - input_arg, input_path, - ]) + oiio_cmd.extend([ + input_arg, input_path, + ]) - # Add last argument - path to output - base_filename = os.path.basename(input_path) - output_path = os.path.join(output_dir, base_filename) - oiio_cmd.extend([ - "-o", output_path - ]) + # Add last argument - path to output + oiio_cmd.extend([ + "-o", out_filepath + ]) - logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) - run_subprocess(oiio_cmd, logger=logger) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 62cf8f0dee..3a05426432 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -10,7 +10,7 @@ from openpype.lib import ( ) from openpype.lib.transcoding import ( - convert_colorspace_for_input_paths, + convert_colorspace, get_transcode_temp_directory, ) @@ -69,7 +69,7 @@ class ExtractColorTranscode(publish.Extractor): continue colorspace_data = repre["colorspaceData"] - source_color_space = colorspace_data["colorspace"] + source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") if not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") @@ -80,8 +80,8 @@ class ExtractColorTranscode(publish.Extractor): for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) - new_staging_dir = get_transcode_temp_directory() original_staging_dir = new_repre["stagingDir"] + new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir files_to_convert = new_repre["files"] if not isinstance(files_to_convert, list): @@ -92,27 +92,28 @@ class ExtractColorTranscode(publish.Extractor): output_extension = output_def["output_extension"] output_extension = output_extension.replace('.', '') if output_extension: - new_repre["name"] = output_extension + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension new_repre["ext"] = output_extension - files_to_convert = self._rename_output_files( - files_to_convert, output_extension) - - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - target_colorspace = output_def["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) + for file_name in files_to_convert: + input_filepath = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_filepath, + new_staging_dir, + output_extension) + convert_colorspace( + input_filepath, + output_path, + config_path, + source_colorspace, + target_colorspace, + self.log + ) instance.context.data["cleanupFullPaths"].extend( files_to_delete) @@ -130,16 +131,16 @@ class ExtractColorTranscode(publish.Extractor): instance.data["representations"].append(new_repre) - def _rename_output_files(self, files_to_convert, output_extension): - """Change extension of converted files.""" - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files - return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, + output_extension): + """Create output file name path.""" + file_name = os.path.basename(input_filepath) + file_name, input_extension = os.path.splitext(file_name) + if not output_extension: + output_extension = input_extension + new_file_name = '{}.{}'.format(file_name, + output_extension) + return os.path.join(output_dir, new_file_name) def _get_profile(self, instance): """Returns profile if and how repre should be color transcoded.""" @@ -161,10 +162,10 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) return profile @@ -181,18 +182,19 @@ class ExtractColorTranscode(publish.Extractor): if repre.get("ext") not in self.supported_exts: self.log.debug(( - "Representation \"{}\" of unsupported extension. Skipped." + "Representation '{}' of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): self.log.debug(( - "Representation \"{}\" have empty files. Skipped." + "Representation '{}' have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.debug("Repre has no colorspace data. Skipped.") + self.log.debug("Representation '{}' has no colorspace data. " + "Skipped.") return False return True From b90e9b69c60ff03b165985350dd74b645793f824 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 16 Jan 2023 21:44:24 +0100 Subject: [PATCH 0149/1271] added more specific functions for current context into host integration --- openpype/host/host.py | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/openpype/host/host.py b/openpype/host/host.py index 94416bb39a..56d01c667a 100644 --- a/openpype/host/host.py +++ b/openpype/host/host.py @@ -100,6 +100,30 @@ class HostBase(object): pass + def get_current_project_name(self): + """ + Returns: + Union[str, None]: Current project name. + """ + + return os.environ.get("AVALON_PROJECT") + + def get_current_asset_name(self): + """ + Returns: + Union[str, None]: Current asset name. + """ + + return os.environ.get("AVALON_ASSET") + + def get_current_task_name(self): + """ + Returns: + Union[str, None]: Current task name. + """ + + return os.environ.get("AVALON_ASSET") + def get_current_context(self): """Get current context information. @@ -111,19 +135,14 @@ class HostBase(object): Default implementation returns values from 'legacy_io.Session'. Returns: - dict: Context with 3 keys 'project_name', 'asset_name' and - 'task_name'. All of them can be 'None'. + Dict[str, Union[str, None]]: Context with 3 keys 'project_name', + 'asset_name' and 'task_name'. All of them can be 'None'. """ - from openpype.pipeline import legacy_io - - if legacy_io.is_installed(): - legacy_io.install() - return { - "project_name": legacy_io.Session["AVALON_PROJECT"], - "asset_name": legacy_io.Session["AVALON_ASSET"], - "task_name": legacy_io.Session["AVALON_TASK"] + "project_name": self.get_current_project_name(), + "asset_name": self.get_current_asset_name(), + "task_name": self.get_current_task_name() } def get_context_title(self): From 2c35bda38146d35f84c3468abf64710c5fb02ba4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 16 Jan 2023 21:45:09 +0100 Subject: [PATCH 0150/1271] added functions for global access to current context --- openpype/pipeline/context_tools.py | 44 +++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/context_tools.py b/openpype/pipeline/context_tools.py index da0ce8ecf4..50ce827a39 100644 --- a/openpype/pipeline/context_tools.py +++ b/openpype/pipeline/context_tools.py @@ -306,6 +306,42 @@ def debug_host(): return host +def get_global_context(): + return { + "project_name": os.environ.get("AVALON_PROJECT"), + "asset_name": os.environ.get("AVALON_ASSET"), + "task_name": os.environ.get("AVALON_TASK"), + } + + +def get_current_context(): + host = registered_host() + if host is not None and hasattr(host, "get_current_context"): + return host.get_current_context() + return get_global_context() + + +def get_current_project_name(): + host = registered_host() + if host is not None and hasattr(host, "get_current_project_name"): + return host.get_current_project_name() + return get_global_context()["project_name"] + + +def get_current_asset_name(): + host = registered_host() + if host is not None and hasattr(host, "get_current_asset_name"): + return host.get_current_asset_name() + return get_global_context()["asset_name"] + + +def get_current_task_name(): + host = registered_host() + if host is not None and hasattr(host, "get_current_task_name"): + return host.get_current_task_name() + return get_global_context()["task_name"] + + def get_current_project(fields=None): """Helper function to get project document based on global Session. @@ -316,7 +352,7 @@ def get_current_project(fields=None): None: Project is not set. """ - project_name = legacy_io.active_project() + project_name = get_current_project_name() return get_project(project_name, fields=fields) @@ -341,12 +377,12 @@ def get_current_project_asset(asset_name=None, asset_id=None, fields=None): None: Asset is not set or not exist. """ - project_name = legacy_io.active_project() + project_name = get_current_project_name() if asset_id: return get_asset_by_id(project_name, asset_id, fields=fields) if not asset_name: - asset_name = legacy_io.Session.get("AVALON_ASSET") + asset_name = get_current_asset_name() # Skip if is not set even on context if not asset_name: return None @@ -363,7 +399,7 @@ def is_representation_from_latest(representation): bool: Whether the representation is of latest version. """ - project_name = legacy_io.active_project() + project_name = get_current_project_name() return version_is_latest(project_name, representation["parent"]) From b75768249c422665fdd380383424326829e00748 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 16 Jan 2023 21:45:17 +0100 Subject: [PATCH 0151/1271] added function to get current host name --- openpype/pipeline/context_tools.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/openpype/pipeline/context_tools.py b/openpype/pipeline/context_tools.py index 50ce827a39..06538dd91f 100644 --- a/openpype/pipeline/context_tools.py +++ b/openpype/pipeline/context_tools.py @@ -306,6 +306,22 @@ def debug_host(): return host +def get_current_host_name(): + """Current host name. + + Function is based on currently registered host integration or environment + variant 'AVALON_APP'. + + Returns: + Union[str, None]: Name of host integration in current process or None. + """ + + host = registered_host() + if host is not None and hasattr(host, "name"): + return host.name + return os.environ.get("AVALON_APP") + + def get_global_context(): return { "project_name": os.environ.get("AVALON_PROJECT"), From 48937de52ce9092d5718e9cb549764887be6c5c6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 16 Jan 2023 22:07:25 +0100 Subject: [PATCH 0152/1271] added missing import --- openpype/host/host.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/host/host.py b/openpype/host/host.py index 56d01c667a..28d0a21b34 100644 --- a/openpype/host/host.py +++ b/openpype/host/host.py @@ -1,3 +1,4 @@ +import os import logging import contextlib from abc import ABCMeta, abstractproperty From 24d7de450b0f8906943638eb223b39f4fd83e70e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 17 Jan 2023 15:39:23 +0800 Subject: [PATCH 0153/1271] improve the validator for gltf texture name --- .../plugins/publish/validate_gltf_textures_names.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py index 6e5e1a832d..ed0ae9c6d5 100644 --- a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py +++ b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py @@ -38,6 +38,9 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): def process(self, instance): """Process all the nodes in the instance""" + pbs_shader = cmds.ls(type="StingrayPBS") + if not pbs_shader: + raise RuntimeError("No PBS Shader in the scene") invalid = self.get_texture_shader_invalid(instance) if invalid: raise RuntimeError("Non PBS material found in " @@ -109,9 +112,12 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): invalid = set() shading_grp = self.shader_selection(instance) - for shader in shading_grp: - if "StingrayPBS" not in shader: - invalid.add(shader) + for material in shading_grp: + main_shader = cmds.listConnections(material, + destination=True, + type="StingrayPBS") + if not main_shader: + invalid.add(material) return list(invalid) def shader_selection(self, instance): From 9f749edd8a13be373d8d76e15ac8c09729adaa60 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 17 Jan 2023 09:58:23 +0000 Subject: [PATCH 0154/1271] Fix When there is no xgenAttributes --- openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py b/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py index 9397d479d2..0f763613c9 100644 --- a/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py +++ b/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py @@ -12,7 +12,7 @@ class ResetXgenAttributes(pyblish.api.InstancePlugin): families = ["workfile"] def process(self, instance): - for palette, data in instance.data.get("xgenAttributes", []).items(): + for palette, data in instance.data.get("xgenAttributes", {}).items(): for attr, value in data.items(): node_attr = "{}.{}".format(palette, attr) self.log.info( From 92e3d10b39de62580f1634898b1e19025a597833 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 17 Jan 2023 18:23:20 +0800 Subject: [PATCH 0155/1271] add cryptomatte into collect render layers --- openpype/hosts/maya/api/lib_renderproducts.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index c54e3ab3e0..0eecedd231 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -1132,6 +1132,7 @@ class RenderProductsRenderman(ARenderProducts): """ renderer = "renderman" + unmerged_aovs = {"PxrCryptomatte"} def get_render_products(self): """Get all AOVs. @@ -1181,6 +1182,17 @@ class RenderProductsRenderman(ARenderProducts): if not display_types.get(display["driverNode"]["type"]): continue + has_cryptomatte = cmds.ls(type=self.unmerged_aovs) + matte_enabled = False + if has_cryptomatte: + for cryptomatte in has_cryptomatte: + cryptomatte_aov = cryptomatte + matte_name = "cryptomatte" + rman_globals = cmds.listConnections(cryptomatte + + ".message") + if rman_globals: + matte_enabled = True + aov_name = name if aov_name == "rmanDefaultDisplay": aov_name = "beauty" @@ -1199,6 +1211,15 @@ class RenderProductsRenderman(ARenderProducts): camera=camera, multipart=True ) + + if has_cryptomatte and matte_enabled: + cryptomatte = RenderProduct( + productName=matte_name, + aov=cryptomatte_aov, + ext=extensions, + camera=camera, + multipart=True + ) else: # this code should handle the case where no multipart # capable format is selected. But since it involves @@ -1218,6 +1239,9 @@ class RenderProductsRenderman(ARenderProducts): products.append(product) + if has_cryptomatte and matte_enabled: + products.append(cryptomatte) + return products def get_files(self, product): From 161e09bcdb681bb5593fe062939917f40efdb7d6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 17 Jan 2023 12:30:59 +0100 Subject: [PATCH 0156/1271] OP-4615 - AE - fix missing list_instances --- .../hosts/aftereffects/plugins/publish/pre_collect_render.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/publish/pre_collect_render.py b/openpype/hosts/aftereffects/plugins/publish/pre_collect_render.py index 03ec184524..85a42830a4 100644 --- a/openpype/hosts/aftereffects/plugins/publish/pre_collect_render.py +++ b/openpype/hosts/aftereffects/plugins/publish/pre_collect_render.py @@ -1,6 +1,6 @@ import json import pyblish.api -from openpype.hosts.aftereffects.api import list_instances +from openpype.hosts.aftereffects.api import AfterEffectsHost class PreCollectRender(pyblish.api.ContextPlugin): @@ -25,7 +25,7 @@ class PreCollectRender(pyblish.api.ContextPlugin): self.log.debug("Not applicable for New Publisher, skip") return - for inst in list_instances(): + for inst in AfterEffectsHost().list_instances(): if inst.get("creator_attributes"): raise ValueError("Instance created in New publisher, " "cannot be published in Pyblish.\n" From 3dd02cec71ceeac49d52a3f85652548aef68e21f Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Tue, 17 Jan 2023 22:49:33 +0800 Subject: [PATCH 0157/1271] update the validator --- .../publish/validate_gltf_textures_names.py | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py index ed0ae9c6d5..635a59a4be 100644 --- a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py +++ b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py @@ -47,8 +47,8 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): "{0}".format(invalid)) invalid = self.get_texture_node_invalid(instance) if invalid: - raise RuntimeError("Related texture file " - "nodes not connected") + raise RuntimeError("At least a Albedo texture file" + "nodes need to be connected") invalid = self.get_texture_name_invalid(instance) if invalid: raise RuntimeError("Invalid texture name(s) found: " @@ -74,17 +74,19 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): if not dif.endswith("_D"): invalid.add(dif_path) orm_packed = cmds.listConnections(shader + ".TEX_ao_mapX")[0] - # "_ORM" - orm_path = cmds.getAttr(orm_packed + ".fileTextureName") - orm = orm_path.split(".")[0] - if not orm.endswith("_ORM"): - invalid.add(orm_path) + if orm_packed: + # "_ORM" + orm_path = cmds.getAttr(orm_packed + ".fileTextureName") + orm = orm_path.split(".")[0] + if not orm.endswith("_ORM"): + invalid.add(orm_path) nrm = cmds.listConnections(shader + ".TEX_normal_map")[0] - nrm_path = cmds.getAttr(nrm + ".fileTextureName") - nrm_map = nrm_path.split(".")[0] - # "_N" - if not nrm_map.endswith("_N"): - invalid.add(nrm_path) + if nrm: + nrm_path = cmds.getAttr(nrm + ".fileTextureName") + nrm_map = nrm_path.split(".")[0] + # "_N" + if not nrm_map.endswith("_N"): + invalid.add(nrm_path) return list(invalid) @@ -100,12 +102,6 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): albedo = cmds.listConnections(shader + ".TEX_color_map") if not albedo: invalid.add(albedo) - orm_packed = cmds.listConnections(shader + ".TEX_ao_mapX") - if not orm_packed: - invalid.add(orm_packed) - nrm = cmds.listConnections(shader + ".TEX_normal_map") - if not nrm: - invalid.add(nrm) return list(invalid) def get_texture_shader_invalid(self, instance): From eb8c40a4e7b985cf5455081fe72301e1942f8480 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Tue, 17 Jan 2023 23:50:27 +0800 Subject: [PATCH 0158/1271] update the validator for ORM --- openpype/hosts/maya/plugins/publish/convert_gltf_shader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py index bbeeb38ef3..ceed4cb062 100644 --- a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py +++ b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py @@ -53,7 +53,7 @@ class ConvertGLSLShader(publish.Extractor): dif_output = albedo + ".outColor" orm_packed = cmds.listConnections(shader + - ".TEX_ao_mapX")[0] + ".TEX_ao_map")[0] ao_output = orm_packed + ".outColorR" rough_output = orm_packed + ".outColorG" metallic_output = orm_packed + ".outColorB" From ba45473f8ceee3a6c9d934b8ac6e5b71a4e273d9 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Tue, 17 Jan 2023 23:51:28 +0800 Subject: [PATCH 0159/1271] update the texture conversion of ORM --- .../plugins/publish/convert_gltf_shader.py | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py index ceed4cb062..39597806bf 100644 --- a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py +++ b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py @@ -54,9 +54,7 @@ class ConvertGLSLShader(publish.Extractor): orm_packed = cmds.listConnections(shader + ".TEX_ao_map")[0] - ao_output = orm_packed + ".outColorR" - rough_output = orm_packed + ".outColorG" - metallic_output = orm_packed + ".outColorB" + orm_output = orm_packed + ".outColor" nrm = cmds.listConnections(shader + ".TEX_normal_map")[0] nrm_output = nrm + ".outColor" @@ -68,18 +66,17 @@ class ConvertGLSLShader(publish.Extractor): cmds.connectAttr(dif_output, glsl_dif) cmds.connectAttr(nrm_output, glsl_nrm) - rgb_list = ["R", "G", "B"] - for ch in rgb_list: - mtl = ".u_MetallicTexture.u_MetallicTexture{}".format(ch) # noqa - mtl = glsl + mtl - ao = ".u_OcclusionTexture.u_OcclusionTexture{}".format(ch) # noqa - ao = glsl + ao - rough = ".u_RoughnessTexture.u_RoughnessTexture{}".format(ch) # noqa - rough = glsl + rough - cmds.connectAttr(metallic_output, mtl) - cmds.connectAttr(ao_output, ao) - cmds.connectAttr(rough_output, rough) + mtl = ".u_MetallicTexture.u_MetallicTexture" + mtl = glsl + mtl + ao = ".u_OcclusionTexture.u_OcclusionTexture" + ao = glsl + ao + rough = ".u_RoughnessTexture.u_RoughnessTexture" + rough = glsl + rough + + cmds.connectAttr(orm_output, mtl) + cmds.connectAttr(orm_output, ao) + cmds.connectAttr(orm_output, rough) # assign the shader to the asset cmds.sets(mesh, forceElement=str(glsl_shadingGrp)) From 0af4f7d9fe21fe86426692308a98e868c79dabba Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Tue, 17 Jan 2023 23:52:26 +0800 Subject: [PATCH 0160/1271] Update convert_gltf_shader.py --- openpype/hosts/maya/plugins/publish/convert_gltf_shader.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py index 39597806bf..7d28bef840 100644 --- a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py +++ b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py @@ -66,7 +66,6 @@ class ConvertGLSLShader(publish.Extractor): cmds.connectAttr(dif_output, glsl_dif) cmds.connectAttr(nrm_output, glsl_nrm) - mtl = ".u_MetallicTexture.u_MetallicTexture" mtl = glsl + mtl ao = ".u_OcclusionTexture.u_OcclusionTexture" From 257859716fe99e3c1214f090b06bb7954c36f582 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Tue, 17 Jan 2023 23:55:43 +0800 Subject: [PATCH 0161/1271] Update convert_gltf_shader.py --- .../hosts/maya/plugins/publish/convert_gltf_shader.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py index 7d28bef840..f7cfd33c0b 100644 --- a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py +++ b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py @@ -66,12 +66,9 @@ class ConvertGLSLShader(publish.Extractor): cmds.connectAttr(dif_output, glsl_dif) cmds.connectAttr(nrm_output, glsl_nrm) - mtl = ".u_MetallicTexture.u_MetallicTexture" - mtl = glsl + mtl - ao = ".u_OcclusionTexture.u_OcclusionTexture" - ao = glsl + ao - rough = ".u_RoughnessTexture.u_RoughnessTexture" - rough = glsl + rough + mtl = glsl + ".u_MetallicTexture" + ao = glsl + ".u_OcclusionTexture" + rough = glsl + "u_RoughnessTexture" cmds.connectAttr(orm_output, mtl) cmds.connectAttr(orm_output, ao) From bdc4a0963577af3fd90985afef88b5cbc1e62f76 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Wed, 18 Jan 2023 00:00:08 +0800 Subject: [PATCH 0162/1271] Update convert_gltf_shader.py --- openpype/hosts/maya/plugins/publish/convert_gltf_shader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py index f7cfd33c0b..0b084223fe 100644 --- a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py +++ b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py @@ -68,7 +68,7 @@ class ConvertGLSLShader(publish.Extractor): mtl = glsl + ".u_MetallicTexture" ao = glsl + ".u_OcclusionTexture" - rough = glsl + "u_RoughnessTexture" + rough = glsl + ".u_RoughnessTexture" cmds.connectAttr(orm_output, mtl) cmds.connectAttr(orm_output, ao) From ff6fe13e2a6049e61b829905f2e3bb9f1b0b4cc5 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 17 Jan 2023 16:05:58 +0000 Subject: [PATCH 0163/1271] Code cosmetics --- openpype/hooks/pre_add_last_workfile_arg.py | 6 ++---- openpype/hosts/maya/startup/userSetup.py | 9 +++++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/openpype/hooks/pre_add_last_workfile_arg.py b/openpype/hooks/pre_add_last_workfile_arg.py index ffb116d2e4..ce59c0b706 100644 --- a/openpype/hooks/pre_add_last_workfile_arg.py +++ b/openpype/hooks/pre_add_last_workfile_arg.py @@ -43,11 +43,9 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook): return # Determine whether to open workfile post initialization. - if self.data["app"].host_name == "maya": - project_name = self.data["project_name"] - settings = get_project_settings(project_name) + if self.host_name == "maya": key = "open_workfile_post_initialization" - if settings["maya"][key]: + if self.data["project_settings"]["maya"][key]: self.log.debug("Opening workfile post initialization.") self.data["env"]["OPENPYPE_" + key.upper()] = "1" return diff --git a/openpype/hosts/maya/startup/userSetup.py b/openpype/hosts/maya/startup/userSetup.py index 5cd27500dc..87025637b6 100644 --- a/openpype/hosts/maya/startup/userSetup.py +++ b/openpype/hosts/maya/startup/userSetup.py @@ -1,5 +1,6 @@ import os import shutil +from functools import partial from openpype.settings import get_project_settings from openpype.pipeline import install_host @@ -18,8 +19,12 @@ print("Starting OpenPype usersetup...") key = "OPENPYPE_OPEN_WORKFILE_POST_INITIALIZATION" if bool(int(os.environ.get(key, "0"))): cmds.evalDeferred( - "cmds.file(os.environ[\"AVALON_LAST_WORKFILE\"], open=True," - " force=True)", + partial( + cmds.file, + os.environ["AVALON_LAST_WORKFILE"], + open=True, + force=True + ), lowestPriority=True ) From 40f8bc25f5adcfb2b70ce219f555b2f09a74e079 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 17 Jan 2023 16:06:16 +0000 Subject: [PATCH 0164/1271] Remove Xgen incremental save feature --- openpype/hosts/maya/startup/userSetup.py | 37 ------------------------ website/docs/artist_hosts_maya_xgen.md | 4 --- 2 files changed, 41 deletions(-) diff --git a/openpype/hosts/maya/startup/userSetup.py b/openpype/hosts/maya/startup/userSetup.py index 87025637b6..a2f6405980 100644 --- a/openpype/hosts/maya/startup/userSetup.py +++ b/openpype/hosts/maya/startup/userSetup.py @@ -29,43 +29,6 @@ if bool(int(os.environ.get(key, "0"))): ) -# Setup Xgen save callback. -def xgen_on_save(): - """Increments the xgen side car files .xgen and .xgd - - Only works when incrementing to the same directory. - """ - - file_path = current_file() - current_dir = os.path.dirname(file_path) - basename = os.path.basename(file_path).split(".")[0] - attrs = ["xgFileName", "xgBaseFile"] - for palette in cmds.ls(type="xgmPalette"): - for attr in attrs: - source = os.path.join( - current_dir, cmds.getAttr(palette + "." + attr) - ) - if not os.path.exists(source): - continue - - destination_basename = "{}__{}{}".format( - basename, - palette.replace(":", "_"), - os.path.splitext(source)[1] - ) - destination = os.path.join(current_dir, destination_basename) - - if source == destination: - continue - - shutil.copy(source, destination) - cmds.setAttr( - palette + "." + attr, destination_basename, type="string" - ) - - -register_event_callback("save", xgen_on_save) - # Build a shelf. settings = get_project_settings(os.environ['AVALON_PROJECT']) shelf_preset = settings['maya'].get('project_shelf') diff --git a/website/docs/artist_hosts_maya_xgen.md b/website/docs/artist_hosts_maya_xgen.md index 3f47428abe..fc75959f2b 100644 --- a/website/docs/artist_hosts_maya_xgen.md +++ b/website/docs/artist_hosts_maya_xgen.md @@ -27,10 +27,6 @@ Importing XGen Collections... # Error: XGen: Failed to import collection from file P:/PROJECTS/OP01_CG_demo/shots/sh040/work/Lighting/cg_ball_xgenMain_v035__ball_rigMain_01___collection.xgen # ``` -### Workfile Incremental Save - -When you increment the Maya workfile to a new version, all `.xgen` and `.xgd` files referenced by the Xgen collection in the workspace is incremented as well. - ## Create Create an Xgen instance to publish. This needs to contain only **one Xgen collection**. From 59b1caacd2f4e662c44d57c23a8deca2d3fe07fb Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 17 Jan 2023 16:06:39 +0000 Subject: [PATCH 0165/1271] Account for not using the published workfile. --- .../hosts/maya/plugins/publish/extract_workfile_xgen.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py index e12a870eaf..13a04615eb 100644 --- a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -37,6 +37,13 @@ class ExtractWorkfileXgen(publish.Extractor): ) return + publish_settings = instance.context["deadline"]["publish"] + if not publish_settings["MayaSubmitDeadline"]["use_published"]: + self.log.debug( + "Not using the published workfile. Abort Xgen extraction." + ) + return + # Collect Xgen and Delta files. xgen_files = [] sources = [] From 1c1a41ba5efd0e883d8985832dd7371554597384 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 17 Jan 2023 16:10:39 +0000 Subject: [PATCH 0166/1271] Hound --- openpype/hooks/pre_add_last_workfile_arg.py | 1 - openpype/hosts/maya/startup/userSetup.py | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/openpype/hooks/pre_add_last_workfile_arg.py b/openpype/hooks/pre_add_last_workfile_arg.py index ce59c0b706..1c8746c559 100644 --- a/openpype/hooks/pre_add_last_workfile_arg.py +++ b/openpype/hooks/pre_add_last_workfile_arg.py @@ -1,7 +1,6 @@ import os from openpype.lib import PreLaunchHook -from openpype.settings import get_project_settings class AddLastWorkfileToLaunchArgs(PreLaunchHook): diff --git a/openpype/hosts/maya/startup/userSetup.py b/openpype/hosts/maya/startup/userSetup.py index a2f6405980..bfa5e6e60d 100644 --- a/openpype/hosts/maya/startup/userSetup.py +++ b/openpype/hosts/maya/startup/userSetup.py @@ -1,11 +1,9 @@ import os -import shutil from functools import partial from openpype.settings import get_project_settings from openpype.pipeline import install_host -from openpype.hosts.maya.api import MayaHost, current_file -from openpype.lib import register_event_callback +from openpype.hosts.maya.api import MayaHost from maya import cmds From 5f709b08929e81f5e7c48a077320fdabee08f946 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 17 Jan 2023 16:22:12 +0000 Subject: [PATCH 0167/1271] Code cosmetics --- openpype/hosts/maya/api/plugin.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index f11757b2ab..b7adf6edfc 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -302,7 +302,6 @@ class ReferenceLoader(Loader): # Update any xgen containers. compound_name = "xgenContainers" - object = "SplinePrimitive" if cmds.objExists("{}.{}".format(node, compound_name)): import xgenm container_amount = cmds.getAttr( @@ -324,7 +323,7 @@ class ReferenceLoader(Loader): path.replace("\\", "/"), palette, description, - object + "SplinePrimitive" ) # Refresh UI and viewport. From 62ebd77fe1d7bfa20901f60f1bb5e494224e6d8f Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 18 Jan 2023 13:55:19 +0800 Subject: [PATCH 0168/1271] clean up the code for validator and add the config options for glsl shader --- .../plugins/publish/convert_gltf_shader.py | 17 ++++++++-- .../publish/validate_gltf_textures_names.py | 32 +++++++++++-------- .../defaults/project_settings/maya.json | 8 ++++- .../schemas/schema_maya_publish.json | 29 +++++++++++++++++ 4 files changed, 68 insertions(+), 18 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py index 0b084223fe..3b8ad9d672 100644 --- a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py +++ b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py @@ -21,7 +21,7 @@ class ConvertGLSLShader(publish.Extractor): meshes = cmds.ls(instance, type="mesh", long=True) self.log.info("meshes: {}".format(meshes)) # load the glsl shader plugin - cmds.loadPlugin("glslShader.mll", quiet=True) + cmds.loadPlugin("glslShader", quiet=True) for mesh in meshes: @@ -33,8 +33,19 @@ class ConvertGLSLShader(publish.Extractor): glsl_shadingGrp + ".surfaceShader") # load the maya2gltf shader - maya_dir = os.getenv("MAYA_APP_DIR") - ogsfx = maya_dir + "/maya2glTF/PBR/shaders/glTF_PBR.ogsfx" + maya_publish = ( + instance.context.data["project_settings"]["maya"]["publish"] + ) + ogsfx_path = maya_publish["ConvertGLSLShader"]["ogsfx_path"] + if not ogsfx_path: + maya_dir = os.getenv("MAYA_APP_DIR") + if not maya_dir: + raise RuntimeError("MAYA_APP_DIR not found") + ogsfx_path = maya_dir + "/maya2glTF/PBR/shaders/" + if not os.path.exists(ogsfx_path): + raise RuntimeError("the ogsfx file not found") + + ogsfx = ogsfx_path + "glTF_PBR.ogsfx" cmds.setAttr(glsl + ".shader", ogsfx, typ="string") # list the materials used for the assets diff --git a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py index 635a59a4be..5c1f5d70fb 100644 --- a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py +++ b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py @@ -15,8 +15,8 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): The texture naming conventions follows the UE5-style-guides: https://github.com/Allar/ue5-style-guide#anc-textures-packing - ORM: Occulsion Roughness Metallic - ORMS: Occulsion Roughness Metallic Specular + ORM: Occlusion Roughness Metallic + ORMS: Occlusion Roughness Metallic Specular Texture Naming Style: @@ -34,7 +34,6 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): families = ['gltf'] hosts = ['maya'] label = 'GLTF Textures Name' - actions = [openpype.hosts.maya.api.action.SelectInvalidAction] def process(self, instance): """Process all the nodes in the instance""" @@ -43,7 +42,7 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): raise RuntimeError("No PBS Shader in the scene") invalid = self.get_texture_shader_invalid(instance) if invalid: - raise RuntimeError("Non PBS material found in " + raise RuntimeError("Non PBS material found" "{0}".format(invalid)) invalid = self.get_texture_node_invalid(instance) if invalid: @@ -57,7 +56,7 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): def get_texture_name_invalid(self, instance): invalid = set() - shading_grp = self.shader_selection(instance) + shading_grp = self.get_material_from_shapes(instance) # get the materials related to the selected assets # get the file textures related to the PBS Shader @@ -73,7 +72,7 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): # "_D" if not dif.endswith("_D"): invalid.add(dif_path) - orm_packed = cmds.listConnections(shader + ".TEX_ao_mapX")[0] + orm_packed = cmds.listConnections(shader + ".TEX_ao_map")[0] if orm_packed: # "_ORM" orm_path = cmds.getAttr(orm_packed + ".fileTextureName") @@ -92,7 +91,7 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): def get_texture_node_invalid(self, instance): invalid = set() - shading_grp = self.shader_selection(instance) + shading_grp = self.get_material_from_shapes(instance) for material in shading_grp: main_shader = cmds.listConnections(material, destination=True, @@ -107,16 +106,21 @@ class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): def get_texture_shader_invalid(self, instance): invalid = set() - shading_grp = self.shader_selection(instance) - for material in shading_grp: - main_shader = cmds.listConnections(material, - destination=True, - type="StingrayPBS") - if not main_shader: + shading_grp = self.get_material_from_shapes(instance) + for shading_group in shading_grp: + material_name = "{}.surfaceShader".format(shading_group) + material = cmds.listConnections(material_name, + source=True, + destination=False, + type="StingrayPBS") + + if not material: + # add material name + material = cmds.listConnections(material_name)[0] invalid.add(material) return list(invalid) - def shader_selection(self, instance): + def get_material_from_shapes(self, instance): shapes = cmds.ls(instance, type="mesh", long=True) for shape in shapes: shading_grp = cmds.listConnections(shape, diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index db64f388c8..c89b41a3e4 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -350,7 +350,7 @@ "active": true }, "ValidateGLTFTexturesNames": { - "enabled": true, + "enabled": false, "optional": false, "active": true }, @@ -829,6 +829,12 @@ } } }, + "ConvertGLSLShader": { + "enabled": false, + "optional": true, + "active": true, + "ogsfx_path": "" + }, "ExtractMayaSceneRaw": { "enabled": true, "add_for_families": [ 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 32280d1934..a124aec1b3 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 @@ -864,6 +864,35 @@ "type": "schema", "name": "schema_maya_capture" }, + { + "type": "dict", + "collapsible": true, + "key": "ConvertGLSLShader", + "label": "Convert PBS Shader to GLSL Shader", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + }, + { + "type": "text", + "key": "ogsfx_path", + "label": "GLSL Shader Directory" + } + ] + }, { "type": "dict", "collapsible": true, From 53470bb0333cd8662e3bf4433fa78898bb29eb19 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 18 Jan 2023 09:56:29 +0000 Subject: [PATCH 0169/1271] Initial draft --- openpype/hosts/maya/api/lib_renderproducts.py | 73 ++++++++++++++----- .../maya/plugins/publish/collect_render.py | 4 +- openpype/hosts/maya/startup/userSetup.py | 8 +- .../plugins/publish/submit_publish_job.py | 33 ++++++++- 4 files changed, 92 insertions(+), 26 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index c54e3ab3e0..b76b441588 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -127,6 +127,7 @@ class RenderProduct(object): """ productName = attr.ib() ext = attr.ib() # extension + colorspace = attr.ib() # colorspace aov = attr.ib(default=None) # source aov driver = attr.ib(default=None) # source driver multipart = attr.ib(default=False) # multichannel file @@ -344,7 +345,6 @@ class ARenderProducts: separator = file_prefix[matches[0].end(1):matches[1].start(1)] return separator - def _get_layer_data(self): # type: () -> LayerMetadata # ______________________________________________ @@ -553,6 +553,9 @@ class RenderProductsArnold(ARenderProducts): ] for ai_driver in ai_drivers: + colorspace = self._get_colorspace( + ai_driver + ".colorManagement" + ) # todo: check aiAOVDriver.prefix as it could have # a custom path prefix set for this driver @@ -590,12 +593,15 @@ class RenderProductsArnold(ARenderProducts): global_aov = self._get_attr(aov, "globalAov") if global_aov: for camera in cameras: - product = RenderProduct(productName=name, - ext=ext, - aov=aov_name, - driver=ai_driver, - multipart=multipart, - camera=camera) + product = RenderProduct( + productName=name, + ext=ext, + aov=aov_name, + driver=ai_driver, + multipart=multipart, + camera=camera, + colorspace=colorspace + ) products.append(product) all_light_groups = self._get_attr(aov, "lightGroups") @@ -603,13 +609,16 @@ class RenderProductsArnold(ARenderProducts): # All light groups is enabled. A single multipart # Render Product for camera in cameras: - product = RenderProduct(productName=name + "_lgroups", - ext=ext, - aov=aov_name, - driver=ai_driver, - # Always multichannel output - multipart=True, - camera=camera) + product = RenderProduct( + productName=name + "_lgroups", + ext=ext, + aov=aov_name, + driver=ai_driver, + # Always multichannel output + multipart=True, + camera=camera, + colorspace=colorspace + ) products.append(product) else: value = self._get_attr(aov, "lightGroupsList") @@ -625,12 +634,28 @@ class RenderProductsArnold(ARenderProducts): aov=aov_name, driver=ai_driver, ext=ext, - camera=camera + camera=camera, + colorspace=colorspace ) products.append(product) return products + def _get_colorspace(self, attribute): + """Resolve colorspace from Arnold settings.""" + + def _view_transform(): + preferences = lib.get_color_management_preferences() + return preferences["view_transform"] + + resolved_values = { + "Raw": lambda: "Raw", + "Use View Transform": _view_transform, + # Default. Same as Maya Preferences. + "Use Output Transform": lib.get_color_management_output_transform + } + return resolved_values[self._get_attr(attribute)]() + def get_render_products(self): """Get all AOVs. @@ -659,11 +684,19 @@ class RenderProductsArnold(ARenderProducts): ] default_ext = self._get_attr("defaultRenderGlobals.imfPluginKey") - beauty_products = [RenderProduct( - productName="beauty", - ext=default_ext, - driver="defaultArnoldDriver", - camera=camera) for camera in cameras] + colorspace = self._get_colorspace( + "defaultArnoldDriver.colorManagement" + ) + beauty_products = [ + RenderProduct( + productName="beauty", + ext=default_ext, + driver="defaultArnoldDriver", + camera=camera, + colorspace=colorspace + ) for camera in cameras + ] + # AOVs > Legacy > Maya Render View > Mode aovs_enabled = bool( self._get_attr("defaultArnoldRenderOptions.aovMode") diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index b1ad3ca58e..2c89424381 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -42,7 +42,6 @@ Provides: import re import os import platform -import json from maya import cmds import maya.app.renderSetup.model.renderSetup as renderSetup @@ -318,6 +317,9 @@ class CollectMayaRender(pyblish.api.ContextPlugin): "aovSeparator": layer_render_products.layer_data.aov_separator, # noqa: E501 "renderSetupIncludeLights": render_instance.data.get( "renderSetupIncludeLights" + ), + "colorspaceConfig": ( + lib.get_color_management_preferences()["config"] ) } diff --git a/openpype/hosts/maya/startup/userSetup.py b/openpype/hosts/maya/startup/userSetup.py index 40cd51f2d8..cb5aa4a898 100644 --- a/openpype/hosts/maya/startup/userSetup.py +++ b/openpype/hosts/maya/startup/userSetup.py @@ -4,10 +4,16 @@ from openpype.pipeline import install_host from openpype.hosts.maya.api import MayaHost from maya import cmds +# MAYA_RESOURCES enviornment variable is referenced in default OCIO path but +# it's not part of the environment. Patching this so it works as expected. +if "MAYA_RESOURCES" not in os.environ: + os.environ["MAYA_RESOURCES"] = os.path.join( + os.environ["MAYA_LOCATION"], "resources" + ).replace("\\", "/") + host = MayaHost() install_host(host) - print("starting OpenPype usersetup") # build a shelf diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 7e39a644a2..8811fa5d34 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -427,7 +427,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self.log.info( "Finished copying %i files" % len(resource_files)) - def _create_instances_for_aov(self, instance_data, exp_files): + def _create_instances_for_aov( + self, instance_data, exp_files, additional_data + ): """Create instance for each AOV found. This will create new instance for every aov it can detect in expected @@ -528,6 +530,14 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): else: files = os.path.basename(col) + # Copy render product "colorspace" data to representation. + colorspace = "" + products = additional_data["renderProducts"].layer_data.products + for product in products: + if product.productName == aov: + colorspace = product.colorspace + break + rep = { "name": ext, "ext": ext, @@ -537,7 +547,14 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # If expectedFile are absolute, we need only filenames "stagingDir": staging, "fps": new_instance.get("fps"), - "tags": ["review"] if preview else [] + "tags": ["review"] if preview else [], + "colorspaceData": { + "colorspace": colorspace, + "configData": { + "path": additional_data["colorspaceConfig"], + "template": "" + } + } } # support conversion from tiled to scanline @@ -561,7 +578,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self.log.debug("instances:{}".format(instances)) return instances - def _get_representations(self, instance, exp_files): + def _get_representations(self, instance, exp_files, additional_data): """Create representations for file sequences. This will return representations of expected files if they are not @@ -897,6 +914,11 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self.log.info(data.get("expectedFiles")) + additional_data = { + "renderProducts": instance.data["renderProducts"], + "colorspaceConfig": instance.data["colorspaceConfig"] + } + if isinstance(data.get("expectedFiles")[0], dict): # we cannot attach AOVs to other subsets as we consider every # AOV subset of its own. @@ -911,12 +933,15 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # there are multiple renderable cameras in scene) instances = self._create_instances_for_aov( instance_skeleton_data, - data.get("expectedFiles")) + data.get("expectedFiles"), + additional_data + ) self.log.info("got {} instance{}".format( len(instances), "s" if len(instances) > 1 else "")) else: + #Need to inject colorspace here. representations = self._get_representations( instance_skeleton_data, data.get("expectedFiles") From cdc0a80846f465d0f12ee6bfa83d28313676d327 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 18 Jan 2023 11:21:43 +0000 Subject: [PATCH 0170/1271] Fix When there is no Deadline setting avaliable. --- .../maya/plugins/publish/extract_workfile_xgen.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py index 13a04615eb..d37a03d1f6 100644 --- a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -37,12 +37,14 @@ class ExtractWorkfileXgen(publish.Extractor): ) return - publish_settings = instance.context["deadline"]["publish"] - if not publish_settings["MayaSubmitDeadline"]["use_published"]: - self.log.debug( - "Not using the published workfile. Abort Xgen extraction." - ) - return + deadline_settings = instance.context.get("deadline") + if deadline_settings: + publish_settings = deadline_settings["publish"] + if not publish_settings["MayaSubmitDeadline"]["use_published"]: + self.log.debug( + "Not using the published workfile. Abort Xgen extraction." + ) + return # Collect Xgen and Delta files. xgen_files = [] From ab9b2d5970b458447e442d9d031bc3d8e76ddc7e Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 18 Jan 2023 15:13:12 +0000 Subject: [PATCH 0171/1271] Missing initial commit --- openpype/hosts/maya/api/lib.py | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index dd5da275e8..95b4db1b42 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -5,6 +5,7 @@ import sys import platform import uuid import math +import re import json import logging @@ -3446,3 +3447,47 @@ def iter_visible_nodes_in_range(nodes, start, end): def get_attribute_input(attr): connections = cmds.listConnections(attr, plugs=True, destination=False) return connections[0] if connections else None + + +def get_color_management_preferences(): + """Get and resolve OCIO preferences.""" + data = { + # Is color management enabled. + "enabled": cmds.colorManagementPrefs( + query=True, cmEnabled=True + ), + "rendering_space": cmds.colorManagementPrefs( + query=True, renderingSpaceName=True + ), + "output_transform": cmds.colorManagementPrefs( + query=True, outputTransformName=True + ), + "output_transform_enabled": cmds.colorManagementPrefs( + query=True, outputTransformEnabled=True + ), + "view_transform": cmds.colorManagementPrefs( + query=True, viewTransformName=True + ) + } + + path = cmds.colorManagementPrefs( + query=True, configFilePath=True + ) + # Resolve environment variables in config path. "MAYA_RESOURCES" are in the + # path by default. + for group in re.search(r'(<.*>)', path).groups(): + path = path.replace( + group, os.environ[group[1:-1]] + ) + + data["config"] = path + + return data + + +def get_color_management_output_transform(): + preferences = get_color_management_preferences() + colorspace = preferences["rendering_space"] + if preferences["output_transform_enabled"]: + colorspace = preferences["output_transform"] + return colorspace From ff2516e219b5eff574e2ba12c13aff1d9033814c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Wed, 18 Jan 2023 16:47:53 +0100 Subject: [PATCH 0172/1271] Feature: Keep synced hero representations up-to-date. Fix #4331 --- .../plugins/publish/integrate_hero_version.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 5f4d284740..c162c83976 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -386,6 +386,25 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): repre["_id"] = old_repre["_id"] update_data = prepare_representation_update_data( old_repre, repre) + + # Keep previously synchronized sites up-to-date + # by comparing old and new sites and adding old sites + # if missing in new ones + old_repre_files_sites = [ + f.get("sites", []) for f in old_repre.get("files", []) + ] + for i, file in enumerate(repre.get("files", [])): + repre_sites_names = { + s["name"] for s in file.get("sites", []) + } + for site in old_repre_files_sites[i]: + if site["name"] not in repre_sites_names: + # Pop the date to tag for sync + site.pop("created_dt") + file["sites"].append(site) + + update_data["files"][i] = file + op_session.update_entity( project_name, old_repre["type"], From 5476217f6ed17b44cf4fdd02849429ef10774c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Wed, 18 Jan 2023 16:59:53 +0100 Subject: [PATCH 0173/1271] linting --- openpype/plugins/publish/integrate_hero_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index c162c83976..427256c137 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -404,7 +404,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): file["sites"].append(site) update_data["files"][i] = file - + op_session.update_entity( project_name, old_repre["type"], From 3279634dacd18e6d38a29708596c822d276781c6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 18 Jan 2023 17:57:28 +0000 Subject: [PATCH 0174/1271] Fix Environment variable substitution in path --- openpype/hosts/maya/api/lib.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 95b4db1b42..b4aa18af65 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3473,12 +3473,14 @@ def get_color_management_preferences(): path = cmds.colorManagementPrefs( query=True, configFilePath=True ) + # Resolve environment variables in config path. "MAYA_RESOURCES" are in the # path by default. - for group in re.search(r'(<.*>)', path).groups(): - path = path.replace( - group, os.environ[group[1:-1]] - ) + def _subst_with_env_value(match): + key = match.group(1) + return os.environ.get(key, "") + + path = re.sub(r'<([^>]+)>', _subst_with_env_value, path) data["config"] = path From cf4df006bdf67fc935db649bbb27db8beef4f6af Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 19 Jan 2023 09:12:33 +0000 Subject: [PATCH 0175/1271] Clean up redundant lib code. --- openpype/hosts/maya/api/lib.py | 31 ------------------- .../plugins/publish/submit_maya_muster.py | 7 ++++- .../plugins/publish/validate_maya_units.py | 7 ++--- 3 files changed, 8 insertions(+), 37 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index dd5da275e8..71d890f46b 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -254,11 +254,6 @@ def read(node): return data -def _get_mel_global(name): - """Return the value of a mel global variable""" - return mel.eval("$%s = $%s;" % (name, name)) - - def matrix_equals(a, b, tolerance=1e-10): """ Compares two matrices with an imperfection tolerance @@ -691,11 +686,6 @@ class delete_after(object): cmds.delete(self._nodes) -def get_renderer(layer): - with renderlayer(layer): - return cmds.getAttr("defaultRenderGlobals.currentRenderer") - - def get_current_renderlayer(): return cmds.editRenderLayerGlobals(query=True, currentRenderLayer=True) @@ -1440,27 +1430,6 @@ def set_id(node, unique_id, overwrite=False): cmds.setAttr(attr, unique_id, type="string") -# endregion ID -def get_reference_node(path): - """ - Get the reference node when the path is found being used in a reference - Args: - path (str): the file path to check - - Returns: - node (str): name of the reference node in question - """ - try: - node = cmds.file(path, query=True, referenceNode=True) - except RuntimeError: - log.debug('File is not referenced : "{}"'.format(path)) - return - - reference_path = cmds.referenceQuery(path, filename=True) - if os.path.normpath(path) == os.path.normpath(reference_path): - return node - - def set_attribute(attribute, value, node): """Adjust attributes based on the value from the attribute data diff --git a/openpype/hosts/maya/plugins/publish/submit_maya_muster.py b/openpype/hosts/maya/plugins/publish/submit_maya_muster.py index 1a6463fb9d..8ae3e5124b 100644 --- a/openpype/hosts/maya/plugins/publish/submit_maya_muster.py +++ b/openpype/hosts/maya/plugins/publish/submit_maya_muster.py @@ -52,6 +52,11 @@ def _get_script(): return module_path +def get_renderer(layer): + with lib.renderlayer(layer): + return cmds.getAttr("defaultRenderGlobals.currentRenderer") + + def get_renderer_variables(renderlayer=None): """Retrieve the extension which has been set in the VRay settings @@ -66,7 +71,7 @@ def get_renderer_variables(renderlayer=None): dict """ - renderer = lib.get_renderer(renderlayer or lib.get_current_renderlayer()) + renderer = get_renderer(renderlayer or lib.get_current_renderlayer()) render_attrs = lib.RENDER_ATTRS.get(renderer, lib.RENDER_ATTRS["default"]) padding = cmds.getAttr("{}.{}".format(render_attrs["node"], diff --git a/openpype/hosts/maya/plugins/publish/validate_maya_units.py b/openpype/hosts/maya/plugins/publish/validate_maya_units.py index 5698d795ff..e6fabb1712 100644 --- a/openpype/hosts/maya/plugins/publish/validate_maya_units.py +++ b/openpype/hosts/maya/plugins/publish/validate_maya_units.py @@ -11,10 +11,6 @@ from openpype.pipeline.publish import ( ) -def float_round(num, places=0, direction=ceil): - return direction(num * (10**places)) / float(10**places) - - class ValidateMayaUnits(pyblish.api.ContextPlugin): """Check if the Maya units are set correct""" @@ -36,6 +32,7 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): # Collected units linearunits = context.data.get('linearUnits') angularunits = context.data.get('angularUnits') + # TODO(antirotor): This is hack as for framerates having multiple # decimal places. FTrack is ceiling decimal values on # fps to two decimal places but Maya 2019+ is reporting those fps @@ -43,7 +40,7 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): # rounding, we have to round those numbers coming from Maya. # NOTE: this must be revisited yet again as it seems that Ftrack is # now flooring the value? - fps = float_round(context.data.get('fps'), 2, ceil) + fps = mayalib.float_round(context.data.get('fps'), 2, ceil) # TODO repace query with using 'context.data["assetEntity"]' asset_doc = get_current_project_asset() From 7a7f87e861abf4aae7fb3a96f660d74fe4866329 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 19 Jan 2023 09:30:11 +0000 Subject: [PATCH 0176/1271] Improvement Cleaner way to resolve in path. --- openpype/hosts/maya/api/lib.py | 11 ++++------- openpype/hosts/maya/startup/userSetup.py | 6 ------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index b4aa18af65..0807db88dc 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3474,13 +3474,10 @@ def get_color_management_preferences(): query=True, configFilePath=True ) - # Resolve environment variables in config path. "MAYA_RESOURCES" are in the - # path by default. - def _subst_with_env_value(match): - key = match.group(1) - return os.environ.get(key, "") - - path = re.sub(r'<([^>]+)>', _subst_with_env_value, path) + # The OCIO config supports a custom token. + maya_resources_token = "" + maya_resources_path = om.MGlobal.getAbsolutePathToResources() + path = path.replace(maya_resources_token, maya_resources_path) data["config"] = path diff --git a/openpype/hosts/maya/startup/userSetup.py b/openpype/hosts/maya/startup/userSetup.py index cb5aa4a898..1104421cd9 100644 --- a/openpype/hosts/maya/startup/userSetup.py +++ b/openpype/hosts/maya/startup/userSetup.py @@ -4,12 +4,6 @@ from openpype.pipeline import install_host from openpype.hosts.maya.api import MayaHost from maya import cmds -# MAYA_RESOURCES enviornment variable is referenced in default OCIO path but -# it's not part of the environment. Patching this so it works as expected. -if "MAYA_RESOURCES" not in os.environ: - os.environ["MAYA_RESOURCES"] = os.path.join( - os.environ["MAYA_LOCATION"], "resources" - ).replace("\\", "/") host = MayaHost() install_host(host) From c2d0bc8f39f69fc8f2cea36972242e946a6c9deb Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 19 Jan 2023 09:34:55 +0000 Subject: [PATCH 0177/1271] HOund --- openpype/hosts/maya/api/lib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 0807db88dc..23f7319d4a 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -5,7 +5,6 @@ import sys import platform import uuid import math -import re import json import logging From 8c626920ccc0ff81145cab1f994478a2c3ef4a44 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 19 Jan 2023 11:01:47 +0000 Subject: [PATCH 0178/1271] Fix Updating --- openpype/hosts/maya/plugins/load/load_xgen.py | 62 +++++++++++-------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 565d1306b4..8249c9092e 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -32,7 +32,7 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): # Copy the xgen palette file from published version. _, maya_extension = os.path.splitext(maya_filepath) source = maya_filepath.replace(maya_extension, ".xgen") - destination = os.path.join( + xgen_file = os.path.join( project_path, "{basename}__{namespace}__{name}.xgen".format( basename=os.path.splitext(os.path.basename(current_file()))[0], @@ -40,15 +40,15 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): name=name ) ).replace("\\", "/") - self.log.info("Copying {} to {}".format(source, destination)) - shutil.copy(source, destination) + self.log.info("Copying {} to {}".format(source, xgen_file)) + shutil.copy(source, xgen_file) # Modify xgDataPath and xgProjectPath to have current workspace first # and published version directory second. This ensure that any newly # created xgen files are created in the current workspace. resources_path = os.path.join(os.path.dirname(source), "resources") lines = [] - with open(destination, "r") as f: + with open(xgen_file, "r") as f: for line in [line.rstrip() for line in f]: if line.startswith("\txgDataPath"): data_path = line.split("\t")[-1] @@ -67,10 +67,12 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): lines.append(line) - with open(destination, "w") as f: + with open(xgen_file, "w") as f: f.write("\n".join(lines)) - return destination + xgd_file = xgen_file.replace(".xgen", ".xgd") + + return xgen_file, xgd_file def process_reference(self, context, name, namespace, options): # Validate workfile has a path. @@ -78,7 +80,8 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): QtWidgets.QMessageBox.warning( None, "", - "Current workfile has not been saved." + "Current workfile has not been saved. Please save the workfile" + " before loading an Xgen." ) return @@ -87,10 +90,9 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): ) name = context["representation"]["data"]["xgenName"] - xgen_file = self.setup_xgen_palette_file( + xgen_file, xgd_file = self.setup_xgen_palette_file( maya_filepath, namespace, name ) - xgd_file = xgen_file.replace(".xgen", ".xgd") # Reference xgen. Xgen does not like being referenced in under a group. new_nodes = [] @@ -105,17 +107,7 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): ) xgen_palette = cmds.ls(nodes, type="xgmPalette", long=True)[0] - cmds.setAttr( - "{}.xgBaseFile".format(xgen_palette), - os.path.basename(xgen_file), - type="string" - ) - cmds.setAttr( - "{}.xgFileName".format(xgen_palette), - os.path.basename(xgd_file), - type="string" - ) - cmds.setAttr("{}.xgExportAsDelta".format(xgen_palette), True) + self.set_palette_attributes(xgen_palette, xgen_file, xgd_file) # This create an expression attribute of float. If we did not add # any changes to collection, then Xgen does not create an xgd file @@ -133,10 +125,24 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): return new_nodes + def set_palette_attributes(self, xgen_palette, xgen_file, xgd_file): + cmds.setAttr( + "{}.xgBaseFile".format(xgen_palette), + os.path.basename(xgen_file), + type="string" + ) + cmds.setAttr( + "{}.xgFileName".format(xgen_palette), + os.path.basename(xgd_file), + type="string" + ) + cmds.setAttr("{}.xgExportAsDelta".format(xgen_palette), True) + def update(self, container, representation): """Workflow for updating Xgen. - - Copy and overwrite the workspace .xgen file. + - Copy and potentially overwrite the workspace .xgen file. + - Export changes to delta file. - Set collection attributes to not include delta files. - Update xgen maya file reference. - Apply the delta file changes. @@ -144,22 +150,28 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): We have to do this workflow because when using referencing of the xgen collection, Maya implicitly imports the Xgen data from the xgen file so - we dont have any control over added the delta file changes. + we dont have any control over when adding the delta file changes. + + There is an implicit increment of the xgen and delta files, due to + using the workfile basename. """ container_node = container["objectName"] members = get_container_members(container_node) + xgen_palette = cmds.ls(members, type="xgmPalette", long=True)[0] reference_node = get_reference_node(members, self.log) namespace = cmds.referenceQuery(reference_node, namespace=True)[1:] - xgen_file = self.setup_xgen_palette_file( + xgen_file, xgd_file = self.setup_xgen_palette_file( get_representation_path(representation), namespace, representation["data"]["xgenName"] ) - xgd_file = xgen_file.replace(".xgen", ".xgd") - xgen_palette = cmds.ls(members, type="xgmPalette", long=True)[0] + # Export current changes to apply later. + xgenm.createDelta(xgen_palette.replace("|", ""), xgd_file) + + self.set_palette_attributes(xgen_palette, xgen_file, xgd_file) attribute_data = { "{}.xgFileName".format(xgen_palette): os.path.basename(xgen_file), From 977d4263cb52e12517d655391f3aec7b6c95b897 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 20 Jan 2023 19:39:52 +0100 Subject: [PATCH 0179/1271] Fix - addSite loader handles hero version If adding site to representation presence of hero version is checked, if found hero version is marked to be donwloaded too. --- openpype/plugins/load/add_site.py | 57 +++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/openpype/plugins/load/add_site.py b/openpype/plugins/load/add_site.py index ac931e41db..64567b746a 100644 --- a/openpype/plugins/load/add_site.py +++ b/openpype/plugins/load/add_site.py @@ -2,6 +2,12 @@ from openpype.client import get_linked_representation_id from openpype.modules import ModulesManager from openpype.pipeline import load from openpype.modules.sync_server.utils import SiteAlreadyPresentError +from openpype.client.entities import ( + get_hero_version_by_subset_id, + get_representation_by_id, + get_version_by_id, + get_representation_by_name +) class AddSyncSite(load.LoaderPlugin): @@ -34,15 +40,20 @@ class AddSyncSite(load.LoaderPlugin): return self._sync_server def load(self, context, name=None, namespace=None, data=None): - self.log.info("Adding {} to representation: {}".format( - data["site_name"], data["_id"])) + # self.log wont propagate + print("Adding {} to representation: {}".format( + data["site_name"], data["_id"])) family = context["representation"]["context"]["family"] project_name = data["project_name"] repre_id = data["_id"] site_name = data["site_name"] - self.sync_server.add_site(project_name, repre_id, site_name, - force=True) + representation_ids = self._add_hero_representation_ids(project_name, + repre_id) + + for repre_id in representation_ids: + self.sync_server.add_site(project_name, repre_id, site_name, + force=True) if family == "workfile": links = get_linked_representation_id( @@ -52,9 +63,12 @@ class AddSyncSite(load.LoaderPlugin): ) for link_repre_id in links: try: - self.sync_server.add_site(project_name, link_repre_id, - site_name, - force=False) + representation_ids = self._add_hero_representation_ids( + project_name, link_repre_id) + for repre_id in representation_ids: + self.sync_server.add_site(project_name, repre_id, + site_name, + force=False) except SiteAlreadyPresentError: # do not add/reset working site for references self.log.debug("Site present", exc_info=True) @@ -64,3 +78,32 @@ class AddSyncSite(load.LoaderPlugin): def filepath_from_context(self, context): """No real file loading""" return "" + + def _add_hero_representation_ids(self, project_name, repre_id): + """Find hero version if exists for repre_id. + + Returns: + (list): at least [repre_id] if no hero version found + """ + representation_ids = [repre_id] + + repre_doc = get_representation_by_id( + project_name, repre_id, fields=["_id", "parent", "name"] + ) + + version_doc = get_version_by_id(project_name, repre_doc["parent"]) + if version_doc["type"] != "hero_version": + hero_version = get_hero_version_by_subset_id( + project_name, version_doc["parent"], + fields=["_id", "version_id"] + ) + if (hero_version and + hero_version["version_id"] == version_doc["_id"]): + hero_repre_doc = get_representation_by_name( + project_name, + repre_doc["name"], + hero_version["_id"] + ) + representation_ids.append(hero_repre_doc["_id"]) + + return representation_ids From a91a980b1becaf112ee0b05d4d5c4498ee13611e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 20 Jan 2023 19:56:38 +0100 Subject: [PATCH 0180/1271] laoder plugin has compatibility method on it's own --- openpype/pipeline/load/plugins.py | 35 ++++++++++++++++++++++++++++++- openpype/pipeline/load/utils.py | 18 +--------------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index b5e55834db..4197ddb2ae 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -2,7 +2,10 @@ import os import logging from openpype.settings import get_system_settings, get_project_settings -from openpype.pipeline import legacy_io +from openpype.pipeline import ( + schema, + legacy_io, +) from openpype.pipeline.plugin_discover import ( discover, register_plugin, @@ -79,6 +82,36 @@ class LoaderPlugin(list): print(" - setting `{}`: `{}`".format(option, value)) setattr(cls, option, value) + @classmethod + def is_compatible_loader(cls, context): + """Return whether a loader is compatible with a context. + + This checks the version's families and the representation for the given + Loader. + + Returns: + bool + """ + + maj_version, _ = schema.get_schema_version(context["subset"]["schema"]) + if maj_version < 3: + families = context["version"]["data"].get("families", []) + else: + families = context["subset"]["data"]["families"] + + representation = context["representation"] + has_family = ( + "*" in cls.families or any( + family in cls.families for family in families + ) + ) + representations = cls.get_representations() + has_representation = ( + "*" in representations + or representation["name"] in representations + ) + return has_family and has_representation + @classmethod def get_representations(cls): return cls.representations diff --git a/openpype/pipeline/load/utils.py b/openpype/pipeline/load/utils.py index e2b3675115..e30923f922 100644 --- a/openpype/pipeline/load/utils.py +++ b/openpype/pipeline/load/utils.py @@ -748,25 +748,9 @@ def is_compatible_loader(Loader, context): Returns: bool - """ - maj_version, _ = schema.get_schema_version(context["subset"]["schema"]) - if maj_version < 3: - families = context["version"]["data"].get("families", []) - else: - families = context["subset"]["data"]["families"] - representation = context["representation"] - has_family = ( - "*" in Loader.families or any( - family in Loader.families for family in families - ) - ) - representations = Loader.get_representations() - has_representation = ( - "*" in representations or representation["name"] in representations - ) - return has_family and has_representation + return Loader.is_compatible_loader(context) def loaders_from_repre_context(loaders, repre_context): From bce5363e4250bc6e9a83c7702889a30790bf909a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 23 Jan 2023 10:23:07 +0100 Subject: [PATCH 0181/1271] reorganized the order of conditions --- openpype/pipeline/load/plugins.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index 4197ddb2ae..9b891a4da3 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -93,24 +93,33 @@ class LoaderPlugin(list): bool """ + plugin_repre_names = cls.get_representations() + plugin_families = cls.families + if not plugin_repre_names or not plugin_families: + return False + + repre_doc = context.get("representation") + if not repre_doc: + return False + + plugin_repre_names = set(plugin_repre_names) + if ( + "*" not in plugin_repre_names + and repre_doc["name"] not in plugin_repre_names + ): + return False + maj_version, _ = schema.get_schema_version(context["subset"]["schema"]) if maj_version < 3: families = context["version"]["data"].get("families", []) else: families = context["subset"]["data"]["families"] - representation = context["representation"] - has_family = ( - "*" in cls.families or any( - family in cls.families for family in families - ) + plugin_families = set(plugin_families) + return ( + "*" in plugin_families + or any(family in plugin_families for family in families) ) - representations = cls.get_representations() - has_representation = ( - "*" in representations - or representation["name"] in representations - ) - return has_family and has_representation @classmethod def get_representations(cls): From e4cd14c2a633f13146920a2c0cb4a70a09154e8e Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 23 Jan 2023 17:44:11 +0000 Subject: [PATCH 0182/1271] Clean up --- .../maya/plugins/publish/extract_xgen.py | 43 ++++++------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 22260df2c7..80b62275cd 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -10,7 +10,14 @@ from openpype.lib import StringTemplate class ExtractXgenCache(publish.Extractor): - """Extract Xgen""" + """Extract Xgen + + Workflow: + - Duplicate nodes used for patches. + - Export palette and import onto duplicate nodes. + - Export/Publish duplicate nodes and palette. + - Publish all xgen files as resources. + """ label = "Extract Xgen" hosts = ["maya"] @@ -55,29 +62,12 @@ class ExtractXgenCache(publish.Extractor): # Duplicate_transform subd patch geometry. duplicate_transform = cmds.duplicate(transform_name)[0] - duplicate_shape = cmds.listRelatives( - duplicate_transform, - shapes=True, - fullPath=True - )[0] # Discard the children. shapes = cmds.listRelatives(duplicate_transform, shapes=True) children = cmds.listRelatives(duplicate_transform, children=True) cmds.delete(set(children) - set(shapes)) - # Connect attributes. - cmds.connectAttr( - "{}.matrix".format(duplicate_transform), - "{}.transform".format(node), - force=True - ) - cmds.connectAttr( - "{}.worldMesh".format(duplicate_shape), - "{}.geometry".format(node), - force=True - ) - duplicate_transform = cmds.parent( duplicate_transform, world=True )[0] @@ -87,17 +77,17 @@ class ExtractXgenCache(publish.Extractor): # Import xgen onto the duplicate. with maintained_selection(): cmds.select(duplicate_nodes) - collection = xgenm.importPalette(xgen_path, []) + palette = xgenm.importPalette(xgen_path, []) attribute_data = { - "{}.xgFileName".format(collection): xgen_filename + "{}.xgFileName".format(palette): xgen_filename } # Export Maya file. type = "mayaAscii" if self.scene_type == "ma" else "mayaBinary" with attribute_values(attribute_data): with maintained_selection(): - cmds.select(duplicate_nodes + [collection]) + cmds.select(duplicate_nodes + [palette]) cmds.file( maya_filepath, force=True, @@ -117,18 +107,11 @@ class ExtractXgenCache(publish.Extractor): "ext": self.scene_type, "files": maya_filename, "stagingDir": staging_dir, - "data": {"xgenName": collection} + "data": {"xgenName": palette} } instance.data["representations"].append(representation) - # Revert to original xgen connections. - for node, connections in instance.data["xgenConnections"].items(): - for attr, src in connections.items(): - cmds.connectAttr( - src, "{}.{}".format(node, attr), force=True - ) - - cmds.delete(duplicate_nodes + [collection]) + cmds.delete(duplicate_nodes + [palette]) # Collect all files under palette root as resources. data_path = xgenm.getAttr( From 81ca789c8b62272283cb74136033fbef61db7e16 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 24 Jan 2023 13:13:16 +0100 Subject: [PATCH 0183/1271] OP-4617 - collect last published files --- .../plugins/publish/collect_frames_fix.py | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/publish/collect_frames_fix.py b/openpype/plugins/publish/collect_frames_fix.py index 1e73f2caaa..2640c08ae1 100644 --- a/openpype/plugins/publish/collect_frames_fix.py +++ b/openpype/plugins/publish/collect_frames_fix.py @@ -1,14 +1,24 @@ import pyblish.api from openpype.lib.attribute_definitions import TextDef from openpype.pipeline.publish import OpenPypePyblishPluginMixin +from openpype.client.entities import ( + get_last_version_by_subset_name, + get_representations +) class CollectFramesFixDef( - pyblish.api.ContextPlugin, + pyblish.api.InstancePlugin, OpenPypePyblishPluginMixin ): - label = "Collect frames to fix" - targets = ["local"] + """Provides text field to insert frame(s) to be rerendered. + + Published files of last version of an instance subset are collected into + instance.data["last_version_published_files"]. All these but frames + mentioned in text field will be reused for new version. + """ + order = pyblish.api.CollectorOrder + 0.495 + label = "Collect Frames to Fix" # Disable plugin by default families = ["render"] enabled = True @@ -19,6 +29,32 @@ class CollectFramesFixDef( if frames_to_fix: instance.data["frames_to_fix"] = frames_to_fix + subset_name = instance.data["subset"] + asset_name = instance.data["asset"] + + project_entity = instance.data["projectEntity"] + project_name = project_entity["name"] + + version = get_last_version_by_subset_name(project_name, + subset_name, + asset_name=asset_name) + if not version: + return + + representations = get_representations(project_name, + version_ids=[version["_id"]]) + published_files = [] + for repre in representations: + if repre["context"]["family"] not in self.families: + continue + + for file_info in repre.get("files"): + published_files.append(file_info["path"]) + + instance.data["last_version_published_files"] = published_files + self.log.debug("last_version_published_files::{}".format( + instance.data["last_version_published_files"])) + @classmethod def get_attribute_defs(cls): return [ @@ -26,4 +62,3 @@ class CollectFramesFixDef( placeholder="5,10-15", regex="[0-9,-]+") ] - From 27df249a1c61a33ef754c205735505925e98caed Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 24 Jan 2023 13:16:22 +0100 Subject: [PATCH 0184/1271] OP-4617 - reuse last published and rerender specific If frames_to_fix is filled, use this to render only these frames, use 'last_version_published_files' for other --- .../plugins/publish/extract_render_local.py | 156 ++++++++++++------ 1 file changed, 102 insertions(+), 54 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_render_local.py b/openpype/hosts/nuke/plugins/publish/extract_render_local.py index 842a62ab4a..5a857de509 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_render_local.py +++ b/openpype/hosts/nuke/plugins/publish/extract_render_local.py @@ -1,15 +1,12 @@ import os +import shutil import pyblish.api import clique import nuke from openpype.pipeline import publish -from openpype.client import ( - get_version_by_id, - get_last_version_by_subset_id, -) - +from openpype.lib import collect_frames class NukeRenderLocal(publish.ExtractorColormanaged): @@ -18,6 +15,8 @@ class NukeRenderLocal(publish.ExtractorColormanaged): Extract the result of savers by starting a comp render This will run the local render of Fusion. + Allows to use last published frames and overwrite only specific ones + (set in instance.data.get("frames_to_fix")) """ order = pyblish.api.ExtractorOrder @@ -26,7 +25,6 @@ class NukeRenderLocal(publish.ExtractorColormanaged): families = ["render.local", "prerender.local", "still.local"] def process(self, instance): - families = instance.data["families"] child_nodes = ( instance.data.get("transientData", {}).get("childNodes") or instance @@ -40,64 +38,54 @@ class NukeRenderLocal(publish.ExtractorColormanaged): self.log.debug("instance collected: {}".format(instance.data)) node_subset_name = instance.data.get("name", None) + + first_frame = instance.data.get("frameStartHandle", None) + last_frame = instance.data.get("frameEndHandle", None) + frames_to_fix = instance.data.get("frames_to_fix") - frames_to_render = [] - if not frames_to_fix: - first_frame = instance.data.get("frameStartHandle", None) - last_frame = instance.data.get("frameEndHandle", None) - frames_to_render.append((first_frame, last_frame)) - else: - for frame_range in frames_to_fix.split(","): - if isinstance(frame_range, int): - first_frame = frame_range - last_frame = frame_range - elif '-' in frame_range: - frames = frame_range.split('-') - first_frame = int(frames[0]) - last_frame = int(frames[1]) - else: - raise ValueError("Wrong format of frames to fix {}" - .format(frames_to_fix)) - frames_to_render.append((first_frame, last_frame)) + frames_to_render = self._get_frames_to_render(frames_to_fix, + first_frame, + last_frame) filenames = [] - for first_frame, last_frame in frames_to_render: + node_file = node["file"] + # Collect expected filepaths for each frame + # - for cases that output is still image is first created set of + # paths which is then sorted and converted to list + expected_paths = list(sorted({ + node_file.evaluate(frame) + for frame in range(first_frame, last_frame + 1) + })) + # Extract only filenames for representation + filenames.extend([ + os.path.basename(filepath) + for filepath in expected_paths + ]) + + # Ensure output directory exists. + out_dir = os.path.dirname(expected_paths[0]) + if not os.path.exists(out_dir): + os.makedirs(out_dir) + + if instance.data.get("last_version_published_files"): + anatomy = instance.context.data["anatomy"] + self._copy_last_published(anatomy, instance, out_dir, filenames) + + for render_first_frame, render_last_frame in frames_to_render: self.log.info("Starting render") - self.log.info("Start frame: {}".format(first_frame)) - self.log.info("End frame: {}".format(last_frame)) - - node_file = node["file"] - # Collecte expected filepaths for each frame - # - for cases that output is still image is first created set of - # paths which is then sorted and converted to list - expected_paths = list(sorted({ - node_file.evaluate(frame) - for frame in range(first_frame, last_frame + 1) - })) - # Extract only filenames for representation - filenames.extend([ - os.path.basename(filepath) - for filepath in expected_paths - ]) - - # Ensure output directory exists. - out_dir = os.path.dirname(expected_paths[0]) - if not os.path.exists(out_dir): - os.makedirs(out_dir) + self.log.info("Start frame: {}".format(render_first_frame)) + self.log.info("End frame: {}".format(render_last_frame)) # Render frames nuke.execute( - node_subset_name, - int(first_frame), - int(last_frame) + str(node_subset_name), + int(render_first_frame), + int(render_last_frame) ) - ext = node["file_type"].value() - colorspace = node["colorspace"].value() - - if frames_to_fix: - pass + ext = node["file_type"].value() + colorspace = node["colorspace"].value() if "representations" not in instance.data: instance.data["representations"] = [] @@ -135,6 +123,7 @@ class NukeRenderLocal(publish.ExtractorColormanaged): out_dir )) + families = instance.data["families"] # redefinition of families if "render.local" in families: instance.data['family'] = 'render' @@ -162,3 +151,62 @@ class NukeRenderLocal(publish.ExtractorColormanaged): self.log.info('Finished render') self.log.debug("_ instance.data: {}".format(instance.data)) + + def _copy_last_published(self, anatomy, instance, out_dir, + expected_filenames): + """Copies last published files to temporary out_dir. + + These are base of files which will be extended/fixed for specific + frames. + Renames published file to expected file name based on frame, eg. + test_project_test_asset_subset_v005.1001.exr > new_render.1001.exr + """ + last_published = instance.data["last_version_published_files"] + last_published_and_frames = collect_frames(last_published) + + expected_and_frames = collect_frames(expected_filenames) + frames_and_expected = {v: k for k, v in expected_and_frames.items()} + for file_path, frame in last_published_and_frames.items(): + file_path = anatomy.fill_root(file_path) + if not os.path.exists(file_path): + continue + target_file_name = frames_and_expected.get(frame) + if not target_file_name: + continue + + out_path = os.path.join(out_dir, target_file_name) + self.log.debug("Copying '{}' -> '{}'".format(file_path, out_path)) + shutil.copy(file_path, out_path) + + # TODO shouldn't this be uncommented + # instance.context.data["cleanupFullPaths"].append(out_path) + + def _get_frames_to_render(self, frames_to_fix, first_frame, last_frame): + """Return list of frame range tuples to render + + Args: + frames_to_fix (str): specific or range of frames to be rerendered + (1005, 1009-1010) + first_frame (int): original first frame + last_frame (int) + Returns: + (list): [(1005, 1005), (1009-1010)] + """ + frames_to_render = [] + if not frames_to_fix: + frames_to_render.append((first_frame, last_frame)) + else: + for frame_range in frames_to_fix.split(","): + if isinstance(frame_range, int) or frame_range.isdigit(): + render_first_frame = frame_range + render_last_frame = frame_range + elif '-' in frame_range: + frames = frame_range.split('-') + render_first_frame = int(frames[0]) + render_last_frame = int(frames[1]) + else: + raise ValueError("Wrong format of frames to fix {}" + .format(frames_to_fix)) + frames_to_render.append((render_first_frame, + render_last_frame)) + return frames_to_render From 9d8ad09fa1f2636011f9bc625a00cbea2ace1110 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 24 Jan 2023 13:19:48 +0100 Subject: [PATCH 0185/1271] OP-4617 - implemented currently only in Nuke New host implementation should be added to hosts filter --- openpype/plugins/publish/collect_frames_fix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_frames_fix.py b/openpype/plugins/publish/collect_frames_fix.py index 2640c08ae1..560b8fa41c 100644 --- a/openpype/plugins/publish/collect_frames_fix.py +++ b/openpype/plugins/publish/collect_frames_fix.py @@ -19,7 +19,7 @@ class CollectFramesFixDef( """ order = pyblish.api.CollectorOrder + 0.495 label = "Collect Frames to Fix" - # Disable plugin by default + hosts = ["nuke"] families = ["render"] enabled = True From ec248c6ab761561293108a9c312f7b6234bae55f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 24 Jan 2023 13:52:01 +0100 Subject: [PATCH 0186/1271] OP-4617 - better handle if no last version --- .../plugins/publish/extract_render_local.py | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_render_local.py b/openpype/hosts/nuke/plugins/publish/extract_render_local.py index 5a857de509..a48db60179 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_render_local.py +++ b/openpype/hosts/nuke/plugins/publish/extract_render_local.py @@ -42,11 +42,6 @@ class NukeRenderLocal(publish.ExtractorColormanaged): first_frame = instance.data.get("frameStartHandle", None) last_frame = instance.data.get("frameEndHandle", None) - frames_to_fix = instance.data.get("frames_to_fix") - frames_to_render = self._get_frames_to_render(frames_to_fix, - first_frame, - last_frame) - filenames = [] node_file = node["file"] # Collect expected filepaths for each frame @@ -67,9 +62,14 @@ class NukeRenderLocal(publish.ExtractorColormanaged): if not os.path.exists(out_dir): os.makedirs(out_dir) - if instance.data.get("last_version_published_files"): + frames_to_render = [(first_frame, last_frame)] + + frames_to_fix = instance.data.get("frames_to_fix") + if instance.data.get("last_version_published_files") and frames_to_fix: + frames_to_render = self._get_frames_to_render(frames_to_fix) anatomy = instance.context.data["anatomy"] - self._copy_last_published(anatomy, instance, out_dir, filenames) + self._copy_last_published(anatomy, instance, out_dir, + filenames) for render_first_frame, render_last_frame in frames_to_render: @@ -181,7 +181,7 @@ class NukeRenderLocal(publish.ExtractorColormanaged): # TODO shouldn't this be uncommented # instance.context.data["cleanupFullPaths"].append(out_path) - def _get_frames_to_render(self, frames_to_fix, first_frame, last_frame): + def _get_frames_to_render(self, frames_to_fix): """Return list of frame range tuples to render Args: @@ -193,20 +193,18 @@ class NukeRenderLocal(publish.ExtractorColormanaged): (list): [(1005, 1005), (1009-1010)] """ frames_to_render = [] - if not frames_to_fix: - frames_to_render.append((first_frame, last_frame)) - else: - for frame_range in frames_to_fix.split(","): - if isinstance(frame_range, int) or frame_range.isdigit(): - render_first_frame = frame_range - render_last_frame = frame_range - elif '-' in frame_range: - frames = frame_range.split('-') - render_first_frame = int(frames[0]) - render_last_frame = int(frames[1]) - else: - raise ValueError("Wrong format of frames to fix {}" - .format(frames_to_fix)) - frames_to_render.append((render_first_frame, - render_last_frame)) + + for frame_range in frames_to_fix.split(","): + if isinstance(frame_range, int) or frame_range.isdigit(): + render_first_frame = frame_range + render_last_frame = frame_range + elif '-' in frame_range: + frames = frame_range.split('-') + render_first_frame = int(frames[0]) + render_last_frame = int(frames[1]) + else: + raise ValueError("Wrong format of frames to fix {}" + .format(frames_to_fix)) + frames_to_render.append((render_first_frame, + render_last_frame)) return frames_to_render From 98170356b37e17543404db2b428ad9df6e649070 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 24 Jan 2023 14:01:52 +0100 Subject: [PATCH 0187/1271] OP-4617 - added logging if no last version --- openpype/plugins/publish/collect_frames_fix.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/plugins/publish/collect_frames_fix.py b/openpype/plugins/publish/collect_frames_fix.py index 560b8fa41c..4ada8120e0 100644 --- a/openpype/plugins/publish/collect_frames_fix.py +++ b/openpype/plugins/publish/collect_frames_fix.py @@ -39,6 +39,8 @@ class CollectFramesFixDef( subset_name, asset_name=asset_name) if not version: + self.log.warning("No last version found, " + "re-render not possible") return representations = get_representations(project_name, From 9208b76f103699a30d827fd05f30ac904ac7219a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 24 Jan 2023 14:28:12 +0100 Subject: [PATCH 0188/1271] OP-4617 - added prerender family --- openpype/plugins/publish/collect_frames_fix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_frames_fix.py b/openpype/plugins/publish/collect_frames_fix.py index 4ada8120e0..413cbfe978 100644 --- a/openpype/plugins/publish/collect_frames_fix.py +++ b/openpype/plugins/publish/collect_frames_fix.py @@ -20,7 +20,7 @@ class CollectFramesFixDef( order = pyblish.api.CollectorOrder + 0.495 label = "Collect Frames to Fix" hosts = ["nuke"] - families = ["render"] + families = ["render", "prerender"] enabled = True def process(self, instance): From ff80b73de875a26d4d787d06e55825dc0879ad72 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 24 Jan 2023 14:29:00 +0100 Subject: [PATCH 0189/1271] OP-4617 - added local targets This plugin should run only on local (eg not no farm - no one to fill valu in UI). --- openpype/plugins/publish/collect_frames_fix.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/plugins/publish/collect_frames_fix.py b/openpype/plugins/publish/collect_frames_fix.py index 413cbfe978..fdaa810897 100644 --- a/openpype/plugins/publish/collect_frames_fix.py +++ b/openpype/plugins/publish/collect_frames_fix.py @@ -19,6 +19,7 @@ class CollectFramesFixDef( """ order = pyblish.api.CollectorOrder + 0.495 label = "Collect Frames to Fix" + targets = ["local"] hosts = ["nuke"] families = ["render", "prerender"] enabled = True From a03ce5d6fea965761d960ca3eb54a5c7c816fffc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 24 Jan 2023 17:05:19 +0100 Subject: [PATCH 0190/1271] OP-4617 - fix - os.path.samefile not present on Python2 --- openpype/lib/file_transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/lib/file_transaction.py b/openpype/lib/file_transaction.py index cba361a8d4..fe70b37cb1 100644 --- a/openpype/lib/file_transaction.py +++ b/openpype/lib/file_transaction.py @@ -189,6 +189,6 @@ class FileTransaction(object): def _same_paths(self, src, dst): # handles same paths but with C:/project vs c:/project if os.path.exists(src) and os.path.exists(dst): - return os.path.samefile(src, dst) + return os.stat(src) == os.stat(dst) return src == dst From 30b00705f4b74c163111534acfd154706143c703 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 24 Jan 2023 17:06:54 +0100 Subject: [PATCH 0191/1271] OP-4617 - fix - allow overwriting of old version in integrate.py Without it it will always use version from anatomy, even if it logs that it uses old version. --- openpype/plugins/publish/integrate.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index a97ab2acca..7521f91cbd 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -534,6 +534,9 @@ class IntegrateAsset(pyblish.api.InstancePlugin): template_data["representation"] = repre["name"] template_data["ext"] = repre["ext"] + # allow overwriting existing version + template_data["version"] = version["name"] + # add template data for colorspaceData if repre.get("colorspaceData"): colorspace = repre["colorspaceData"]["colorspace"] From eed6ec6bf56bf0d776eeec55e5bceeb99e324503 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 24 Jan 2023 17:08:02 +0100 Subject: [PATCH 0192/1271] OP-4617 - added rewrite_version toggle Allows to rewrite latest version, eg. not duplicating files. --- openpype/plugins/publish/collect_frames_fix.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/collect_frames_fix.py b/openpype/plugins/publish/collect_frames_fix.py index fdaa810897..bdd49585a5 100644 --- a/openpype/plugins/publish/collect_frames_fix.py +++ b/openpype/plugins/publish/collect_frames_fix.py @@ -1,5 +1,9 @@ import pyblish.api -from openpype.lib.attribute_definitions import TextDef +from openpype.lib.attribute_definitions import ( + TextDef, + BoolDef +) + from openpype.pipeline.publish import OpenPypePyblishPluginMixin from openpype.client.entities import ( get_last_version_by_subset_name, @@ -27,6 +31,8 @@ class CollectFramesFixDef( def process(self, instance): attribute_values = self.get_attr_values_from_data(instance.data) frames_to_fix = attribute_values.get("frames_to_fix") + rewrite_version = attribute_values.get("rewrite_version") + if frames_to_fix: instance.data["frames_to_fix"] = frames_to_fix @@ -58,10 +64,17 @@ class CollectFramesFixDef( self.log.debug("last_version_published_files::{}".format( instance.data["last_version_published_files"])) + if rewrite_version: + instance.data["version"] = version["name"] + # limits triggering version validator + instance.data.pop("latestVersion") + @classmethod def get_attribute_defs(cls): return [ TextDef("frames_to_fix", label="Frames to fix", placeholder="5,10-15", - regex="[0-9,-]+") + regex="[0-9,-]+"), + BoolDef("rewrite_version", label="Rewrite latest version", + default=False), ] From 6ef2b4be9a5c458364f480325483ead90febdcf9 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 24 Jan 2023 16:09:56 +0000 Subject: [PATCH 0193/1271] Working import --- openpype/hosts/maya/plugins/load/load_xgen.py | 76 +++++++++++++------ .../maya/plugins/publish/extract_xgen.py | 43 ++++------- 2 files changed, 70 insertions(+), 49 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 8249c9092e..fec1b389fa 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -1,5 +1,6 @@ import os import shutil +import tempfile import maya.cmds as cmds import xgenm @@ -25,6 +26,18 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): icon = "code-fork" color = "orange" + def write_xgen_file(self, file_path, data): + lines = [] + with open(file_path, "r") as f: + for key, value in data.items(): + for line in [line.rstrip() for line in f]: + if line.startswith("\t" + key): + line = "\t{}\t\t{}".format(key, value) + lines.append(line) + + with open(file_path, "w") as f: + f.write("\n".join(lines)) + def setup_xgen_palette_file(self, maya_filepath, namespace, name): # Setup xgen palette file. project_path = os.path.dirname(current_file()) @@ -47,28 +60,26 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): # and published version directory second. This ensure that any newly # created xgen files are created in the current workspace. resources_path = os.path.join(os.path.dirname(source), "resources") - lines = [] + with open(xgen_file, "r") as f: for line in [line.rstrip() for line in f]: if line.startswith("\txgDataPath"): data_path = line.split("\t")[-1] - line = "\txgDataPath\t\t{}{}{}".format( - data_path, - os.pathsep, - data_path.replace( - "${PROJECT}xgen", resources_path.replace("\\", "/") - ) + + data = { + "xgDataPath": ( + "${{PROJECT}}xgen/collections/{}__ns__{}/{}{}".format( + namespace, + name, + os.pathsep, + data_path.replace( + "${PROJECT}xgen", resources_path.replace("\\", "/") ) - - if line.startswith("\txgProjectPath"): - line = "\txgProjectPath\t\t{}/".format( - project_path.replace("\\", "/") - ) - - lines.append(line) - - with open(xgen_file, "w") as f: - f.write("\n".join(lines)) + ) + ), + "xgProjectPath": project_path.replace("\\", "/") + } + self.write_xgen_file(xgen_file, data) xgd_file = xgen_file.replace(".xgen", ".xgd") @@ -89,14 +100,31 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): self.fname, context["project"]["name"] ) - name = context["representation"]["data"]["xgenName"] xgen_file, xgd_file = self.setup_xgen_palette_file( - maya_filepath, namespace, name + maya_filepath, namespace, "collection" ) + # Making temporary copy of xgen file from published so we can + # modify the paths. + temp_xgen_file = os.path.join(tempfile.gettempdir(), "temp.xgen") + _, maya_extension = os.path.splitext(maya_filepath) + source = maya_filepath.replace(maya_extension, ".xgen") + shutil.copy(source, temp_xgen_file) + + resources_path = os.path.join(os.path.dirname(source), "resources") + with open(xgen_file, "r") as f: + for line in [line.rstrip() for line in f]: + if line.startswith("\txgDataPath"): + data_path = line.split("\t")[-1] + data = { + "xgDataPath": data_path.replace( + "${PROJECT}xgen", resources_path.replace("\\", "/") + ) + } + self.write_xgen_file(temp_xgen_file, data) + # Reference xgen. Xgen does not like being referenced in under a group. new_nodes = [] - with maintained_selection(): nodes = cmds.file( maya_filepath, @@ -106,7 +134,11 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): returnNewNodes=True ) - xgen_palette = cmds.ls(nodes, type="xgmPalette", long=True)[0] + xgen_palette = xgenm.importPalette( + temp_xgen_file.replace("\\", "/"), [], nameSpace=namespace + ) + os.remove(temp_xgen_file) + self.set_palette_attributes(xgen_palette, xgen_file, xgd_file) # This create an expression attribute of float. If we did not add @@ -119,7 +151,7 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): shapes = cmds.ls(nodes, shapes=True, long=True) - new_nodes = (list(set(nodes) - set(shapes))) + new_nodes = (list(set(nodes) - set(shapes)) + [xgen_palette]) self[:] = new_nodes diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 80b62275cd..91d4352449 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -5,7 +5,7 @@ from maya import cmds import xgenm from openpype.pipeline import publish -from openpype.hosts.maya.api.lib import maintained_selection, attribute_values +from openpype.hosts.maya.api.lib import maintained_selection from openpype.lib import StringTemplate @@ -74,31 +74,21 @@ class ExtractXgenCache(publish.Extractor): duplicate_nodes.append(duplicate_transform) - # Import xgen onto the duplicate. - with maintained_selection(): - cmds.select(duplicate_nodes) - palette = xgenm.importPalette(xgen_path, []) - - attribute_data = { - "{}.xgFileName".format(palette): xgen_filename - } - # Export Maya file. type = "mayaAscii" if self.scene_type == "ma" else "mayaBinary" - with attribute_values(attribute_data): - with maintained_selection(): - cmds.select(duplicate_nodes + [palette]) - cmds.file( - maya_filepath, - force=True, - type=type, - exportSelected=True, - preserveReferences=False, - constructionHistory=True, - shader=True, - constraints=True, - expressions=True - ) + with maintained_selection(): + cmds.select(duplicate_nodes) + cmds.file( + maya_filepath, + force=True, + type=type, + exportSelected=True, + preserveReferences=False, + constructionHistory=True, + shader=True, + constraints=True, + expressions=True + ) self.log.info("Extracted to {}".format(maya_filepath)) @@ -106,12 +96,11 @@ class ExtractXgenCache(publish.Extractor): "name": self.scene_type, "ext": self.scene_type, "files": maya_filename, - "stagingDir": staging_dir, - "data": {"xgenName": palette} + "stagingDir": staging_dir } instance.data["representations"].append(representation) - cmds.delete(duplicate_nodes + [palette]) + cmds.delete(duplicate_nodes) # Collect all files under palette root as resources. data_path = xgenm.getAttr( From 04c1b8905508253456e9bab7008bfe1263d98999 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Wed, 25 Jan 2023 09:33:23 +0100 Subject: [PATCH 0194/1271] fix typo + remove part which creates second reference per placeholder --- .../hosts/maya/api/workfile_template_builder.py | 2 +- .../pipeline/workfile/workfile_template_builder.py | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index ef043ed0f4..8e0f1d0f01 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -240,7 +240,7 @@ class MayaPlaceholderLoadPlugin(PlaceholderPlugin, PlaceholderLoadMixin): cmds.setAttr(node + ".hiddenInOutliner", True) def load_succeed(self, placeholder, container): - self._parent_in_hierarhchy(placeholder, container) + self._parent_in_hierarchy(placeholder, container) def _parent_in_hierarchy(self, placeholder, container): """Parent loaded container to placeholder's parent. diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 582657c735..d262d6a771 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -521,10 +521,6 @@ class AbstractTemplateBuilder(object): if not level_limit: level_limit = 1000 - placeholder_by_scene_id = { - placeholder.scene_identifier: placeholder - for placeholder in placeholders - } all_processed = len(placeholders) == 0 # Counter is checked at the ned of a loop so the loop happens at least # once. @@ -573,16 +569,6 @@ class AbstractTemplateBuilder(object): break all_processed = True - collected_placeholders = self.get_placeholders() - for placeholder in collected_placeholders: - identifier = placeholder.scene_identifier - if identifier in placeholder_by_scene_id: - continue - - all_processed = False - placeholder_by_scene_id[identifier] = placeholder - placeholders.append(placeholder) - self.refresh() def _get_build_profiles(self): From 6a992409f0adac4cf4fe534e5feee5e6f610b363 Mon Sep 17 00:00:00 2001 From: Libor Batek Date: Wed, 25 Jan 2023 09:51:42 +0100 Subject: [PATCH 0195/1271] Added more elaborate info on installing/running OP and corrected few typos. --- website/docs/artist_getting_started.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index 2f88a9f238..474a39642b 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -18,13 +18,17 @@ If this is not the case, please contact your administrator to consult on how to If you are working from home though, you'll need to install it yourself. You should, however, receive the OpenPype installer files from your studio admin, supervisor or production, because OpenPype versions and executables might not be compatible between studios. -To install OpenPype you just need to unzip it anywhere on the disk +Installing OpenPype is possible by Windows installer or by unzipping it anywhere on the disk from downloaded ZIP archive. -To use it, you have two options +There are two options running OpenPype -**openpype_gui.exe** is the most common for artists. It runs OpenPype GUI in system tray. From there you can run all the available tools. To use any of the features, OpenPype must be running in the tray. +first most common one by using OP icon on the Desktop triggering -**openpype_console.exe** in useful for debugging and error reporting. It opens console window where all the necessary information will appear during user's work. +**openpype_gui.exe** suitable for artists. It runs OpenPype GUI in the OS tray. From there you can run all the available tools. To use any of the features, OpenPype must be running in the tray. + +or alternatively by using + +**openpype_console.exe** located in the OpenPype folder which is suitable for TDs/admins for debugging and error reporting. It opens console window where all the necessary information will appear during user's work. Date: Wed, 25 Jan 2023 09:38:17 +0000 Subject: [PATCH 0196/1271] Revert "Working import" This reverts commit 6ef2b4be9a5c458364f480325483ead90febdcf9. --- openpype/hosts/maya/plugins/load/load_xgen.py | 76 ++++++------------- .../maya/plugins/publish/extract_xgen.py | 43 +++++++---- 2 files changed, 49 insertions(+), 70 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index fec1b389fa..8249c9092e 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -1,6 +1,5 @@ import os import shutil -import tempfile import maya.cmds as cmds import xgenm @@ -26,18 +25,6 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): icon = "code-fork" color = "orange" - def write_xgen_file(self, file_path, data): - lines = [] - with open(file_path, "r") as f: - for key, value in data.items(): - for line in [line.rstrip() for line in f]: - if line.startswith("\t" + key): - line = "\t{}\t\t{}".format(key, value) - lines.append(line) - - with open(file_path, "w") as f: - f.write("\n".join(lines)) - def setup_xgen_palette_file(self, maya_filepath, namespace, name): # Setup xgen palette file. project_path = os.path.dirname(current_file()) @@ -60,26 +47,28 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): # and published version directory second. This ensure that any newly # created xgen files are created in the current workspace. resources_path = os.path.join(os.path.dirname(source), "resources") - + lines = [] with open(xgen_file, "r") as f: for line in [line.rstrip() for line in f]: if line.startswith("\txgDataPath"): data_path = line.split("\t")[-1] - - data = { - "xgDataPath": ( - "${{PROJECT}}xgen/collections/{}__ns__{}/{}{}".format( - namespace, - name, - os.pathsep, - data_path.replace( - "${PROJECT}xgen", resources_path.replace("\\", "/") + line = "\txgDataPath\t\t{}{}{}".format( + data_path, + os.pathsep, + data_path.replace( + "${PROJECT}xgen", resources_path.replace("\\", "/") + ) ) - ) - ), - "xgProjectPath": project_path.replace("\\", "/") - } - self.write_xgen_file(xgen_file, data) + + if line.startswith("\txgProjectPath"): + line = "\txgProjectPath\t\t{}/".format( + project_path.replace("\\", "/") + ) + + lines.append(line) + + with open(xgen_file, "w") as f: + f.write("\n".join(lines)) xgd_file = xgen_file.replace(".xgen", ".xgd") @@ -100,31 +89,14 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): self.fname, context["project"]["name"] ) + name = context["representation"]["data"]["xgenName"] xgen_file, xgd_file = self.setup_xgen_palette_file( - maya_filepath, namespace, "collection" + maya_filepath, namespace, name ) - # Making temporary copy of xgen file from published so we can - # modify the paths. - temp_xgen_file = os.path.join(tempfile.gettempdir(), "temp.xgen") - _, maya_extension = os.path.splitext(maya_filepath) - source = maya_filepath.replace(maya_extension, ".xgen") - shutil.copy(source, temp_xgen_file) - - resources_path = os.path.join(os.path.dirname(source), "resources") - with open(xgen_file, "r") as f: - for line in [line.rstrip() for line in f]: - if line.startswith("\txgDataPath"): - data_path = line.split("\t")[-1] - data = { - "xgDataPath": data_path.replace( - "${PROJECT}xgen", resources_path.replace("\\", "/") - ) - } - self.write_xgen_file(temp_xgen_file, data) - # Reference xgen. Xgen does not like being referenced in under a group. new_nodes = [] + with maintained_selection(): nodes = cmds.file( maya_filepath, @@ -134,11 +106,7 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): returnNewNodes=True ) - xgen_palette = xgenm.importPalette( - temp_xgen_file.replace("\\", "/"), [], nameSpace=namespace - ) - os.remove(temp_xgen_file) - + xgen_palette = cmds.ls(nodes, type="xgmPalette", long=True)[0] self.set_palette_attributes(xgen_palette, xgen_file, xgd_file) # This create an expression attribute of float. If we did not add @@ -151,7 +119,7 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): shapes = cmds.ls(nodes, shapes=True, long=True) - new_nodes = (list(set(nodes) - set(shapes)) + [xgen_palette]) + new_nodes = (list(set(nodes) - set(shapes))) self[:] = new_nodes diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 91d4352449..80b62275cd 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -5,7 +5,7 @@ from maya import cmds import xgenm from openpype.pipeline import publish -from openpype.hosts.maya.api.lib import maintained_selection +from openpype.hosts.maya.api.lib import maintained_selection, attribute_values from openpype.lib import StringTemplate @@ -74,21 +74,31 @@ class ExtractXgenCache(publish.Extractor): duplicate_nodes.append(duplicate_transform) - # Export Maya file. - type = "mayaAscii" if self.scene_type == "ma" else "mayaBinary" + # Import xgen onto the duplicate. with maintained_selection(): cmds.select(duplicate_nodes) - cmds.file( - maya_filepath, - force=True, - type=type, - exportSelected=True, - preserveReferences=False, - constructionHistory=True, - shader=True, - constraints=True, - expressions=True - ) + palette = xgenm.importPalette(xgen_path, []) + + attribute_data = { + "{}.xgFileName".format(palette): xgen_filename + } + + # Export Maya file. + type = "mayaAscii" if self.scene_type == "ma" else "mayaBinary" + with attribute_values(attribute_data): + with maintained_selection(): + cmds.select(duplicate_nodes + [palette]) + cmds.file( + maya_filepath, + force=True, + type=type, + exportSelected=True, + preserveReferences=False, + constructionHistory=True, + shader=True, + constraints=True, + expressions=True + ) self.log.info("Extracted to {}".format(maya_filepath)) @@ -96,11 +106,12 @@ class ExtractXgenCache(publish.Extractor): "name": self.scene_type, "ext": self.scene_type, "files": maya_filename, - "stagingDir": staging_dir + "stagingDir": staging_dir, + "data": {"xgenName": palette} } instance.data["representations"].append(representation) - cmds.delete(duplicate_nodes) + cmds.delete(duplicate_nodes + [palette]) # Collect all files under palette root as resources. data_path = xgenm.getAttr( From 8ca483adaf2135ad66f2a126228fc1b71164c16e Mon Sep 17 00:00:00 2001 From: Libor Batek Date: Wed, 25 Jan 2023 11:36:07 +0100 Subject: [PATCH 0197/1271] first rough concept for 3dsmax user doc --- website/docs/artist_hosts_3dsmax.md | 226 ++++++++++++++++++++++++++++ website/sidebars.js | 1 + 2 files changed, 227 insertions(+) create mode 100644 website/docs/artist_hosts_3dsmax.md diff --git a/website/docs/artist_hosts_3dsmax.md b/website/docs/artist_hosts_3dsmax.md new file mode 100644 index 0000000000..f959f48533 --- /dev/null +++ b/website/docs/artist_hosts_3dsmax.md @@ -0,0 +1,226 @@ +--- +id: artist_hosts_3dsmax +title: 3dsmax +sidebar_label: 3dsmax +--- + +## Still Work In Progress Doc + +- [Set Context](artist_tools_context_manager) +- [Work Files](artist_tools_workfiles) +- [Create](artist_tools_creator) +- [Load](artist_tools_loader) +- [Manage (Inventory)](artist_tools_inventory) +- [Publish](artist_tools_publisher) +- [Library Loader](artist_tools_library_loader) + +## Working with OpenPype in 3dsmax + +OpenPype is here to ease you the burden of working on project with lots of +collaborators, worrying about naming, setting stuff, browsing through endless +directories, loading and exporting and so on. To achieve that, OpenPype is using +concept of being _"data driven"_. This means that what happens when publishing +is influenced by data in scene. This can by slightly confusing so let's get to +it with few examples. + + +## Setting scene data + +Blender settings concerning framerate, resolution and frame range are handled +by OpenPype. If set correctly in Ftrack, Blender will automatically set the +values for you. + + +## Publishing models + +### Intro + +Publishing models in Blender is pretty straightforward. Create your model as you +need. You might need to adhere to specifications of your studio that can be different +between studios and projects but by default your geometry does not need any +other convention. + +![Model example](assets/blender-model_example.jpg) + +### Creating instance + +Now create **Model instance** from it to let OpenPype know what in the scene you want to +publish. Go **OpenPype → Create... → Model**. + +![Model create instance](assets/blender-model_create_instance.jpg) + +`Asset` field is a name of asset you are working on - it should be already filled +with correct name as you've started Blender or switched context to specific asset. You +can edit that field to change it to different asset (but that one must already exists). + +`Subset` field is a name you can decide on. It should describe what kind of data you +have in the model. For example, you can name it `Proxy` to indicate that this is +low resolution stuff. See [Subset](artist_concepts.md#subset). + + + +Read-only field just under it show final subset name, adding subset field to +name of the group you have selected. + +`Use selection` checkbox will use whatever you have selected in Outliner to be +wrapped in Model instance. This is usually what you want. Click on **Create** button. + +You'll notice then after you've created new Model instance, there is a new +collection in Outliner called after your asset and subset, in our case it is +`character1_modelDefault`. The assets selected when creating the Model instance +are linked in the new collection. + +And that's it, you have your first model ready to publish. + +Now save your scene (if you didn't do it already). You will notice that path +in Save dialog is already set to place where scenes related to modeling task on +your asset should reside. As in our case we are working on asset called +**character1** and on task **modeling**, path relative to your project directory will be +`project_XY/assets/character1/work/modeling`. The default name for the file will +be `project_XY_asset_task_version`, so in our case +`simonetest_character1_modeling_v001.blend`. Let's save it. + +![Model create instance](assets/blender-save_modelling_file.jpg) + +### Publishing models + +Now let's publish it. Go **OpenPype → Publish...**. You will be presented with following window: + +![Model publish](assets/blender-model_pre_publish.jpg) + +Note that content of this window can differs by your pipeline configuration. +For more detail see [Publisher](artist_tools_publisher). + +Items in left column are instances you will be publishing. You can disable them +by clicking on square next to them. White filled square indicate they are ready for +publishing, red means something went wrong either during collection phase +or publishing phase. Empty one with gray text is disabled. + +See that in this case we are publishing from the scene file +`simonetest_character1_modeling_v001.blend` the Blender model named +`character1_modelDefault`. + +Right column lists all tasks that are run during collection, validation, +extraction and integration phase. White items are optional and you can disable +them by clicking on them. + +Lets do dry-run on publishing to see if we pass all validators. Click on flask +icon at the bottom. Validators are run. Ideally you will end up with everything +green in validator section. + +### Fixing problems + +For the sake of demonstration, I intentionally kept the model in Edit Mode, to +trigger the validator designed to check just this. + +![Failed Model Validator](assets/blender-model_publish_error.jpg) + +You can see our model is now marked red in left column and in right we have +red box next to `Mesh is in Object Mode` validator. + +You can click on arrow next to it to see more details: + +![Failed Model Validator details](assets/blender-model_error_details.jpg) + +From there you can see in **Records** entry that there is problem with the +object `Suzanne`. Some validators have option to fix problem for you or just +select objects that cause trouble. This is the case with our failed validator. + +In main overview you can notice little A in a circle next to validator +name. Right click on it and you can see menu item `select invalid`. This +will select offending object in Blender. + +Fix is easy. Without closing Publisher window we just turn back the Object Mode. +Then we need to reset it to make it notice changes we've made. Click on arrow +circle button at the bottom and it will reset the Publisher to initial state. Run +validators again (flask icon) to see if everything is ok. + +It should OK be now. Write some comment if you want and click play icon button +when ready. + +Publish process will now take its course. Depending on data you are publishing +it can take a while. You should end up with everything green and message +**Finished successfully ...** You can now close publisher window. + +To check for yourself that model is published, open +[Asset Loader](artist_tools_loader) - **OpenPype → Load...**. +There you should see your model, named `modelDefault`. + +### Loading models + +You can load model with [Loader](artist_tools_loader). Go **OpenPype → Load...**, +select your rig, right click on it and click **Link model (blend)**. + +## Creating Rigs + +Creating and publishing rigs with OpenPype follows similar workflow as with +other data types. Create your rig and mark parts of your hierarchy in sets to +help OpenPype validators and extractors to check it and publish it. + +### Preparing rig for publish + +When creating rigs in Blender, it is important to keep a specific structure for +the bones and the geometry. Let's first create a model and its rig. For +demonstration, I'll create a simple model for a robotic arm made of simple boxes. + +![Blender - Simple model for rigging](assets/blender-rig_model_setup.jpg) + +I have now created the armature `RIG_RobotArm`. While the naming is not important, +you can just adhere to your naming conventions, the hierarchy is. Once the models +are skinned to the armature, the geometry must be organized in a separate Collection. +In this case, I have the armature in the main Collection, and the geometry in +the `Geometry` Collection. + +![Blender - Rig Hierarchy Example](assets/blender-rig_hierarchy_example.jpg) + +When you've prepared your hierarchy, it's time to create *Rig instance* in OpenPype. +Select your whole rig hierarchy and go **OpenPype → Create...**. Select **Rig**. + +![Blender - Rig Hierarchy Example](assets/blender-rig_create.jpg) + +A new collection named after the selected Asset and Subset should have been created. +In our case, it is `character1_rigDefault`. All the selected armature and models +have been linked in this new collection. You should end up with something like +this: + +![Blender - Rig Hierarchy Example](assets/blender-rig_hierarchy_before_publish.jpg) + +### Publishing rigs + +Publishing rig is done in same way as publishing everything else. Save your scene +and go **OpenPype → Publish**. For more detail see [Publisher](artist_tools_publisher). + +### Loading rigs + +You can load rig with [Loader](artist_tools_loader). Go **OpenPype → Load...**, +select your rig, right click on it and click **Link rig (blend)**. + +## Layouts in Blender + +A layout is a set of elements that populate a scene. OpenPype allows to version +and manage those sets. + +### Publishing a layout + +Working with Layout is easy. Just load your assets into scene with +[Loader](artist_tools_loader) (**OpenPype → Load...**). Populate your scene as +you wish, translate each piece to fit your need. When ready, select all imported +stuff and go **OpenPype → Create...** and select **Layout**. When selecting rigs, +you need to select only the armature, the geometry will automatically be included. +This will create set containing your selection and marking it for publishing. + +Now you can publish is with **OpenPype → Publish**. + +### Loading layouts + +You can load a Layout using [Loader](artist_tools_loader) +(**OpenPype → Load...**). Select your layout, right click on it and +select **Link Layout (blend)**. This will populate your scene with all those +models you've put into layout. diff --git a/website/sidebars.js b/website/sidebars.js index a19aade712..6802dd6918 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -49,6 +49,7 @@ module.exports = { ], }, "artist_hosts_blender", + "artist_hosts_3dsmax", "artist_hosts_harmony", "artist_hosts_houdini", "artist_hosts_aftereffects", From 4d5f498b9b512a69febd3e8d36351a6569621e6d Mon Sep 17 00:00:00 2001 From: Libor Batek Date: Wed, 25 Jan 2023 12:31:33 +0100 Subject: [PATCH 0198/1271] more changes to the 3dsmax --- website/docs/artist_hosts_3dsmax.md | 12 +++++++----- website/docs/assets/3dsmax_model_OP.png | Bin 0 -> 85689 bytes 2 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 website/docs/assets/3dsmax_model_OP.png diff --git a/website/docs/artist_hosts_3dsmax.md b/website/docs/artist_hosts_3dsmax.md index f959f48533..0ca6f008cf 100644 --- a/website/docs/artist_hosts_3dsmax.md +++ b/website/docs/artist_hosts_3dsmax.md @@ -4,7 +4,9 @@ title: 3dsmax sidebar_label: 3dsmax --- -## Still Work In Progress Doc +### *Still Work In Progress Page* + +## OpenPype Global Tools - [Set Context](artist_tools_context_manager) - [Work Files](artist_tools_workfiles) @@ -26,8 +28,8 @@ it with few examples. ## Setting scene data -Blender settings concerning framerate, resolution and frame range are handled -by OpenPype. If set correctly in Ftrack, Blender will automatically set the +3dsmax settings concerning framerate, resolution and frame range are handled +by OpenPype. If set correctly in OP Project Manager/Ftrack, 3dsmax will automatically set the values for you. @@ -35,12 +37,12 @@ values for you. ### Intro -Publishing models in Blender is pretty straightforward. Create your model as you +Publishing models in 3dsmax is pretty straightforward. Create your model as you need. You might need to adhere to specifications of your studio that can be different between studios and projects but by default your geometry does not need any other convention. -![Model example](assets/blender-model_example.jpg) +![Model example](assets/3dsmax_model_OP.png) ### Creating instance diff --git a/website/docs/assets/3dsmax_model_OP.png b/website/docs/assets/3dsmax_model_OP.png new file mode 100644 index 0000000000000000000000000000000000000000..293c06642c721cf0a6b78bd5374da477f142e7ae GIT binary patch literal 85689 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%0D-{^odT??FPIN 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 e(eB#~mH!W3?Sd>^Y}}3j0000 Date: Wed, 25 Jan 2023 13:09:34 +0100 Subject: [PATCH 0199/1271] fix pop created_dt --- openpype/plugins/publish/integrate_hero_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 427256c137..1a4f4bacfd 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -400,7 +400,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): for site in old_repre_files_sites[i]: if site["name"] not in repre_sites_names: # Pop the date to tag for sync - site.pop("created_dt") + site.pop("created_dt", None) file["sites"].append(site) update_data["files"][i] = file From 036509dc4733595e07b000097b6b841b2c76e40e Mon Sep 17 00:00:00 2001 From: Libor Batek Date: Wed, 25 Jan 2023 14:17:20 +0100 Subject: [PATCH 0200/1271] additional changes incorporated --- website/docs/artist_getting_started.md | 54 +++++++++----------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index 474a39642b..40961dbc77 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -7,6 +7,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; + ## Working in the studio In studio environment you should have OpenPype already installed and deployed, so you can start using it without much setup. Your admin has probably put OpenPype icon on your desktop or even had your computer set up so OpenPype will start automatically. @@ -15,63 +16,46 @@ If this is not the case, please contact your administrator to consult on how to ## Working from home -If you are working from home though, you'll need to install it yourself. You should, however, receive the OpenPype installer files from your studio +If you are working from **home** though, you'll **need to install** it yourself. You should, however, receive the OpenPype installer files from your studio admin, supervisor or production, because OpenPype versions and executables might not be compatible between studios. Installing OpenPype is possible by Windows installer or by unzipping it anywhere on the disk from downloaded ZIP archive. -There are two options running OpenPype +For more detailed info about installation on different OS please visit [Installation section](artist_install.md). + +There are two ways running OpenPype first most common one by using OP icon on the Desktop triggering -**openpype_gui.exe** suitable for artists. It runs OpenPype GUI in the OS tray. From there you can run all the available tools. To use any of the features, OpenPype must be running in the tray. +**openpype_gui.exe** suitable **for artists**. It runs OpenPype GUI in the OS tray. From there you can run all the available tools. To use any of the features, OpenPype must be running in the tray. or alternatively by using -**openpype_console.exe** located in the OpenPype folder which is suitable for TDs/admins for debugging and error reporting. It opens console window where all the necessary information will appear during user's work. - - - - - - -WIP - Windows instructions once installers are finished - - - - -WIP - Linux instructions once installers are finished - - - - -WIP - Mac instructions once installers are finished - - - +**openpype_console.exe** located in the OpenPype folder which is suitable for **TDs/Admin** for debugging and error reporting. This one runs with opened console window where all the necessary info will appear during user's work session. ## First Launch -When you first start OpenPype, you will be asked to give it some basic information. +When you first start OpenPype, you will be asked to fill in some basic informations. + ### MongoDB In most cases that will only be your studio MongoDB Address. +It's a URL that you should have received from your Studio admin and most often will look like this -It is a URL that you should receive from you studio and most often will look like this `mongodb://username:passwword@mongo.mystudiodomain.com:12345` or `mongodb://192.168.100.15:27071`, it really depends on your studio setup. When OpenPype Igniter +`mongodb://username:passwword@mongo.mystudiodomain.com:12345` + + or + + `mongodb://192.168.100.15:27071` + +it really depends on your studio setup. When OpenPype Igniter asks for it, just put it in the corresponding text field and press `install` button. ### OpenPype Version Repository -Sometimes your studio might also ask you to fill in the path to it's version +Sometimes your Studio might also ask you to fill in the path to it's version repository. This is a location where OpenPype will be looking for when checking if it's up to date and where updates are installed from automatically. @@ -80,7 +64,7 @@ This path is usually taken from the database directly, so you shouldn't need it. ## Updates -If you're connected to your studio, OpenPype will check for, and install updates automatically every time you run it. That's why during the first start, it will go through a quick update installation process, even though you might have just installed it. +If you're connected to your Studio, OpenPype will check for, and install updates automatically every time you run it. That's why during the first start, it will go through a quick update installation process, even though you might have just installed it. ## Advanced Usage From d0354f5deb37f76f82e366ecab54d4000aaafbbd Mon Sep 17 00:00:00 2001 From: Libor Batek Date: Wed, 25 Jan 2023 14:44:29 +0100 Subject: [PATCH 0201/1271] just added note --- website/docs/artist_hosts_3dsmax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/artist_hosts_3dsmax.md b/website/docs/artist_hosts_3dsmax.md index 0ca6f008cf..af90d93106 100644 --- a/website/docs/artist_hosts_3dsmax.md +++ b/website/docs/artist_hosts_3dsmax.md @@ -4,7 +4,7 @@ title: 3dsmax sidebar_label: 3dsmax --- -### *Still Work In Progress Page* +### *Still Work In Progress Docs Page* ## OpenPype Global Tools From 9c1e663cbcffc85f2a85769ca33c66a9eb71e18b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Jan 2023 14:56:47 +0000 Subject: [PATCH 0202/1271] Bump ua-parser-js from 0.7.31 to 0.7.33 in /website Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.31 to 0.7.33. - [Release notes](https://github.com/faisalman/ua-parser-js/releases) - [Changelog](https://github.com/faisalman/ua-parser-js/blob/master/changelog.md) - [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.31...0.7.33) --- updated-dependencies: - dependency-name: ua-parser-js 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 9af21c7500..ad80bf6915 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -7180,9 +7180,9 @@ typedarray-to-buffer@^3.1.5: is-typedarray "^1.0.0" ua-parser-js@^0.7.30: - version "0.7.31" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" - integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== + version "0.7.33" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" + integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== unherit@^1.0.4: version "1.1.3" From 14bc9affe6dfd0a5b6a9d289195c6c3ac4fc7d46 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 25 Jan 2023 17:46:39 +0100 Subject: [PATCH 0203/1271] Rewrite cache subsets for readability --- openpype/hosts/houdini/api/plugin.py | 45 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/houdini/api/plugin.py b/openpype/hosts/houdini/api/plugin.py index e15e27c83f..9841e1a7ac 100644 --- a/openpype/hosts/houdini/api/plugin.py +++ b/openpype/hosts/houdini/api/plugin.py @@ -114,30 +114,31 @@ class HoudiniCreatorBase(object): Dict[str, Any]: Shared data dictionary. """ - if shared_data.get("houdini_cached_subsets") is None: - shared_data["houdini_cached_subsets"] = {} - if shared_data.get("houdini_cached_legacy_subsets") is None: - shared_data["houdini_cached_legacy_subsets"] = {} - cached_instances = lsattr("id", "pyblish.avalon.instance") - for i in cached_instances: - if not i.parm("creator_identifier"): - # we have legacy instance - family = i.parm("family").eval() - if family not in shared_data[ - "houdini_cached_legacy_subsets"]: - shared_data["houdini_cached_legacy_subsets"][ - family] = [i] - else: - shared_data[ - "houdini_cached_legacy_subsets"][family].append(i) - continue + if shared_data.get("houdini_cached_subsets") is not None: + cache = dict() + cache_legacy = dict() + + for node in lsattr("id", "pyblish.avalon.instance"): + + creator_identifier_parm = node.parm("creator_identifier") + if creator_identifier_parm: + # creator instance + creator_id = creator_identifier_parm.eval() + cache.setdefault(creator_id, []).append(node) - creator_id = i.parm("creator_identifier").eval() - if creator_id not in shared_data["houdini_cached_subsets"]: - shared_data["houdini_cached_subsets"][creator_id] = [i] else: - shared_data[ - "houdini_cached_subsets"][creator_id].append(i) # noqa + # legacy instance + family_parm = node.parm("family") + if not family_parm: + # must be a broken instance + continue + + family = family_parm.eval() + cache_legacy.setdefault(family, []).append(node) + + shared_data["houdini_cached_subsets"] = dict(cache) + shared_data["houdini_cached_legacy_subsets"] = dict(cache_legacy) + return shared_data @staticmethod From 396ce2d3aa08a4dc69ed6c51accfce2e86e994a0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 25 Jan 2023 18:08:01 +0100 Subject: [PATCH 0204/1271] Fix docstring, the `houdini_cached_legacy_subsets` was actually always created --- openpype/hosts/houdini/api/plugin.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/houdini/api/plugin.py b/openpype/hosts/houdini/api/plugin.py index 9841e1a7ac..8bee1f3304 100644 --- a/openpype/hosts/houdini/api/plugin.py +++ b/openpype/hosts/houdini/api/plugin.py @@ -103,9 +103,8 @@ class HoudiniCreatorBase(object): fill it with all collected instances from the scene under its respective creator identifiers. - If legacy instances are detected in the scene, create - `houdini_cached_legacy_subsets` there and fill it with - all legacy subsets under family as a key. + Create `houdini_cached_legacy_subsets` key for any legacy instances + detected in the scene as instances per family. Args: Dict[str, Any]: Shared data. From f76897187fca873e5f5510187d604bb77ed295c2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 25 Jan 2023 18:10:01 +0100 Subject: [PATCH 0205/1271] Remove redundant conversion (we didn't use `defaultdict` anymore) --- openpype/hosts/houdini/api/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/api/plugin.py b/openpype/hosts/houdini/api/plugin.py index 8bee1f3304..4ca6b50702 100644 --- a/openpype/hosts/houdini/api/plugin.py +++ b/openpype/hosts/houdini/api/plugin.py @@ -135,8 +135,8 @@ class HoudiniCreatorBase(object): family = family_parm.eval() cache_legacy.setdefault(family, []).append(node) - shared_data["houdini_cached_subsets"] = dict(cache) - shared_data["houdini_cached_legacy_subsets"] = dict(cache_legacy) + shared_data["houdini_cached_subsets"] = cache + shared_data["houdini_cached_legacy_subsets"] = cache_legacy return shared_data From b5ccd03ebd9ece448b3308d827a4ff50db245198 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 25 Jan 2023 18:25:04 +0100 Subject: [PATCH 0206/1271] Fix - handle inputLinks and hero version Hero version doesn't store inputLinks, but with changes in comparing it should work. --- openpype/client/entity_links.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/client/entity_links.py b/openpype/client/entity_links.py index e42ac58aff..b74b4ce7f6 100644 --- a/openpype/client/entity_links.py +++ b/openpype/client/entity_links.py @@ -164,7 +164,6 @@ def get_linked_representation_id( # Recursive graph lookup for inputs {"$graphLookup": graph_lookup} ] - conn = get_project_connection(project_name) result = conn.aggregate(query_pipeline) referenced_version_ids = _process_referenced_pipeline_result( @@ -213,7 +212,7 @@ def _process_referenced_pipeline_result(result, link_type): for output in sorted(outputs_recursive, key=lambda o: o["depth"]): output_links = output.get("data", {}).get("inputLinks") - if not output_links: + if not output_links and output["type"] != "hero_version": continue # Leaf @@ -232,6 +231,9 @@ def _process_referenced_pipeline_result(result, link_type): def _filter_input_links(input_links, link_type, correctly_linked_ids): + if not input_links: # to handle hero versions + return + for input_link in input_links: if link_type and input_link["type"] != link_type: continue From 60c6a61779b132e90233a7b3ca83068a5e4a56f8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 25 Jan 2023 18:27:08 +0100 Subject: [PATCH 0207/1271] Fix - added docstring --- openpype/plugins/load/add_site.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/plugins/load/add_site.py b/openpype/plugins/load/add_site.py index 64567b746a..860e0ef15e 100644 --- a/openpype/plugins/load/add_site.py +++ b/openpype/plugins/load/add_site.py @@ -82,6 +82,9 @@ class AddSyncSite(load.LoaderPlugin): def _add_hero_representation_ids(self, project_name, repre_id): """Find hero version if exists for repre_id. + Args: + project_name (str) + repre_id (ObjectId) Returns: (list): at least [repre_id] if no hero version found """ From a6df39c77b26c12ef236bf36a601c032633680ae Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 01:55:11 +0100 Subject: [PATCH 0208/1271] fix 'update_context_data' in tray publisher --- openpype/hosts/traypublisher/api/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/traypublisher/api/pipeline.py b/openpype/hosts/traypublisher/api/pipeline.py index 0a8ddaa343..3264f52b0f 100644 --- a/openpype/hosts/traypublisher/api/pipeline.py +++ b/openpype/hosts/traypublisher/api/pipeline.py @@ -37,7 +37,7 @@ class TrayPublisherHost(HostBase, IPublishHost): return HostContext.get_context_data() def update_context_data(self, data, changes): - HostContext.save_context_data(data, changes) + HostContext.save_context_data(data) def set_project_name(self, project_name): # TODO Deregister project specific plugins and register new project From fa62e7954c0d061695651052651e55c6d1b59ef2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 01:56:18 +0100 Subject: [PATCH 0209/1271] removed unused methods --- openpype/pipeline/create/context.py | 78 ----------------------------- 1 file changed, 78 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9c468ae8fc..0d42c663d9 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -315,16 +315,6 @@ class AttributeValues(object): def changes(self): return self.calculate_changes(self._data, self._origin_data) - def apply_changes(self, changes): - for key, item in changes.items(): - old_value, new_value = item - if new_value is None: - if key in self: - self.pop(key) - - elif self.get(key) != new_value: - self[key] = new_value - class CreatorAttributeValues(AttributeValues): """Creator specific attribute values of an instance. @@ -458,21 +448,6 @@ class PublishAttributes: changes[key] = (value, None) return changes - def apply_changes(self, changes): - for key, item in changes.items(): - if isinstance(item, dict): - self._data[key].apply_changes(item) - continue - - old_value, new_value = item - if new_value is not None: - raise ValueError( - "Unexpected type \"{}\" expected None".format( - str(type(new_value)) - ) - ) - self.pop(key) - def set_publish_plugins(self, attr_plugins): """Set publish plugins attribute definitions.""" @@ -909,59 +884,6 @@ class CreatedInstance: return obj - def remote_changes(self): - """Prepare serializable changes on remote side. - - Returns: - Dict[str, Any]: Prepared changes that can be send to client side. - """ - - return { - "changes": self.changes(), - "asset_is_valid": self._asset_is_valid, - "task_is_valid": self._task_is_valid, - } - - def update_from_remote(self, remote_changes): - """Apply changes from remote side on client side. - - Args: - remote_changes (Dict[str, Any]): Changes created on remote side. - """ - - self._asset_is_valid = remote_changes["asset_is_valid"] - self._task_is_valid = remote_changes["task_is_valid"] - - changes = remote_changes["changes"] - creator_attributes = changes.pop("creator_attributes", None) or {} - publish_attributes = changes.pop("publish_attributes", None) or {} - if changes: - self.apply_changes(changes) - - if creator_attributes: - self.creator_attributes.apply_changes(creator_attributes) - - if publish_attributes: - self.publish_attributes.apply_changes(publish_attributes) - - def apply_changes(self, changes): - """Apply changes created via 'changes'. - - Args: - Dict[str, Tuple[Any, Any]]: Instance changes to apply. Same values - are kept untouched. - """ - - for key, item in changes.items(): - old_value, new_value = item - if new_value is None: - if key in self: - self.pop(key) - else: - current_value = self.get(key) - if current_value != new_value: - self[key] = new_value - class ConvertorItem(object): """Item representing convertor plugin. From 1496b74fc2668e8c075be1a771002b7b7c8c7fb5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 01:58:52 +0100 Subject: [PATCH 0210/1271] added helper object to handle changes --- openpype/pipeline/create/context.py | 93 +++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 0d42c663d9..94230bda09 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -177,6 +177,99 @@ def prepare_failed_creator_operation_info( } +class ChangedItem(object): + def __init__(self, old_value, new_value): + self._old_value = copy.deepcopy(old_value) + self._new_value = copy.deepcopy(new_value) + self._changed = self._old_value != self._new_value + + old_is_dict = isinstance(old_value, dict) + new_is_dict = isinstance(new_value, dict) + children = {} + changed_keys = set() + available_keys = set() + if old_is_dict and new_is_dict: + old_keys = set(old_value.keys()) + new_keys = set(new_value.keys()) + available_keys = old_keys | new_keys + for key in available_keys: + item = ChangedItem( + old_value.get(key), new_value.get(key) + ) + children[key] = item + if item.changed or key not in old_keys or key not in new_keys: + changed_keys.add(key) + + elif old_is_dict: + available_keys = set(old_value.keys()) + changed_keys = set(available_keys) + for key in available_keys: + children[key] = ChangedItem(old_value.get(key), None) + + elif new_is_dict: + available_keys = set(new_value.keys()) + changed_keys = set(available_keys) + for key in available_keys: + children[key] = ChangedItem(None, new_value.get(key)) + + self._changed_keys = changed_keys + self._available_keys = available_keys + self._children = children + self._old_is_dict = old_is_dict + self._new_is_dict = new_is_dict + + def __getitem__(self, key): + return self._children[key] + + def __bool__(self): + return self._changed + + def __iter__(self): + for key in self._changed_keys: + yield key + + def keys(self): + return set(self._changed_keys) + + @property + def changed(self): + return self._changed + + @property + def changes(self): + if not self._old_is_dict and not self._new_is_dict: + return (self.old_value, self.new_value) + + old_value = self.old_value + new_value = self.new_value + output = {} + for key in self.changed_keys: + _old = None + _new = None + if self._old_is_dict: + _old = old_value.get(key) + if self._new_is_dict: + _new = new_value.get(key) + output[key] = (_old, _new) + return output + + @property + def changed_keys(self): + return set(self._changed_keys) + + @property + def available_keys(self): + return set(self._available_keys) + + @property + def old_value(self): + return copy.deepcopy(self._old_value) + + @property + def new_value(self): + return copy.deepcopy(self._new_value) + + class InstanceMember: """Representation of instance member. From e46042c2d646333ee8723cd98aaecbe06881f878 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 01:59:35 +0100 Subject: [PATCH 0211/1271] use the 'ChangedItem' object to handle changes --- openpype/pipeline/create/context.py | 71 ++++++----------------------- 1 file changed, 15 insertions(+), 56 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 94230bda09..7db4849300 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -384,6 +384,10 @@ class AttributeValues(object): """Pointer to attribute definitions.""" return self._attr_defs + @property + def origin_data(self): + return copy.deepcopy(self._origin_data) + def data_to_store(self): """Create new dictionary with data to store.""" output = {} @@ -395,19 +399,6 @@ class AttributeValues(object): output[key] = attr_def.default return output - @staticmethod - def calculate_changes(new_data, old_data): - """Calculate changes of 2 dictionary objects.""" - changes = {} - for key, new_value in new_data.items(): - old_value = old_data.get(key) - if old_value != new_value: - changes[key] = (old_value, new_value) - return changes - - def changes(self): - return self.calculate_changes(self._data, self._origin_data) - class CreatorAttributeValues(AttributeValues): """Creator specific attribute values of an instance. @@ -525,21 +516,9 @@ class PublishAttributes: output[key] = attr_value.data_to_store() return output - def changes(self): - """Return changes per each key.""" - - changes = {} - for key, attr_val in self._data.items(): - attr_changes = attr_val.changes() - if attr_changes: - if key not in changes: - changes[key] = {} - changes[key].update(attr_val) - - for key, value in self._origin_data.items(): - if key not in self._data: - changes[key] = (value, None) - return changes + @property + def origin_data(self): + return copy.deepcopy(self._origin_data) def set_publish_plugins(self, attr_plugins): """Set publish plugins attribute definitions.""" @@ -746,6 +725,10 @@ class CreatedInstance: return label return self.creator.get_group_label() + @property + def origin_data(self): + return copy.deepcopy(self._orig_data) + @property def creator_identifier(self): return self.creator.identifier @@ -837,29 +820,7 @@ class CreatedInstance: def changes(self): """Calculate and return changes.""" - changes = {} - new_keys = set() - for key, new_value in self._data.items(): - new_keys.add(key) - if key in ("creator_attributes", "publish_attributes"): - continue - - old_value = self._orig_data.get(key) - if old_value != new_value: - changes[key] = (old_value, new_value) - - creator_attr_changes = self.creator_attributes.changes() - if creator_attr_changes: - changes["creator_attributes"] = creator_attr_changes - - publish_attr_changes = self.publish_attributes.changes() - if publish_attr_changes: - changes["publish_attributes"] = publish_attr_changes - - for key, old_value in self._orig_data.items(): - if key not in new_keys: - changes[key] = (old_value, None) - return changes + return ChangedItem(self.origin_data, self.data_to_store()) def mark_as_stored(self): """Should be called when instance data are stored. @@ -1390,11 +1351,9 @@ class CreateContext: def context_data_changes(self): """Changes of attributes.""" - changes = {} - publish_attribute_changes = self._publish_attributes.changes() - if publish_attribute_changes: - changes["publish_attributes"] = publish_attribute_changes - return changes + + old_value = copy.deepcopy(self._original_context_data) + return ChangedItem(old_value, self.context_data_to_store()) def creator_adds_instance(self, instance): """Creator adds new instance to context. From f9f077722c161048f7f3328ce1ab5a2aab12f7e1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 02:06:26 +0100 Subject: [PATCH 0212/1271] adde items method to extend dictionary approach --- openpype/pipeline/create/context.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 7db4849300..b6a6747f65 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -231,6 +231,14 @@ class ChangedItem(object): def keys(self): return set(self._changed_keys) + def items(self): + changes = self.changes + if isinstance(changes, tuple): + yield None, changes + else: + for item in changes.items(): + yield item + @property def changed(self): return self._changed From c88579591a7fb5f9d079b6ee656f5af2937266b7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 02:14:16 +0100 Subject: [PATCH 0213/1271] keep track of new/old keys --- openpype/pipeline/create/context.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index b6a6747f65..485252b4f8 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -188,6 +188,8 @@ class ChangedItem(object): children = {} changed_keys = set() available_keys = set() + new_keys = set() + old_keys = set() if old_is_dict and new_is_dict: old_keys = set(old_value.keys()) new_keys = set(new_value.keys()) @@ -201,13 +203,15 @@ class ChangedItem(object): changed_keys.add(key) elif old_is_dict: - available_keys = set(old_value.keys()) + old_keys = set(old_value.keys()) + available_keys = set(old_keys) changed_keys = set(available_keys) for key in available_keys: children[key] = ChangedItem(old_value.get(key), None) elif new_is_dict: - available_keys = set(new_value.keys()) + new_keys = set(new_value.keys()) + available_keys = set(new_keys) changed_keys = set(available_keys) for key in available_keys: children[key] = ChangedItem(None, new_value.get(key)) @@ -217,6 +221,8 @@ class ChangedItem(object): self._children = children self._old_is_dict = old_is_dict self._new_is_dict = new_is_dict + self._old_keys = old_keys + self._new_keys = new_keys def __getitem__(self, key): return self._children[key] @@ -225,11 +231,14 @@ class ChangedItem(object): return self._changed def __iter__(self): - for key in self._changed_keys: + for key in self.changed_keys: yield key + def get(self, key, default=None): + return self._children.get(key, default) + def keys(self): - return set(self._changed_keys) + return self.changed_keys def items(self): changes = self.changes @@ -269,6 +278,14 @@ class ChangedItem(object): def available_keys(self): return set(self._available_keys) + @property + def old_keys(self): + return set(self._old_keys) + + @property + def new_keys(self): + return set(self._new_keys) + @property def old_value(self): return copy.deepcopy(self._old_value) From 7c1a39d5e4f17d3abf94268d9da6dcf8d5eeea9c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 26 Jan 2023 02:17:13 +0100 Subject: [PATCH 0214/1271] added 'is_dict' property --- openpype/pipeline/create/context.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 485252b4f8..e44e5db851 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -241,20 +241,23 @@ class ChangedItem(object): return self.changed_keys def items(self): - changes = self.changes - if isinstance(changes, tuple): - yield None, changes + if self.is_dict: + yield None, self.changes else: - for item in changes.items(): + for item in self.changes.items(): yield item @property def changed(self): return self._changed + @property + def is_dict(self): + return self._old_is_dict or self._new_is_dict + @property def changes(self): - if not self._old_is_dict and not self._new_is_dict: + if not self.is_dict: return (self.old_value, self.new_value) old_value = self.old_value From 7d7442590793dddc22e30e9b8661f68cc09fb2a2 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 26 Jan 2023 14:48:06 +0800 Subject: [PATCH 0215/1271] add extractors and validators for cameras --- .../hosts/max/plugins/create/create_camera.py | 26 +++++++ .../max/plugins/publish/extract_camera_abc.py | 69 +++++++++++++++++++ .../max/plugins/publish/extract_camera_fbx.py | 68 ++++++++++++++++++ .../plugins/publish/extract_max_scene_raw.py | 68 ++++++++++++++++++ .../max/plugins/publish/extract_pointcache.py | 2 +- .../publish/validate_camera_contents.py | 57 +++++++++++++++ 6 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/max/plugins/create/create_camera.py create mode 100644 openpype/hosts/max/plugins/publish/extract_camera_abc.py create mode 100644 openpype/hosts/max/plugins/publish/extract_camera_fbx.py create mode 100644 openpype/hosts/max/plugins/publish/extract_max_scene_raw.py create mode 100644 openpype/hosts/max/plugins/publish/validate_camera_contents.py diff --git a/openpype/hosts/max/plugins/create/create_camera.py b/openpype/hosts/max/plugins/create/create_camera.py new file mode 100644 index 0000000000..45f437b7ee --- /dev/null +++ b/openpype/hosts/max/plugins/create/create_camera.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +"""Creator plugin for creating camera.""" +from openpype.hosts.max.api import plugin +from openpype.pipeline import CreatedInstance + + +class CreateCamera(plugin.MaxCreator): + identifier = "io.openpype.creators.max.camera" + label = "Camera" + family = "camera" + icon = "gear" + + def create(self, subset_name, instance_data, pre_create_data): + from pymxs import runtime as rt + sel_obj = list(rt.selection) + _ = super(CreateCamera, self).create( + subset_name, + instance_data, + pre_create_data) # type: CreatedInstance + container = rt.getNodeByName(subset_name) + # TODO: Disable "Add to Containers?" Panel + # parent the selected cameras into the container + for obj in sel_obj: + obj.parent = container + # for additional work on the node: + # instance_node = rt.getNodeByName(instance.get("instance_node")) diff --git a/openpype/hosts/max/plugins/publish/extract_camera_abc.py b/openpype/hosts/max/plugins/publish/extract_camera_abc.py new file mode 100644 index 0000000000..83cf2c3a6e --- /dev/null +++ b/openpype/hosts/max/plugins/publish/extract_camera_abc.py @@ -0,0 +1,69 @@ +import os +import pyblish.api +from openpype.pipeline import publish +from pymxs import runtime as rt +from openpype.hosts.max.api import ( + maintained_selection, + get_all_children +) + + +class ExtractAlembicCamera(publish.Extractor): + """ + Extract Camera with AlembicExport + """ + + order = pyblish.api.ExtractorOrder - 0.1 + label = "Extract Almebic Camera" + hosts = ["max"] + families = ["camera"] + optional = True + + def process(self, instance): + start = float(instance.data.get("frameStartHandle", 1)) + end = float(instance.data.get("frameEndHandle", 1)) + + container = instance.data["instance_node"] + + self.log.info("Extracting Camera ...") + + stagingdir = self.staging_dir(instance) + filename = "{name}.abc".format(**instance.data) + path = os.path.join(stagingdir, filename) + + # We run the render + self.log.info("Writing alembic '%s' to '%s'" % (filename, + stagingdir)) + + export_cmd = ( + f""" +AlembicExport.ArchiveType = #ogawa +AlembicExport.CoordinateSystem = #maya +AlembicExport.StartFrame = {start} +AlembicExport.EndFrame = {end} +AlembicExport.CustomAttributes = true + +exportFile @"{path}" #noPrompt selectedOnly:on using:AlembicExport + + """) + + self.log.debug(f"Executing command: {export_cmd}") + + with maintained_selection(): + # select and export + rt.select(get_all_children(rt.getNodeByName(container))) + rt.execute(export_cmd) + + self.log.info("Performing Extraction ...") + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'abc', + 'ext': 'abc', + 'files': filename, + "stagingDir": stagingdir, + } + instance.data["representations"].append(representation) + self.log.info("Extracted instance '%s' to: %s" % (instance.name, + path)) diff --git a/openpype/hosts/max/plugins/publish/extract_camera_fbx.py b/openpype/hosts/max/plugins/publish/extract_camera_fbx.py new file mode 100644 index 0000000000..5a68edfbe8 --- /dev/null +++ b/openpype/hosts/max/plugins/publish/extract_camera_fbx.py @@ -0,0 +1,68 @@ +import os +import pyblish.api +from openpype.pipeline import publish +from pymxs import runtime as rt +from openpype.hosts.max.api import ( + maintained_selection, + get_all_children +) + + +class ExtractCameraFbx(publish.Extractor): + """ + Extract Camera with FbxExporter + """ + + order = pyblish.api.ExtractorOrder - 0.2 + label = "Extract Fbx Camera" + hosts = ["max"] + families = ["camera"] + + def process(self, instance): + container = instance.data["instance_node"] + + self.log.info("Extracting Camera ...") + stagingdir = self.staging_dir(instance) + filename = "{name}.fbx".format(**instance.data) + + filepath = os.path.join(stagingdir, filename) + self.log.info("Writing fbx file '%s' to '%s'" % (filename, + filepath)) + + # Need to export: + # Animation = True + # Cameras = True + # AxisConversionMethod + fbx_export_cmd = ( + f""" + +FBXExporterSetParam "Animation" true +FBXExporterSetParam "Cameras" true +FBXExporterSetParam "AxisConversionMethod" "Animation" +FbxExporterSetParam "UpAxis" "Y" +FbxExporterSetParam "Preserveinstances" true + +exportFile @"{filepath}" #noPrompt selectedOnly:true using:FBXEXP + + """) + + self.log.debug(f"Executing command: {fbx_export_cmd}") + + with maintained_selection(): + # select and export + rt.select(get_all_children(rt.getNodeByName(container))) + rt.execute(fbx_export_cmd) + + self.log.info("Performing Extraction ...") + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'fbx', + 'ext': 'fbx', + 'files': filename, + "stagingDir": stagingdir, + } + instance.data["representations"].append(representation) + self.log.info("Extracted instance '%s' to: %s" % (instance.name, + filepath)) \ No newline at end of file diff --git a/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py b/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py new file mode 100644 index 0000000000..4524609a02 --- /dev/null +++ b/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py @@ -0,0 +1,68 @@ +import os +import pyblish.api +from openpype.pipeline import publish +from pymxs import runtime as rt +from openpype.hosts.max.api import ( + maintained_selection, + get_all_children +) + + +class ExtractMaxSceneRaw(publish.Extractor): + """ + Extract Raw Max Scene with SaveSelected + """ + + order = pyblish.api.ExtractorOrder - 0.2 + label = "Max Scene(Raw)" + hosts = ["max"] + families = ["camera"] + + def process(self, instance): + container = instance.data["instance_node"] + + # publish the raw scene for camera + self.log.info("Extracting Camera ...") + + stagingdir = self.staging_dir(instance) + filename = "{name}.max".format(**instance.data) + + max_path = os.path.join(stagingdir, filename) + self.log.info("Writing max file '%s' to '%s'" % (filename, + max_path)) + + if "representations" not in instance.data: + instance.data["representations"] = [] + + #add extra blacklash for saveNodes in MaxScript + re_max_path = stagingdir + "\\\\" + filename + # saving max scene + raw_export_cmd = ( + f""" +sel = getCurrentSelection() +for s in sel do +( + select s + f="{re_max_path}" + print f + saveNodes selection f quiet:true +) + """) + + self.log.debug(f"Executing Maxscript command: {raw_export_cmd}") + + with maintained_selection(): + # need to figure out how to select the camera + rt.select(get_all_children(rt.getNodeByName(container))) + rt.execute(raw_export_cmd) + + self.log.info("Performing Extraction ...") + representation = { + 'name': 'max', + 'ext': 'max', + 'files': filename, + "stagingDir": stagingdir, + } + instance.data["representations"].append(representation) + self.log.info("Extracted instance '%s' to: %s" % (instance.name, + max_path)) \ No newline at end of file diff --git a/openpype/hosts/max/plugins/publish/extract_pointcache.py b/openpype/hosts/max/plugins/publish/extract_pointcache.py index 904c1656da..75d8a7972c 100644 --- a/openpype/hosts/max/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/max/plugins/publish/extract_pointcache.py @@ -51,7 +51,7 @@ class ExtractAlembic(publish.Extractor): order = pyblish.api.ExtractorOrder label = "Extract Pointcache" hosts = ["max"] - families = ["pointcache", "camera"] + families = ["pointcache"] def process(self, instance): start = float(instance.data.get("frameStartHandle", 1)) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_contents.py b/openpype/hosts/max/plugins/publish/validate_camera_contents.py new file mode 100644 index 0000000000..3990103e61 --- /dev/null +++ b/openpype/hosts/max/plugins/publish/validate_camera_contents.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +import pyblish.api +from openpype.pipeline import PublishValidationError +from pymxs import runtime as rt +from openpype.hosts.max.api import get_all_children + + +class ValidateCameraContent(pyblish.api.InstancePlugin): + """Validates Camera instance contents. + + A Camera instance may only hold a SINGLE camera's transform + """ + + order = pyblish.api.ValidatorOrder + families = ["camera"] + hosts = ["max"] + label = "Camera Contents" + camera_type = ["$Free_Camera", "$Target_Camera", + "$Physical_Camera", "$Target"] + + def process(self, instance): + invalid = self.get_invalid(instance) + if invalid: + raise PublishValidationError("Camera instance must only include" + "camera (and camera target)") + + + def get_invalid(self, instance): + """ + Get invalid nodes if the instance is not camera + """ + invalid = list() + container = instance.data["instance_node"] + self.log.info("Validating look content for " + "'{}'".format(container)) + + con = rt.getNodeByName(container) + selection_list = self.list_children(con) + validation_msg = list() + for sel in selection_list: + # to avoid Attribute Error from pymxs wrapper + sel_tmp = str(sel) + for cam in self.camera_type: + if sel_tmp.startswith(cam): + validation_msg.append("Camera Found") + else: + validation_msg.append("Camera Not Found") + if "Camera Found" not in validation_msg: + invalid.append(sel) + # go through the camera type to see if there are same name + return invalid + + def list_children(self, node): + children = [] + for c in node.Children: + children.append(c) + return children From c4fe43a1f71adddb99e39e4c9d0c6f9874adc8fe Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Thu, 26 Jan 2023 15:07:40 +0800 Subject: [PATCH 0216/1271] Delete convert_gltf_shader.py --- .../plugins/publish/convert_gltf_shader.py | 89 ------------------- 1 file changed, 89 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/publish/convert_gltf_shader.py diff --git a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py b/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py deleted file mode 100644 index 3b8ad9d672..0000000000 --- a/openpype/hosts/maya/plugins/publish/convert_gltf_shader.py +++ /dev/null @@ -1,89 +0,0 @@ -import os -from maya import cmds -import pyblish.api - -from openpype.pipeline import publish - - -class ConvertGLSLShader(publish.Extractor): - """ - Converting StingrayPBS material to GLSL Shaders - specially for the glb export through Maya2GLTF plugin - - """ - order = pyblish.api.ExtractorOrder - 0.1 - hosts = ["maya"] - label = "Convert StingrayPBS to GLTF" - families = ["gltf"] - optional = True - - def process(self, instance): - meshes = cmds.ls(instance, type="mesh", long=True) - self.log.info("meshes: {}".format(meshes)) - # load the glsl shader plugin - cmds.loadPlugin("glslShader", quiet=True) - - for mesh in meshes: - - # create glsl shader - glsl = cmds.createNode('GLSLShader') - glsl_shadingGrp = cmds.sets(name=glsl + "SG", empty=True, - renderable=True, noSurfaceShader=True) - cmds.connectAttr(glsl + ".outColor", - glsl_shadingGrp + ".surfaceShader") - - # load the maya2gltf shader - maya_publish = ( - instance.context.data["project_settings"]["maya"]["publish"] - ) - ogsfx_path = maya_publish["ConvertGLSLShader"]["ogsfx_path"] - if not ogsfx_path: - maya_dir = os.getenv("MAYA_APP_DIR") - if not maya_dir: - raise RuntimeError("MAYA_APP_DIR not found") - ogsfx_path = maya_dir + "/maya2glTF/PBR/shaders/" - if not os.path.exists(ogsfx_path): - raise RuntimeError("the ogsfx file not found") - - ogsfx = ogsfx_path + "glTF_PBR.ogsfx" - cmds.setAttr(glsl + ".shader", ogsfx, typ="string") - - # list the materials used for the assets - shading_grp = cmds.listConnections(mesh, - destination=True, - type="shadingEngine") - - # get the materials related to the selected assets - for material in shading_grp: - main_shader = cmds.listConnections(material, - destination=True, - type="StingrayPBS") - for shader in main_shader: - # get the file textures related to the PBS Shader - albedo = cmds.listConnections(shader + ".TEX_color_map")[0] - dif_output = albedo + ".outColor" - - orm_packed = cmds.listConnections(shader + - ".TEX_ao_map")[0] - orm_output = orm_packed + ".outColor" - - nrm = cmds.listConnections(shader + ".TEX_normal_map")[0] - nrm_output = nrm + ".outColor" - - # get the glsl_shader input - # reconnect the file nodes to maya2gltf shader - glsl_dif = glsl + ".u_BaseColorTexture" - glsl_nrm = glsl + ".u_NormalTexture" - cmds.connectAttr(dif_output, glsl_dif) - cmds.connectAttr(nrm_output, glsl_nrm) - - mtl = glsl + ".u_MetallicTexture" - ao = glsl + ".u_OcclusionTexture" - rough = glsl + ".u_RoughnessTexture" - - cmds.connectAttr(orm_output, mtl) - cmds.connectAttr(orm_output, ao) - cmds.connectAttr(orm_output, rough) - - # assign the shader to the asset - cmds.sets(mesh, forceElement=str(glsl_shadingGrp)) From d370f8ac14a1918cdf24bc74cddcbeade437d913 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Thu, 26 Jan 2023 15:08:23 +0800 Subject: [PATCH 0217/1271] Delete validate_gltf_textures_names.py --- .../publish/validate_gltf_textures_names.py | 130 ------------------ 1 file changed, 130 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py diff --git a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py b/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py deleted file mode 100644 index 5c1f5d70fb..0000000000 --- a/openpype/hosts/maya/plugins/publish/validate_gltf_textures_names.py +++ /dev/null @@ -1,130 +0,0 @@ -from maya import cmds - -import pyblish.api -import openpype.hosts.maya.api.action -from openpype.pipeline.publish import ValidateContentsOrder - - -class ValidateGLTFTexturesNames(pyblish.api.InstancePlugin): - """ - Validate if the asset uses StingrayPBS material before conversion - of the GLSL Shader - Validate if the names of GLTF Textures follow - the packed ORM/ORMS standard. - - The texture naming conventions follows the UE5-style-guides: - https://github.com/Allar/ue5-style-guide#anc-textures-packing - - ORM: Occlusion Roughness Metallic - ORMS: Occlusion Roughness Metallic Specular - - Texture Naming Style: - - Albedo/Diffuse: {Name}_D.{imageExtension} or - {Name}_D..{imageExtension} - - Normal: {Name}_N.{imageExtension} or - {Name}_N..{imageExtension} - ORM: {Name}_ORM.{imageExtension} or - {Name}_ORM..{imageExtension} - - """ - - order = ValidateContentsOrder - families = ['gltf'] - hosts = ['maya'] - label = 'GLTF Textures Name' - - def process(self, instance): - """Process all the nodes in the instance""" - pbs_shader = cmds.ls(type="StingrayPBS") - if not pbs_shader: - raise RuntimeError("No PBS Shader in the scene") - invalid = self.get_texture_shader_invalid(instance) - if invalid: - raise RuntimeError("Non PBS material found" - "{0}".format(invalid)) - invalid = self.get_texture_node_invalid(instance) - if invalid: - raise RuntimeError("At least a Albedo texture file" - "nodes need to be connected") - invalid = self.get_texture_name_invalid(instance) - if invalid: - raise RuntimeError("Invalid texture name(s) found: " - "{0}".format(invalid)) - - def get_texture_name_invalid(self, instance): - - invalid = set() - shading_grp = self.get_material_from_shapes(instance) - - # get the materials related to the selected assets - # get the file textures related to the PBS Shader - # validate the names of the textures - for material in shading_grp: - main_shader = cmds.listConnections(material, - destination=True, - type="StingrayPBS") - for shader in main_shader: - albedo = cmds.listConnections(shader + ".TEX_color_map")[0] - dif_path = cmds.getAttr(albedo + ".fileTextureName") - dif = dif_path.split(".")[0] - # "_D" - if not dif.endswith("_D"): - invalid.add(dif_path) - orm_packed = cmds.listConnections(shader + ".TEX_ao_map")[0] - if orm_packed: - # "_ORM" - orm_path = cmds.getAttr(orm_packed + ".fileTextureName") - orm = orm_path.split(".")[0] - if not orm.endswith("_ORM"): - invalid.add(orm_path) - nrm = cmds.listConnections(shader + ".TEX_normal_map")[0] - if nrm: - nrm_path = cmds.getAttr(nrm + ".fileTextureName") - nrm_map = nrm_path.split(".")[0] - # "_N" - if not nrm_map.endswith("_N"): - invalid.add(nrm_path) - - return list(invalid) - - def get_texture_node_invalid(self, instance): - invalid = set() - shading_grp = self.get_material_from_shapes(instance) - for material in shading_grp: - main_shader = cmds.listConnections(material, - destination=True, - type="StingrayPBS") - for shader in main_shader: - # diffuse texture file node - albedo = cmds.listConnections(shader + ".TEX_color_map") - if not albedo: - invalid.add(albedo) - return list(invalid) - - def get_texture_shader_invalid(self, instance): - - invalid = set() - shading_grp = self.get_material_from_shapes(instance) - for shading_group in shading_grp: - material_name = "{}.surfaceShader".format(shading_group) - material = cmds.listConnections(material_name, - source=True, - destination=False, - type="StingrayPBS") - - if not material: - # add material name - material = cmds.listConnections(material_name)[0] - invalid.add(material) - return list(invalid) - - def get_material_from_shapes(self, instance): - shapes = cmds.ls(instance, type="mesh", long=True) - for shape in shapes: - shading_grp = cmds.listConnections(shape, - destination=True, - type="shadingEngine") - - return shading_grp From 32b557efcb1bbd9cd184af6e71d7b5a86bdbe447 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Thu, 26 Jan 2023 15:10:22 +0800 Subject: [PATCH 0218/1271] Delete maya.json --- .../defaults/project_settings/maya.json | 1054 ----------------- 1 file changed, 1054 deletions(-) delete mode 100644 openpype/settings/defaults/project_settings/maya.json diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json deleted file mode 100644 index c89b41a3e4..0000000000 --- a/openpype/settings/defaults/project_settings/maya.json +++ /dev/null @@ -1,1054 +0,0 @@ -{ - "imageio": { - "colorManagementPreference_v2": { - "enabled": true, - "configFilePath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "renderSpace": "ACEScg", - "displayName": "sRGB", - "viewName": "ACES 1.0 SDR-video" - }, - "colorManagementPreference": { - "configFilePath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "renderSpace": "scene-linear Rec 709/sRGB", - "viewTransform": "sRGB gamma" - } - }, - "mel_workspace": "workspace -fr \"shaders\" \"renderData/shaders\";\nworkspace -fr \"images\" \"renders/maya\";\nworkspace -fr \"particles\" \"particles\";\nworkspace -fr \"mayaAscii\" \"\";\nworkspace -fr \"mayaBinary\" \"\";\nworkspace -fr \"scene\" \"\";\nworkspace -fr \"alembicCache\" \"cache/alembic\";\nworkspace -fr \"renderData\" \"renderData\";\nworkspace -fr \"sourceImages\" \"sourceimages\";\nworkspace -fr \"fileCache\" \"cache/nCache\";\n", - "ext_mapping": { - "model": "ma", - "mayaAscii": "ma", - "camera": "ma", - "rig": "ma", - "workfile": "ma", - "yetiRig": "ma" - }, - "maya-dirmap": { - "use_env_var_as_root": false, - "enabled": false, - "paths": { - "source-path": [], - "destination-path": [] - } - }, - "scriptsmenu": { - "name": "OpenPype Tools", - "definition": [ - { - "type": "action", - "command": "import openpype.hosts.maya.api.commands as op_cmds; op_cmds.edit_shader_definitions()", - "sourcetype": "python", - "title": "Edit shader name definitions", - "tooltip": "Edit shader name definitions used in validation and renaming.", - "tags": [ - "pipeline", - "shader" - ] - } - ] - }, - "RenderSettings": { - "apply_render_settings": true, - "default_render_image_folder": "renders/maya", - "enable_all_lights": true, - "aov_separator": "underscore", - "remove_aovs": false, - "reset_current_frame": false, - "arnold_renderer": { - "image_prefix": "//_", - "image_format": "exr", - "multilayer_exr": true, - "tiled": true, - "aov_list": [], - "additional_options": [] - }, - "vray_renderer": { - "image_prefix": "//", - "engine": "1", - "image_format": "exr", - "aov_list": [], - "additional_options": [] - }, - "redshift_renderer": { - "image_prefix": "//", - "primary_gi_engine": "0", - "secondary_gi_engine": "0", - "image_format": "exr", - "multilayer_exr": true, - "force_combine": true, - "aov_list": [], - "additional_options": [] - } - }, - "create": { - "CreateLook": { - "enabled": true, - "make_tx": true, - "defaults": [ - "Main" - ] - }, - "CreateRender": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateUnrealStaticMesh": { - "enabled": true, - "defaults": [ - "", - "_Main" - ], - "static_mesh_prefix": "S", - "collision_prefixes": [ - "UBX", - "UCP", - "USP", - "UCX" - ] - }, - "CreateUnrealSkeletalMesh": { - "enabled": true, - "defaults": [], - "joint_hints": "jnt_org" - }, - "CreateMultiverseLook": { - "enabled": true, - "publish_mip_map": true - }, - "CreateAnimation": { - "enabled": true, - "write_color_sets": false, - "write_face_sets": false, - "defaults": [ - "Main" - ] - }, - "CreateModel": { - "enabled": true, - "write_color_sets": false, - "write_face_sets": false, - "defaults": [ - "Main", - "Proxy", - "Sculpt" - ] - }, - "CreatePointCache": { - "enabled": true, - "write_color_sets": false, - "write_face_sets": false, - "defaults": [ - "Main" - ] - }, - "CreateProxyAlembic": { - "enabled": true, - "write_color_sets": false, - "write_face_sets": false, - "defaults": [ - "Main" - ] - }, - "CreateMultiverseUsd": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateMultiverseUsdComp": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateMultiverseUsdOver": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateAss": { - "enabled": true, - "defaults": [ - "Main" - ], - "expandProcedurals": false, - "motionBlur": true, - "motionBlurKeys": 2, - "motionBlurLength": 0.5, - "maskOptions": false, - "maskCamera": false, - "maskLight": false, - "maskShape": false, - "maskShader": false, - "maskOverride": false, - "maskDriver": false, - "maskFilter": false, - "maskColor_manager": false, - "maskOperator": false - }, - "CreateAssembly": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateCamera": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateLayout": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateMayaScene": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateRenderSetup": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateReview": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateRig": { - "enabled": true, - "defaults": [ - "Main", - "Sim", - "Cloth" - ] - }, - "CreateSetDress": { - "enabled": true, - "defaults": [ - "Main", - "Anim" - ] - }, - "CreateVrayProxy": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateVRayScene": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateYetiRig": { - "enabled": true, - "defaults": [ - "Main" - ] - } - }, - "publish": { - "CollectMayaRender": { - "sync_workfile_version": false - }, - "CollectFbxCamera": { - "enabled": false - }, - "CollectGLTF": { - "enabled": false - }, - "ValidateInstanceInContext": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateContainers": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateFrameRange": { - "enabled": true, - "optional": true, - "active": true, - "exclude_families": [ - "model", - "rig", - "staticMesh" - ] - }, - "ValidateShaderName": { - "enabled": false, - "optional": true, - "regex": "(?P.*)_(.*)_SHD" - }, - "ValidateShadingEngine": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateAttributes": { - "enabled": false, - "attributes": {} - }, - "ValidateLoadedPlugin": { - "enabled": false, - "optional": true, - "whitelist_native_plugins": false, - "authorized_plugins": [] - }, - "ValidateMayaUnits": { - "enabled": true, - "optional": false, - "validate_linear_units": true, - "linear_units": "cm", - "validate_angular_units": true, - "angular_units": "deg", - "validate_fps": true - }, - "ValidateUnrealStaticMeshName": { - "enabled": true, - "optional": true, - "validate_mesh": false, - "validate_collision": true - }, - "ValidateCycleError": { - "enabled": true, - "optional": false, - "families": [ - "rig" - ] - }, - "ValidateRenderSettings": { - "arnold_render_attributes": [], - "vray_render_attributes": [], - "redshift_render_attributes": [], - "renderman_render_attributes": [] - }, - "ValidateCurrentRenderLayerIsRenderable": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateGLTFTexturesNames": { - "enabled": false, - "optional": false, - "active": true - }, - "ValidateRenderImageRule": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateRenderNoDefaultCameras": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateRenderSingleCamera": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateRenderLayerAOVs": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateStepSize": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateVRayDistributedRendering": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateVrayReferencedAOVs": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateVRayTranslatorEnabled": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateVrayProxy": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateVrayProxyMembers": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateYetiRenderScriptCallbacks": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateYetiRigCacheState": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateYetiRigInputShapesInInstance": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateYetiRigSettings": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateModelName": { - "enabled": false, - "database": true, - "material_file": { - "windows": "", - "darwin": "", - "linux": "" - }, - "regex": "(.*)_(\\d)*_(?P.*)_(GEO)", - "top_level_regex": ".*_GRP" - }, - "ValidateModelContent": { - "enabled": true, - "optional": false, - "validate_top_group": true - }, - "ValidateTransformNamingSuffix": { - "enabled": true, - "optional": true, - "SUFFIX_NAMING_TABLE": { - "mesh": [ - "_GEO", - "_GES", - "_GEP", - "_OSD" - ], - "nurbsCurve": [ - "_CRV" - ], - "nurbsSurface": [ - "_NRB" - ], - "locator": [ - "_LOC" - ], - "group": [ - "_GRP" - ] - }, - "ALLOW_IF_NOT_IN_SUFFIX_TABLE": true - }, - "ValidateColorSets": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMeshHasOverlappingUVs": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshArnoldAttributes": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshShaderConnections": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMeshSingleUVSet": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshHasUVs": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMeshLaminaFaces": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshNgons": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshNonManifold": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshNoNegativeScale": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateMeshNonZeroEdgeLength": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateMeshNormalsUnlocked": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshUVSetMap1": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateMeshVerticesHaveEdges": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateNoAnimation": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateNoNamespace": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateNoNullTransforms": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateNoUnknownNodes": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateNodeNoGhosting": { - "enabled": false, - "optional": false, - "active": true - }, - "ValidateShapeDefaultNames": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateShapeRenderStats": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateShapeZero": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateTransformZero": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateUniqueNames": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateNoVRayMesh": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateUnrealMeshTriangulated": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateAlembicVisibleOnly": { - "enabled": true, - "optional": false, - "active": true - }, - "ExtractProxyAlembic": { - "enabled": true, - "families": [ - "proxyAbc" - ] - }, - "ExtractAlembic": { - "enabled": true, - "families": [ - "pointcache", - "model", - "vrayproxy" - ] - }, - "ExtractObj": { - "enabled": false, - "optional": true - }, - "ValidateRigContents": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateRigJointsHidden": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateRigControllers": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateAnimationContent": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateOutRelatedNodeIds": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateRigControllersArnoldAttributes": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateSkeletalMeshHierarchy": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateSkinclusterDeformerSet": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateRigOutSetNodeIds": { - "enabled": true, - "optional": false, - "allow_history_only": false - }, - "ValidateCameraAttributes": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateAssemblyName": { - "enabled": true, - "optional": true, - "active": true - }, - "ValidateAssemblyNamespaces": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateAssemblyModelTransforms": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateAssRelativePaths": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateInstancerContent": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateInstancerFrameRanges": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateNoDefaultCameras": { - "enabled": true, - "optional": false, - "active": true - }, - "ValidateUnrealUpAxis": { - "enabled": false, - "optional": true, - "active": true - }, - "ValidateCameraContents": { - "enabled": true, - "optional": false, - "validate_shapes": true - }, - "ExtractPlayblast": { - "capture_preset": { - "Codec": { - "compression": "jpg", - "format": "image", - "quality": 95 - }, - "Display Options": { - "background": [ - 125, - 125, - 125, - 255 - ], - "backgroundBottom": [ - 125, - 125, - 125, - 255 - ], - "backgroundTop": [ - 125, - 125, - 125, - 255 - ], - "override_display": true - }, - "Generic": { - "isolate_view": true, - "off_screen": true - }, - "Renderer": { - "rendererName": "vp2Renderer" - }, - "Resolution": { - "width": 1920, - "height": 1080 - }, - "Viewport Options": { - "override_viewport_options": true, - "displayLights": "default", - "displayTextures": true, - "textureMaxResolution": 1024, - "renderDepthOfField": true, - "shadows": true, - "twoSidedLighting": true, - "lineAAEnable": true, - "multiSample": 8, - "ssaoEnable": false, - "ssaoAmount": 1, - "ssaoRadius": 16, - "ssaoFilterRadius": 16, - "ssaoSamples": 16, - "fogging": false, - "hwFogFalloff": "0", - "hwFogDensity": 0.0, - "hwFogStart": 0, - "hwFogEnd": 100, - "hwFogAlpha": 0, - "hwFogColorR": 1.0, - "hwFogColorG": 1.0, - "hwFogColorB": 1.0, - "motionBlurEnable": false, - "motionBlurSampleCount": 8, - "motionBlurShutterOpenFraction": 0.2, - "cameras": false, - "clipGhosts": false, - "deformers": false, - "dimensions": false, - "dynamicConstraints": false, - "dynamics": false, - "fluids": false, - "follicles": false, - "gpuCacheDisplayFilter": false, - "greasePencils": false, - "grid": false, - "hairSystems": true, - "handles": false, - "headsUpDisplay": false, - "ikHandles": false, - "imagePlane": true, - "joints": false, - "lights": false, - "locators": false, - "manipulators": false, - "motionTrails": false, - "nCloths": false, - "nParticles": false, - "nRigids": false, - "controlVertices": false, - "nurbsCurves": false, - "hulls": false, - "nurbsSurfaces": false, - "particleInstancers": false, - "pivots": false, - "planes": false, - "pluginShapes": false, - "polymeshes": true, - "strokes": false, - "subdivSurfaces": false, - "textures": false - }, - "Camera Options": { - "displayGateMask": false, - "displayResolution": false, - "displayFilmGate": false, - "displayFieldChart": false, - "displaySafeAction": false, - "displaySafeTitle": false, - "displayFilmPivot": false, - "displayFilmOrigin": false, - "overscan": 1.0 - } - } - }, - "ConvertGLSLShader": { - "enabled": false, - "optional": true, - "active": true, - "ogsfx_path": "" - }, - "ExtractMayaSceneRaw": { - "enabled": true, - "add_for_families": [ - "layout" - ] - }, - "ExtractCameraAlembic": { - "enabled": true, - "optional": true, - "active": true, - "bake_attributes": [] - } - }, - "load": { - "colors": { - "model": [ - 209, - 132, - 30, - 255 - ], - "rig": [ - 59, - 226, - 235, - 255 - ], - "pointcache": [ - 94, - 209, - 30, - 255 - ], - "animation": [ - 94, - 209, - 30, - 255 - ], - "ass": [ - 249, - 135, - 53, - 255 - ], - "camera": [ - 136, - 114, - 244, - 255 - ], - "fbx": [ - 215, - 166, - 255, - 255 - ], - "mayaAscii": [ - 67, - 174, - 255, - 255 - ], - "mayaScene": [ - 67, - 174, - 255, - 255 - ], - "setdress": [ - 255, - 250, - 90, - 255 - ], - "layout": [ - 255, - 250, - 90, - 255 - ], - "vdbcache": [ - 249, - 54, - 0, - 255 - ], - "vrayproxy": [ - 255, - 150, - 12, - 255 - ], - "vrayscene_layer": [ - 255, - 150, - 12, - 255 - ], - "yeticache": [ - 99, - 206, - 220, - 255 - ], - "yetiRig": [ - 0, - 205, - 125, - 255 - ] - } - }, - "workfile_build": { - "profiles": [ - { - "task_types": [], - "tasks": [ - "Lighting" - ], - "current_context": [ - { - "subset_name_filters": [ - ".+[Mm]ain" - ], - "families": [ - "model" - ], - "repre_names": [ - "abc", - "ma" - ], - "loaders": [ - "ReferenceLoader" - ] - }, - { - "subset_name_filters": [], - "families": [ - "animation", - "pointcache", - "proxyAbc" - ], - "repre_names": [ - "abc" - ], - "loaders": [ - "ReferenceLoader" - ] - }, - { - "subset_name_filters": [], - "families": [ - "rendersetup" - ], - "repre_names": [ - "json" - ], - "loaders": [ - "RenderSetupLoader" - ] - }, - { - "subset_name_filters": [], - "families": [ - "camera" - ], - "repre_names": [ - "abc" - ], - "loaders": [ - "ReferenceLoader" - ] - } - ], - "linked_assets": [ - { - "subset_name_filters": [], - "families": [ - "sedress" - ], - "repre_names": [ - "ma" - ], - "loaders": [ - "ReferenceLoader" - ] - }, - { - "subset_name_filters": [], - "families": [ - "ArnoldStandin" - ], - "repre_names": [ - "ass" - ], - "loaders": [ - "assLoader" - ] - } - ] - } - ] - }, - "templated_workfile_build": { - "profiles": [] - }, - "filters": { - "preset 1": { - "ValidateNoAnimation": false, - "ValidateShapeDefaultNames": false - }, - "preset 2": { - "ValidateNoAnimation": false - } - } -} From f0fb628caf5c0baa1fc8fca0aea4ebef71837e73 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Thu, 26 Jan 2023 15:10:31 +0800 Subject: [PATCH 0219/1271] Delete schema_maya_publish.json --- .../schemas/schema_maya_publish.json | 955 ------------------ 1 file changed, 955 deletions(-) delete mode 100644 openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json 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 deleted file mode 100644 index a124aec1b3..0000000000 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ /dev/null @@ -1,955 +0,0 @@ -{ - "type": "dict", - "collapsible": true, - "key": "publish", - "label": "Publish plugins", - "children": [ - { - "type": "label", - "label": "Collectors" - }, - { - "type": "dict", - "collapsible": true, - "key": "CollectMayaRender", - "label": "Collect Render Layers", - "children": [ - { - "type": "boolean", - "key": "sync_workfile_version", - "label": "Sync render version with workfile" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "CollectFbxCamera", - "label": "Collect Camera for FBX export", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "CollectGLTF", - "label": "Collect Assets for GLTF/GLB export", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - } - ] - }, - { - "type": "splitter" - }, - { - "type": "label", - "label": "Validators" - }, - { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ - { - "key": "ValidateInstanceInContext", - "label": "Validate Instance In Context" - } - ] - }, - { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ - { - "key": "ValidateContainers", - "label": "ValidateContainers" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateFrameRange", - "label": "Validate Frame Range", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "boolean", - "key": "active", - "label": "Active" - }, - { - "type": "splitter" - }, - { - "key": "exclude_families", - "label": "Families", - "type": "list", - "object_type": "text" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateShaderName", - "label": "ValidateShaderName", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "label", - "label": "Shader name regex can use named capture group asset to validate against current asset name.

Example:
^.*(?P=<asset>.+)_SHD

" - }, - { - "type": "text", - "key": "regex", - "label": "Validation regex" - } - ] - }, - { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ - { - "key": "ValidateShadingEngine", - "label": "Validate Look Shading Engine Naming" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateAttributes", - "label": "ValidateAttributes", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "raw-json", - "key": "attributes", - "label": "Attributes" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateLoadedPlugin", - "label": "Validate Loaded Plugin", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "boolean", - "key": "whitelist_native_plugins", - "label": "Whitelist Maya Native Plugins" - }, - { - "type": "list", - "key": "authorized_plugins", - "label": "Authorized plugins", - "object_type": "text" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateMayaUnits", - "label": "Validate Maya Units", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "boolean", - "key": "validate_linear_units", - "label": "Validate linear units" - }, - { - "key": "linear_units", - "label": "Linear units", - "type": "enum", - "multiselection": false, - "defaults": "cm", - "enum_items": [ - {"mm": "millimeter"}, - {"cm": "centimeter"}, - {"m": "meter"}, - {"km": "kilometer"}, - {"in": "inch"}, - {"ft": "foot"}, - {"yd": "yard"}, - {"mi": "mile"} - ] - }, - { - "type": "boolean", - "key": "validate_angular_units", - "label": "Validate angular units" - }, - { - "key": "angular_units", - "label": "Angular units", - "type": "enum", - "multiselection": false, - "defaults": "cm", - "enum_items": [ - {"deg": "degree"}, - {"rad": "radian"} - ] - }, - { - "type": "boolean", - "key": "validate_fps", - "label": "Validate fps" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateUnrealStaticMeshName", - "label": "Validate Unreal Static Mesh Name", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "boolean", - "key": "validate_mesh", - "label": "Validate mesh Names " - }, - { - "type": "boolean", - "key": "validate_collision", - "label": "Validate collision names" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ValidateCycleError", - "label": "Validate Cycle Error", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateRenderSettings", - "label": "ValidateRenderSettings", - "children": [ - { - "type": "dict-modifiable", - "store_as_list": true, - "key": "arnold_render_attributes", - "label": "Arnold Render Attributes", - "use_label_wrap": true, - "object_type": { - "type": "text" - } - }, - { - "type": "dict-modifiable", - "store_as_list": true, - "key": "vray_render_attributes", - "label": "Vray Render Attributes", - "use_label_wrap": true, - "object_type": { - "type": "text" - } - }, - { - "type": "dict-modifiable", - "store_as_list": true, - "key": "redshift_render_attributes", - "label": "Redshift Render Attributes", - "use_label_wrap": true, - "object_type": { - "type": "text" - } - }, - { - "type": "dict-modifiable", - "store_as_list": true, - "key": "renderman_render_attributes", - "label": "Renderman Render Attributes", - "use_label_wrap": true, - "object_type": { - "type": "text" - } - } - ] - }, - { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ - { - "key": "ValidateCurrentRenderLayerIsRenderable", - "label": "Validate Current Render Layer Has Renderable Camera" - }, - { - "key": "ValidateGLTFTexturesNames", - "label": "Validate GLTF Textures Names" - }, - { - "key": "ValidateRenderImageRule", - "label": "Validate Images File Rule (Workspace)" - }, - { - "key": "ValidateRenderNoDefaultCameras", - "label": "Validate No Default Cameras Renderable" - }, - { - "key": "ValidateRenderSingleCamera", - "label": "Validate Render Single Camera" - }, - { - "key": "ValidateRenderLayerAOVs", - "label": "Validate Render Passes / AOVs Are Registered" - }, - { - "key": "ValidateStepSize", - "label": "Validate Step Size" - }, - { - "key": "ValidateVRayDistributedRendering", - "label": "VRay Distributed Rendering" - }, - { - "key": "ValidateVrayReferencedAOVs", - "label": "VRay Referenced AOVs" - }, - { - "key": "ValidateVRayTranslatorEnabled", - "label": "VRay Translator Settings" - }, - { - "key": "ValidateVrayProxy", - "label": "VRay Proxy Settings" - }, - { - "key": "ValidateVrayProxyMembers", - "label": "VRay Proxy Members" - }, - { - "key": "ValidateYetiRenderScriptCallbacks", - "label": "Yeti Render Script Callbacks" - }, - { - "key": "ValidateYetiRigCacheState", - "label": "Yeti Rig Cache State" - }, - { - "key": "ValidateYetiRigInputShapesInInstance", - "label": "Yeti Rig Input Shapes In Instance" - }, - { - "key": "ValidateYetiRigSettings", - "label": "Yeti Rig Settings" - } - ] - }, - { - "type": "collapsible-wrap", - "label": "Model", - "children": [ - { - "type": "dict", - "collapsible": true, - "key": "ValidateModelName", - "label": "Validate Model Name", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "database", - "label": "Use database shader name definitions" - }, - { - "type": "label", - "label": "Path to material file defining list of material names to check. This is material name per line simple text file.
It will be checked against named group shader in your Validation regex.

For example:
^.*(?P=<shader>.+)_GEO

This is used instead of database definitions if they are disabled." - }, - { - "type": "path", - "key": "material_file", - "label": "Material File", - "multiplatform": true, - "multipath": false - }, - { - "type": "text", - "key": "regex", - "label": "Validation regex" - }, - { - "type": "label", - "label": "Regex for validating name of top level group name.
You can use named capturing groups:
(?P<asset>.*) for Asset name
(?P<subset>.*) for Subset
(?P<project>.*) for project

For example to check for asset in name so *_some_asset_name_GRP is valid, use:
.*?_(?P<asset>.*)_GEO" - }, - { - "type": "text", - "key": "top_level_regex", - "label": "Top level group name regex" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateModelContent", - "label": "Validate Model Content", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "boolean", - "key": "validate_top_group", - "label": "Validate one top group" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateTransformNamingSuffix", - "label": "ValidateTransformNamingSuffix", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "label", - "label": "Validates transform suffix based on the type of its children shapes." - }, - { - "type": "raw-json", - "key": "SUFFIX_NAMING_TABLE", - "label": "Suffix Naming Table" - }, - { - "type": "boolean", - "key": "ALLOW_IF_NOT_IN_SUFFIX_TABLE", - "label": "Allow if suffix not in table" - } - ] - }, - { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ - { - "key": "ValidateColorSets", - "label": "ValidateColorSets" - }, - { - "key": "ValidateMeshHasOverlappingUVs", - "label": "ValidateMeshHasOverlappingUVs" - }, - { - "key": "ValidateMeshArnoldAttributes", - "label": "ValidateMeshArnoldAttributes" - }, - { - "key": "ValidateMeshShaderConnections", - "label": "ValidateMeshShaderConnections" - }, - { - "key": "ValidateMeshSingleUVSet", - "label": "ValidateMeshSingleUVSet" - }, - { - "key": "ValidateMeshHasUVs", - "label": "ValidateMeshHasUVs" - }, - { - "key": "ValidateMeshLaminaFaces", - "label": "ValidateMeshLaminaFaces" - }, - { - "key": "ValidateMeshNgons", - "label": "ValidateMeshNgons" - }, - { - "key": "ValidateMeshNonManifold", - "label": "ValidateMeshNonManifold" - }, - { - "key": "ValidateMeshNoNegativeScale", - "label": "Validate Mesh No Negative Scale" - }, - { - "key": "ValidateMeshNonZeroEdgeLength", - "label": "Validate Mesh Edge Length Non Zero" - }, - { - "key": "ValidateMeshNormalsUnlocked", - "label": "ValidateMeshNormalsUnlocked" - }, - { - "key": "ValidateMeshUVSetMap1", - "label": "ValidateMeshUVSetMap1", - "docstring": "Validate model's default uv set exists and is named 'map1'.

In Maya meshes by default have a uv set named 'map1' that cannot be deleted. It can be renamed, however,
introducing some issues with some renderers. As such we ensure the first (default) UV set index is named 'map1'." - }, - { - "key": "ValidateMeshVerticesHaveEdges", - "label": "ValidateMeshVerticesHaveEdges" - }, - { - "key": "ValidateNoAnimation", - "label": "ValidateNoAnimation", - "docstring": "Ensure no keyframes on nodes in the Instance.
Even though a Model would extract without animCurves correctly this avoids getting different
output from a model when extracted from a different frame than the first frame. (Might be overly restrictive though)." - }, - { - "key": "ValidateNoNamespace", - "label": "ValidateNoNamespace" - }, - { - "key": "ValidateNoNullTransforms", - "label": "ValidateNoNullTransforms" - }, - { - "key": "ValidateNoUnknownNodes", - "label": "ValidateNoUnknownNodes" - }, - { - "key": "ValidateNodeNoGhosting", - "label": "ValidateNodeNoGhosting" - }, - { - "key": "ValidateShapeDefaultNames", - "label": "ValidateShapeDefaultNames" - }, - { - "key": "ValidateShapeRenderStats", - "label": "ValidateShapeRenderStats" - }, - { - "key": "ValidateShapeZero", - "label": "ValidateShapeZero" - }, - { - "key": "ValidateTransformZero", - "label": "ValidateTransformZero" - }, - { - "key": "ValidateUniqueNames", - "label": "ValidateUniqueNames" - }, - { - "key": "ValidateNoVRayMesh", - "label": "Validate No V-Ray Proxies (VRayMesh)" - }, - { - "key": "ValidateUnrealMeshTriangulated", - "label": "Validate if Mesh is Triangulated" - }, - { - "key": "ValidateAlembicVisibleOnly", - "label": "Validate Alembic visible node" - } - ] - }, - { - "type": "label", - "label": "Extractors" - }, - { - "type": "dict", - "collapsible": true, - "key": "ExtractProxyAlembic", - "label": "Extract Proxy Alembic", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ExtractAlembic", - "label": "Extract Alembic", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ExtractObj", - "label": "Extract OBJ", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - } - ] - } - ] - }, - { - "type": "collapsible-wrap", - "label": "Rig", - "children": [ - { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ - { - "key": "ValidateRigContents", - "label": "Validate Rig Contents" - }, - { - "key": "ValidateRigJointsHidden", - "label": "Validate Rig Joints Hidden" - }, - { - "key": "ValidateRigControllers", - "label": "Validate Rig Controllers" - }, - { - "key": "ValidateAnimationContent", - "label": "Validate Animation Content" - }, - { - "key": "ValidateOutRelatedNodeIds", - "label": "Validate Animation Out Set Related Node Ids" - }, - { - "key": "ValidateRigControllersArnoldAttributes", - "label": "Validate Rig Controllers (Arnold Attributes)" - }, - { - "key": "ValidateSkeletalMeshHierarchy", - "label": "Validate Skeletal Mesh Top Node" - }, - { - "key": "ValidateSkinclusterDeformerSet", - "label": "Validate Skincluster Deformer Relationships" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "checkbox_key": "enabled", - "key": "ValidateRigOutSetNodeIds", - "label": "Validate Rig Out Set Node Ids", - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "boolean", - "key": "allow_history_only", - "label": "Allow history only" - } - ] - } - ] - }, - { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ - { - "key": "ValidateCameraAttributes", - "label": "Validate Camera Attributes", - "docstring": "" - }, - { - "key": "ValidateAssemblyName", - "label": "Validate Assembly Name" - }, - { - "key": "ValidateAssemblyNamespaces", - "label": "Validate Assembly Namespaces" - }, - { - "key": "ValidateAssemblyModelTransforms", - "label": "Validate Assembly Model Transforms" - }, - { - "key": "ValidateAssRelativePaths", - "label": "ValidateAssRelativePaths" - }, - { - "key": "ValidateInstancerContent", - "label": "Validate Instancer Content" - }, - { - "key": "ValidateInstancerFrameRanges", - "label": "Validate Instancer Cache Frame Ranges" - }, - { - "key": "ValidateNoDefaultCameras", - "label": "Validate No Default Cameras" - }, - { - "key": "ValidateUnrealUpAxis", - "label": "Validate Unreal Up-Axis check" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ValidateCameraContents", - "label": "Validate Camera Content", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "boolean", - "key": "validate_shapes", - "label": "Validate presence of shapes" - } - ] - }, - { - "type": "splitter" - }, - { - "type": "label", - "label": "Extractors" - }, - { - "type": "schema", - "name": "schema_maya_capture" - }, - { - "type": "dict", - "collapsible": true, - "key": "ConvertGLSLShader", - "label": "Convert PBS Shader to GLSL Shader", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "boolean", - "key": "active", - "label": "Active" - }, - { - "type": "text", - "key": "ogsfx_path", - "label": "GLSL Shader Directory" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ExtractMayaSceneRaw", - "label": "Maya Scene (Raw)", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "label", - "label": "Add loaded instances to those published families:" - }, - { - "key": "add_for_families", - "label": "Families", - "type": "list", - "object_type": "text" - } - ] - }, - { - "type": "dict", - "collapsible": true, - "key": "ExtractCameraAlembic", - "label": "Extract camera to Alembic", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "label", - "label": "List of attributes that will be added to the baked alembic camera. Needs to be written in python list syntax.

For example:
[\"attributeName\", \"anotherAttribute\"]

" - }, - { - "type": "boolean", - "key": "optional", - "label": "Optional" - }, - { - "type": "boolean", - "key": "active", - "label": "Active" - }, - { - "type": "raw-json", - "key": "bake_attributes", - "label": "Bake Attributes", - "is_list": true - } - ] - } - ] -} From 4ecb95506abe6a316bc924208f1360801de0e273 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 26 Jan 2023 15:27:21 +0800 Subject: [PATCH 0220/1271] hound fix --- .../max/plugins/publish/extract_camera_fbx.py | 2 +- .../max/plugins/publish/extract_max_scene_raw.py | 15 +++++++-------- .../plugins/publish/validate_camera_contents.py | 3 +-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/extract_camera_fbx.py b/openpype/hosts/max/plugins/publish/extract_camera_fbx.py index 5a68edfbe8..e450de1275 100644 --- a/openpype/hosts/max/plugins/publish/extract_camera_fbx.py +++ b/openpype/hosts/max/plugins/publish/extract_camera_fbx.py @@ -65,4 +65,4 @@ exportFile @"{filepath}" #noPrompt selectedOnly:true using:FBXEXP } instance.data["representations"].append(representation) self.log.info("Extracted instance '%s' to: %s" % (instance.name, - filepath)) \ No newline at end of file + filepath)) diff --git a/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py b/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py index 4524609a02..97de602216 100644 --- a/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py +++ b/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py @@ -34,7 +34,7 @@ class ExtractMaxSceneRaw(publish.Extractor): if "representations" not in instance.data: instance.data["representations"] = [] - #add extra blacklash for saveNodes in MaxScript + # add extra blacklash for saveNodes in MaxScript re_max_path = stagingdir + "\\\\" + filename # saving max scene raw_export_cmd = ( @@ -42,13 +42,12 @@ class ExtractMaxSceneRaw(publish.Extractor): sel = getCurrentSelection() for s in sel do ( - select s - f="{re_max_path}" - print f - saveNodes selection f quiet:true + select s + f="{re_max_path}" + print f + saveNodes selection f quiet:true ) - """) - + """) # noqa self.log.debug(f"Executing Maxscript command: {raw_export_cmd}") with maintained_selection(): @@ -65,4 +64,4 @@ for s in sel do } instance.data["representations"].append(representation) self.log.info("Extracted instance '%s' to: %s" % (instance.name, - max_path)) \ No newline at end of file + max_path)) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_contents.py b/openpype/hosts/max/plugins/publish/validate_camera_contents.py index 3990103e61..9c411c7a3e 100644 --- a/openpype/hosts/max/plugins/publish/validate_camera_contents.py +++ b/openpype/hosts/max/plugins/publish/validate_camera_contents.py @@ -24,7 +24,6 @@ class ValidateCameraContent(pyblish.api.InstancePlugin): raise PublishValidationError("Camera instance must only include" "camera (and camera target)") - def get_invalid(self, instance): """ Get invalid nodes if the instance is not camera @@ -32,7 +31,7 @@ class ValidateCameraContent(pyblish.api.InstancePlugin): invalid = list() container = instance.data["instance_node"] self.log.info("Validating look content for " - "'{}'".format(container)) + "{}".format(container)) con = rt.getNodeByName(container) selection_list = self.list_children(con) From ab7737dcb3e4cf1e6544b004653304bb816133d4 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 26 Jan 2023 15:28:08 +0800 Subject: [PATCH 0221/1271] hound fix --- openpype/hosts/max/plugins/publish/validate_camera_contents.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_contents.py b/openpype/hosts/max/plugins/publish/validate_camera_contents.py index 9c411c7a3e..c7d13ac5a3 100644 --- a/openpype/hosts/max/plugins/publish/validate_camera_contents.py +++ b/openpype/hosts/max/plugins/publish/validate_camera_contents.py @@ -2,7 +2,6 @@ import pyblish.api from openpype.pipeline import PublishValidationError from pymxs import runtime as rt -from openpype.hosts.max.api import get_all_children class ValidateCameraContent(pyblish.api.InstancePlugin): From b29f382f8747a4e08caf8f6079f181105330275e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 26 Jan 2023 15:31:39 +0800 Subject: [PATCH 0222/1271] resolve conflict --- .../defaults/project_settings/maya.json | 1090 +++++++++++++++++ .../schemas/scheme_maya_publish.json | 961 +++++++++++++++ 2 files changed, 2051 insertions(+) create mode 100644 openpype/settings/defaults/project_settings/maya.json create mode 100644 openpype/settings/entities/schemas/projects_schema/schemas/scheme_maya_publish.json diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json new file mode 100644 index 0000000000..31591bf734 --- /dev/null +++ b/openpype/settings/defaults/project_settings/maya.json @@ -0,0 +1,1090 @@ +{ + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + }, + "colorManagementPreference_v2": { + "enabled": true, + "configFilePath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "renderSpace": "ACEScg", + "displayName": "sRGB", + "viewName": "ACES 1.0 SDR-video" + }, + "colorManagementPreference": { + "configFilePath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "renderSpace": "scene-linear Rec 709/sRGB", + "viewTransform": "sRGB gamma" + } + }, + "mel_workspace": "workspace -fr \"shaders\" \"renderData/shaders\";\nworkspace -fr \"images\" \"renders/maya\";\nworkspace -fr \"particles\" \"particles\";\nworkspace -fr \"mayaAscii\" \"\";\nworkspace -fr \"mayaBinary\" \"\";\nworkspace -fr \"scene\" \"\";\nworkspace -fr \"alembicCache\" \"cache/alembic\";\nworkspace -fr \"renderData\" \"renderData\";\nworkspace -fr \"sourceImages\" \"sourceimages\";\nworkspace -fr \"fileCache\" \"cache/nCache\";\n", + "ext_mapping": { + "model": "ma", + "mayaAscii": "ma", + "camera": "ma", + "rig": "ma", + "workfile": "ma", + "yetiRig": "ma" + }, + "maya-dirmap": { + "use_env_var_as_root": false, + "enabled": false, + "paths": { + "source-path": [], + "destination-path": [] + } + }, + "scriptsmenu": { + "name": "OpenPype Tools", + "definition": [ + { + "type": "action", + "command": "import openpype.hosts.maya.api.commands as op_cmds; op_cmds.edit_shader_definitions()", + "sourcetype": "python", + "title": "Edit shader name definitions", + "tooltip": "Edit shader name definitions used in validation and renaming.", + "tags": [ + "pipeline", + "shader" + ] + } + ] + }, + "RenderSettings": { + "apply_render_settings": true, + "default_render_image_folder": "renders/maya", + "enable_all_lights": true, + "aov_separator": "underscore", + "remove_aovs": false, + "reset_current_frame": false, + "arnold_renderer": { + "image_prefix": "//_", + "image_format": "exr", + "multilayer_exr": true, + "tiled": true, + "aov_list": [], + "additional_options": [] + }, + "vray_renderer": { + "image_prefix": "//", + "engine": "1", + "image_format": "exr", + "aov_list": [], + "additional_options": [] + }, + "redshift_renderer": { + "image_prefix": "//", + "primary_gi_engine": "0", + "secondary_gi_engine": "0", + "image_format": "exr", + "multilayer_exr": true, + "force_combine": true, + "aov_list": [], + "additional_options": [] + } + }, + "create": { + "CreateLook": { + "enabled": true, + "make_tx": true, + "defaults": [ + "Main" + ] + }, + "CreateRender": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateUnrealStaticMesh": { + "enabled": true, + "defaults": [ + "", + "_Main" + ], + "static_mesh_prefix": "S", + "collision_prefixes": [ + "UBX", + "UCP", + "USP", + "UCX" + ] + }, + "CreateUnrealSkeletalMesh": { + "enabled": true, + "defaults": [], + "joint_hints": "jnt_org" + }, + "CreateMultiverseLook": { + "enabled": true, + "publish_mip_map": true + }, + "CreateAnimation": { + "enabled": true, + "write_color_sets": false, + "write_face_sets": false, + "defaults": [ + "Main" + ] + }, + "CreateModel": { + "enabled": true, + "write_color_sets": false, + "write_face_sets": false, + "defaults": [ + "Main", + "Proxy", + "Sculpt" + ] + }, + "CreatePointCache": { + "enabled": true, + "write_color_sets": false, + "write_face_sets": false, + "defaults": [ + "Main" + ] + }, + "CreateProxyAlembic": { + "enabled": true, + "write_color_sets": false, + "write_face_sets": false, + "defaults": [ + "Main" + ] + }, + "CreateAss": { + "enabled": true, + "defaults": [ + "Main" + ], + "expandProcedurals": false, + "motionBlur": true, + "motionBlurKeys": 2, + "motionBlurLength": 0.5, + "maskOptions": false, + "maskCamera": false, + "maskLight": false, + "maskShape": false, + "maskShader": false, + "maskOverride": false, + "maskDriver": false, + "maskFilter": false, + "maskColor_manager": false, + "maskOperator": false + }, + "CreateMultiverseUsd": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateMultiverseUsdComp": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateMultiverseUsdOver": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateAssembly": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateCamera": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateLayout": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateMayaScene": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateRenderSetup": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateReview": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateRig": { + "enabled": true, + "defaults": [ + "Main", + "Sim", + "Cloth" + ] + }, + "CreateSetDress": { + "enabled": true, + "defaults": [ + "Main", + "Anim" + ] + }, + "CreateVrayProxy": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateVRayScene": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateYetiRig": { + "enabled": true, + "defaults": [ + "Main" + ] + } + }, + "publish": { + "CollectMayaRender": { + "sync_workfile_version": false + }, + "CollectFbxCamera": { + "enabled": false + }, + "CollectGLTF": { + "enabled": false + }, + "ValidateInstanceInContext": { + "enabled": true, + "optional": true, + "active": true + }, + "ValidateContainers": { + "enabled": true, + "optional": true, + "active": true + }, + "ValidateFrameRange": { + "enabled": true, + "optional": true, + "active": true, + "exclude_families": [ + "model", + "rig", + "staticMesh" + ] + }, + "ValidateShaderName": { + "enabled": false, + "optional": true, + "regex": "(?P.*)_(.*)_SHD" + }, + "ValidateShadingEngine": { + "enabled": true, + "optional": true, + "active": true + }, + "ValidateAttributes": { + "enabled": false, + "attributes": {} + }, + "ValidateLoadedPlugin": { + "enabled": false, + "optional": true, + "whitelist_native_plugins": false, + "authorized_plugins": [] + }, + "ValidateMayaUnits": { + "enabled": true, + "optional": false, + "validate_linear_units": true, + "linear_units": "cm", + "validate_angular_units": true, + "angular_units": "deg", + "validate_fps": true + }, + "ValidateUnrealStaticMeshName": { + "enabled": true, + "optional": true, + "validate_mesh": false, + "validate_collision": true + }, + "ValidateCycleError": { + "enabled": true, + "optional": false, + "families": [ + "rig" + ] + }, + "ValidatePluginPathAttributes": { + "enabled": true, + "optional": false, + "active": true, + "attribute": { + "AlembicNode": "abc_File", + "VRayProxy": "fileName", + "RenderManArchive": "filename", + "pgYetiMaya": "cacheFileName", + "aiStandIn": "dso", + "RedshiftSprite": "tex0", + "RedshiftBokeh": "dofBokehImage", + "RedshiftCameraMap": "tex0", + "RedshiftEnvironment": "tex2", + "RedshiftDomeLight": "tex1", + "RedshiftIESLight": "profile", + "RedshiftLightGobo": "tex0", + "RedshiftNormalMap": "tex0", + "RedshiftProxyMesh": "fileName", + "RedshiftVolumeShape": "fileName", + "VRayTexGLSL": "fileName", + "VRayMtlGLSL": "fileName", + "VRayVRmatMtl": "fileName", + "VRayPtex": "ptexFile", + "VRayLightIESShape": "iesFile", + "VRayMesh": "materialAssignmentsFile", + "VRayMtlOSL": "fileName", + "VRayTexOSL": "fileName", + "VRayTexOCIO": "ocioConfigFile", + "VRaySettingsNode": "pmap_autoSaveFile2", + "VRayScannedMtl": "file", + "VRayScene": "parameterOverrideFilePath", + "VRayMtlMDL": "filename", + "VRaySimbiont": "file", + "dlOpenVDBShape": "filename", + "pgYetiMayaShape": "liveABCFilename", + "gpuCache": "cacheFileName" + } + }, + "ValidateRenderSettings": { + "arnold_render_attributes": [], + "vray_render_attributes": [], + "redshift_render_attributes": [], + "renderman_render_attributes": [] + }, + "ValidateCurrentRenderLayerIsRenderable": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateRenderImageRule": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateRenderNoDefaultCameras": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateRenderSingleCamera": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateRenderLayerAOVs": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateStepSize": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateVRayDistributedRendering": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateVrayReferencedAOVs": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateVRayTranslatorEnabled": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateVrayProxy": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateVrayProxyMembers": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateYetiRenderScriptCallbacks": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateYetiRigCacheState": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateYetiRigInputShapesInInstance": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateYetiRigSettings": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateModelName": { + "enabled": false, + "database": true, + "material_file": { + "windows": "", + "darwin": "", + "linux": "" + }, + "regex": "(.*)_(\\d)*_(?P.*)_(GEO)", + "top_level_regex": ".*_GRP" + }, + "ValidateModelContent": { + "enabled": true, + "optional": false, + "validate_top_group": true + }, + "ValidateTransformNamingSuffix": { + "enabled": true, + "optional": true, + "SUFFIX_NAMING_TABLE": { + "mesh": [ + "_GEO", + "_GES", + "_GEP", + "_OSD" + ], + "nurbsCurve": [ + "_CRV" + ], + "nurbsSurface": [ + "_NRB" + ], + "locator": [ + "_LOC" + ], + "group": [ + "_GRP" + ] + }, + "ALLOW_IF_NOT_IN_SUFFIX_TABLE": true + }, + "ValidateColorSets": { + "enabled": true, + "optional": true, + "active": true + }, + "ValidateMeshHasOverlappingUVs": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateMeshArnoldAttributes": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateMeshShaderConnections": { + "enabled": true, + "optional": true, + "active": true + }, + "ValidateMeshSingleUVSet": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateMeshHasUVs": { + "enabled": true, + "optional": true, + "active": true + }, + "ValidateMeshLaminaFaces": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateMeshNgons": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateMeshNonManifold": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateMeshNoNegativeScale": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateMeshNonZeroEdgeLength": { + "enabled": true, + "optional": true, + "active": true + }, + "ValidateMeshNormalsUnlocked": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateMeshUVSetMap1": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateMeshVerticesHaveEdges": { + "enabled": true, + "optional": true, + "active": true + }, + "ValidateNoAnimation": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateNoNamespace": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateNoNullTransforms": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateNoUnknownNodes": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateNodeNoGhosting": { + "enabled": false, + "optional": false, + "active": true + }, + "ValidateShapeDefaultNames": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateShapeRenderStats": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateShapeZero": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateTransformZero": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateUniqueNames": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateNoVRayMesh": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateUnrealMeshTriangulated": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateAlembicVisibleOnly": { + "enabled": true, + "optional": false, + "active": true + }, + "ExtractProxyAlembic": { + "enabled": true, + "families": [ + "proxyAbc" + ] + }, + "ExtractAlembic": { + "enabled": true, + "families": [ + "pointcache", + "model", + "vrayproxy" + ] + }, + "ExtractObj": { + "enabled": false, + "optional": true + }, + "ValidateRigContents": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateRigJointsHidden": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateRigControllers": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateAnimationContent": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateOutRelatedNodeIds": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateRigControllersArnoldAttributes": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateSkeletalMeshHierarchy": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateSkinclusterDeformerSet": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateRigOutSetNodeIds": { + "enabled": true, + "optional": false, + "allow_history_only": false + }, + "ValidateCameraAttributes": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateAssemblyName": { + "enabled": true, + "optional": true, + "active": true + }, + "ValidateAssemblyNamespaces": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateAssemblyModelTransforms": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateAssRelativePaths": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateInstancerContent": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateInstancerFrameRanges": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateNoDefaultCameras": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateUnrealUpAxis": { + "enabled": false, + "optional": true, + "active": true + }, + "ValidateCameraContents": { + "enabled": true, + "optional": false, + "validate_shapes": true + }, + "ExtractPlayblast": { + "capture_preset": { + "Codec": { + "compression": "jpg", + "format": "image", + "quality": 95 + }, + "Display Options": { + "background": [ + 125, + 125, + 125, + 255 + ], + "backgroundBottom": [ + 125, + 125, + 125, + 255 + ], + "backgroundTop": [ + 125, + 125, + 125, + 255 + ], + "override_display": true + }, + "Generic": { + "isolate_view": true, + "off_screen": true + }, + "Renderer": { + "rendererName": "vp2Renderer" + }, + "Resolution": { + "width": 1920, + "height": 1080 + }, + "Viewport Options": { + "override_viewport_options": true, + "displayLights": "default", + "displayTextures": true, + "textureMaxResolution": 1024, + "renderDepthOfField": true, + "shadows": true, + "twoSidedLighting": true, + "lineAAEnable": true, + "multiSample": 8, + "ssaoEnable": false, + "ssaoAmount": 1, + "ssaoRadius": 16, + "ssaoFilterRadius": 16, + "ssaoSamples": 16, + "fogging": false, + "hwFogFalloff": "0", + "hwFogDensity": 0.0, + "hwFogStart": 0, + "hwFogEnd": 100, + "hwFogAlpha": 0, + "hwFogColorR": 1.0, + "hwFogColorG": 1.0, + "hwFogColorB": 1.0, + "motionBlurEnable": false, + "motionBlurSampleCount": 8, + "motionBlurShutterOpenFraction": 0.2, + "cameras": false, + "clipGhosts": false, + "deformers": false, + "dimensions": false, + "dynamicConstraints": false, + "dynamics": false, + "fluids": false, + "follicles": false, + "gpuCacheDisplayFilter": false, + "greasePencils": false, + "grid": false, + "hairSystems": true, + "handles": false, + "headsUpDisplay": false, + "ikHandles": false, + "imagePlane": true, + "joints": false, + "lights": false, + "locators": false, + "manipulators": false, + "motionTrails": false, + "nCloths": false, + "nParticles": false, + "nRigids": false, + "controlVertices": false, + "nurbsCurves": false, + "hulls": false, + "nurbsSurfaces": false, + "particleInstancers": false, + "pivots": false, + "planes": false, + "pluginShapes": false, + "polymeshes": true, + "strokes": false, + "subdivSurfaces": false, + "textures": false + }, + "Camera Options": { + "displayGateMask": false, + "displayResolution": false, + "displayFilmGate": false, + "displayFieldChart": false, + "displaySafeAction": false, + "displaySafeTitle": false, + "displayFilmPivot": false, + "displayFilmOrigin": false, + "overscan": 1.0 + } + } + }, + "ExtractMayaSceneRaw": { + "enabled": true, + "add_for_families": [ + "layout" + ] + }, + "ExtractCameraAlembic": { + "enabled": true, + "optional": true, + "active": true, + "bake_attributes": [] + } + }, + "load": { + "colors": { + "model": [ + 209, + 132, + 30, + 255 + ], + "rig": [ + 59, + 226, + 235, + 255 + ], + "pointcache": [ + 94, + 209, + 30, + 255 + ], + "animation": [ + 94, + 209, + 30, + 255 + ], + "ass": [ + 249, + 135, + 53, + 255 + ], + "camera": [ + 136, + 114, + 244, + 255 + ], + "fbx": [ + 215, + 166, + 255, + 255 + ], + "mayaAscii": [ + 67, + 174, + 255, + 255 + ], + "mayaScene": [ + 67, + 174, + 255, + 255 + ], + "setdress": [ + 255, + 250, + 90, + 255 + ], + "layout": [ + 255, + 250, + 90, + 255 + ], + "vdbcache": [ + 249, + 54, + 0, + 255 + ], + "vrayproxy": [ + 255, + 150, + 12, + 255 + ], + "vrayscene_layer": [ + 255, + 150, + 12, + 255 + ], + "yeticache": [ + 99, + 206, + 220, + 255 + ], + "yetiRig": [ + 0, + 205, + 125, + 255 + ] + } + }, + "workfile_build": { + "profiles": [ + { + "task_types": [], + "tasks": [ + "Lighting" + ], + "current_context": [ + { + "subset_name_filters": [ + ".+[Mm]ain" + ], + "families": [ + "model" + ], + "repre_names": [ + "abc", + "ma" + ], + "loaders": [ + "ReferenceLoader" + ] + }, + { + "subset_name_filters": [], + "families": [ + "animation", + "pointcache", + "proxyAbc" + ], + "repre_names": [ + "abc" + ], + "loaders": [ + "ReferenceLoader" + ] + }, + { + "subset_name_filters": [], + "families": [ + "rendersetup" + ], + "repre_names": [ + "json" + ], + "loaders": [ + "RenderSetupLoader" + ] + }, + { + "subset_name_filters": [], + "families": [ + "camera" + ], + "repre_names": [ + "abc" + ], + "loaders": [ + "ReferenceLoader" + ] + } + ], + "linked_assets": [ + { + "subset_name_filters": [], + "families": [ + "sedress" + ], + "repre_names": [ + "ma" + ], + "loaders": [ + "ReferenceLoader" + ] + }, + { + "subset_name_filters": [], + "families": [ + "ArnoldStandin" + ], + "repre_names": [ + "ass" + ], + "loaders": [ + "assLoader" + ] + } + ] + } + ] + }, + "templated_workfile_build": { + "profiles": [] + }, + "filters": { + "preset 1": { + "ValidateNoAnimation": false, + "ValidateShapeDefaultNames": false + }, + "preset 2": { + "ValidateNoAnimation": false + } + } +} \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/scheme_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/scheme_maya_publish.json new file mode 100644 index 0000000000..873bb79c95 --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schemas/scheme_maya_publish.json @@ -0,0 +1,961 @@ +{ + "type": "dict", + "collapsible": true, + "key": "publish", + "label": "Publish plugins", + "children": [ + { + "type": "label", + "label": "Collectors" + }, + { + "type": "dict", + "collapsible": true, + "key": "CollectMayaRender", + "label": "Collect Render Layers", + "children": [ + { + "type": "boolean", + "key": "sync_workfile_version", + "label": "Sync render version with workfile" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "CollectFbxCamera", + "label": "Collect Camera for FBX export", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "CollectGLTF", + "label": "Collect Assets for GLTF/GLB export", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Validators" + }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateInstanceInContext", + "label": "Validate Instance In Context" + } + ] + }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateContainers", + "label": "ValidateContainers" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateFrameRange", + "label": "Validate Frame Range", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + }, + { + "type": "splitter" + }, + { + "key": "exclude_families", + "label": "Families", + "type": "list", + "object_type": "text" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateShaderName", + "label": "ValidateShaderName", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "label", + "label": "Shader name regex can use named capture group asset to validate against current asset name.

Example:
^.*(?P=<asset>.+)_SHD

" + }, + { + "type": "text", + "key": "regex", + "label": "Validation regex" + } + ] + }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateShadingEngine", + "label": "Validate Look Shading Engine Naming" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateAttributes", + "label": "ValidateAttributes", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "raw-json", + "key": "attributes", + "label": "Attributes" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateLoadedPlugin", + "label": "Validate Loaded Plugin", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "whitelist_native_plugins", + "label": "Whitelist Maya Native Plugins" + }, + { + "type": "list", + "key": "authorized_plugins", + "label": "Authorized plugins", + "object_type": "text" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateMayaUnits", + "label": "Validate Maya Units", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "validate_linear_units", + "label": "Validate linear units" + }, + { + "key": "linear_units", + "label": "Linear units", + "type": "enum", + "multiselection": false, + "defaults": "cm", + "enum_items": [ + {"mm": "millimeter"}, + {"cm": "centimeter"}, + {"m": "meter"}, + {"km": "kilometer"}, + {"in": "inch"}, + {"ft": "foot"}, + {"yd": "yard"}, + {"mi": "mile"} + ] + }, + { + "type": "boolean", + "key": "validate_angular_units", + "label": "Validate angular units" + }, + { + "key": "angular_units", + "label": "Angular units", + "type": "enum", + "multiselection": false, + "defaults": "cm", + "enum_items": [ + {"deg": "degree"}, + {"rad": "radian"} + ] + }, + { + "type": "boolean", + "key": "validate_fps", + "label": "Validate fps" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateUnrealStaticMeshName", + "label": "Validate Unreal Static Mesh Name", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "validate_mesh", + "label": "Validate mesh Names " + }, + { + "type": "boolean", + "key": "validate_collision", + "label": "Validate collision names" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateCycleError", + "label": "Validate Cycle Error", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidatePluginPathAttributes", + "label": "Plug-in Path Attributes", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + }, + { + "type": "label", + "label": "Fill in the node types and attributes you want to validate.

e.g. AlembicNode.abc_file, the node type is AlembicNode and the node attribute is abc_file" + }, + { + "type": "dict-modifiable", + "collapsible": true, + "key": "attribute", + "label": "File Attribute", + "use_label_wrap": true, + "object_type": { + "type": "text" + } + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateRenderSettings", + "label": "ValidateRenderSettings", + "children": [ + { + "type": "dict-modifiable", + "store_as_list": true, + "key": "arnold_render_attributes", + "label": "Arnold Render Attributes", + "use_label_wrap": true, + "object_type": { + "type": "text" + } + }, + { + "type": "dict-modifiable", + "store_as_list": true, + "key": "vray_render_attributes", + "label": "Vray Render Attributes", + "use_label_wrap": true, + "object_type": { + "type": "text" + } + }, + { + "type": "dict-modifiable", + "store_as_list": true, + "key": "redshift_render_attributes", + "label": "Redshift Render Attributes", + "use_label_wrap": true, + "object_type": { + "type": "text" + } + }, + { + "type": "dict-modifiable", + "store_as_list": true, + "key": "renderman_render_attributes", + "label": "Renderman Render Attributes", + "use_label_wrap": true, + "object_type": { + "type": "text" + } + } + ] + }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateCurrentRenderLayerIsRenderable", + "label": "Validate Current Render Layer Has Renderable Camera" + }, + { + "key": "ValidateRenderImageRule", + "label": "Validate Images File Rule (Workspace)" + }, + { + "key": "ValidateRenderNoDefaultCameras", + "label": "Validate No Default Cameras Renderable" + }, + { + "key": "ValidateRenderSingleCamera", + "label": "Validate Render Single Camera" + }, + { + "key": "ValidateRenderLayerAOVs", + "label": "Validate Render Passes / AOVs Are Registered" + }, + { + "key": "ValidateStepSize", + "label": "Validate Step Size" + }, + { + "key": "ValidateVRayDistributedRendering", + "label": "VRay Distributed Rendering" + }, + { + "key": "ValidateVrayReferencedAOVs", + "label": "VRay Referenced AOVs" + }, + { + "key": "ValidateVRayTranslatorEnabled", + "label": "VRay Translator Settings" + }, + { + "key": "ValidateVrayProxy", + "label": "VRay Proxy Settings" + }, + { + "key": "ValidateVrayProxyMembers", + "label": "VRay Proxy Members" + }, + { + "key": "ValidateYetiRenderScriptCallbacks", + "label": "Yeti Render Script Callbacks" + }, + { + "key": "ValidateYetiRigCacheState", + "label": "Yeti Rig Cache State" + }, + { + "key": "ValidateYetiRigInputShapesInInstance", + "label": "Yeti Rig Input Shapes In Instance" + }, + { + "key": "ValidateYetiRigSettings", + "label": "Yeti Rig Settings" + } + ] + }, + { + "type": "collapsible-wrap", + "label": "Model", + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "ValidateModelName", + "label": "Validate Model Name", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "database", + "label": "Use database shader name definitions" + }, + { + "type": "label", + "label": "Path to material file defining list of material names to check. This is material name per line simple text file.
It will be checked against named group shader in your Validation regex.

For example:
^.*(?P=<shader>.+)_GEO

This is used instead of database definitions if they are disabled." + }, + { + "type": "path", + "key": "material_file", + "label": "Material File", + "multiplatform": true, + "multipath": false + }, + { + "type": "text", + "key": "regex", + "label": "Validation regex" + }, + { + "type": "label", + "label": "Regex for validating name of top level group name.
You can use named capturing groups:
(?P<asset>.*) for Asset name
(?P<subset>.*) for Subset
(?P<project>.*) for project

For example to check for asset in name so *_some_asset_name_GRP is valid, use:
.*?_(?P<asset>.*)_GEO" + }, + { + "type": "text", + "key": "top_level_regex", + "label": "Top level group name regex" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateModelContent", + "label": "Validate Model Content", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "validate_top_group", + "label": "Validate one top group" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateTransformNamingSuffix", + "label": "ValidateTransformNamingSuffix", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "label", + "label": "Validates transform suffix based on the type of its children shapes." + }, + { + "type": "raw-json", + "key": "SUFFIX_NAMING_TABLE", + "label": "Suffix Naming Table" + }, + { + "type": "boolean", + "key": "ALLOW_IF_NOT_IN_SUFFIX_TABLE", + "label": "Allow if suffix not in table" + } + ] + }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateColorSets", + "label": "ValidateColorSets" + }, + { + "key": "ValidateMeshHasOverlappingUVs", + "label": "ValidateMeshHasOverlappingUVs" + }, + { + "key": "ValidateMeshArnoldAttributes", + "label": "ValidateMeshArnoldAttributes" + }, + { + "key": "ValidateMeshShaderConnections", + "label": "ValidateMeshShaderConnections" + }, + { + "key": "ValidateMeshSingleUVSet", + "label": "ValidateMeshSingleUVSet" + }, + { + "key": "ValidateMeshHasUVs", + "label": "ValidateMeshHasUVs" + }, + { + "key": "ValidateMeshLaminaFaces", + "label": "ValidateMeshLaminaFaces" + }, + { + "key": "ValidateMeshNgons", + "label": "ValidateMeshNgons" + }, + { + "key": "ValidateMeshNonManifold", + "label": "ValidateMeshNonManifold" + }, + { + "key": "ValidateMeshNoNegativeScale", + "label": "Validate Mesh No Negative Scale" + }, + { + "key": "ValidateMeshNonZeroEdgeLength", + "label": "Validate Mesh Edge Length Non Zero" + }, + { + "key": "ValidateMeshNormalsUnlocked", + "label": "ValidateMeshNormalsUnlocked" + }, + { + "key": "ValidateMeshUVSetMap1", + "label": "ValidateMeshUVSetMap1", + "docstring": "Validate model's default uv set exists and is named 'map1'.

In Maya meshes by default have a uv set named 'map1' that cannot be deleted. It can be renamed, however,
introducing some issues with some renderers. As such we ensure the first (default) UV set index is named 'map1'." + }, + { + "key": "ValidateMeshVerticesHaveEdges", + "label": "ValidateMeshVerticesHaveEdges" + }, + { + "key": "ValidateNoAnimation", + "label": "ValidateNoAnimation", + "docstring": "Ensure no keyframes on nodes in the Instance.
Even though a Model would extract without animCurves correctly this avoids getting different
output from a model when extracted from a different frame than the first frame. (Might be overly restrictive though)." + }, + { + "key": "ValidateNoNamespace", + "label": "ValidateNoNamespace" + }, + { + "key": "ValidateNoNullTransforms", + "label": "ValidateNoNullTransforms" + }, + { + "key": "ValidateNoUnknownNodes", + "label": "ValidateNoUnknownNodes" + }, + { + "key": "ValidateNodeNoGhosting", + "label": "ValidateNodeNoGhosting" + }, + { + "key": "ValidateShapeDefaultNames", + "label": "ValidateShapeDefaultNames" + }, + { + "key": "ValidateShapeRenderStats", + "label": "ValidateShapeRenderStats" + }, + { + "key": "ValidateShapeZero", + "label": "ValidateShapeZero" + }, + { + "key": "ValidateTransformZero", + "label": "ValidateTransformZero" + }, + { + "key": "ValidateUniqueNames", + "label": "ValidateUniqueNames" + }, + { + "key": "ValidateNoVRayMesh", + "label": "Validate No V-Ray Proxies (VRayMesh)" + }, + { + "key": "ValidateUnrealMeshTriangulated", + "label": "Validate if Mesh is Triangulated" + }, + { + "key": "ValidateAlembicVisibleOnly", + "label": "Validate Alembic visible node" + } + ] + }, + { + "type": "label", + "label": "Extractors" + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractProxyAlembic", + "label": "Extract Proxy Alembic", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractAlembic", + "label": "Extract Alembic", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractObj", + "label": "Extract OBJ", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + } + ] + } + ] + }, + { + "type": "collapsible-wrap", + "label": "Rig", + "children": [ + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateRigContents", + "label": "Validate Rig Contents" + }, + { + "key": "ValidateRigJointsHidden", + "label": "Validate Rig Joints Hidden" + }, + { + "key": "ValidateRigControllers", + "label": "Validate Rig Controllers" + }, + { + "key": "ValidateAnimationContent", + "label": "Validate Animation Content" + }, + { + "key": "ValidateOutRelatedNodeIds", + "label": "Validate Animation Out Set Related Node Ids" + }, + { + "key": "ValidateRigControllersArnoldAttributes", + "label": "Validate Rig Controllers (Arnold Attributes)" + }, + { + "key": "ValidateSkeletalMeshHierarchy", + "label": "Validate Skeletal Mesh Top Node" + }, + { + "key": "ValidateSkinclusterDeformerSet", + "label": "Validate Skincluster Deformer Relationships" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateRigOutSetNodeIds", + "label": "Validate Rig Out Set Node Ids", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "allow_history_only", + "label": "Allow history only" + } + ] + } + ] + }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateCameraAttributes", + "label": "Validate Camera Attributes", + "docstring": "" + }, + { + "key": "ValidateAssemblyName", + "label": "Validate Assembly Name" + }, + { + "key": "ValidateAssemblyNamespaces", + "label": "Validate Assembly Namespaces" + }, + { + "key": "ValidateAssemblyModelTransforms", + "label": "Validate Assembly Model Transforms" + }, + { + "key": "ValidateAssRelativePaths", + "label": "ValidateAssRelativePaths" + }, + { + "key": "ValidateInstancerContent", + "label": "Validate Instancer Content" + }, + { + "key": "ValidateInstancerFrameRanges", + "label": "Validate Instancer Cache Frame Ranges" + }, + { + "key": "ValidateNoDefaultCameras", + "label": "Validate No Default Cameras" + }, + { + "key": "ValidateUnrealUpAxis", + "label": "Validate Unreal Up-Axis check" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateCameraContents", + "label": "Validate Camera Content", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "validate_shapes", + "label": "Validate presence of shapes" + } + ] + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Extractors" + }, + { + "type": "schema", + "name": "schema_maya_capture" + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractMayaSceneRaw", + "label": "Maya Scene (Raw)", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Add loaded instances to those published families:" + }, + { + "key": "add_for_families", + "label": "Families", + "type": "list", + "object_type": "text" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractCameraAlembic", + "label": "Extract camera to Alembic", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "List of attributes that will be added to the baked alembic camera. Needs to be written in python list syntax.

For example:
[\"attributeName\", \"anotherAttribute\"]

" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + }, + { + "type": "raw-json", + "key": "bake_attributes", + "label": "Bake Attributes", + "is_list": true + } + ] + } + ] +} From 53186ffa77ef481847ad332e8e664afac62a681b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 26 Jan 2023 15:33:06 +0800 Subject: [PATCH 0223/1271] resolve conflict --- .../{scheme_maya_publish.json => schema_maya_publish.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename openpype/settings/entities/schemas/projects_schema/schemas/{scheme_maya_publish.json => schema_maya_publish.json} (100%) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/scheme_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json similarity index 100% rename from openpype/settings/entities/schemas/projects_schema/schemas/scheme_maya_publish.json rename to openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json From a28bae61971f59a45d0678211c23d1b74247bd9a Mon Sep 17 00:00:00 2001 From: Libor Batek Date: Thu, 26 Jan 2023 08:54:29 +0100 Subject: [PATCH 0224/1271] added some scrn for 3dsmax and new paragraphs --- website/docs/artist_hosts_3dsmax.md | 87 ++++--------------- website/docs/assets/3dsmax_menu_OP.png | Bin 0 -> 69195 bytes website/docs/assets/3dsmax_menu_first_OP.png | Bin 0 -> 8376 bytes 3 files changed, 16 insertions(+), 71 deletions(-) create mode 100644 website/docs/assets/3dsmax_menu_OP.png create mode 100644 website/docs/assets/3dsmax_menu_first_OP.png diff --git a/website/docs/artist_hosts_3dsmax.md b/website/docs/artist_hosts_3dsmax.md index af90d93106..4379f7e71a 100644 --- a/website/docs/artist_hosts_3dsmax.md +++ b/website/docs/artist_hosts_3dsmax.md @@ -4,7 +4,9 @@ title: 3dsmax sidebar_label: 3dsmax --- -### *Still Work In Progress Docs Page* +:::note Work in progress +This part of documentation is still work in progress. +::: ## OpenPype Global Tools @@ -16,14 +18,20 @@ sidebar_label: 3dsmax - [Publish](artist_tools_publisher) - [Library Loader](artist_tools_library_loader) -## Working with OpenPype in 3dsmax -OpenPype is here to ease you the burden of working on project with lots of -collaborators, worrying about naming, setting stuff, browsing through endless -directories, loading and exporting and so on. To achieve that, OpenPype is using -concept of being _"data driven"_. This means that what happens when publishing -is influenced by data in scene. This can by slightly confusing so let's get to -it with few examples. +## First Steps With OpenPype Running + +When **OpenPype** (reffered as **OP** ) properly installed and 3dsmax launched via OP Launcher/Ftrack (or similar) there should be **OpenPype Menu** visible in 3dsmax top header after start. + +![Menu OpenPype](assets/3dsmax_menu_first_OP.png) + +## Working With Scene Files + +Most user actions happens in ```Work Files``` menu item. There user can perform Save/Load actions as he would normally do with ```File Save ``` and/or ```File Open``` in the standard 3dsmax File Menu. ```OP Menu > Work Files...``` basically substitutes all file operations user can perform. + +Here you have an overview of the **Work Files window** with descriptions what each area is used for. + +![Menu OpenPype](assets/3dsmax_menu_OP.png) ## Setting scene data @@ -160,69 +168,6 @@ There you should see your model, named `modelDefault`. You can load model with [Loader](artist_tools_loader). Go **OpenPype → Load...**, select your rig, right click on it and click **Link model (blend)**. -## Creating Rigs -Creating and publishing rigs with OpenPype follows similar workflow as with -other data types. Create your rig and mark parts of your hierarchy in sets to -help OpenPype validators and extractors to check it and publish it. -### Preparing rig for publish -When creating rigs in Blender, it is important to keep a specific structure for -the bones and the geometry. Let's first create a model and its rig. For -demonstration, I'll create a simple model for a robotic arm made of simple boxes. - -![Blender - Simple model for rigging](assets/blender-rig_model_setup.jpg) - -I have now created the armature `RIG_RobotArm`. While the naming is not important, -you can just adhere to your naming conventions, the hierarchy is. Once the models -are skinned to the armature, the geometry must be organized in a separate Collection. -In this case, I have the armature in the main Collection, and the geometry in -the `Geometry` Collection. - -![Blender - Rig Hierarchy Example](assets/blender-rig_hierarchy_example.jpg) - -When you've prepared your hierarchy, it's time to create *Rig instance* in OpenPype. -Select your whole rig hierarchy and go **OpenPype → Create...**. Select **Rig**. - -![Blender - Rig Hierarchy Example](assets/blender-rig_create.jpg) - -A new collection named after the selected Asset and Subset should have been created. -In our case, it is `character1_rigDefault`. All the selected armature and models -have been linked in this new collection. You should end up with something like -this: - -![Blender - Rig Hierarchy Example](assets/blender-rig_hierarchy_before_publish.jpg) - -### Publishing rigs - -Publishing rig is done in same way as publishing everything else. Save your scene -and go **OpenPype → Publish**. For more detail see [Publisher](artist_tools_publisher). - -### Loading rigs - -You can load rig with [Loader](artist_tools_loader). Go **OpenPype → Load...**, -select your rig, right click on it and click **Link rig (blend)**. - -## Layouts in Blender - -A layout is a set of elements that populate a scene. OpenPype allows to version -and manage those sets. - -### Publishing a layout - -Working with Layout is easy. Just load your assets into scene with -[Loader](artist_tools_loader) (**OpenPype → Load...**). Populate your scene as -you wish, translate each piece to fit your need. When ready, select all imported -stuff and go **OpenPype → Create...** and select **Layout**. When selecting rigs, -you need to select only the armature, the geometry will automatically be included. -This will create set containing your selection and marking it for publishing. - -Now you can publish is with **OpenPype → Publish**. - -### Loading layouts - -You can load a Layout using [Loader](artist_tools_loader) -(**OpenPype → Load...**). Select your layout, right click on it and -select **Link Layout (blend)**. This will populate your scene with all those -models you've put into layout. diff --git a/website/docs/assets/3dsmax_menu_OP.png b/website/docs/assets/3dsmax_menu_OP.png new file mode 100644 index 0000000000000000000000000000000000000000..f707a7fd956aeb7367abd3fda3ba520012540e95 GIT binary patch literal 69195 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 XbfJ?s==gs6hXciXs_ Date: Thu, 26 Jan 2023 12:45:08 +0100 Subject: [PATCH 0225/1271] new scrnshots and text --- website/docs/artist_hosts_3dsmax.md | 58 +++++++++++++++++++++---- website/docs/assets/3dsmax_tray_OP.png | Bin 0 -> 218373 bytes 2 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 website/docs/assets/3dsmax_tray_OP.png diff --git a/website/docs/artist_hosts_3dsmax.md b/website/docs/artist_hosts_3dsmax.md index 4379f7e71a..0d57d362c3 100644 --- a/website/docs/artist_hosts_3dsmax.md +++ b/website/docs/artist_hosts_3dsmax.md @@ -8,7 +8,7 @@ sidebar_label: 3dsmax This part of documentation is still work in progress. ::: -## OpenPype Global Tools + ## First Steps With OpenPype Running -When **OpenPype** (reffered as **OP** ) properly installed and 3dsmax launched via OP Launcher/Ftrack (or similar) there should be **OpenPype Menu** visible in 3dsmax top header after start. +Locate **OpenPype Icon** in the OS tray (if hidden dive in the tray toolbar). + +*If you cannot locate the OpenPype icon ...it is not probably running so check [Getting Started](artist_getting_started.md) first.* + +By **clicking the OP icon** ```OpenPype Menu``` rolls out. Choose ```OpenPype Menu > Launcher``` to open the ```Launcher``` window. + +When opened you can **choose** the **project** to work in from the list. Then choose the particular **asset** you want to work on then choose **task** +and finally **run 3dsmax by its icon** in the tools. + +![Menu OpenPype](assets/3dsmax_tray_OP.png) + +:::note Launcher Content +The list of available projects, assets, tasks and tools will differ according to your Studio and need to be set in advance by supervisor/admin. +::: + +## Running in the 3dsmax + +If 3dsmax has been launched via OP Launcher there should be **OpenPype Menu** visible in 3dsmax **top header** after start. +This is the core functional area for you as a user. Most of your actions will take place here. ![Menu OpenPype](assets/3dsmax_menu_first_OP.png) + +:::note OpenPype Menu +User instead of using classic ```File > Open``` and ```Save As``` actions uses this menu for working with scene files (reffered as **workfiles**) completely discarding native file operations even though still functional and available. User can use ```File > Save``` by constantly hitting ```CTRL+S``` keys for quickly saving changes for example. +::: + ## Working With Scene Files -Most user actions happens in ```Work Files``` menu item. There user can perform Save/Load actions as he would normally do with ```File Save ``` and/or ```File Open``` in the standard 3dsmax File Menu. ```OP Menu > Work Files...``` basically substitutes all file operations user can perform. +First go to ```Work Files``` menu item so **Work Files Window** shows up. Here you can perform Save/Load actions as you would normally do with ```File Save ``` and/or ```File Open``` in the standard 3dsmax File Menu. -Here you have an overview of the **Work Files window** with descriptions what each area is used for. +```OP Menu > Work Files...``` basically substitutes all file operations user can perform. + +You first choose the project in left top window then particular asset present in the project and it's task available to you as an artist. Finally you choose the workfile to open. If not any workfile present you simply hit ```Save As``` button. + + + +Here is an overview of the Work Files window with descriptions what each area is used for and could contain. ![Menu OpenPype](assets/3dsmax_menu_OP.png) +:::note Work Files window +Think of reading from left to right manner mimicking hiearchy of listed items ```Project``` > ```asset``` > ```task``` > ```workfile``` going from parent to children. +::: -## Setting scene data + +--- + +# *...to be edited for 3dsmax* + +## ~~Setting scene data~~ 3dsmax settings concerning framerate, resolution and frame range are handled by OpenPype. If set correctly in OP Project Manager/Ftrack, 3dsmax will automatically set the values for you. -## Publishing models +## ~~Publishing models~~ ### Intro @@ -99,7 +137,7 @@ be `project_XY_asset_task_version`, so in our case ![Model create instance](assets/blender-save_modelling_file.jpg) -### Publishing models +### ~~Publishing models~~ Now let's publish it. Go **OpenPype → Publish...**. You will be presented with following window: @@ -125,7 +163,9 @@ Lets do dry-run on publishing to see if we pass all validators. Click on flask icon at the bottom. Validators are run. Ideally you will end up with everything green in validator section. -### Fixing problems +--- + +### ~~Fixing problems~~ For the sake of demonstration, I intentionally kept the model in Edit Mode, to trigger the validator designed to check just this. @@ -163,7 +203,7 @@ To check for yourself that model is published, open [Asset Loader](artist_tools_loader) - **OpenPype → Load...**. There you should see your model, named `modelDefault`. -### Loading models +### ~~Loading models~~ You can load model with [Loader](artist_tools_loader). Go **OpenPype → Load...**, select your rig, right click on it and click **Link model (blend)**. diff --git a/website/docs/assets/3dsmax_tray_OP.png b/website/docs/assets/3dsmax_tray_OP.png new file mode 100644 index 0000000000000000000000000000000000000000..cfd0b07ef6e9377cd75f7a0383d1061d9394c1d1 GIT binary patch literal 218373 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⩔g^4RmBv&M5V}RX%b#SV`%BGupowwv zO`%HlcSO|aWQa$lW8*13`xm^IsDshY5QhS~ZiV>3NH{h-ordiS47Rb)%Ml*o%FIw| z^>SJ2?+W>4OyCCZgrl)oNOi=KUp=9o1;$~p18KNFGylu=S8Wc-qD=xy+3X zueml{$Q6ZJ&+t$gCg{9OK+f_GS|~DZyMDy)p>OF3KNEW&t_N5lx8M$|)eQ>YefRs_ z7pBj_?6hA6?Zy?`ZT3di{`>8F$~1B~$xf*QxV1~4{?$ihAgBeVea}w2Q^7f+J=kX< zIKZ(Qi|w|FH8D%Y;XH`K`%auMM|0VSzPt}hq|TA?Ba|A;Y4PB($`iBUFk^5qa=a7qP+<>l#T&Xzt-j6{bjB$b^$XFdcC@o&}v7@+}}PwWZ$Ouitq+L&h~$Fdeo3pSYSYb z-ZvB?Ndy9}`+}fm+co~w(~>?#MrpKzyhA`Uwu?cwxJZ zrkvvvmDXO5pP-zRUw;og(g;xBshhwPH!)4S9)7QVXc)9oRp!z6r{UK;HAv}MvV9HH zG!AIUt`N;^T_8!Y)jb-tk#({XkW$M!E}Q zxpnad0x*@4pSRruM_6GfxDLxG(LM+F_Pj2cm;}Z^#q*!uNe=0IFP^H18g>4Q)sTVe zW0wj13bhqrqg--vW%yA`f!DGCf`&rKG130H5%cG>=-?L`mCwO*CVqLD_EJ2V1|0U* z2`LKUEOjjHP2Wxs3+OmTl929a=B+o}ug%Nwu?_AlH5erx^7H(m3?Ju>|M zCq^nQKFNg6u>7-8M1joxQ<6^!rz)Hr24?wbd-&`b$yOABE^<(_1W(}TJ(P_x2PJRb zDN`{l1~coPCng%5QYoX6NN}`2!KV;O;!#<|6znKjzWGEdcPv{3tSyg1NJ>c`K>lx0 z2W9W}&o0Jn4917zOxE#Uh&8)<@pHlqW8?8|8Mfu(_#xVLE>;^&e>qT4VD(~8lwp6{ z^5qZDkZ5O|g1}OwAM2Q?Gbvb^A0#7~3b2m`Sd}ypfJnrWT2V6VL8U%ZML-ZbWRjuC z0P9{ifE99hvHs>f4OFOelk-wx9fMAcRm`Pde5c0|G3nLYb%>aFEc$+{)JG~=9N3u9 z2vA>rj#c(pdXIS%gacoAK^N_IjSgo#oHdwsvX_NV07cG zzo`Z3oY5-bqrXZ0Y!T$c#JcD*6}udM6}%ts34fl(gsNn44(SRd001dWJL7CPhPRPZ zNShGWo_%G7w~`F)pvC=msBHq}Cum)4BhBcL7X#!L?ZxeAJgkDVxZxHR@{xE`)z3Dv zUqvWm3;Ogw_~@~J7ZdKol%u`KE$3Nl{+#Qw*0d?f4Qu+-dTGrj>}jT{OTURoZ~#LO z7C1tARTE!pH9DmAU@aC@VCUfIF1*~KMu%*JQ+v6;equqzM&-ne-{dO#r5}oB1}6Ib zU4srxU_5F12?+anJ3>DqihHn4VUB;eD(ta*odDc86BHe^%--Gpy0l8NLQXjQ6=PGB zcc7BURY^A*u!qOq?{er`-7lK6fePRBt?wsr#UM~Y(v!Tq>+5m^^$V|HI^Y@bCRR%P zVefnQZ1XwqC%6j4t8@zfa%=LO-%v7mU2h>#HR=*3M9_d64kc^0&}KCi9e36Jw$|mT z4`UWYq`K1P>3&)DDAwfoxZb4{hky}+fcCOc_YsM5-0qz){YwC*Z$!znQuM{6e;@(G zZzEC)eWQ!XmyqbbL=_d%EKVU+cP`PJ>1=65k6tUYWb>5rYw~ zZ!2Bmdfy8wM~u|9^lnB;X*^F1DRFQQdih@g!|&;){xw@~2^=C@+7r*{|6fgC85TwV z{k=5O2uMhSbcl2=NQtz7bV-AVbhB^+(v6^WBOxd$xkz`1bayVf3(L;4zw3WJyYJ@3 zyqPoKIp=&T!m!psZ&@NHm~AkhIFqD+nW}oA0=e~)Y51S+7wPFPKbUSE4Wd3)QnRX? zypODoLxp*3R-Ta)^Hv9!iOR(5_ccZ;{txTmqHhFB9L7)o8oTtoR03eF@N;TGOy4=`!i~mqdskubimv8EMC8M8C5bl*Q$fJK+kiLOtF`;-!TCF{d`c zRYp)3qc=HSfoJXR8KgbTzpRa7fmy2pe`K+(JUDO2M;$J%j3E6&Y9e0avgPrKhAvUWSyS#ELA}OBz1qxXBJA?N~vJ|!f zPb5Cgg%0)`lAOK|jJxBKnE3is;o_7zo}q78i3RF_85&`)i_p>$L)XPRQEDSrylB`` zw9v-{3`zXtoU8BzxR{4~`A`_Y_x*efFv87#j;FaFlz?~cS?>zAj>hjURm?d*0N(qB^(Y|l&Dr%HXRcwUS!h@BJK zNYYsh#23B9$5a~kvHjUN|MYMSWAQ8X&%3GTv^x&%-ivnFIkwZhco%V$0YX2L%`23H z!|@enm<8v)@?)kCOfB0!)qTQc%#pU-fRW;)|33G77OkRs2Da#SbE=b?vo|-t{tj~o zd8tEyw=wf-)Zx)*9*(q77RCK|`jX>V{hac6G3rGRyCK<+wr`~QdLSQ`0(j7EG_J0e z#nVgxy(uf3wL@snukM_(cEqRfd+IL_K-pAbK|GmHI|7YkA<9lI`iKTzjd@2eCb&O${2>CuzS7w9 z?=ykEGBIA|yDSKdsy@$j{mg=aX@Z5z5=F)WDkxbNM#G&-(8hLSBUA0;{;1))a<7>I9%zu|3p(xrCkvv5D0N`^@3ciF z7bW`ps18YCti&pXh!8Y?T&+!#+J34dM(c9wnnL@^_Mjanj=93OA!H5H&yfDP&nwp+ zs`8FA{2ljH-5eoyUXWUxr_9s<_r+MhNvsWy={r4_(@GayQrhXxRy}2d9%;`E`s)@d z2wI5_+L!`s1vrdHz4RSQr?yn=Ehv&D)Av1)@w-UAWsuG$&MqH^50IFtZnbBSHoydv;E_gF{(D|#Hmmh=f|c*2Ib7mzp8u0b^$vKO zAGJg#=o#v0zz9*4T1xiePJ0zyMf)SA`3L_RFFQ}FsljyH_7j7nlP#e_G4ATq<-!Qn1;)DPd zlNy6}wkPTriEN9zb*H`^SeV};W-5WJhCYvKEpFR>XuLRuP+c;qr2HXhcm|x6v3S{v zUlq5&lUlz)&SbTZ_G*?$_ZPaUJnN9E(z%eke=L*rxsr$HMDlt_8(kE4G2s8$P=Z>= zK)KP!f-bi28nF{wu3Z@Ji4a~>0rDW1NxGM~m?Z=yJkgchM+Z&b1eq3hlTBZbWAMpy z*uv$-Pqmdk_sF<8gKF)cT~0jEkUe&n2E_)O%JY>XYQbo-s!dE6O%8DH+0*Hl)gYMr zYFYeh-S5-CO&f(&+|TU&UP1fDHmd8l+wP!Wz@|;#ipfRNK5%uE-}!Qz>N&P5Q^;VO z=d;=Qc`T5sc?=%b{Oc7N>y6M(ZJ8k7sH5AQFiX@utO9~q&JPJ(P*pd~@q|eT9Cl#@ zGd<3^Hm@Kyt}`v%bw&0{w%yt`+YAi8f_??<4(wY5?UpUIMhHC9%7TdWQ*E#R`pc5z z*Q1rURTr0?jL#lRFAW8OSOp1iqLU9FeVb60LO`ipj}})kAPVqOj~;XYT(Y*S0w}@z z<{1dkdlmLRh&GHv=P65Qn$8{%lUB=+aA#r;JvUv&%axq!=Bh1U72V#j2Y-f$*-joM zTX7!Q&zVm#dvn7)$?>{GaJO&#!0$3ib9zQ!>K_}M3l+37(Pq%vgw_Sbs@o#J5Yi^S z9fL)riG7(|-uSlWP%@`ergEMC-aD#wfVhL|McAQ8BPf*2&4i4yAw?j!1IZmza1=wR z;1Humu(gA2AwBZ)^~#`(!RME>TUH6r7Rsp{=nNUAJt`kp*%cGRLmrGq=5M;AQuCV|~f9?z(9c%k}@oN-}lMn{2a)Qz=R}KGke%ZlO z%zGP|Isc94dBO>XIgeEc|EP(>dS%@D;4Z`5SCw;|j^&nC8G-7ks0=+<8J9%r+gMzK z8COCW_5BS!S_{@!J*2#<7_XFvHapVmPe@%H*#&Uvt zBs$-AsxWsYiMm_-K?Tp}KN5^!eK$r^<+vu1G~jAn>U(rvkbMApk4ZCpp)H?CZv=|7 z`c)V*)8BXAe7sn1e?W#0!Ys%MfBQqq zxY7GYd>z)+>rkE+-x=L5O@|ZWlpP*uXlN*yFRfnjYG-hoBn<{-d&SYYh}-hV>%?L& zZFegla?1^d?&7UOQRDgRmsmDwZ2q7AoOV8GHV-`ErmkK81549hIIq96xXEtz&{MXN z(A?U@gVAs}xSkz7=hVoON<%Qo{DKny7xr)qtWjmI1h2F8o7jZko}wAx?=d%MZM6TK zesNz|4G_c|09GMDwQAB?}aJ8dfK$QQlBQ&zSr;pL{__Oir*SYp8-~!$~dB4A>z~>;}t|s z3JO?#kIUmFQlF<|O}#RY)(himVY_&$A@&$H-(I;h-Zx{@?rPCEhc8rv;);D~0n;FO zk)fcHby;~ zuJ5$oL+aUmVAY&EV)GpAtiRB09S4QvWpAYP7KMzzZZ$jb&=}?OZ2wcY_NC4sgz>Wl zKj@F56pc+mM`=&%G){2wIZ@dcd_y;H&fEA;a$UiN;Y#@b0VmoEXWCIdVM)!e6_spm z3-L(B9+Y4nMse1&}(-Z)(M4^T@y*p z-*S4ZPuBJ4u0F^0iO%>~_(@Wto!3k(S>Qzbn66Mj+u{04elQ7MzrTFA*N9GQ5E*gR z-%`^qX7kC3$4mJiV*fNyc<)>CiOYX?6v&m_Cy$@1LuIfZ4Et_w9)nj66bmf_k)MS{ zq$Dahk@q@uYjX1dR@LUkr;GZ>!yGxQ(ARha*U@K^WC4aEQGn-!%;8zSne1lyLJs<^ zD1{Gr5wW4h46zUg|4Cvmx(s})hLby22(xO2n& zMZS!by`I&5yrCA@+qSvJ{Et>O+6JOUZ;W&RaXL#bx1E=gnljkv=u zy#3PZ&IjI1)#M8+$Ic%Xmn7bP%7M7n9Te8 z%P6#L57S;Af*?y7iEOXcM-ZfZf-|2L*;tjV24*OMrew%`Nn14V%_zLC)%EQaDb_L; zsQQyU-#9qPh!pe-LVVx%mPW_*+>!`vW>xdh6?A9*W1_9<%e#b_obVo(gNC(Ve;5Tr z(rWVDLVkflq?m3iJ=RYKkZi2_bd@5vslKCs{BNJ1|2+|%Wa;`H$X2Gp`~+CVlBmba zU3d<<7$a8N;Rq(e_A^V002HO=CwPMF_?2K9Sl6{;;JhetfrfQc(84332S@8CE>piL zr`3(YGx&S}ayZ-X5$|&!J$x*x!A;a`x|tGqrM#Kb(>2MDCtoURI2Kk#{jG|6qtTPu zVFP@R1xh#|;=JqiZ}b~#^V?>n2d^V{f)8l^@ZRFYxp%PX+`YZJ@!c@>Ja9Sho=qyu zjhwmrlnPu;$k}yk*S1`?F4wl7B|ZUn>>=y5xrf13@kD^qIPHG~CG@W&SzJI}?;8oF z=H<74TKiuEs`l`o^-&x|xfCd$@GC8|i8|BO3!glqJJExBN1~VFcIITHsYoCun#x|7cHRJIOHDw>whR)J_q{y_LX$6 zcj^V{gL_%}zo7?WKWJfQrRE{zaDaTsuobOQdi;4u_ngk{0rm$Qx7Q{K_1Zh?c0%j> z@8fHz0%HXA6u^VkrVhBQisM!g{C_UMbjc%IWtrW5K7}Sdb$s`my!fg#%W5%hTON&c z4@%{yx*3sGf#mP(%TVw3Ega}RxWt#wz`6H1iQk^jzm$AG8uG~=}>B$xavO0rRaAfF`SkV_dGbq`o1!-FkX)Ovf6u3p^tw8`4B^$Io!^ z?Jox|oaD5uZ&i$1X%kOAwHZ7m7mWQ5MrQ>j2m!U#c%UN-7FS}>!O`PEOwUSw=SCj< zzlT+8uRgMa8OHs&Ek#~XR}GOhii7U`Hah!nobv_*%BjO8_m82qu*3s?#6==Z_}eeJ zuU*dX4VYPTnn=;JE$^s3=>>@w07?9B*;`f*FWTHb|TWy(7}VK1{B5sIigfpwQOc;Va1BOzn;bPD4xo z)l+Rb;l~)Tb_cq;&s%RQ2|H!V@PW6S_yBpbuo4wM)fXO(2f5%cz=JPI3tCLFFF=M( zw$o(-8Wdbl-d*2bC_&+crj4We&7nws(BNO5Zs-khD7T5E+umi1aK=O~1_*}B>|&Nq zP!JxSvbFAq>Knuo{WXOI4iBBmb3PNXcB1wId+wMtluVz~4N03lO`)gB6~RWUv#Wpj zU7S6C&;RWPm92+Mau6OW55hJydz@h-m4=W0E+%3HVUlCkn4%2?zi7@cS!%*zl{muG zP+@dRWAO%1iCSrU;JMbmyO^AqMaE2GP2{<{m{yo-8nVshe%l5;O z^^g(`u0PY?VLUVGB@&EG5ldx{syhoG)=tLCxRkY~G+wuhJxf+?&~LHW@s9jtf%#o0 z^~o1wiIPFew;6iIlI1 zEXdsSY@pU=Bdf~E$Wa{V7b6W!-=-7qh(Mi{-jm2KyItg zVM9nynfk!G$WG=Xr`#(H{d(8nN2ZK<01d3RT|?VzoF32Jx}GhA<)di^KgnI(GH~Tn zg}8hq_#va#dE-K+TQ)q&EHPR7HT`F!%*jytdnJx&3i9lvcYb}we1tAPmFm65hcp`B zXnM17Ptc^TiM>FFc_;@9>pUIOXgY}FuQpA*VbNe`D1Me#TH*O_>3-@~s3gfVgADXO z0bJd<^V7o|drTGig3e|eVbP&#IGJ)jHDNG#i$fevskb13@E2uCee7yrYQ3uGyG+nh z3TJl~!oXsgQH$#mc9l=pEmI3+8auFi^?1aApr*_2fifKy@eV)-D$zK8f~8^A7MN0& zOZ4AU)Xyi)FnzL2kw&?17w55P_!0vMstg!~TKe%10jAlY(EA2q9xIzzpz{|f~pJBz?3*;4-)_Wh&lP;NG z5fSx>eTcp@!4lh7fxxC{LO%kzP-9AAaP$e-2Xla1=8%W;U zzlK@f>WNjB>lo1DkQa2P{UKXp{R&->pbd0ECPZr^{j)9v$3*+Ue+E;lltjK%v&8;j zc_jvN!C;VAQp!gK1pFxz>w(~-etDJx3X2q3fD;AeCVL<+aZwRFR9+^B?C&wqwdAMQ zPMy-ojUr}c8e@IF-6g2<`!A=PZ4$nYut@(m!p@-KhD_9#=mB~JVwcwtOCG%pijJ|E zX5h7PCNi>4rtFVfVn^ehh&xV2jVRFxxXxUJx4i~> z&K7`63&(dr)r1ry)Pxl;gIB?aJPwYwjIY--s%Hzxscp1g)(5X5gwoEOcEsMb ziM}w_Y>6KE@zu$dh}Nc?>s1cldI5H6f_q3HeM~}?BsI;YE>?PSX~_}^uK`11t=SjIs$cA-_FKZ| zN+Jg)2>`o#>#x-RCg(g*SB1a(jVShnk6R$;ia}J&?7Dm&bIQ z8v-?M0!Fm^ESu0_m4R~{?2CC0M*lCM*+$H-e^xB0K%weQAkfEuxT2EnA#&A@?5v05 zEf!PkV+~)IR$6gDO9|Vc#7|S_9`{CUL9 z(nyd2o3-e&kr|9FI1Fem1exv)y+VQ!qavWz!>n^8tb)56Dnc*@rrq~QtW2&}urWJe z&L{oQ_Hw2D^Ux8r{g3}Ro-zW@SQbrQY^p(}PYq}V!fG*(DvFbS{zOBZIc{sg#|G%l z3M-98?MBW8HjTE|(2zMt-dx-V2R*m5=RPDpYw{XbZoyZ^Vs|e3Gy z&M!!-%TYbRgdxCh$qhfDe`uBAB(TKpDX&V-(!tW6p?0j z+$KTR+^_ET1dJN|hAvktfDWF29ruyOnGk0Tia>w!8qlK`y01#90K);F%T#TYlq6BR~|O?kyUg*>hL}3u{TL+? zTz)gpugGZ9_Avor+n~@~tC9|-Qh}eO+3a%~&OrrCzeDgG$*{l6IBs13$Hw|LZdZtw zH8t%&&YdU3Mr+Tiag;17hB5$sJ4+o%PPK}EC3f(E?=A-`ofoTr&sy&)6bvFn zr%HnhRbQZ7*s4j@?76=zkg+4TH_e}P3H&=DbLx9WqoY}VTVTl_hu~CgKEs>CSVPLW zMhCS={`*b$)+~`;ih6{HX;=Xbn&dQnQ8~dWZkxH#y0{If_Aa<64wvq+sfvKeln_eP zc{WQanY7^CR>i1ze;LZ}N9vp2)m!T@P2UhQYeyoBEqmEn&Dy_$4hpp3y_`ZF@}Eyb zCd+eM;I7Rd32$7J27G;l#qj|+LO|YX|5HMada6fa^O3Do^J^5T#HE)`f=CZQ0m&1C zEmDgB1p#`S>xFS1J7j;E>GYr<=js4|qNzWF%FA>=J)(Ga@~5q9FgE)0SAFlaYwq&{ z-7-Ed%+IaE>mM@sExl0-HR&p@KdJU#T11LO_nTOta6_rC>@z+vzo2kwavj0TgCxp3 zB^=6rWCxjD?CNnMubuh3)_PRvY&t#0zv-zkdCtZFOyu6jOD#=J7O-<$>O@cX6~X^C zr+@SD^ZMGpD-LiX3ci&g2>u=~G_^sgg3&XErzdW@UT^Rndv zr_;YL1YOw=^6M!=hBiXvlC1i=R7xEt z(Br=**B=0{a&=|1GP&y1qz%h>?~8}gXqp!Lg@qsz9S&$}10qu{yhbO#=tmVv zFd4pXCD4D?ydySK1!FC!u|nuAcc(%UDg<)%<27U)g$MnEf3}(O%QGgxSia5U*@4je zJVbL`#IEwZiN)`EekDRAxfS1^w7`nrf=e2H&}-9&5aczWHDdUbk^oX*se| z52f_vP&^)WKb36!T*i;}b4>GZ&^ubxZs{&!Ay#yW1jVNJv7c)U;=Cz8>A@3JM1HQ= zC|Y5R#Ct~W--ecTJ9@3q9ybR!;GkN}=zquG#IqABBY-gisb3TfvyuTDo_lvA7pqc^ z>@;g~Qq80cE3{&}W9D&Ti2a2dC(;GdLe-3!XzA3 z3I_#gJn3QlIzwnuZHYHa&`)vVs<1<*8FXiGLCSm3k9B~Q8C=J#nhTgI|7O7Q>4 z9f4Zu5dl1oSXo<}UEIexq}q9N8mW;83O$BD^?^Fj+lw>Y=GK&uW}?o2W5J%5Z5-!& zuDEe~bs?V|RncSGtm^!d-xhI#`b4A^9qD4#)GJhBC@}a=f72||u09@u5G@$^GdHZMn~rVGkHxo-+=XQfD0VGI|{7D;I^t#0iELzWbgt_MUS|ZO9@D zRYvb4$xO}M1E%j{4|OnctS-2lWzcT3;QYEq@X_D!7mMd>vyJNI`zS9t$5#78T45^;i92(_@mGs@`6X(i%uw8mb*6^SF z4&z!5O(1wydHhr;@7t7l=2;IH%)qN3dmi^4Jl^M4`4t*6(?XLZ=ljdV1wS|+YbI?= z=4Hc#^u1@M6ZGz20i|XHeb!MWsCW&s^pf<2IU4 z-;m;km?%SawXSBp`IU|W(x<{d@(2xEa?U(RLHpT+I90S?&>fgUK#wS*Qqu>2Fv3^$e_lO(jsgA@xd zK1FHr5VSOU1Q8zbo%*0`qNrSYp%k;pt@r@R;e+SqaF`d;;_T=V0~wpemQ(-a-0V$N z(_ts}`;Ucet;(vtQA;vj6``fshy8apLwsm*us~5QX@ojn8zG+jxO`+-X*MjZw6pg0 zhlj6Rd-?TvA>CAy2>;*#PDuS^Ae%qGoW0#~cBM^#vC=F0U1H3ZP zy7o-pK~uYZlnCEm0qEZRrw|x=gZBjJzh)Fmy_%^#mqRx@jg2=it|65y=X8&jGr#W< zoQ#9}SKis;;nRR@tFP?o&Njyi@DI($z8FpaUY--UIQbWgLr!Y*T2DnIPs(Mu7cWD= zQuMZccF4x&V=o`{8$*txrTk=JV#I|$1*WOKP@^mS zJ-{D_SWu>@FTE|tYHW?CZb<#EpmkVT1XPzbq$U|EVG%Esp>2A}H~=$=IB;L}ALQLJ z)NxInl~T7CU=NAdijhw#I7lg?;u|xNUm$)bMYX;9Y3F%`q5saQ+M_dq zM>zyceXr9I@bzb{+opxWQWDK zGE6ru>0n`kMR>e=@A?GD7wPU%zv$t2_qQ|)Cv>(pRy6x;nh5a=WHXzjwm_sKoz`_J ze}`T$9mD=6Z5#-UgWBsz)@oGaZDeU|ilW|U8H!C(ODpEz{_+y;Z2!x@d^CI3O$1!@ zdg!H5z`9d$0}=GTZ^dc!H6?rdvJby&b6r#2jhX~hJyZ26mOK5d$<}GEem)m|br0;` zG}K`%>`c+^$kH~FFj)|&H%ml|hn+G5po)&PQHV`XhS}FR9*m*F;-KwkEGxTLxICd< zqI+?dwJA2JC}IY`ZPE2S)bzRqI%UjrPta?ecpY+q=4V}(=vuZS67lv%O}Tc^+PiJ6 zxUXsh*010ua)PK8H~5ne4aR*?SZUfz`;QH>axlaIr<|q4y>jo`!^z`>Bq2PZ55YO` z{Ne2}U|X(SbpR~2jt%5^Ocew_pkRg4-mAffaZ%OlM!cp92|~x>A5EG=iiIR>KSNwj z8^zby>b6GdANv&N-G1bX*p*4}RGT+juYO4g_3N)-Tc|dN9`x8n!{2;i_P)rr+g8+J;_5>pZ?8b77eEHU$k8JcSt;aS~AL@dw`S2qku>fdPHC*SWU6!V6w7i03nY(!Rp`Z-W%50Cl8Wz!UHq6Y zMkV8uwqc%ERI&w|D%7mhIHgxjlk&wqH8%lU)7Qc-U%Z)c#-Z%%-49EIp98eivDomP zezKn$kC-X3uSaLNSGdbCvobT6a@0&RITuE`ACz0{sQ>KtLW%C1kkF!DsQmc!G0VX` zDPUE>jCbVH5_|juQk6qCGIG;kb zk$L^>lPIC##l<)s#if&e_e5p;!{hKbXFv%#!`pbhkYEP;oxHGn$|ScbO`c-!7G2dp zqWNDx)#bU07oIA7lyl>+A~ZTC^z5hRU$D3$Ur`DAfP?&^T3ctOTEzy^7GjOI&YLBL z{atBlZb=vihh_HsjoDD2MDKB^XP-A)Rz*VHKK%Zy#8{bd;mb6DRQ=flO@9ryyt!U) zGb}n@d-9|rWchx;fp8ifkN3ADiNUz}wzw5C%G(WvRO~@ecuaDz(FQ#&iM?a${N-j} zZIID#kDC>oI8M2T?m*8qYSdyrdy`50lj1?dRc5tN`+rXHhtfg%tO?iAiFz9qqQ_yB zcfb#904b32Z`~qLdJM|F0gkc2=(_>8cPjyz5!_;N2u3g<%|IQ_MSK!WObr8&~e}2)(s-RIaq5%>v(ef2?z4LRxYWk zT3&;V8oaJymXxSA?^Cmx!hkcRGV2xkUia5CH*uMK)R9=yWk4-*lfjH-yS9VP1)$76 zTO@g$c;-_kn+)|(c=f*D?>z_$5kTu!n0>h zLChUr*_`@yZGYN%O6Kuww`kZ9eK^SZWS&(YFQSxt*i;$Geg3rZ$G%O;%oua2W|2mz z%dlug$$wgE_BmPLf`7d+GqV{PO-Jma7)1K{CCjFJf$x0ezW~f{#;BaEGq&#TS5Cwyi(5K+=v_(Z+wzQDHS%8qiA3h|=pICMRY zChkS-pRewbU!>6jiZRGS7jbRB-04O{2W&N=p+UhgDo%SYqG zZ6N;8=+-Ii3rpQqnr!x+;Dz`NlvLE`K`v3tAu?yv?&8FZ|IP`c=s)jwK&d2hJ@3{+ z#!SygA1;ciFrgw`n*epQ<<)%K)iXh3EhXrEUy`;o0uCAXhm87XHVXjgN+@OrqziyB zmk6%5SstFRuQCXV^y)$}6OwhPj8-$Q`>e>wnR5SIJJw_?)w{wYeEW@PDDFv*jIOhJ zJlCOH!=GG}-8ntYr4+MmUcW>2W6bgLH^Onn&@n`eC3(ds^%U?wQ|C98$pjhR?C$P} z30rN`^{gj5ikb>Lher6RiYcV`#N4_8iupUn?22;OVqC*4t4hk1LFGojP4HPo-aSUa zqK-fyH#VF(E3cHFU~;Zv$8X+m0{IS8=po6U>b@h@^9T{XPN?wE)t#wl`(WL&u?_GL zghQBq3_J$;5a`MT2=qWQ1h|Hvk>(>{LCd-?Q-*3NgUHv$s8ahyy6yMRcq?*;p}I&z z(?6S_R$I$|^jm^*vmPVU&_K9$33e=mrxMWQeq8rJ6&fHoW3j+fw+E2@OhLPuQvMML zrKC$7jOp2CzGw_@4|HMl`^ndhb&-6?N7u&*F_v}zTc_gn0N=I$BGut3HRe)}u1m!u z?`t}}Ry3pWm6-S}?nkeLK^vuuwRgw$NkV(^LTu~z`|W+|8CPt%(%wNgf>6t*52As0 zaW7qi=$=aLqgVddM}$z*PLG@Bn)XN+S@4q9^RtS7O&Bu1^Q-ks!A2!HcJ&Gcej6l?Tge+FL^2B>J0 zA(JPHeBH`*!;C#ZCz|uzx!k4iKKbiG`IvelpZ`Q=vBk6u;Lhqo^jq`sxW zLb6O41_w2g49Rpcg~(F)K1%=stK(_!iHEeMY$Anu-{a{0k{kSw^8FB4SLhvCuK(eO z(v1U7qE|;M&CV5f*hCsnM}MfllTC=$C7ijpA}%qr6UEYMQ4<}4a*sf<1!7pEaEtvq z%4C4bZ<-g{ILPwuuWG?V(B(0x0`=EjOYft$Ou$b%*knM#A5}q%taW}VKG7q(44MKB zdPUFv_i@ir6J?Ai)dy6%UTmd)s4i zW&)6$*#(U@Rf4%$Z9n$`?VUZSf z-U9rlP^Fjjx&RSd^Bw`LNO4yneH)yOh0tS^XJVQtISpPd4sRyjZXr%HN%?BKE2f!5 zN%bh9eTfG;5mDKqq@|vQ2@Qd|RLX@i%&B9Z8jqOn82h~0ny?-`4v85_qk_-Nt?ppV!OisNyDD}H3x&PfZDcFdjCl~sPQa%S~Up+ggI;yW1F{H zyZvVh1R_dSQ&P~czpnDmy%evb#p@VJtijnIDEQ9!CS*?dRLh^*iRDd;2+^yh4J_HI zi5+!o;W4aPkTNP>pX_y}+_%xnKNp&pf-kzu;LT`5+AL(bJz+_T`s$Mj0olm4cc=ts zFD=57go}x6mB@X2SpHtQ1dz=JKUOh2c!W^_Cj$&;L?~jq>^(4R&Yym{`V8$g%9mRM z3wxWj*?xAOcd-18tj+ZwEwTcJRab(GE>tMtKE84QE%!WRjq1b*y|dlO?yKXZmUidZ ziTgN%^wco9&|{%RDS|tG5-v+2n_E$wsRk!Di85Yje)WFY*j|u@$L1n&!ZL?u?u(a5 z|4a3b+r{H+JyowmGtV z&jF5;lGLC1V_O%3dQk!6u+d7GqROgFNosr|<*v4s(k`xJJo?No5p=@MDGRz7=IWN% z`6vTQ#ft%v|ErBONA;a*JTRo0(5IY*2~MS6T5zZ6r_LrLxlb*N-3azPy0o3%6i-vJ zu?b%)8hzLQbwtUKYdxlugWtDFBVS=-o&(2X#z(OQg$g8^6_FKJ72d=@jIp~Dwxz^ZQOB@hDsZHS}@h|jvl7;l3nN;F{?uC4w3qac80 z#^$HOEZk9FEu5btv6!^YVj&v77t)b{HN!#KW63;QvSL|iaD^Y3?cUCE91r3{HnH)U zq3aik1eCGa5?B@i+Iy*{8rX2;&LBj91jXG@Q9gOKdh z0r0x<7!&^k;n- zM|dZXTd-Z`v;wcHk_VBersr__@e#U>9B{FEB#})FcJtt^Nx#Je>P=8l;&4j< zx1BJv&A8IofZjY4QW#4)QsJu1=lXuOtqCgfjrK#l)3P7L4W5jg-rAnET~#7r?Mu1k z7FW`Z2qCfj@pZZ3TC~or=nc-*?9Vp@0%$k$)VgEgwA)@N-8O;#ml`D{eXabVm5TP) z4(JG0Y35aUAC^9EK(1mrF7ahmM(W^uY+g5V;ulc#djB_!);iVnd!~SX@zVQb4$ki> zKChiMBOuPHK>AJ`?3}$RpN=DAm+4wu6PC|+H^BEWE?=gQxwhbGbNtm za$f7yGN-QGPbg6)Ey+%@+hT>v^l?uZ;R)8d)SwKJBWiCC9E2L^KYjr|OT&}03W1SJ zw`nA&c={eV1yXekJbpQO7?Fm+zt&ndTHCmiqa+QT{wI>ZL$!r?p$wtT6-|uG^nm6f z7$>cIxeth>&bfWy%o{-EgFA;ztpnR)2N%0M>h;Z)*=u^Exmfs_XAIIszUEX;NqBsu z?3;1Q%X8%Pq58$9St{B_@RK@g&H!}T|Kt73uO`J+lkPyNz|LHS>g1q){z^ah!6}_d zbA&)eb3IqJip>3gv;ZN)x>! z4|%Z?Yz-=nKc(5bA)n;^xTi_^8lnyx2>$BG5*>7{UP^r6!*xev3 z`CCUbO`>>?-aRiY&Ak%eG9+MF@+b~fWuaMc8sFbWe@Y^{DD;2lT7kWho z&DoH&O)xFODRdbh_Il4$Fg zm~@|TRFa$`Qm-B^_z4h+1Hwt^?`vBKT^H7{yyZ$N8}P}<|H)+j@WokUS839fnC4eX z`?-Ym)NZ$m_Z!pQyX_lF&b#R9MUtzZaTBD`Q=^mBef$}6Kl*ETOB1}bWbiRbK72pq zSNcP1cKyPOKp^tzxGjgde5%)${+bEI$pfPlyCQS49T>2B&ci^^eaQ&QV71vWYJX_G%-FP*PumtGe$s6yPy&o zqJ5B*zjjV2ic_6yZj!djclR>&`wlL!C1+k(RL1y`c-6{r}nRUM6)K0 z)HN2uLF;1#Gz+2T_EYbnA^@z@#gbk~MI#FsjAHh|d79yb3z7%#@D4$t(b2J2+NhBd z=U0uRnT;5_C+^M5*N~-;pO%s0J*Yy4Ens8wp+o#G{N$EG12ZhNHX!94S&b-_IW~4@^sI^NnqMcN-pW&)XNAvl&##%QmDhmD=JXz645(l;{~H$44a<8 zf|T3Z7t9H&hDlr`7g~|iU%Z8c?_YOL*+`fd2f2G7UQ#{~O^@LQi?FG!+}Ag=lOj8~ zUOfuz`>GZv*|IHCQ1z_;A}#)uUnGvOuL#Z+-*yO4J8e%hOjb(Q{oC*g`xv)ixpGJ8 zoF&z#W<6W2UIw_t|GHs$VzX?9${lR-M{-t5jzQJ0@~>6+ch^7X+9LKJ5w78AlMs(y z)lUbQO$0#o7W)Td8s$%%FeXVCT>2>b&B8Dr7(UjTV6bdN*Oui9NJ9gM*BsMfv_;SvF@p&R_#7`|m2B zb-2kV$zZ?2;$R@rR_B&M@_4kFaUu>Gz0mSg`?jF>bhiHM7Eq zVbPStQBL-C;gX{cigGCWrMI>B&Ct@b^@_VjTJYUj`-lVD<7$|H7@`GZ!k$OJWl9X` zyI^-?GQ@#c&K?*B{U`t|Umo<(a$%@MyI*O*+`a*yePK-MU34NeJD8W|9u&wKp9>}~ zl%2TLJ&Y9M$bfRaf)6zHL!AGM0GD4wkLKjCkZzeM@qQYLX9tichP`E9*_JXUp6C9q zLyl~37zz*NQ*;RupzSciqxr7vFDB?Ygqe2{>%{l@z6{?d2>$mik?se>n`sox)?@1! z<6~4K)h15VP`2%uYaZBc>Th|cH4_J&DBkkSmEdUvH4s+xFqQPY@nwmI!Ml#coi%nX z$&+E!pwC3lt@Ym}gP(lxCDW9j5(5B3-%rwH|Ggw*_zM7=UwxQD7cp3!=TG{?DPAepbE^l7R(c{wpKF>e19o)^g%ZSP&?i>}o%Jw{O7)XpI8!#z!h>^(9W!teb3(v2Dp z88Xm72)a16@hh8?JVjyETry#Wo+_ZOSjYcet@GiLSni+`;V5q;_MjolYA8t=m5zQL z(uCU}+%A43XEA>pvQ5wONE|*80RdD|+IxVtWV;F7QKI=;4L@MaY%>o1v}<9C65q94 vY3qS@41vdxjwtb@ Date: Thu, 26 Jan 2023 12:53:02 +0100 Subject: [PATCH 0226/1271] OP-4643 - updated schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 7155510fef..80c18ce118 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -267,8 +267,8 @@ "type": "dict", "children": [ { - "key": "output_extension", - "label": "Output extension", + "key": "extension", + "label": "Extension", "type": "text" }, { From 0858c16ce0882949c570beeefe050f71219d28dc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:54:46 +0100 Subject: [PATCH 0227/1271] OP-4643 - updated plugin name in schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 80c18ce118..357cbfb287 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -200,8 +200,8 @@ { "type": "dict", "collapsible": true, - "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode (ImageIO)", + "key": "ExtractOIIOTranscode", + "label": "Extract OIIO Transcode", "checkbox_key": "enabled", "is_group": true, "children": [ From 875cac007dd0a3630b87cf545f73d038c4de79c0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:55:57 +0100 Subject: [PATCH 0228/1271] OP-4643 - updated key in schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 357cbfb287..0281b0ded6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -272,8 +272,8 @@ "type": "text" }, { - "key": "output_colorspace", - "label": "Output colorspace", + "key": "colorspace", + "label": "Colorspace", "type": "text" }, { From 18b728aaf53a90422614a97ddf7e528ff9a50955 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:57:03 +0100 Subject: [PATCH 0229/1271] OP-4643 - changed oiio_cmd creation Co-authored-by: Toke Jepsen --- openpype/lib/transcoding.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 8e3432e0e9..828861e21e 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1068,25 +1068,15 @@ def convert_colorspace( if logger is None: logger = logging.getLogger(__name__) - input_arg = "-i" oiio_cmd = [ get_oiio_tools_path(), - + input_path, # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace - ] - # Prepare subprocess arguments - - oiio_cmd.extend([ - input_arg, input_path, - ]) - - # Add last argument - path to output - oiio_cmd.extend([ + "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath - ]) + ] logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From 669e38d2c37481d089816d9dc663573f4a6895c3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:44:45 +0100 Subject: [PATCH 0230/1271] OP-4643 - updated new keys into settings --- .../settings/defaults/project_settings/global.json | 2 +- .../projects_schema/schemas/schema_global_publish.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 167f7611ce..f448f1a79a 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -70,7 +70,7 @@ "output": [] } }, - "ExtractColorTranscode": { + "ExtractOIIOTranscode": { "enabled": true, "profiles": [] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 0281b0ded6..74b81b13af 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -276,6 +276,16 @@ "label": "Colorspace", "type": "text" }, + { + "key": "display", + "label": "Display", + "type": "text" + }, + { + "key": "view", + "label": "View", + "type": "text" + }, { "type": "schema", "name": "schema_representation_tags" From 104dd91bba17cb59f5254426cfc98b7b48dbe91f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:45:42 +0100 Subject: [PATCH 0231/1271] OP-4643 - renanmed plugin, added new keys into outputs --- openpype/plugins/publish/extract_color_transcode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3a05426432..cc63b35988 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -17,7 +17,7 @@ from openpype.lib.transcoding import ( from openpype.lib.profiles_filtering import filter_profiles -class ExtractColorTranscode(publish.Extractor): +class ExtractOIIOTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. @@ -89,14 +89,14 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) - output_extension = output_def["output_extension"] + output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: if new_repre["name"] == new_repre["ext"]: new_repre["name"] = output_extension new_repre["ext"] = output_extension - target_colorspace = output_def["output_colorspace"] + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From ad6fef12fa0bc0d271bafed13165488397685f27 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 26 Jan 2023 14:00:47 +0100 Subject: [PATCH 0232/1271] celaction: ditching agrparse --- .../publish/collect_celaction_cli_kwargs.py | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/celaction/plugins/publish/collect_celaction_cli_kwargs.py b/openpype/hosts/celaction/plugins/publish/collect_celaction_cli_kwargs.py index bf97dd744b..43b81b83e7 100644 --- a/openpype/hosts/celaction/plugins/publish/collect_celaction_cli_kwargs.py +++ b/openpype/hosts/celaction/plugins/publish/collect_celaction_cli_kwargs.py @@ -1,5 +1,4 @@ import pyblish.api -import argparse import sys from pprint import pformat @@ -11,20 +10,40 @@ class CollectCelactionCliKwargs(pyblish.api.Collector): order = pyblish.api.Collector.order - 0.1 def process(self, context): - parser = argparse.ArgumentParser(prog="celaction") - parser.add_argument("--currentFile", - help="Pass file to Context as `currentFile`") - parser.add_argument("--chunk", - help=("Render chanks on farm")) - parser.add_argument("--frameStart", - help=("Start of frame range")) - parser.add_argument("--frameEnd", - help=("End of frame range")) - parser.add_argument("--resolutionWidth", - help=("Width of resolution")) - parser.add_argument("--resolutionHeight", - help=("Height of resolution")) - passing_kwargs = parser.parse_args(sys.argv[1:]).__dict__ + args = list(sys.argv[1:]) + self.log.info(str(args)) + missing_kwargs = [] + passing_kwargs = {} + for key in ( + "chunk", + "frameStart", + "frameEnd", + "resolutionWidth", + "resolutionHeight", + "currentFile", + ): + arg_key = f"--{key}" + if arg_key not in args: + missing_kwargs.append(key) + continue + arg_idx = args.index(arg_key) + args.pop(arg_idx) + if key != "currentFile": + value = args.pop(arg_idx) + else: + path_parts = [] + while arg_idx < len(args): + path_parts.append(args.pop(arg_idx)) + value = " ".join(path_parts).strip('"') + + passing_kwargs[key] = value + + if missing_kwargs: + raise RuntimeError("Missing arguments {}".format( + ", ".join( + [f'"{key}"' for key in missing_kwargs] + ) + )) self.log.info("Storing kwargs ...") self.log.debug("_ passing_kwargs: {}".format(pformat(passing_kwargs))) From 44bcfc167a448fd4d50aec91aed1195f76430ccf Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 26 Jan 2023 14:34:53 +0100 Subject: [PATCH 0233/1271] fix 'items' method --- openpype/pipeline/create/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index e44e5db851..95e194fda1 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -241,7 +241,7 @@ class ChangedItem(object): return self.changed_keys def items(self): - if self.is_dict: + if not self.is_dict: yield None, self.changes else: for item in self.changes.items(): From 20e32d0d259992efcf37970bb42b9b5a59c2209d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 26 Jan 2023 23:23:18 +0800 Subject: [PATCH 0234/1271] clean up the extractors and validator --- .../hosts/max/plugins/create/create_camera.py | 4 +-- .../max/plugins/publish/extract_camera_abc.py | 10 ++++-- .../max/plugins/publish/extract_camera_fbx.py | 11 +++++-- .../plugins/publish/extract_max_scene_raw.py | 31 +++++++------------ .../publish/validate_camera_contents.py | 19 ++++-------- 5 files changed, 37 insertions(+), 38 deletions(-) diff --git a/openpype/hosts/max/plugins/create/create_camera.py b/openpype/hosts/max/plugins/create/create_camera.py index 45f437b7ee..91d0d4d3dc 100644 --- a/openpype/hosts/max/plugins/create/create_camera.py +++ b/openpype/hosts/max/plugins/create/create_camera.py @@ -13,11 +13,11 @@ class CreateCamera(plugin.MaxCreator): def create(self, subset_name, instance_data, pre_create_data): from pymxs import runtime as rt sel_obj = list(rt.selection) - _ = super(CreateCamera, self).create( + instance = super(CreateCamera, self).create( subset_name, instance_data, pre_create_data) # type: CreatedInstance - container = rt.getNodeByName(subset_name) + container = rt.getNodeByName(instance.data.get("instance_node")) # TODO: Disable "Add to Containers?" Panel # parent the selected cameras into the container for obj in sel_obj: diff --git a/openpype/hosts/max/plugins/publish/extract_camera_abc.py b/openpype/hosts/max/plugins/publish/extract_camera_abc.py index 83cf2c3a6e..2a62c12927 100644 --- a/openpype/hosts/max/plugins/publish/extract_camera_abc.py +++ b/openpype/hosts/max/plugins/publish/extract_camera_abc.py @@ -1,6 +1,9 @@ import os import pyblish.api -from openpype.pipeline import publish +from openpype.pipeline import ( + publish, + OptionalPyblishPluginMixin +) from pymxs import runtime as rt from openpype.hosts.max.api import ( maintained_selection, @@ -8,7 +11,8 @@ from openpype.hosts.max.api import ( ) -class ExtractAlembicCamera(publish.Extractor): +class ExtractAlembicCamera(publish.Extractor, + OptionalPyblishPluginMixin): """ Extract Camera with AlembicExport """ @@ -20,6 +24,8 @@ class ExtractAlembicCamera(publish.Extractor): optional = True def process(self, instance): + if not self.is_active(instance.data): + return start = float(instance.data.get("frameStartHandle", 1)) end = float(instance.data.get("frameEndHandle", 1)) diff --git a/openpype/hosts/max/plugins/publish/extract_camera_fbx.py b/openpype/hosts/max/plugins/publish/extract_camera_fbx.py index e450de1275..7e92f355ed 100644 --- a/openpype/hosts/max/plugins/publish/extract_camera_fbx.py +++ b/openpype/hosts/max/plugins/publish/extract_camera_fbx.py @@ -1,6 +1,9 @@ import os import pyblish.api -from openpype.pipeline import publish +from openpype.pipeline import ( + publish, + OptionalPyblishPluginMixin +) from pymxs import runtime as rt from openpype.hosts.max.api import ( maintained_selection, @@ -8,7 +11,8 @@ from openpype.hosts.max.api import ( ) -class ExtractCameraFbx(publish.Extractor): +class ExtractCameraFbx(publish.Extractor, + OptionalPyblishPluginMixin): """ Extract Camera with FbxExporter """ @@ -17,8 +21,11 @@ class ExtractCameraFbx(publish.Extractor): label = "Extract Fbx Camera" hosts = ["max"] families = ["camera"] + optional = True def process(self, instance): + if not self.is_active(instance.data): + return container = instance.data["instance_node"] self.log.info("Extracting Camera ...") diff --git a/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py b/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py index 97de602216..7ac072b829 100644 --- a/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py +++ b/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py @@ -1,6 +1,9 @@ import os import pyblish.api -from openpype.pipeline import publish +from openpype.pipeline import ( + publish, + OptionalPyblishPluginMixin +) from pymxs import runtime as rt from openpype.hosts.max.api import ( maintained_selection, @@ -8,7 +11,8 @@ from openpype.hosts.max.api import ( ) -class ExtractMaxSceneRaw(publish.Extractor): +class ExtractMaxSceneRaw(publish.Extractor, + OptionalPyblishPluginMixin): """ Extract Raw Max Scene with SaveSelected """ @@ -17,12 +21,15 @@ class ExtractMaxSceneRaw(publish.Extractor): label = "Max Scene(Raw)" hosts = ["max"] families = ["camera"] + optional = True def process(self, instance): + if not self.is_active(instance.data): + return container = instance.data["instance_node"] # publish the raw scene for camera - self.log.info("Extracting Camera ...") + self.log.info("Extracting Raw Max Scene ...") stagingdir = self.staging_dir(instance) filename = "{name}.max".format(**instance.data) @@ -34,28 +41,14 @@ class ExtractMaxSceneRaw(publish.Extractor): if "representations" not in instance.data: instance.data["representations"] = [] - # add extra blacklash for saveNodes in MaxScript - re_max_path = stagingdir + "\\\\" + filename # saving max scene - raw_export_cmd = ( - f""" -sel = getCurrentSelection() -for s in sel do -( - select s - f="{re_max_path}" - print f - saveNodes selection f quiet:true -) - """) # noqa - self.log.debug(f"Executing Maxscript command: {raw_export_cmd}") - with maintained_selection(): # need to figure out how to select the camera rt.select(get_all_children(rt.getNodeByName(container))) - rt.execute(raw_export_cmd) + rt.execute(f'saveNodes selection "{max_path}" quiet:true') self.log.info("Performing Extraction ...") + representation = { 'name': 'max', 'ext': 'max', diff --git a/openpype/hosts/max/plugins/publish/validate_camera_contents.py b/openpype/hosts/max/plugins/publish/validate_camera_contents.py index c7d13ac5a3..c81e28a61f 100644 --- a/openpype/hosts/max/plugins/publish/validate_camera_contents.py +++ b/openpype/hosts/max/plugins/publish/validate_camera_contents.py @@ -33,23 +33,16 @@ class ValidateCameraContent(pyblish.api.InstancePlugin): "{}".format(container)) con = rt.getNodeByName(container) - selection_list = self.list_children(con) - validation_msg = list() + selection_list = list(con.Children) for sel in selection_list: # to avoid Attribute Error from pymxs wrapper sel_tmp = str(sel) + found = False for cam in self.camera_type: if sel_tmp.startswith(cam): - validation_msg.append("Camera Found") - else: - validation_msg.append("Camera Not Found") - if "Camera Found" not in validation_msg: + found = True + break + if not found: + self.log.error("Camera not found") invalid.append(sel) - # go through the camera type to see if there are same name return invalid - - def list_children(self, node): - children = [] - for c in node.Children: - children.append(c) - return children From 02aef52e2b77f3c9f3eb71d1e3352e7d31dc8a19 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 26 Jan 2023 16:25:40 +0100 Subject: [PATCH 0235/1271] Do not visualize the hidden OpenPypeContext node --- openpype/hosts/houdini/api/pipeline.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py index f8e2c16d21..211074d90b 100644 --- a/openpype/hosts/houdini/api/pipeline.py +++ b/openpype/hosts/houdini/api/pipeline.py @@ -146,11 +146,19 @@ class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): obj_network = hou.node("/obj") op_ctx = obj_network.createNode( "null", node_name="OpenPypeContext") + + # A null in houdini by default comes with content inside to visualize + # the null. However since we explicitly want to hide the node lets + # remove the content and disable the display flag of the node + for node in op_ctx.children(): + node.destroy() + op_ctx.moveToGoodPosition() op_ctx.setBuiltExplicitly(False) op_ctx.setCreatorState("OpenPype") op_ctx.setComment("OpenPype node to hold context metadata") op_ctx.setColor(hou.Color((0.081, 0.798, 0.810))) + op_ctx.setDisplayFlag(False) op_ctx.hide(True) return op_ctx From 9bb8d38d32a00a73c0e3795e725dec6c4373c83b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 26 Jan 2023 16:32:13 +0100 Subject: [PATCH 0236/1271] Cosmetics --- openpype/hosts/houdini/api/pipeline.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py index 211074d90b..9793679b45 100644 --- a/openpype/hosts/houdini/api/pipeline.py +++ b/openpype/hosts/houdini/api/pipeline.py @@ -144,8 +144,7 @@ class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): """ obj_network = hou.node("/obj") - op_ctx = obj_network.createNode( - "null", node_name="OpenPypeContext") + op_ctx = obj_network.createNode("null", node_name="OpenPypeContext") # A null in houdini by default comes with content inside to visualize # the null. However since we explicitly want to hide the node lets From 5cf9ce768404241d99bed02d248a4a9cf583296b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 26 Jan 2023 23:38:21 +0800 Subject: [PATCH 0237/1271] fix typo --- openpype/hosts/max/plugins/publish/extract_camera_abc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/extract_camera_abc.py b/openpype/hosts/max/plugins/publish/extract_camera_abc.py index 83cf2c3a6e..38afbd8441 100644 --- a/openpype/hosts/max/plugins/publish/extract_camera_abc.py +++ b/openpype/hosts/max/plugins/publish/extract_camera_abc.py @@ -8,13 +8,13 @@ from openpype.hosts.max.api import ( ) -class ExtractAlembicCamera(publish.Extractor): +class ExtractCameraAlembic(publish.Extractor): """ Extract Camera with AlembicExport """ order = pyblish.api.ExtractorOrder - 0.1 - label = "Extract Almebic Camera" + label = "Extract Alembic Camera" hosts = ["max"] families = ["camera"] optional = True From 8334323a4f62a1b99571a668101f21617fe867f4 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 26 Jan 2023 23:52:30 +0800 Subject: [PATCH 0238/1271] fix typo --- openpype/hosts/max/plugins/publish/extract_camera_abc.py | 2 +- openpype/hosts/max/plugins/publish/extract_max_scene_raw.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/extract_camera_abc.py b/openpype/hosts/max/plugins/publish/extract_camera_abc.py index 5f88df041b..8c23ff9878 100644 --- a/openpype/hosts/max/plugins/publish/extract_camera_abc.py +++ b/openpype/hosts/max/plugins/publish/extract_camera_abc.py @@ -11,7 +11,7 @@ from openpype.hosts.max.api import ( ) -class ExtractAlembicCamera(publish.Extractor, +class ExtractCameraAlembic(publish.Extractor, OptionalPyblishPluginMixin): """ Extract Camera with AlembicExport diff --git a/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py b/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py index 7ac072b829..cacc84c591 100644 --- a/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py +++ b/openpype/hosts/max/plugins/publish/extract_max_scene_raw.py @@ -18,7 +18,7 @@ class ExtractMaxSceneRaw(publish.Extractor, """ order = pyblish.api.ExtractorOrder - 0.2 - label = "Max Scene(Raw)" + label = "Extract Max Scene (Raw)" hosts = ["max"] families = ["camera"] optional = True From 83d611a1f64d3a3a840a31117208f28427318a7f Mon Sep 17 00:00:00 2001 From: Libor Batek Date: Thu, 26 Jan 2023 16:59:20 +0100 Subject: [PATCH 0239/1271] new pics and descriptions, also marked unused topics originating from blender host doc --- website/docs/artist_hosts_3dsmax.md | 39 +++++++++++------- .../assets/3dsmax_SavingFirstFile2_OP.png | Bin 0 -> 32985 bytes .../docs/assets/3dsmax_SavingFirstFile_OP.png | Bin 0 -> 36589 bytes website/docs/assets/3dsmax_context.png | Bin 0 -> 116939 bytes website/docs/assets/3dsmax_menu_OP.png | Bin 69195 -> 63305 bytes 5 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 website/docs/assets/3dsmax_SavingFirstFile2_OP.png create mode 100644 website/docs/assets/3dsmax_SavingFirstFile_OP.png create mode 100644 website/docs/assets/3dsmax_context.png diff --git a/website/docs/artist_hosts_3dsmax.md b/website/docs/artist_hosts_3dsmax.md index 0d57d362c3..eac89f740b 100644 --- a/website/docs/artist_hosts_3dsmax.md +++ b/website/docs/artist_hosts_3dsmax.md @@ -20,13 +20,15 @@ This part of documentation is still work in progress. --> -## First Steps With OpenPype Running +## First Steps With OpenPype Locate **OpenPype Icon** in the OS tray (if hidden dive in the tray toolbar). -*If you cannot locate the OpenPype icon ...it is not probably running so check [Getting Started](artist_getting_started.md) first.* +> If you cannot locate the OpenPype icon ...it is not probably running so check [Getting Started](artist_getting_started.md) first. -By **clicking the OP icon** ```OpenPype Menu``` rolls out. Choose ```OpenPype Menu > Launcher``` to open the ```Launcher``` window. +By clicking the icon ```OpenPype Menu``` rolls out. + +Choose ```OpenPype Menu > Launcher``` to open the ```Launcher``` window. When opened you can **choose** the **project** to work in from the list. Then choose the particular **asset** you want to work on then choose **task** and finally **run 3dsmax by its icon** in the tools. @@ -44,29 +46,36 @@ This is the core functional area for you as a user. Most of your actions will ta ![Menu OpenPype](assets/3dsmax_menu_first_OP.png) - :::note OpenPype Menu -User instead of using classic ```File > Open``` and ```Save As``` actions uses this menu for working with scene files (reffered as **workfiles**) completely discarding native file operations even though still functional and available. User can use ```File > Save``` by constantly hitting ```CTRL+S``` keys for quickly saving changes for example. +User should use this menu for Opening / Saving when dealing with work files not standard 3dsmax ```File Menu``` even though still possible. ::: ## Working With Scene Files -First go to ```Work Files``` menu item so **Work Files Window** shows up. Here you can perform Save/Load actions as you would normally do with ```File Save ``` and/or ```File Open``` in the standard 3dsmax File Menu. +In OpenPype menu first go to ```Work Files``` menu item so **Work Files Window** shows up. -```OP Menu > Work Files...``` basically substitutes all file operations user can perform. + Here you can perform Save / Load actions as you would normally do with ```File Save ``` and ```File Open``` in the standard 3dsmax ```File Menu``` and navigate to different project components like assets, tasks, workfiles etc. -You first choose the project in left top window then particular asset present in the project and it's task available to you as an artist. Finally you choose the workfile to open. If not any workfile present you simply hit ```Save As``` button. - - - -Here is an overview of the Work Files window with descriptions what each area is used for and could contain. ![Menu OpenPype](assets/3dsmax_menu_OP.png) -:::note Work Files window -Think of reading from left to right manner mimicking hiearchy of listed items ```Project``` > ```asset``` > ```task``` > ```workfile``` going from parent to children. -::: +You first choose particular asset and assigned task and corresponding workfile you would like to open. +If not any workfile present simply hit ```Save As``` and keep ```Subversion``` empty and hitting ```Ok```. + +![Save As Dialog](assets/3dsmax_SavingFirstFile_OP.png) + +OpenPype correctly names it and add version to the workfile. This basically happens whenever user trigger ```Save As``` action. Resulting into incremental version numbers like ```workfileName_v001``` ```workfileName_v002``` etc. + +> There also additional tools for naming like ```Subversion``` in ```Save As``` dialog but we won't dive into it for now. + +![Save As Dialog](assets/3dsmax_SavingFirstFile2_OP.png) + +## Understanding Context + +It is good to be aware that whenever you as a user choose ```asset``` and ```task``` you happen to be in so called **context** meaning that all user actions are in relation with particular ```asset```. This could be quickly seen in host application header and or ```OpenPype Menu``` and its accompanying tools. + +![Workfile Context](assets/3dsmax_context.png) --- diff --git a/website/docs/assets/3dsmax_SavingFirstFile2_OP.png b/website/docs/assets/3dsmax_SavingFirstFile2_OP.png new file mode 100644 index 0000000000000000000000000000000000000000..4066ee0f1acf3e8b3509b7bebb5c39d6a70a6163 GIT binary patch literal 32985 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 literal 0 HcmV?d00001 diff --git a/website/docs/assets/3dsmax_context.png b/website/docs/assets/3dsmax_context.png new file mode 100644 index 0000000000000000000000000000000000000000..9b84cb2587c23af025f81345970a7e91ed59aebb GIT binary patch literal 116939 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

mF6e$k}om*27 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 a_div_MF#sfQJ6(Rzco~~;KeFd&;Ad6id;7U literal 0 HcmV?d00001 diff --git a/website/docs/assets/3dsmax_menu_OP.png b/website/docs/assets/3dsmax_menu_OP.png index f707a7fd956aeb7367abd3fda3ba520012540e95..bce2f9aac072f227c9f92cdf17c91c5e9b4d979c 100644 GIT binary patch literal 63305 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 T21Ktx`Ou5!a^gjzy59c_^@|^B literal 69195 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 Date: Thu, 26 Jan 2023 18:03:13 +0100 Subject: [PATCH 0240/1271] OP-4643 - fixed config path key --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cc63b35988..245faeb306 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -70,8 +70,8 @@ class ExtractOIIOTranscode(publish.Extractor): colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): + config_path = colorspace_data.get("config", {}).get("path") + if not config_path or not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") continue From 381f65bc8c973725f8d57a345d70a12ebcf95786 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:03:42 +0100 Subject: [PATCH 0241/1271] OP-4643 - fixed renaming files --- openpype/plugins/publish/extract_color_transcode.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 245faeb306..c079dcf70e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -96,6 +96,14 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["name"] = output_extension new_repre["ext"] = output_extension + renamed_files = [] + _, orig_ext = os.path.splitext(files_to_convert[0]) + for file_name in files_to_convert: + file_name = file_name.replace(orig_ext, + "."+output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From c1bb93d0fbf75f978b1a04195edc102e3b113f70 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:04:44 +0100 Subject: [PATCH 0242/1271] OP-4643 - updated to calculate sequence format --- .../publish/extract_color_transcode.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c079dcf70e..09c86909cb 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,5 +1,6 @@ import os import copy +import clique import pyblish.api @@ -108,6 +109,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not target_colorspace: raise RuntimeError("Target colorspace must be set") + files_to_convert = self._translate_to_sequence( + files_to_convert) for file_name in files_to_convert: input_filepath = os.path.join(original_staging_dir, file_name) @@ -139,6 +142,40 @@ class ExtractOIIOTranscode(publish.Extractor): instance.data["representations"].append(new_repre) + def _translate_to_sequence(self, files_to_convert): + """Returns original list of files or single sequence format filename. + + Uses clique to find frame sequence, in this case it merges all frames + into sequence format (%0X) and returns it. + If sequence not found, it returns original list + + Args: + files_to_convert (list): list of file names + Returns: + (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + """ + pattern = [clique.PATTERNS["frames"]] + collections, remainder = clique.assemble( + files_to_convert, patterns=pattern, + assume_padded_when_ambiguous=True) + + if collections: + if len(collections) > 1: + raise ValueError( + "Too many collections {}".format(collections)) + + collection = collections[0] + padding = collection.padding + padding_str = "%0{}".format(padding) + frames = list(collection.indexes) + frame_str = "{}-{}#".format(frames[0], frames[-1]) + file_name = "{}{}{}".format(collection.head, frame_str, + collection.tail) + + files_to_convert = [file_name] + + return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, output_extension): """Create output file name path.""" From 5a386b58d6bc9125adf6148bbb854e0688a0adf0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:54:02 +0100 Subject: [PATCH 0243/1271] OP-4643 - implemented display and viewer color space --- openpype/lib/transcoding.py | 23 +++++++++++++++++-- .../publish/extract_color_transcode.py | 13 +++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 828861e21e..fab9eeaaad 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1045,6 +1045,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, + view, + display, logger=None ): """Convert source files from one color space to another. @@ -1062,8 +1064,11 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + view (str): name for viewer space (ocio valid) + display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. - + Raises: + ValueError: if misconfigured """ if logger is None: logger = logging.getLogger(__name__) @@ -1074,9 +1079,23 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath ] + if all([target_colorspace, view, display]): + raise ValueError("Colorspace and both screen and display" + " cannot be set together." + "Choose colorspace or screen and display") + if not target_colorspace and not all([view, display]): + raise ValueError("Both screen and display must be set.") + + if target_colorspace: + oiio_cmd.extend(["--colorconvert", + source_colorspace, + target_colorspace]) + if view and display: + oiio_cmd.extend(["--iscolorspace", source_colorspace]) + oiio_cmd.extend(["--ociodisplay", display, view]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 09c86909cb..cd8421c0cd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -106,8 +106,15 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["files"] = renamed_files target_colorspace = output_def["colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, + # but could be overwritten + if view: + new_repre["colorspaceData"]["view"] = view + if display: + new_repre["colorspaceData"]["view"] = display files_to_convert = self._translate_to_sequence( files_to_convert) @@ -123,6 +130,8 @@ class ExtractOIIOTranscode(publish.Extractor): config_path, source_colorspace, target_colorspace, + view, + display, self.log ) From 2245869ffd0723177c2e76111723d9aeb43446c4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 19:03:10 +0100 Subject: [PATCH 0244/1271] OP-4643 - fix wrong order of deletion of representation --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cd8421c0cd..9cca5cc969 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -69,6 +69,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not self._repre_is_valid(repre): continue + added_representations = False + colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("config", {}).get("path") @@ -76,8 +78,6 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - repre = self._handle_original_repre(repre, profile) - for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) @@ -150,6 +150,10 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["tags"].append(tag) instance.data["representations"].append(new_repre) + added_representations = True + + if added_representations: + self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): """Returns original list of files or single sequence format filename. @@ -253,7 +257,8 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _handle_original_repre(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile): + """If new transcoded representation created, delete old.""" delete_original = profile["delete_original"] if delete_original: @@ -264,5 +269,3 @@ class ExtractOIIOTranscode(publish.Extractor): repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") - - return repre From 31effd0a23137103a6ce9c3416edec5352781a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Thu, 26 Jan 2023 23:38:32 +0100 Subject: [PATCH 0245/1271] Docs: Question about renaming in Kitsu --- website/docs/module_kitsu.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/website/docs/module_kitsu.md b/website/docs/module_kitsu.md index 73e31a280b..7738ee1ce2 100644 --- a/website/docs/module_kitsu.md +++ b/website/docs/module_kitsu.md @@ -37,3 +37,8 @@ This functionality cannot deal with all cases and is not error proof, some inter ```bash openpype_console module kitsu push-to-zou -l me@domain.ext -p my_password ``` + +## Q&A +### Is it safe to rename an entity from Kitsu? +Absolutely! Entities are linked by their unique IDs between the two databases. +But renaming from the OP's Project Manager won't apply the change to Kitsu, it'll be overriden during the next synchronization. From 4d7d1773a96d2ab28f271460c067e10786457a42 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 27 Jan 2023 01:25:57 +0100 Subject: [PATCH 0246/1271] :bug: fix pointcache loader update --- openpype/hosts/max/api/pipeline.py | 24 +++++++++- .../hosts/max/plugins/load/load_pointcache.py | 45 ++++++++++++++++--- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/max/api/pipeline.py b/openpype/hosts/max/api/pipeline.py index f3cdf245fb..8a1483dc51 100644 --- a/openpype/hosts/max/api/pipeline.py +++ b/openpype/hosts/max/api/pipeline.py @@ -2,6 +2,7 @@ """Pipeline tools for OpenPype Houdini integration.""" import os import logging +from operator import attrgetter import json @@ -141,5 +142,26 @@ def ls() -> list: if rt.getUserProp(obj, "id") == AVALON_CONTAINER_ID ] - for container in sorted(containers, key=lambda name: container.name): + for container in sorted(containers, key=attrgetter("name")): yield lib.read(container) + + +def containerise(name: str, nodes: list, context, loader=None, suffix="_CON"): + data = { + "schema": "openpype:container-2.0", + "id": AVALON_CONTAINER_ID, + "name": name, + "namespace": "", + "loader": loader, + "representation": context["representation"]["_id"], + } + + container_name = f"{name}{suffix}" + container = rt.container(name=container_name) + print(nodes) + for node in nodes: + node.Parent = container + + if not lib.imprint(container_name, data): + print(f"imprinting of {container_name} failed.") + return container diff --git a/openpype/hosts/max/plugins/load/load_pointcache.py b/openpype/hosts/max/plugins/load/load_pointcache.py index 285d84b7b6..ca959715f7 100644 --- a/openpype/hosts/max/plugins/load/load_pointcache.py +++ b/openpype/hosts/max/plugins/load/load_pointcache.py @@ -6,8 +6,10 @@ Because of limited api, alembics can be only loaded, but not easily updated. """ import os from openpype.pipeline import ( - load + load, get_representation_path ) +from openpype.hosts.max.api.pipeline import containerise +from openpype.hosts.max.api import lib class AbcLoader(load.LoaderPlugin): @@ -52,14 +54,47 @@ importFile @"{file_path}" #noPrompt abc_container = abc_containers.pop() - container_name = f"{name}_CON" - container = rt.container(name=container_name) - abc_container.Parent = container + return containerise( + name, [abc_container], context, loader=self.__class__.__name__) - return container + def update(self, container, representation): + from pymxs import runtime as rt + + path = get_representation_path(representation) + node = rt.getNodeByName(container["instance_node"]) + + alembic_objects = self.get_container_children(node, "AlembicObject") + for alembic_object in alembic_objects: + alembic_object.source = path + + lib.imprint(container["instance_node"], { + "representation": str(representation["_id"]) + }) + + def switch(self, container, representation): + self.update(container, representation) def remove(self, container): from pymxs import runtime as rt node = container["node"] rt.delete(node) + + @staticmethod + def get_container_children(parent, type_name): + from pymxs import runtime as rt + + def list_children(node): + children = [] + for c in node.Children: + children.append(c) + children = children + list_children(c) + return children + + filtered = [] + for child in list_children(parent): + class_type = str(rt.classOf(child.baseObject)) + if class_type == type_name: + filtered.append(child) + + return filtered From 534c3d05212553597777733d5129a5b0a6ab5ca5 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 27 Jan 2023 01:26:25 +0100 Subject: [PATCH 0247/1271] :bug: fix scene inventory menu --- openpype/hosts/max/api/menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/max/api/menu.py b/openpype/hosts/max/api/menu.py index 02d8315af6..5c273b49b4 100644 --- a/openpype/hosts/max/api/menu.py +++ b/openpype/hosts/max/api/menu.py @@ -119,7 +119,7 @@ class OpenPypeMenu(object): def manage_callback(self): """Callback to show Scene Manager/Inventory tool.""" - host_tools.show_subset_manager(parent=self.main_widget) + host_tools.show_scene_inventory(parent=self.main_widget) def library_callback(self): """Callback to show Library Loader tool.""" From 5e02a1859d87a76fdfdb9a52d4f373fa86d5ac7c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 27 Jan 2023 01:28:15 +0100 Subject: [PATCH 0248/1271] Fix setting key --- .../maya/plugins/publish/validate_plugin_path_attributes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py b/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py index 7e3c656a1e..5b72dfc907 100644 --- a/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py +++ b/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py @@ -24,7 +24,7 @@ class ValidatePluginPathAttributes(pyblish.api.InstancePlugin): validate_path = ( instance.context.data["project_settings"]["maya"]["publish"] ) - file_attr = validate_path["ValidatePathForPlugin"]["attribute"] + file_attr = validate_path["ValidatePluginPathAttributes"]["attribute"] if not file_attr: return invalid From 7153ba6eb90af83dcf5b58bc6db447e1f1ef9a06 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 27 Jan 2023 01:28:26 +0100 Subject: [PATCH 0249/1271] Fix code style --- .../maya/plugins/publish/validate_plugin_path_attributes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py b/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py index 5b72dfc907..6135c9c695 100644 --- a/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py +++ b/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py @@ -39,7 +39,7 @@ class ValidatePluginPathAttributes(pyblish.api.InstancePlugin): filepath = cmds.getAttr(file_attr) if filepath and not os.path.exists(filepath): - self.log.error("File {0} not exists".format(filepath)) # noqa + self.log.error("File {0} not exists".format(filepath)) # noqa invalid.append(target) return invalid From 79d90e5048c0ad9e0050c807681c07f56c4f8e90 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 27 Jan 2023 10:58:38 +0100 Subject: [PATCH 0250/1271] global: fixing hosts without imageio ocio settings in cases old project with imageio settings group is having override --- openpype/pipeline/colorspace.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/colorspace.py b/openpype/pipeline/colorspace.py index e7480cf59e..e1ffe9d333 100644 --- a/openpype/pipeline/colorspace.py +++ b/openpype/pipeline/colorspace.py @@ -344,9 +344,9 @@ def get_imageio_config( imageio_global, imageio_host = _get_imageio_settings( project_settings, host_name) - config_host = imageio_host["ocio_config"] + config_host = imageio_host.get("ocio_config", {}) - if config_host["enabled"]: + if config_host.get("enabled"): config_data = _get_config_data( config_host["filepath"], anatomy_data ) From d30b311e00a4256f07909148ee3385b187ff6f9e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 27 Jan 2023 10:59:17 +0100 Subject: [PATCH 0251/1271] global: extension needs to be checked before settings --- openpype/pipeline/publish/publish_plugins.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index d9145275f7..5ba3ded475 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -372,6 +372,12 @@ class ExtractorColormanaged(Extractor): ``` """ + ext = representation["ext"] + # check extension + self.log.debug("__ ext: `{}`".format(ext)) + if ext.lower() not in self.allowed_ext: + return + if colorspace_settings is None: colorspace_settings = self.get_colorspace_settings(context) @@ -386,12 +392,6 @@ class ExtractorColormanaged(Extractor): self.log.info("Config data is : `{}`".format( config_data)) - ext = representation["ext"] - # check extension - self.log.debug("__ ext: `{}`".format(ext)) - if ext.lower() not in self.allowed_ext: - return - project_name = context.data["projectName"] host_name = context.data["hostName"] project_settings = context.data["project_settings"] From ce784ac78207e1a16f2a14f7cdaaad5268fd6c26 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:26:27 +0100 Subject: [PATCH 0252/1271] OP-4643 - updated docstring, standardized arguments --- openpype/lib/transcoding.py | 19 +++++++---------- .../publish/extract_color_transcode.py | 21 +++++++++---------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index fab9eeaaad..752712166f 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1041,7 +1041,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace( input_path, - out_filepath, + output_path, config_path, source_colorspace, target_colorspace, @@ -1049,18 +1049,13 @@ def convert_colorspace( display, logger=None ): - """Convert source files from one color space to another. - - Filenames of input files are kept so make sure that output directory - is not the same directory as input files have. - - This way it can handle gaps and can keep input filenames without handling - frame template + """Convert source file from one color space to another. Args: - input_path (str): Paths that should be converted. It is expected that - contains single file or image sequence of samy type. - out_filepath (str): Path to directory where output will be rendered. - Must not be same as input's directory. + input_path (str): Path that should be converted. It is expected that + contains single file or image sequence of same type + (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + output_path (str): Path to output filename. config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space @@ -1079,7 +1074,7 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "-o", out_filepath + "-o", output_path ] if all([target_colorspace, view, display]): diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 9cca5cc969..c4cef15ea6 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -119,13 +119,13 @@ class ExtractOIIOTranscode(publish.Extractor): files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: - input_filepath = os.path.join(original_staging_dir, - file_name) - output_path = self._get_output_file_path(input_filepath, + input_path = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_path, new_staging_dir, output_extension) convert_colorspace( - input_filepath, + input_path, output_path, config_path, source_colorspace, @@ -156,16 +156,17 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): - """Returns original list of files or single sequence format filename. + """Returns original list or list with filename formatted in single + sequence format. Uses clique to find frame sequence, in this case it merges all frames - into sequence format (%0X) and returns it. + into sequence format (FRAMESTART-FRAMEEND#) and returns it. If sequence not found, it returns original list Args: files_to_convert (list): list of file names Returns: - (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + (list) of [file.1001-1010#.exr] or [fileA.exr, fileB.exr] """ pattern = [clique.PATTERNS["frames"]] collections, remainder = clique.assemble( @@ -178,8 +179,6 @@ class ExtractOIIOTranscode(publish.Extractor): "Too many collections {}".format(collections)) collection = collections[0] - padding = collection.padding - padding_str = "%0{}".format(padding) frames = list(collection.indexes) frame_str = "{}-{}#".format(frames[0], frames[-1]) file_name = "{}{}{}".format(collection.head, frame_str, @@ -189,10 +188,10 @@ class ExtractOIIOTranscode(publish.Extractor): return files_to_convert - def _get_output_file_path(self, input_filepath, output_dir, + def _get_output_file_path(self, input_path, output_dir, output_extension): """Create output file name path.""" - file_name = os.path.basename(input_filepath) + file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: output_extension = input_extension From 02ad1d1998235e5416f41b3a973577659cb0d971 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:27:06 +0100 Subject: [PATCH 0253/1271] OP-4643 - fix wrong assignment --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c4cef15ea6..4e899a519c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -114,7 +114,7 @@ class ExtractOIIOTranscode(publish.Extractor): if view: new_repre["colorspaceData"]["view"] = view if display: - new_repre["colorspaceData"]["view"] = display + new_repre["colorspaceData"]["display"] = display files_to_convert = self._translate_to_sequence( files_to_convert) From 7d4a17169377dc8f8f68b54ae5338c296fd362a7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:59:13 +0100 Subject: [PATCH 0254/1271] OP-4643 - fix files to delete --- .../publish/extract_color_transcode.py | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4e899a519c..99e684ba21 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -84,26 +84,18 @@ class ExtractOIIOTranscode(publish.Extractor): original_staging_dir = new_repre["stagingDir"] new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir - files_to_convert = new_repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_delete = copy.deepcopy(files_to_convert) + if isinstance(new_repre["files"], list): + files_to_convert = copy.deepcopy(new_repre["files"]) + else: + files_to_convert = [new_repre["files"]] output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension - new_repre["ext"] = output_extension - - renamed_files = [] - _, orig_ext = os.path.splitext(files_to_convert[0]) - for file_name in files_to_convert: - file_name = file_name.replace(orig_ext, - "."+output_extension) - renamed_files.append(file_name) - new_repre["files"] = renamed_files + self._rename_in_representation(new_repre, + files_to_convert, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -135,8 +127,12 @@ class ExtractOIIOTranscode(publish.Extractor): self.log ) - instance.context.data["cleanupFullPaths"].extend( - files_to_delete) + # cleanup temporary transcoded files + for file_name in new_repre["files"]: + transcoded_file_path = os.path.join(new_staging_dir, + file_name) + instance.context.data["cleanupFullPaths"].append( + transcoded_file_path) custom_tags = output_def.get("custom_tags") if custom_tags: @@ -155,6 +151,21 @@ class ExtractOIIOTranscode(publish.Extractor): if added_representations: self._mark_original_repre_for_deletion(repre, profile) + def _rename_in_representation(self, new_repre, files_to_convert, + output_extension): + """Replace old extension with new one everywhere in representation.""" + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + def _translate_to_sequence(self, files_to_convert): """Returns original list or list with filename formatted in single sequence format. From 23a6e1bdafe65c4cae2f29b9813fbc80b6b23946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Fri, 27 Jan 2023 12:01:20 +0100 Subject: [PATCH 0255/1271] Update openpype/hosts/max/plugins/load/load_pointcache.py Co-authored-by: Roy Nieterau --- openpype/hosts/max/plugins/load/load_pointcache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/max/plugins/load/load_pointcache.py b/openpype/hosts/max/plugins/load/load_pointcache.py index ca959715f7..a2e567ed5d 100644 --- a/openpype/hosts/max/plugins/load/load_pointcache.py +++ b/openpype/hosts/max/plugins/load/load_pointcache.py @@ -88,7 +88,7 @@ importFile @"{file_path}" #noPrompt children = [] for c in node.Children: children.append(c) - children = children + list_children(c) + children += list_children(c) return children filtered = [] From 8b3f2891d4845fa8238475e7577e9343c8c9815d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Fri, 27 Jan 2023 12:01:26 +0100 Subject: [PATCH 0256/1271] Update openpype/hosts/max/api/pipeline.py Co-authored-by: Roy Nieterau --- openpype/hosts/max/api/pipeline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/max/api/pipeline.py b/openpype/hosts/max/api/pipeline.py index 8a1483dc51..f8a7b8ea5c 100644 --- a/openpype/hosts/max/api/pipeline.py +++ b/openpype/hosts/max/api/pipeline.py @@ -158,7 +158,6 @@ def containerise(name: str, nodes: list, context, loader=None, suffix="_CON"): container_name = f"{name}{suffix}" container = rt.container(name=container_name) - print(nodes) for node in nodes: node.Parent = container From 302a79095ec50031a2c411dd2704e75e6e51d6cf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:17:59 +0100 Subject: [PATCH 0257/1271] OP-4643 - moved output argument to the end --- openpype/lib/transcoding.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 752712166f..1629058beb 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1073,8 +1073,7 @@ def convert_colorspace( input_path, # Don't add any additional attributes "--nosoftwareattrib", - "--colorconfig", config_path, - "-o", output_path + "--colorconfig", config_path ] if all([target_colorspace, view, display]): @@ -1092,5 +1091,7 @@ def convert_colorspace( oiio_cmd.extend(["--iscolorspace", source_colorspace]) oiio_cmd.extend(["--ociodisplay", display, view]) + oiio_cmd.extend(["-o", output_path]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From 1de178e98fd0a59533826ae132b86ec85b742ce8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:18:33 +0100 Subject: [PATCH 0258/1271] OP-4643 - fix no tags in repre --- openpype/plugins/publish/extract_color_transcode.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 99e684ba21..3d897c6d9f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -142,6 +142,8 @@ class ExtractOIIOTranscode(publish.Extractor): # Add additional tags from output definition to representation for tag in output_def["tags"]: + if not new_repre.get("tags"): + new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) From 8ce2c151ce920e22089712e81b0d782a66e8fa38 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:26:07 +0100 Subject: [PATCH 0259/1271] OP-4643 - changed docstring Elaborated more that 'target_colorspace' and ('view', 'display') are disjunctive. --- openpype/lib/transcoding.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 1629058beb..6d91f514ec 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1045,8 +1045,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, - view, - display, + view=None, + display=None, logger=None ): """Convert source file from one color space to another. @@ -1059,7 +1059,9 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + if filled, 'view' and 'display' must be empty view (str): name for viewer space (ocio valid) + both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. Raises: From 6842dc8eba9875fb6951e0ef3cc0f5a78d91d828 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:29:04 +0100 Subject: [PATCH 0260/1271] OP-4617 - reworked condition Co-authored-by: Roy Nieterau --- openpype/hosts/nuke/plugins/publish/extract_render_local.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_render_local.py b/openpype/hosts/nuke/plugins/publish/extract_render_local.py index a48db60179..4fee6350f0 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_render_local.py +++ b/openpype/hosts/nuke/plugins/publish/extract_render_local.py @@ -195,7 +195,7 @@ class NukeRenderLocal(publish.ExtractorColormanaged): frames_to_render = [] for frame_range in frames_to_fix.split(","): - if isinstance(frame_range, int) or frame_range.isdigit(): + if frame_range.isdigit(): render_first_frame = frame_range render_last_frame = frame_range elif '-' in frame_range: From a015a1a27a71013821ebe667805fd4363fb73950 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:29:32 +0100 Subject: [PATCH 0261/1271] OP-4617 - removed arguments from docstring Co-authored-by: Roy Nieterau --- openpype/hosts/nuke/plugins/publish/extract_render_local.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_render_local.py b/openpype/hosts/nuke/plugins/publish/extract_render_local.py index 4fee6350f0..b99a7a9548 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_render_local.py +++ b/openpype/hosts/nuke/plugins/publish/extract_render_local.py @@ -187,8 +187,6 @@ class NukeRenderLocal(publish.ExtractorColormanaged): Args: frames_to_fix (str): specific or range of frames to be rerendered (1005, 1009-1010) - first_frame (int): original first frame - last_frame (int) Returns: (list): [(1005, 1005), (1009-1010)] """ From 7cb976a01da3255db84109a716bc03f62ce02f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Fri, 27 Jan 2023 15:33:07 +0100 Subject: [PATCH 0262/1271] Fix: import workfile all families --- openpype/hosts/blender/plugins/load/import_workfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/blender/plugins/load/import_workfile.py b/openpype/hosts/blender/plugins/load/import_workfile.py index 618fb83e31..bbdf1c7ea0 100644 --- a/openpype/hosts/blender/plugins/load/import_workfile.py +++ b/openpype/hosts/blender/plugins/load/import_workfile.py @@ -44,7 +44,7 @@ class AppendBlendLoader(plugin.AssetLoader): """ representations = ["blend"] - families = ["*"] + families = ["workfile"] label = "Append Workfile" order = 9 @@ -68,7 +68,7 @@ class ImportBlendLoader(plugin.AssetLoader): """ representations = ["blend"] - families = ["*"] + families = ["workfile"] label = "Import Workfile" order = 9 From 06222e727b4fe3483bf0a0ccc1adaf3b3d641041 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 15:46:57 +0100 Subject: [PATCH 0263/1271] implemented "not equal" method for attribute definitions (Py2 comp) --- openpype/lib/attribute_definitions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index 04db0edc64..ca82644894 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -156,6 +156,9 @@ class AbtractAttrDef(object): return False return self.key == other.key + def __ne__(self, other): + return not self.__eq__(other) + @abstractproperty def type(self): """Attribute definition type also used as identifier of class. From 36df58aba11a2334bc6e0d664f1dc08097f22e82 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 15:47:30 +0100 Subject: [PATCH 0264/1271] added more conditions for comparison --- openpype/lib/attribute_definitions.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index ca82644894..1debd0aa79 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -154,7 +154,12 @@ class AbtractAttrDef(object): def __eq__(self, other): if not isinstance(other, self.__class__): return False - return self.key == other.key + return ( + self.key == other.key + and self.hidden == other.hidden + and self.default == other.default + and self.disabled == other.disabled + ) def __ne__(self, other): return not self.__eq__(other) From d48b73ed61c9ff385a8fd2bf376ecfaaa51ad91d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:28:00 +0100 Subject: [PATCH 0265/1271] fix typo 'AbtractAttrDef' to 'AbstractAttrDef' --- openpype/lib/__init__.py | 4 +-- openpype/lib/attribute_definitions.py | 36 +++++++++---------- openpype/pipeline/create/context.py | 2 +- openpype/pipeline/create/creator_plugins.py | 4 +-- openpype/pipeline/publish/publish_plugins.py | 2 +- .../workfile/workfile_template_builder.py | 6 ++-- openpype/tools/attribute_defs/widgets.py | 6 ++-- openpype/tools/loader/lib.py | 4 +-- openpype/tools/utils/widgets.py | 4 +-- 9 files changed, 34 insertions(+), 34 deletions(-) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index a64b7c2911..b5fb955a84 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -30,7 +30,7 @@ from .vendor_bin_utils import ( ) from .attribute_definitions import ( - AbtractAttrDef, + AbstractAttrDef, UIDef, UISeparatorDef, @@ -246,7 +246,7 @@ __all__ = [ "get_ffmpeg_tool_path", "is_oiio_supported", - "AbtractAttrDef", + "AbstractAttrDef", "UIDef", "UISeparatorDef", diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index 1debd0aa79..efd38761c8 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -20,7 +20,7 @@ def register_attr_def_class(cls): Currently are registered definitions used to deserialize data to objects. Attrs: - cls (AbtractAttrDef): Non-abstract class to be registered with unique + cls (AbstractAttrDef): Non-abstract class to be registered with unique 'type' attribute. Raises: @@ -36,7 +36,7 @@ def get_attributes_keys(attribute_definitions): """Collect keys from list of attribute definitions. Args: - attribute_definitions (List[AbtractAttrDef]): Objects of attribute + attribute_definitions (List[AbstractAttrDef]): Objects of attribute definitions. Returns: @@ -57,7 +57,7 @@ def get_default_values(attribute_definitions): """Receive default values for attribute definitions. Args: - attribute_definitions (List[AbtractAttrDef]): Attribute definitions for + attribute_definitions (List[AbstractAttrDef]): Attribute definitions for which default values should be collected. Returns: @@ -76,15 +76,15 @@ def get_default_values(attribute_definitions): class AbstractAttrDefMeta(ABCMeta): - """Meta class to validate existence of 'key' attribute. + """Metaclass to validate existence of 'key' attribute. - Each object of `AbtractAttrDef` mus have defined 'key' attribute. + Each object of `AbstractAttrDef` mus have defined 'key' attribute. """ def __call__(self, *args, **kwargs): obj = super(AbstractAttrDefMeta, self).__call__(*args, **kwargs) init_class = getattr(obj, "__init__class__", None) - if init_class is not AbtractAttrDef: + if init_class is not AbstractAttrDef: raise TypeError("{} super was not called in __init__.".format( type(obj) )) @@ -92,7 +92,7 @@ class AbstractAttrDefMeta(ABCMeta): @six.add_metaclass(AbstractAttrDefMeta) -class AbtractAttrDef(object): +class AbstractAttrDef(object): """Abstraction of attribute definiton. Each attribute definition must have implemented validation and @@ -145,7 +145,7 @@ class AbtractAttrDef(object): self.disabled = disabled self._id = uuid.uuid4().hex - self.__init__class__ = AbtractAttrDef + self.__init__class__ = AbstractAttrDef @property def id(self): @@ -220,7 +220,7 @@ class AbtractAttrDef(object): # UI attribute definitoins won't hold value # ----------------------------------------- -class UIDef(AbtractAttrDef): +class UIDef(AbstractAttrDef): is_value_def = False def __init__(self, key=None, default=None, *args, **kwargs): @@ -245,7 +245,7 @@ class UILabelDef(UIDef): # Attribute defintioins should hold value # --------------------------------------- -class UnknownDef(AbtractAttrDef): +class UnknownDef(AbstractAttrDef): """Definition is not known because definition is not available. This attribute can be used to keep existing data unchanged but does not @@ -262,7 +262,7 @@ class UnknownDef(AbtractAttrDef): return value -class HiddenDef(AbtractAttrDef): +class HiddenDef(AbstractAttrDef): """Hidden value of Any type. This attribute can be used for UI purposes to pass values related @@ -282,7 +282,7 @@ class HiddenDef(AbtractAttrDef): return value -class NumberDef(AbtractAttrDef): +class NumberDef(AbstractAttrDef): """Number definition. Number can have defined minimum/maximum value and decimal points. Value @@ -358,7 +358,7 @@ class NumberDef(AbtractAttrDef): return round(float(value), self.decimals) -class TextDef(AbtractAttrDef): +class TextDef(AbstractAttrDef): """Text definition. Text can have multiline option so endline characters are allowed regex @@ -423,7 +423,7 @@ class TextDef(AbtractAttrDef): return data -class EnumDef(AbtractAttrDef): +class EnumDef(AbstractAttrDef): """Enumeration of single item from items. Args: @@ -531,7 +531,7 @@ class EnumDef(AbtractAttrDef): return output -class BoolDef(AbtractAttrDef): +class BoolDef(AbstractAttrDef): """Boolean representation. Args: @@ -776,7 +776,7 @@ class FileDefItem(object): return output -class FileDef(AbtractAttrDef): +class FileDef(AbstractAttrDef): """File definition. It is possible to define filters of allowed file extensions and if supports folders. @@ -894,7 +894,7 @@ def serialize_attr_def(attr_def): """Serialize attribute definition to data. Args: - attr_def (AbtractAttrDef): Attribute definition to serialize. + attr_def (AbstractAttrDef): Attribute definition to serialize. Returns: Dict[str, Any]: Serialized data. @@ -907,7 +907,7 @@ def serialize_attr_defs(attr_defs): """Serialize attribute definitions to data. Args: - attr_defs (List[AbtractAttrDef]): Attribute definitions to serialize. + attr_defs (List[AbstractAttrDef]): Attribute definitions to serialize. Returns: List[Dict[str, Any]]: Serialized data. diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9c468ae8fc..c3cf3e9b4b 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -208,7 +208,7 @@ class AttributeValues(object): Has dictionary like methods. Not all of them are allowed all the time. Args: - attr_defs(AbtractAttrDef): Defintions of value type and properties. + attr_defs(AbstractAttrDef): Defintions of value type and properties. values(dict): Values after possible conversion. origin_data(dict): Values loaded from host before conversion. """ diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 8500dd1e22..8ac8959c76 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -425,7 +425,7 @@ class BaseCreator: keys/values when plugin attributes change. Returns: - List[AbtractAttrDef]: Attribute definitions that can be tweaked for + List[AbstractAttrDef]: Attribute definitions that can be tweaked for created instance. """ @@ -563,7 +563,7 @@ class Creator(BaseCreator): updating keys/values when plugin attributes change. Returns: - List[AbtractAttrDef]: Attribute definitions that can be tweaked for + List[AbstractAttrDef]: Attribute definitions that can be tweaked for created instance. """ return self.pre_create_attr_defs diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index d9145275f7..96e4aae237 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -118,7 +118,7 @@ class OpenPypePyblishPluginMixin: Attributes available for all families in plugin's `families` attribute. Returns: - list: Attribute definitions for plugin. + list: Attribute definitions for plugin. """ return [] diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 1266c27fd7..051eb444c6 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -842,7 +842,7 @@ class PlaceholderPlugin(object): """Placeholder options for data showed. Returns: - List[AbtractAttrDef]: Attribute definitions of placeholder options. + List[AbstractAttrDef]: Attribute definitions of placeholder options. """ return [] @@ -1143,7 +1143,7 @@ class PlaceholderLoadMixin(object): as defaults for attributes. Returns: - List[AbtractAttrDef]: Attribute definitions common for load + List[AbstractAttrDef]: Attribute definitions common for load plugins. """ @@ -1513,7 +1513,7 @@ class PlaceholderCreateMixin(object): as defaults for attributes. Returns: - List[AbtractAttrDef]: Attribute definitions common for create + List[AbstractAttrDef]: Attribute definitions common for create plugins. """ diff --git a/openpype/tools/attribute_defs/widgets.py b/openpype/tools/attribute_defs/widgets.py index bf61dc3776..3cec1d2683 100644 --- a/openpype/tools/attribute_defs/widgets.py +++ b/openpype/tools/attribute_defs/widgets.py @@ -4,7 +4,7 @@ import copy from qtpy import QtWidgets, QtCore from openpype.lib.attribute_definitions import ( - AbtractAttrDef, + AbstractAttrDef, UnknownDef, HiddenDef, NumberDef, @@ -33,9 +33,9 @@ def create_widget_for_attr_def(attr_def, parent=None): def _create_widget_for_attr_def(attr_def, parent=None): - if not isinstance(attr_def, AbtractAttrDef): + if not isinstance(attr_def, AbstractAttrDef): raise TypeError("Unexpected type \"{}\" expected \"{}\"".format( - str(type(attr_def)), AbtractAttrDef + str(type(attr_def)), AbstractAttrDef )) if isinstance(attr_def, NumberDef): diff --git a/openpype/tools/loader/lib.py b/openpype/tools/loader/lib.py index 552dc91a10..d47bc7e07a 100644 --- a/openpype/tools/loader/lib.py +++ b/openpype/tools/loader/lib.py @@ -2,7 +2,7 @@ import inspect from qtpy import QtGui import qtawesome -from openpype.lib.attribute_definitions import AbtractAttrDef +from openpype.lib.attribute_definitions import AbstractAttrDef from openpype.tools.attribute_defs import AttributeDefinitionsDialog from openpype.tools.utils.widgets import ( OptionalAction, @@ -43,7 +43,7 @@ def get_options(action, loader, parent, repre_contexts): if not getattr(action, "optioned", False) or not loader_options: return options - if isinstance(loader_options[0], AbtractAttrDef): + if isinstance(loader_options[0], AbstractAttrDef): qargparse_options = False dialog = AttributeDefinitionsDialog(loader_options, parent) else: diff --git a/openpype/tools/utils/widgets.py b/openpype/tools/utils/widgets.py index a9d6fa35b2..41573687e1 100644 --- a/openpype/tools/utils/widgets.py +++ b/openpype/tools/utils/widgets.py @@ -8,7 +8,7 @@ from openpype.style import ( get_objected_colors, get_style_image_path ) -from openpype.lib.attribute_definitions import AbtractAttrDef +from openpype.lib.attribute_definitions import AbstractAttrDef log = logging.getLogger(__name__) @@ -406,7 +406,7 @@ class OptionalAction(QtWidgets.QWidgetAction): def set_option_tip(self, options): sep = "\n\n" - if not options or not isinstance(options[0], AbtractAttrDef): + if not options or not isinstance(options[0], AbstractAttrDef): mak = (lambda opt: opt["name"] + " :\n " + opt["help"]) self.option_tip = sep.join(mak(opt) for opt in options) return From 36065080c7796cf61e9e1d8e2f55436bd58f46fd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:30:02 +0100 Subject: [PATCH 0266/1271] import 'UnknownDef' at the top of file --- openpype/pipeline/create/context.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index c3cf3e9b4b..9d3aa37de2 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -13,6 +13,9 @@ from openpype.settings import ( get_system_settings, get_project_settings ) +from openpype.lib.attribute_definitions import ( + UnknownDef, +) from openpype.host import IPublishHost from openpype.pipeline import legacy_io from openpype.pipeline.mongodb import ( @@ -214,8 +217,6 @@ class AttributeValues(object): """ def __init__(self, attr_defs, values, origin_data=None): - from openpype.lib.attribute_definitions import UnknownDef - if origin_data is None: origin_data = copy.deepcopy(values) self._origin_data = origin_data From 1d91ee53388c806e8c04be79e8e2249a052cd4d7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:31:01 +0100 Subject: [PATCH 0267/1271] attr_defs property returns list copy of attributes --- openpype/pipeline/create/context.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9d3aa37de2..9360db63ed 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -289,8 +289,13 @@ class AttributeValues(object): @property def attr_defs(self): - """Pointer to attribute definitions.""" - return self._attr_defs + """Pointer to attribute definitions. + + Returns: + List[AbstractAttrDef]: Attribute definitions. + """ + + return list(self._attr_defs) def data_to_store(self): """Create new dictionary with data to store.""" From 8f6d36dfa0291590cf245cd10592b40e4606f5c5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:33:50 +0100 Subject: [PATCH 0268/1271] implemented de/serialization methods for attribute values --- openpype/pipeline/create/context.py | 47 +++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9360db63ed..fe88fbc5c3 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -15,6 +15,8 @@ from openpype.settings import ( ) from openpype.lib.attribute_definitions import ( UnknownDef, + serialize_attr_defs, + deserialize_attr_defs, ) from openpype.host import IPublishHost from openpype.pipeline import legacy_io @@ -331,6 +333,15 @@ class AttributeValues(object): elif self.get(key) != new_value: self[key] = new_value + def get_serialized_attr_defs(self): + """Serialize attribute definitions to json serializable types. + + Returns: + List[Dict[str, Any]]: Serialized attribute definitions. + """ + + return serialize_attr_defs(self._attr_defs) + class CreatorAttributeValues(AttributeValues): """Creator specific attribute values of an instance. @@ -515,6 +526,42 @@ class PublishAttributes: self, [], value, value ) + def serialize_attributes(self): + return { + "attr_defs": { + plugin_name: attrs_value.get_serialized_attr_defs() + for plugin_name, attrs_value in self._data.items() + }, + "plugin_names_order": self._plugin_names_order, + "missing_plugins": self._missing_plugins + } + + def deserialize_attributes(self, data): + self._plugin_names_order = data["plugin_names_order"] + self._missing_plugins = data["missing_plugins"] + + attr_defs = deserialize_attr_defs(data["attr_defs"]) + + origin_data = self._origin_data + data = self._data + self._data = {} + + added_keys = set() + for plugin_name, attr_defs_data in attr_defs.items(): + attr_defs = deserialize_attr_defs(attr_defs_data) + value = data.get(plugin_name) or {} + orig_value = copy.deepcopy(origin_data.get(plugin_name) or {}) + self._data[plugin_name] = PublishAttributeValues( + self, attr_defs, value, orig_value + ) + + for key, value in data.items(): + if key not in added_keys: + self._missing_plugins.append(key) + self._data[key] = PublishAttributeValues( + self, [], value, value + ) + class CreatedInstance: """Instance entity with data that will be stored to workfile. From eac4167a22703cc2b77adcf45d73978c28c3b5f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Fri, 27 Jan 2023 16:33:51 +0100 Subject: [PATCH 0269/1271] :bug: hotfix condition --- openpype/hosts/houdini/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/houdini/api/plugin.py b/openpype/hosts/houdini/api/plugin.py index 4ca6b50702..61127bda57 100644 --- a/openpype/hosts/houdini/api/plugin.py +++ b/openpype/hosts/houdini/api/plugin.py @@ -113,7 +113,7 @@ class HoudiniCreatorBase(object): Dict[str, Any]: Shared data dictionary. """ - if shared_data.get("houdini_cached_subsets") is not None: + if shared_data.get("houdini_cached_subsets") is None: cache = dict() cache_legacy = dict() From 6a1acc166425ca716b10403f02d38c73ba2f2ab8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:34:36 +0100 Subject: [PATCH 0270/1271] 'CreatedInstance' can be initialized without creator --- openpype/pipeline/create/context.py | 50 +++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index fe88fbc5c3..2c1a13100b 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -570,15 +570,22 @@ class CreatedInstance: about instance like "asset" and "task" and all data used for filling subset name as creators may have custom data for subset name filling. + Notes: + Object have 2 possible initialization. One using 'creator' object which + is recommended for api usage. Second by passing information about + creator. + Args: - family(str): Name of family that will be created. - subset_name(str): Name of subset that will be created. - data(dict): Data used for filling subset name or override data from - already existing instance. - creator(BaseCreator): Creator responsible for instance. - host(ModuleType): Host implementation loaded with - `openpype.pipeline.registered_host`. - new(bool): Is instance new. + family (str): Name of family that will be created. + subset_name (str): Name of subset that will be created. + data (Dict[str, Any]): Data used for filling subset name or override + data from already existing instance. + creator (Union[BaseCreator, None]): Creator responsible for instance. + creator_identifier (str): Identifier of creator plugin. + creator_label (str): Creator plugin label. + group_label (str): Default group label from creator plugin. + creator_attr_defs (List[AbstractAttrDef]): Attribute definitions from + creator. """ # Keys that can't be changed or removed from data after loading using @@ -595,9 +602,24 @@ class CreatedInstance: ) def __init__( - self, family, subset_name, data, creator, new=True + self, + family, + subset_name, + data, + creator=None, + creator_identifier=None, + creator_label=None, + group_label=None, + creator_attr_defs=None, ): - self.creator = creator + if creator is not None: + creator_identifier = creator.identifier + group_label = creator.get_group_label() + creator_label = creator.label + creator_attr_defs = creator.get_instance_attr_defs() + + self._creator_label = creator_label + self._group_label = group_label or creator_identifier # Instance members may have actions on them # TODO implement members logic @@ -627,7 +649,7 @@ class CreatedInstance: self._data["family"] = family self._data["subset"] = subset_name self._data["active"] = data.get("active", True) - self._data["creator_identifier"] = creator.identifier + self._data["creator_identifier"] = creator_identifier # Pop from source data all keys that are defined in `_data` before # this moment and through their values away @@ -641,10 +663,12 @@ class CreatedInstance: # Stored creator specific attribute values # {key: value} creator_values = copy.deepcopy(orig_creator_attributes) - creator_attr_defs = creator.get_instance_attr_defs() self._data["creator_attributes"] = CreatorAttributeValues( - self, creator_attr_defs, creator_values, orig_creator_attributes + self, + list(creator_attr_defs), + creator_values, + orig_creator_attributes ) # Stored publish specific attribute values From 373d2452423f7a7eca5161361022d7cba15fb705 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:35:20 +0100 Subject: [PATCH 0271/1271] moved context validation methods to bottom --- openpype/pipeline/create/context.py | 88 ++++++++++++++--------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 2c1a13100b..081fdd86ff 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -763,51 +763,6 @@ class CreatedInstance: def creator_label(self): return self.creator.label or self.creator_identifier - @property - def create_context(self): - return self.creator.create_context - - @property - def host(self): - return self.create_context.host - - @property - def has_set_asset(self): - """Asset name is set in data.""" - return "asset" in self._data - - @property - def has_set_task(self): - """Task name is set in data.""" - return "task" in self._data - - @property - def has_valid_context(self): - """Context data are valid for publishing.""" - return self.has_valid_asset and self.has_valid_task - - @property - def has_valid_asset(self): - """Asset set in context exists in project.""" - if not self.has_set_asset: - return False - return self._asset_is_valid - - @property - def has_valid_task(self): - """Task set in context exists in project.""" - if not self.has_set_task: - return False - return self._task_is_valid - - def set_asset_invalid(self, invalid): - # TODO replace with `set_asset_name` - self._asset_is_valid = not invalid - - def set_task_invalid(self, invalid): - # TODO replace with `set_task_name` - self._task_is_valid = not invalid - @property def id(self): """Instance identifier.""" @@ -1039,6 +994,49 @@ class CreatedInstance: if current_value != new_value: self[key] = new_value + # Context validation related methods/properties + @property + def has_set_asset(self): + """Asset name is set in data.""" + + return "asset" in self._data + + @property + def has_set_task(self): + """Task name is set in data.""" + + return "task" in self._data + + @property + def has_valid_context(self): + """Context data are valid for publishing.""" + + return self.has_valid_asset and self.has_valid_task + + @property + def has_valid_asset(self): + """Asset set in context exists in project.""" + + if not self.has_set_asset: + return False + return self._asset_is_valid + + @property + def has_valid_task(self): + """Task set in context exists in project.""" + + if not self.has_set_task: + return False + return self._task_is_valid + + def set_asset_invalid(self, invalid): + # TODO replace with `set_asset_name` + self._asset_is_valid = not invalid + + def set_task_invalid(self, invalid): + # TODO replace with `set_task_name` + self._task_is_valid = not invalid + class ConvertorItem(object): """Item representing convertor plugin. From e7fdb1d151a117f975eb6094be4b4d761029136f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:36:42 +0100 Subject: [PATCH 0272/1271] don't use creator in methods --- openpype/pipeline/create/context.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 081fdd86ff..0f288f0bd5 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -753,15 +753,15 @@ class CreatedInstance: label = self._data.get("group") if label: return label - return self.creator.get_group_label() + return self._group_label @property def creator_identifier(self): - return self.creator.identifier + return self._data["creator_identifier"] @property def creator_label(self): - return self.creator.label or self.creator_identifier + return self._creator_label or self.creator_identifier @property def id(self): @@ -1477,7 +1477,7 @@ class CreateContext: self._instances_by_id[instance.id] = instance # Prepare publish plugin attributes and set it on instance attr_plugins = self._get_publish_plugins_with_attr_for_family( - instance.creator.family + instance.family ) instance.set_publish_plugins(attr_plugins) From 86ece89a3a2756516ec1e3903e1ba4d4f6b52f69 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:37:14 +0100 Subject: [PATCH 0273/1271] de/serialization of CreatedInstance does not require creator --- openpype/pipeline/create/context.py | 35 ++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 0f288f0bd5..0a5d186273 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -904,9 +904,21 @@ class CreatedInstance: self._members.append(member) def serialize_for_remote(self): + """Serialize object into data to be possible recreated object. + + Returns: + Dict[str, Any]: Serialized data. + """ + + creator_attr_defs = self.creator_attributes.get_serialized_attr_defs() + publish_attributes = self.publish_attributes.serialize_attributes() return { "data": self.data_to_store(), - "orig_data": copy.deepcopy(self._orig_data) + "orig_data": copy.deepcopy(self._orig_data), + "creator_attr_defs": creator_attr_defs, + "publish_attributes": publish_attributes, + "creator_label": self._creator_label, + "group_label": self._group_label, } @classmethod @@ -927,17 +939,28 @@ class CreatedInstance: instance_data = copy.deepcopy(serialized_data["data"]) creator_identifier = instance_data["creator_identifier"] - creator_item = creator_items[creator_identifier] - family = instance_data.get("family", None) - if family is None: - family = creator_item.family + family = instance_data["family"] subset_name = instance_data.get("subset", None) + creator_label = serialized_data["creator_label"] + group_label = serialized_data["group_label"] + creator_attr_defs = deserialize_attr_defs( + serialized_data["creator_attr_defs"] + ) + publish_attributes = serialized_data["publish_attributes"] + obj = cls( - family, subset_name, instance_data, creator_item, new=False + family, + subset_name, + instance_data, + creator_identifier=creator_identifier, + creator_label=creator_label, + group_label=group_label, + creator_attributes=creator_attr_defs ) obj._orig_data = serialized_data["orig_data"] + obj.publish_attributes.deserialize_attributes(publish_attributes) return obj From 4ec904e2bfd592f956917888014bdc5acc1195e5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:37:40 +0100 Subject: [PATCH 0274/1271] don't pass removed 'new' attribute --- openpype/pipeline/create/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 0a5d186273..2ccd50006e 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -891,7 +891,7 @@ class CreatedInstance: subset_name = instance_data.get("subset", None) return cls( - family, subset_name, instance_data, creator, new=False + family, subset_name, instance_data, creator ) def set_publish_plugins(self, attr_plugins): From 951e8a50557c1e3545da8cb63b557813b163d179 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:42:22 +0100 Subject: [PATCH 0275/1271] don't pass creator_items to 'deserialize_on_remote' --- openpype/pipeline/create/context.py | 2 +- openpype/tools/publisher/control_qt.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 2ccd50006e..b52c870329 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -922,7 +922,7 @@ class CreatedInstance: } @classmethod - def deserialize_on_remote(cls, serialized_data, creator_items): + def deserialize_on_remote(cls, serialized_data): """Convert instance data to CreatedInstance. This is fake instance in remote process e.g. in UI process. The creator diff --git a/openpype/tools/publisher/control_qt.py b/openpype/tools/publisher/control_qt.py index 3639c4bb30..132b42f9ec 100644 --- a/openpype/tools/publisher/control_qt.py +++ b/openpype/tools/publisher/control_qt.py @@ -136,10 +136,7 @@ class QtRemotePublishController(BasePublisherController): created_instances = {} for serialized_data in serialized_instances: - item = CreatedInstance.deserialize_on_remote( - serialized_data, - self._creator_items - ) + item = CreatedInstance.deserialize_on_remote(serialized_data) created_instances[item.id] = item self._created_instances = created_instances From 75d72f4d2dd7fa8c8e65085ee1351ca54ab21121 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:42:38 +0100 Subject: [PATCH 0276/1271] fix 'pre_create_attributes_defs' --- openpype/tools/publisher/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 50a814de5c..ca2b083e80 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -910,7 +910,7 @@ class CreatorItem: pre_create_attributes_defs = None if self.pre_create_attributes_defs is not None: - instance_attributes_defs = serialize_attr_defs( + pre_create_attributes_defs = serialize_attr_defs( self.pre_create_attributes_defs ) From b5e35eaa63bd03463abd4f6b4a5d410745321afa Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:43:06 +0100 Subject: [PATCH 0277/1271] CreatorItem in controller does not have 'instance_attributes_defs' --- openpype/tools/publisher/control.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index ca2b083e80..61969691c6 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -826,7 +826,6 @@ class CreatorItem: label, group_label, icon, - instance_attributes_defs, description, detailed_description, default_variant, @@ -847,12 +846,8 @@ class CreatorItem: self.default_variants = default_variants self.create_allow_context_change = create_allow_context_change self.create_allow_thumbnail = create_allow_thumbnail - self.instance_attributes_defs = instance_attributes_defs self.pre_create_attributes_defs = pre_create_attributes_defs - def get_instance_attr_defs(self): - return self.instance_attributes_defs - def get_group_label(self): return self.group_label @@ -891,7 +886,6 @@ class CreatorItem: creator.label or identifier, creator.get_group_label(), creator.get_icon(), - creator.get_instance_attr_defs(), description, detail_description, default_variant, @@ -902,12 +896,6 @@ class CreatorItem: ) def to_data(self): - instance_attributes_defs = None - if self.instance_attributes_defs is not None: - instance_attributes_defs = serialize_attr_defs( - self.instance_attributes_defs - ) - pre_create_attributes_defs = None if self.pre_create_attributes_defs is not None: pre_create_attributes_defs = serialize_attr_defs( @@ -927,18 +915,11 @@ class CreatorItem: "default_variants": self.default_variants, "create_allow_context_change": self.create_allow_context_change, "create_allow_thumbnail": self.create_allow_thumbnail, - "instance_attributes_defs": instance_attributes_defs, "pre_create_attributes_defs": pre_create_attributes_defs, } @classmethod def from_data(cls, data): - instance_attributes_defs = data["instance_attributes_defs"] - if instance_attributes_defs is not None: - data["instance_attributes_defs"] = deserialize_attr_defs( - instance_attributes_defs - ) - pre_create_attributes_defs = data["pre_create_attributes_defs"] if pre_create_attributes_defs is not None: data["pre_create_attributes_defs"] = deserialize_attr_defs( From d448004f541114c50a987a96bc5454a828c4de72 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:43:24 +0100 Subject: [PATCH 0278/1271] get attributes from instances directly --- openpype/tools/publisher/control.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 61969691c6..7c8da66744 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -1860,12 +1860,12 @@ class PublisherController(BasePublisherController): which should be attribute definitions returned. """ + # NOTE it would be great if attrdefs would have hash method implemented + # so they could be used as keys in dictionary output = [] _attr_defs = {} for instance in instances: - creator_identifier = instance.creator_identifier - creator_item = self.creator_items[creator_identifier] - for attr_def in creator_item.instance_attributes_defs: + for attr_def in instance.creator_attribute_defs: found_idx = None for idx, _attr_def in _attr_defs.items(): if attr_def == _attr_def: From 96094324fabfe015730415b265188ce84e000924 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:43:50 +0100 Subject: [PATCH 0279/1271] check for 'creator_identifier' instead of 'creator' --- openpype/tools/publisher/widgets/widgets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 2e8d0ce37c..587bcb059d 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -1220,7 +1220,8 @@ class GlobalAttrsWidget(QtWidgets.QWidget): asset_task_combinations = [] for instance in instances: - if instance.creator is None: + # NOTE I'm not sure how this can even happen? + if instance.creator_identifier is None: editable = False variants.add(instance.get("variant") or self.unknown_value) From 3aaf349b694dcb6306eda7ebcda10a37636dd2f7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 16:44:05 +0100 Subject: [PATCH 0280/1271] modified few docstrings --- openpype/pipeline/create/context.py | 60 ++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index b52c870329..9d42764aef 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -33,6 +33,7 @@ from .creator_plugins import ( CreatorError, ) +# Changes of instances and context are send as tuple of 2 information UpdateData = collections.namedtuple("UpdateData", ["instance", "changes"]) @@ -300,7 +301,12 @@ class AttributeValues(object): return list(self._attr_defs) def data_to_store(self): - """Create new dictionary with data to store.""" + """Create new dictionary with data to store. + + Returns: + Dict[str, Any]: Attribute values that should be stored. + """ + output = {} for key in self._data: output[key] = self[key] @@ -313,6 +319,7 @@ class AttributeValues(object): @staticmethod def calculate_changes(new_data, old_data): """Calculate changes of 2 dictionary objects.""" + changes = {} for key, new_value in new_data.items(): old_value = old_data.get(key) @@ -379,13 +386,14 @@ class PublishAttributes: """Wrapper for publish plugin attribute definitions. Cares about handling attribute definitions of multiple publish plugins. + Keep information about attribute definitions and their values. Args: parent(CreatedInstance, CreateContext): Parent for which will be data stored and from which are data loaded. origin_data(dict): Loaded data by plugin class name. - attr_plugins(list): List of publish plugins that may have defined - attribute definitions. + attr_plugins(Union[List[pyblish.api.Plugin], None]): List of publish + plugins that may have defined attribute definitions. """ def __init__(self, parent, origin_data, attr_plugins=None): @@ -765,7 +773,11 @@ class CreatedInstance: @property def id(self): - """Instance identifier.""" + """Instance identifier. + + Returns: + str: UUID of instance. + """ return self._data["instance_id"] @@ -774,6 +786,10 @@ class CreatedInstance: """Legacy access to data. Access to data is needed to modify values. + + Returns: + CreatedInstance: Object can be used as dictionary but with + validations of immutable keys. """ return self @@ -850,6 +866,12 @@ class CreatedInstance: @property def creator_attribute_defs(self): + """Attribute defintions defined by creator plugin. + + Returns: + List[AbstractAttrDef]: Attribute defitions. + """ + return self.creator_attributes.attr_defs @property @@ -861,7 +883,7 @@ class CreatedInstance: It is possible to recreate the instance using these data. - Todo: + Todos: We probably don't need OrderedDict. When data are loaded they are not ordered anymore. @@ -882,7 +904,15 @@ class CreatedInstance: @classmethod def from_existing(cls, instance_data, creator): - """Convert instance data from workfile to CreatedInstance.""" + """Convert instance data from workfile to CreatedInstance. + + Args: + instance_data (Dict[str, Any]): Data in a structure ready for + 'CreatedInstance' object. + creator (Creator): Creator plugin which is creating the instance + of for which the instance belong. + """ + instance_data = copy.deepcopy(instance_data) family = instance_data.get("family", None) @@ -895,10 +925,21 @@ class CreatedInstance: ) def set_publish_plugins(self, attr_plugins): + """Set publish plugins with attribute definitions. + + This method should be called only from 'CreateContext'. + + Args: + attr_plugins (List[pyblish.api.Plugin]): Pyblish plugins which + inherit from 'OpenPypePyblishPluginMixin' and may contain + attribute definitions. + """ + self.publish_attributes.set_publish_plugins(attr_plugins) def add_members(self, members): """Currently unused method.""" + for member in members: if member not in self._members: self._members.append(member) @@ -932,9 +973,6 @@ class CreatedInstance: Args: serialized_data (Dict[str, Any]): Serialized data for remote recreating. Should contain 'data' and 'orig_data'. - creator_items (Dict[str, Any]): Mapping of creator identifier and - objects that behave like a creator for most of attribute - access. """ instance_data = copy.deepcopy(serialized_data["data"]) @@ -1098,6 +1136,10 @@ class CreateContext: Context itself also can store data related to whole creation (workfile). - those are mainly for Context publish plugins + Todos: + Don't use 'AvalonMongoDB'. It's used only to keep track about current + context which should be handled by host. + Args: host(ModuleType): Host implementation which handles implementation and global metadata. From 5d9e7cf52d02ab2346d8a5e06a9ce06d1c2b675b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 27 Jan 2023 17:21:28 +0100 Subject: [PATCH 0281/1271] fix formatting --- openpype/lib/attribute_definitions.py | 5 +++-- openpype/pipeline/create/context.py | 4 ++-- openpype/pipeline/create/creator_plugins.py | 8 ++++---- openpype/pipeline/workfile/workfile_template_builder.py | 3 ++- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index efd38761c8..a2fe8314b2 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -57,8 +57,8 @@ def get_default_values(attribute_definitions): """Receive default values for attribute definitions. Args: - attribute_definitions (List[AbstractAttrDef]): Attribute definitions for - which default values should be collected. + attribute_definitions (List[AbstractAttrDef]): Attribute definitions + for which default values should be collected. Returns: Dict[str, Any]: Default values for passet attribute definitions. @@ -531,6 +531,7 @@ class EnumDef(AbstractAttrDef): return output + class BoolDef(AbstractAttrDef): """Boolean representation. diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9d42764aef..2b4c8a05ca 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -867,11 +867,11 @@ class CreatedInstance: @property def creator_attribute_defs(self): """Attribute defintions defined by creator plugin. - + Returns: List[AbstractAttrDef]: Attribute defitions. """ - + return self.creator_attributes.attr_defs @property diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 8ac8959c76..1f92056b23 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -425,8 +425,8 @@ class BaseCreator: keys/values when plugin attributes change. Returns: - List[AbstractAttrDef]: Attribute definitions that can be tweaked for - created instance. + List[AbstractAttrDef]: Attribute definitions that can be tweaked + for created instance. """ return self.instance_attr_defs @@ -563,8 +563,8 @@ class Creator(BaseCreator): updating keys/values when plugin attributes change. Returns: - List[AbstractAttrDef]: Attribute definitions that can be tweaked for - created instance. + List[AbstractAttrDef]: Attribute definitions that can be tweaked + for created instance. """ return self.pre_create_attr_defs diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 051eb444c6..119e4aaeb7 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -842,7 +842,8 @@ class PlaceholderPlugin(object): """Placeholder options for data showed. Returns: - List[AbstractAttrDef]: Attribute definitions of placeholder options. + List[AbstractAttrDef]: Attribute definitions of + placeholder options. """ return [] From 6246bac7742aa18f852704588ba37d228f0a1413 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:29:21 +0100 Subject: [PATCH 0282/1271] renamed 'reset_avalon_context' to 'reset_current_context' --- openpype/pipeline/create/context.py | 6 +++--- openpype/tools/publisher/control.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9c468ae8fc..29bc32b658 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1138,7 +1138,7 @@ class CreateContext: self.reset_preparation() - self.reset_avalon_context() + self.reset_current_context() self.reset_plugins(discover_publish_plugins) self.reset_context_data() @@ -1185,8 +1185,8 @@ class CreateContext: self._collection_shared_data = None self.refresh_thumbnails() - def reset_avalon_context(self): - """Give ability to reset avalon context. + def reset_current_context(self): + """Refresh current context. Reset is based on optional host implementation of `get_current_context` function or using `legacy_io.Session`. diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 50a814de5c..c11d7c53d3 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -1756,7 +1756,7 @@ class PublisherController(BasePublisherController): self._create_context.reset_preparation() # Reset avalon context - self._create_context.reset_avalon_context() + self._create_context.reset_current_context() self._asset_docs_cache.reset() From d1b41ebac0b7bbac4a1404ca0233d2a7d92e6230 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:30:24 +0100 Subject: [PATCH 0283/1271] AvalonMongoDB is not needed for CreateContext or Controller --- openpype/pipeline/create/context.py | 38 ++++++++++++++--------------- openpype/tools/publisher/control.py | 5 ++-- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 29bc32b658..e421a76b6e 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1003,8 +1003,6 @@ class CreateContext: Args: host(ModuleType): Host implementation which handles implementation and global metadata. - dbcon(AvalonMongoDB): Connection to mongo with context (at least - project). headless(bool): Context is created out of UI (Current not used). reset(bool): Reset context on initialization. discover_publish_plugins(bool): Discover publish plugins during reset @@ -1012,16 +1010,8 @@ class CreateContext: """ def __init__( - self, host, dbcon=None, headless=False, reset=True, - discover_publish_plugins=True + self, host, headless=False, reset=True, discover_publish_plugins=True ): - # Create conncetion if is not passed - if dbcon is None: - session = session_data_from_environment(True) - dbcon = AvalonMongoDB(session) - dbcon.install() - - self.dbcon = dbcon self.host = host # Prepare attribute for logger (Created on demand in `log` property) @@ -1045,6 +1035,10 @@ class CreateContext: " Missing methods: {}" ).format(joined_methods)) + self._current_project_name = None + self._current_asset_name = None + self._current_task_name = None + self._host_is_valid = host_is_valid # Currently unused variable self.headless = headless @@ -1119,9 +1113,16 @@ class CreateContext: def host_name(self): return os.environ["AVALON_APP"] - @property - def project_name(self): - return self.dbcon.active_project() + def get_current_project_name(self): + return self._current_project_name + + def get_current_asset_name(self): + return self._current_asset_name + + def get_current_task_name(self): + return self._current_task_name + + project_name = property(get_current_project_name) @property def log(self): @@ -1210,12 +1211,9 @@ class CreateContext: if not task_name: task_name = legacy_io.Session.get("AVALON_TASK") - if project_name: - self.dbcon.Session["AVALON_PROJECT"] = project_name - if asset_name: - self.dbcon.Session["AVALON_ASSET"] = asset_name - if task_name: - self.dbcon.Session["AVALON_TASK"] = task_name + self._current_project_name = project_name + self._current_asset_name = asset_name + self._current_task_name = task_name def reset_plugins(self, discover_publish_plugins=True): """Reload plugins. diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index c11d7c53d3..83c2dd4b1c 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -1589,20 +1589,19 @@ class PublisherController(BasePublisherController): Handle both creation and publishing parts. Args: - dbcon (AvalonMongoDB): Connection to mongo with context. headless (bool): Headless publishing. ATM not implemented or used. """ _log = None - def __init__(self, dbcon=None, headless=False): + def __init__(self, headless=False): super(PublisherController, self).__init__() self._host = registered_host() self._headless = headless self._create_context = CreateContext( - self._host, dbcon, headless=headless, reset=False + self._host, headless=headless, reset=False ) self._publish_plugins_proxy = None From 0a900f8ae1e4ac3b1ba48aca017be03a7f743e89 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:30:59 +0100 Subject: [PATCH 0284/1271] use 'name' attribute of host implementation if is available --- openpype/pipeline/create/context.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index e421a76b6e..867809a4c1 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1111,6 +1111,8 @@ class CreateContext: @property def host_name(self): + if hasattr(self.host, "name"): + return self.host.name return os.environ["AVALON_APP"] def get_current_project_name(self): From 8678f4e2fa36bab16aa8033bf518dd0231fa885c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:31:27 +0100 Subject: [PATCH 0285/1271] 'create' returns output from creator --- openpype/pipeline/create/context.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 867809a4c1..413580526e 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1429,6 +1429,7 @@ class CreateContext: failed = False add_traceback = False exc_info = None + result = None try: # Fake CreatorError (Could be maybe specific exception?) if creator is None: @@ -1436,7 +1437,7 @@ class CreateContext: "Creator {} was not found".format(identifier) ) - creator.create(*args, **kwargs) + result = creator.create(*args, **kwargs) except CreatorError: failed = True @@ -1458,6 +1459,7 @@ class CreateContext: identifier, label, exc_info, add_traceback ) ]) + return result def creator_removed_instance(self, instance): """When creator removes instance context should be acknowledged. From d09b7812616bfaee29c4089e2ece893ff6bd3faa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:32:53 +0100 Subject: [PATCH 0286/1271] implemented helper function 'create_with_context' to trigger standartized creation --- openpype/pipeline/create/context.py | 64 ++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 413580526e..655af1b8ed 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -8,17 +8,13 @@ import inspect from uuid import uuid4 from contextlib import contextmanager -from openpype.client import get_assets +from openpype.client import get_assets, get_asset_by_name from openpype.settings import ( get_system_settings, get_project_settings ) from openpype.host import IPublishHost from openpype.pipeline import legacy_io -from openpype.pipeline.mongodb import ( - AvalonMongoDB, - session_data_from_environment, -) from .creator_plugins import ( Creator, @@ -1461,6 +1457,64 @@ class CreateContext: ]) return result + def create_with_context( + self, + creator_identifier, + variant=None, + asset_doc=None, + task_name=None, + pre_create_data=None + ): + """Trigger create of plugins with standartized + + Args: + creator_identifier (str): + asset_doc (Dict[str, Any]): + task_name (str): Name of task to which is context related. + variant (str): Variant used for subset name. + pre_create_data (Dict[str, Any]): Pre-create attribute values. + + Returns: + Any: Output of triggered creator's 'create' method. + + Raises: + CreatorsCreateFailed: When creation fails. + """ + + if pre_create_data is None: + pre_create_data = {} + + project_name = self.project_name + if asset_doc is None: + asset_name = self.get_current_asset_name() + asset_doc = get_asset_by_name(project_name, asset_name) + task_name = self.get_current_task_name() + + creator = self.creators.get(creator_identifier) + family = None + subset_name = None + if creator is not None: + family = creator.family + subset_name = creator.get_subset_name( + variant, + task_name, + asset_doc, + project_name, + self.host_name + ) + instance_data = { + "asset": asset_doc["name"], + "task": task_name, + "variant": variant, + "family": family + } + return self.raw_create( + creator_identifier, + subset_name, + instance_data, + pre_create_data + ) + def creator_removed_instance(self, instance): """When creator removes instance context should be acknowledged. From 430fe6aed42d8c08f57c7b91dd2eb9185a3d1fde Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:33:33 +0100 Subject: [PATCH 0287/1271] renamed 'create'->'raw_create' and 'create_with_context'->'create' --- openpype/pipeline/create/context.py | 9 ++++++--- openpype/tools/publisher/control.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 655af1b8ed..a9f8ae3ce1 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1407,8 +1407,8 @@ class CreateContext: with self.bulk_instances_collection(): self._bulk_instances_to_process.append(instance) - def create(self, identifier, *args, **kwargs): - """Wrapper for creators to trigger created. + def raw_create(self, identifier, *args, **kwargs): + """Wrapper for creators to trigger 'create' method. Different types of creators may expect different arguments thus the hints for args are blind. @@ -1417,6 +1417,9 @@ class CreateContext: identifier (str): Creator's identifier. *args (Tuple[Any]): Arguments for create method. **kwargs (Dict[Any, Any]): Keyword argument for create method. + + Raises: + CreatorsCreateFailed: When creation fails. """ error_message = "Failed to run Creator with identifier \"{}\". {}" @@ -1457,7 +1460,7 @@ class CreateContext: ]) return result - def create_with_context( + def create( self, creator_identifier, variant=None, diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 83c2dd4b1c..670c22a43e 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -2017,7 +2017,7 @@ class PublisherController(BasePublisherController): success = True try: - self._create_context.create( + self._create_context.raw_create( creator_identifier, subset_name, instance_data, options ) except CreatorsOperationFailed as exc: From 498c8564f71c4d85ad88a101d6de7ae11357bb7d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sat, 28 Jan 2023 00:40:08 +0100 Subject: [PATCH 0288/1271] swapped argments order in docstring --- openpype/pipeline/create/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index a9f8ae3ce1..702731f8b2 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1472,9 +1472,9 @@ class CreateContext: Args: creator_identifier (str): + variant (str): Variant used for subset name. asset_doc (Dict[str, Any]): task_name (str): Name of task to which is context related. - variant (str): Variant used for subset name. pre_create_data (Dict[str, Any]): Pre-create attribute values. Returns: From 6d4e6cd81f617b002b97fdcb55d67ae334b7e5bd Mon Sep 17 00:00:00 2001 From: OpenPype Date: Sat, 28 Jan 2023 03:28:05 +0000 Subject: [PATCH 0289/1271] [Automated] Bump version --- openpype/version.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/version.py b/openpype/version.py index 831df22d6e..ab61b16a14 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.0" +__version__ = "3.15.1-nightly.1" diff --git a/pyproject.toml b/pyproject.toml index a872ed3609..ceab9eeff1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.0" # OpenPype +version = "3.15.1-nightly.1" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 1af1909e0e936b62fa1a029c4921a43744ff1633 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sat, 28 Jan 2023 16:09:23 +0000 Subject: [PATCH 0290/1271] Revert clean up --- openpype/hosts/maya/api/lib.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 71d890f46b..358996fc7f 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -690,6 +690,11 @@ def get_current_renderlayer(): return cmds.editRenderLayerGlobals(query=True, currentRenderLayer=True) +def get_renderer(layer): + with renderlayer(layer): + return cmds.getAttr("defaultRenderGlobals.currentRenderer") + + @contextlib.contextmanager def no_undo(flush=False): """Disable the undo queue during the context From 0f4ca3643eeca0568705570d6c2bf06a89ede319 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sat, 28 Jan 2023 16:09:32 +0000 Subject: [PATCH 0291/1271] Remove redundant variable --- openpype/hosts/maya/api/menu.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/api/menu.py b/openpype/hosts/maya/api/menu.py index 67109e9958..791475173f 100644 --- a/openpype/hosts/maya/api/menu.py +++ b/openpype/hosts/maya/api/menu.py @@ -50,7 +50,6 @@ def install(): parent="MayaWindow" ) - renderer = cmds.getAttr('defaultRenderGlobals.currentRenderer').lower() # Create context menu context_label = "{}, {}".format( legacy_io.Session["AVALON_ASSET"], From 14c23246fbda7a6a4d571c9bbff93252f52466e9 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sat, 28 Jan 2023 16:14:10 +0000 Subject: [PATCH 0292/1271] Resolve plugin --- openpype/hosts/maya/plugins/publish/submit_maya_muster.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/submit_maya_muster.py b/openpype/hosts/maya/plugins/publish/submit_maya_muster.py index 8ae3e5124b..1a6463fb9d 100644 --- a/openpype/hosts/maya/plugins/publish/submit_maya_muster.py +++ b/openpype/hosts/maya/plugins/publish/submit_maya_muster.py @@ -52,11 +52,6 @@ def _get_script(): return module_path -def get_renderer(layer): - with lib.renderlayer(layer): - return cmds.getAttr("defaultRenderGlobals.currentRenderer") - - def get_renderer_variables(renderlayer=None): """Retrieve the extension which has been set in the VRay settings @@ -71,7 +66,7 @@ def get_renderer_variables(renderlayer=None): dict """ - renderer = get_renderer(renderlayer or lib.get_current_renderlayer()) + renderer = lib.get_renderer(renderlayer or lib.get_current_renderlayer()) render_attrs = lib.RENDER_ATTRS.get(renderer, lib.RENDER_ATTRS["default"]) padding = cmds.getAttr("{}.{}".format(render_attrs["node"], From 01a70c06a45ddfd7f20522a592451e753bb27739 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 30 Jan 2023 15:06:55 +0800 Subject: [PATCH 0293/1271] add loaders for fbx import and max scene import --- .../hosts/max/plugins/load/load_camera_fbx.py | 48 +++++++++++++++++ .../hosts/max/plugins/load/load_max_scene.py | 51 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 openpype/hosts/max/plugins/load/load_camera_fbx.py create mode 100644 openpype/hosts/max/plugins/load/load_max_scene.py diff --git a/openpype/hosts/max/plugins/load/load_camera_fbx.py b/openpype/hosts/max/plugins/load/load_camera_fbx.py new file mode 100644 index 0000000000..e7b12ea4c8 --- /dev/null +++ b/openpype/hosts/max/plugins/load/load_camera_fbx.py @@ -0,0 +1,48 @@ +import os +from openpype.pipeline import( + load +) + +class FbxLoader(load.LoaderPlugin): + """Fbx Loader""" + + families = ["camera"] + representations = ["fbx"] + order = -9 + icon = "code-fork" + color = "white" + + def load(self, context, name=None, namespace=None, data=None): + from pymxs import runtime as rt + + filepath = os.path.normpath(self.fname) + + fbx_import_cmd = ( + f""" + +FBXImporterSetParam "Animation" true +FBXImporterSetParam "Cameras" true +FBXImporterSetParam "AxisConversionMethod" true +FbxExporterSetParam "UpAxis" "Y" +FbxExporterSetParam "Preserveinstances" true + +importFile @"{filepath}" #noPrompt using:FBXIMP + """) + + self.log.debug(f"Executing command: {fbx_import_cmd}") + rt.execute(fbx_import_cmd) + + container_name = f"{name}_CON" + + asset = rt.getNodeByName(f"{name}") + # rename the container with "_CON" + container = rt.container(name=container_name) + asset.Parent = container + + return container + + def remove(self, container): + from pymxs import runtime as rt + + node = container["node"] + rt.delete(node) diff --git a/openpype/hosts/max/plugins/load/load_max_scene.py b/openpype/hosts/max/plugins/load/load_max_scene.py new file mode 100644 index 0000000000..54983c1e6e --- /dev/null +++ b/openpype/hosts/max/plugins/load/load_max_scene.py @@ -0,0 +1,51 @@ +import os +from openpype.pipeline import( + load +) + +class MaxSceneLoader(load.LoaderPlugin): + """Max Scene Loader""" + + families = ["camera"] + representations = ["max"] + order = -8 + icon = "code-fork" + color = "green" + + def load(self, context, name=None, namespace=None, data=None): + from pymxs import runtime as rt + import re + path = os.path.normpath(self.fname) + # import the max scene by using "merge file" + path = path.replace('\\', '/') + + merge_before = { + c for c in rt.rootNode.Children + if rt.classOf(c) == rt.Container + } + rt.mergeMaxFile(path) + + merge_after = { + c for c in rt.rootNode.Children + if rt.classOf(c) == rt.Container + } + max_containers = merge_after.difference(merge_before) + + if len(max_containers) != 1: + self.log.error("Something failed when loading.") + + max_container = max_containers.pop() + container_name = f"{name}_CON" + # rename the container with "_CON" + # get the original container + container = rt.container(name=container_name) + max_container.Parent = container + + return container + + def remove(self, container): + from pymxs import runtime as rt + + node = container["node"] + rt.delete(node) + From 92986bcff5f5428793581727355989904ab23fe1 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 30 Jan 2023 15:09:23 +0800 Subject: [PATCH 0294/1271] hound fix --- openpype/hosts/max/plugins/load/load_camera_fbx.py | 3 ++- openpype/hosts/max/plugins/load/load_max_scene.py | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/max/plugins/load/load_camera_fbx.py b/openpype/hosts/max/plugins/load/load_camera_fbx.py index e7b12ea4c8..1b1df364c1 100644 --- a/openpype/hosts/max/plugins/load/load_camera_fbx.py +++ b/openpype/hosts/max/plugins/load/load_camera_fbx.py @@ -1,8 +1,9 @@ import os -from openpype.pipeline import( +from openpype.pipeline import ( load ) + class FbxLoader(load.LoaderPlugin): """Fbx Loader""" diff --git a/openpype/hosts/max/plugins/load/load_max_scene.py b/openpype/hosts/max/plugins/load/load_max_scene.py index 54983c1e6e..57f172cf6a 100644 --- a/openpype/hosts/max/plugins/load/load_max_scene.py +++ b/openpype/hosts/max/plugins/load/load_max_scene.py @@ -1,8 +1,9 @@ import os -from openpype.pipeline import( +from openpype.pipeline import ( load ) + class MaxSceneLoader(load.LoaderPlugin): """Max Scene Loader""" @@ -14,7 +15,6 @@ class MaxSceneLoader(load.LoaderPlugin): def load(self, context, name=None, namespace=None, data=None): from pymxs import runtime as rt - import re path = os.path.normpath(self.fname) # import the max scene by using "merge file" path = path.replace('\\', '/') @@ -48,4 +48,3 @@ class MaxSceneLoader(load.LoaderPlugin): node = container["node"] rt.delete(node) - From 68959d426eb4b37d335f7194d73c12c6c84c2037 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 30 Jan 2023 10:24:17 +0100 Subject: [PATCH 0295/1271] Fix typo --- openpype/pipeline/create/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9c468ae8fc..a0cdb7dfea 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -153,7 +153,7 @@ class CreatorsRemoveFailed(CreatorsOperationFailed): class CreatorsCreateFailed(CreatorsOperationFailed): def __init__(self, failed_info): - msg = "Faled to create instances" + msg = "Failed to create instances" super(CreatorsCreateFailed, self).__init__( msg, failed_info ) From 40712089d94ce22a5d34981584dc7db8beed9554 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 30 Jan 2023 10:48:57 +0100 Subject: [PATCH 0296/1271] Validate creator and asset doc --- openpype/pipeline/create/context.py | 33 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 702731f8b2..35024b5af8 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1484,27 +1484,32 @@ class CreateContext: CreatorsCreateFailed: When creation fails. """ - if pre_create_data is None: - pre_create_data = {} + creator = self.creators.get(creator_identifier) + if creator is None: + raise CreatorError( + "Creator {} was not found".format(creator_identifier) + ) project_name = self.project_name if asset_doc is None: asset_name = self.get_current_asset_name() asset_doc = get_asset_by_name(project_name, asset_name) task_name = self.get_current_task_name() + if asset_doc is None: + raise CreatorError( + "Asset with name {} was not found".format(asset_name) + ) - creator = self.creators.get(creator_identifier) - family = None - subset_name = None - if creator is not None: - family = creator.family - subset_name = creator.get_subset_name( - variant, - task_name, - asset_doc, - project_name, - self.host_name - ) + if pre_create_data is None: + pre_create_data = {} + + subset_name = creator.get_subset_name( + variant, + task_name, + asset_doc, + project_name, + self.host_name + ) instance_data = { "asset": asset_doc["name"], "task": task_name, From 75bffb4daeacc8a31dc584edf3b76b3989a0607d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 30 Jan 2023 10:49:17 +0100 Subject: [PATCH 0297/1271] removed unnecessary family from instance data --- openpype/pipeline/create/context.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 35024b5af8..dbbde9218f 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1513,8 +1513,7 @@ class CreateContext: instance_data = { "asset": asset_doc["name"], "task": task_name, - "variant": variant, - "family": family + "variant": variant } return self.raw_create( creator_identifier, From daa961d24976a4b9a1d5f51320017fa346bbfc84 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 30 Jan 2023 10:49:27 +0100 Subject: [PATCH 0298/1271] variant is required argument --- openpype/pipeline/create/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index dbbde9218f..b10bbc17de 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1463,7 +1463,7 @@ class CreateContext: def create( self, creator_identifier, - variant=None, + variant, asset_doc=None, task_name=None, pre_create_data=None From 839445b7c9fd6bcaeb850b4d012066f1d4fa204e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 30 Jan 2023 11:03:21 +0100 Subject: [PATCH 0299/1271] fix pyproject.toml OP version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ceab9eeff1..a872ed3609 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.1-nightly.1" # OpenPype +version = "3.15.0" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 4d990e6f87964cb4f5fb2c61cebfcaea47ac3151 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 30 Jan 2023 11:03:51 +0100 Subject: [PATCH 0300/1271] Updated docstrings --- openpype/pipeline/create/context.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index b10bbc17de..190d542724 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1191,7 +1191,15 @@ class CreateContext: function or using `legacy_io.Session`. Some hosts have ability to change context file without using workfiles - tool but that change is not propagated to + tool but that change is not propagated to 'legacy_io.Session' + nor 'os.environ'. + + Todos: + UI: Current context should be also checked on save - compare + initial values vs. current values. + Related to UI checks: Current workfile can be also considered + as current context information as that's where the metadata + are stored. We should store the workfile (if is available) too. """ project_name = asset_name = task_name = None @@ -1468,12 +1476,19 @@ class CreateContext: task_name=None, pre_create_data=None ): - """Trigger create of plugins with standartized + """Trigger create of plugins with standartized arguments. + + Arguments 'asset_doc' and 'task_name' use current context as default + values. If only 'task_name' is provided it will be overriden by + task name from current context. If 'task_name' is not provided + when 'asset_doc' is, it is considered that task name is not specified, + which can lead to error if subset name template requires task name. Args: - creator_identifier (str): + creator_identifier (str): Identifier of creator plugin. variant (str): Variant used for subset name. - asset_doc (Dict[str, Any]): + asset_doc (Dict[str, Any]): Asset document which define context of + creation (possible context of created instance/s). task_name (str): Name of task to which is context related. pre_create_data (Dict[str, Any]): Pre-create attribute values. @@ -1481,6 +1496,7 @@ class CreateContext: Any: Output of triggered creator's 'create' method. Raises: + CreatorError: If creator was not found or asset is empty. CreatorsCreateFailed: When creation fails. """ From 1f08a2734339ae7c40424df778d7402807beed10 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 30 Jan 2023 10:15:04 +0000 Subject: [PATCH 0301/1271] Workaround for motion blur --- .../plugins/publish/extract_workfile_xgen.py | 147 +++++++++++++----- .../maya/plugins/publish/extract_xgen.py | 62 ++++---- 2 files changed, 146 insertions(+), 63 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py index d37a03d1f6..5847563e5b 100644 --- a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -1,11 +1,14 @@ import os import shutil +import copy from maya import cmds import pyblish.api from openpype.hosts.maya.api import current_file +from openpype.hosts.maya.api.lib import extract_alembic from openpype.pipeline import publish +from openpype.lib import StringTemplate class ExtractWorkfileXgen(publish.Extractor): @@ -18,9 +21,22 @@ class ExtractWorkfileXgen(publish.Extractor): hosts = ["maya"] def process(self, instance): + transfers = [] + + # Validate there is any palettes in the scene. + if not cmds.ls(type="xgmPalette"): + self.log.debug( + "No collections found in the scene. Abort Xgen extraction." + ) + return + else: + import xgenm + # Validate to extract only when we are publishing a renderlayer as # well. renderlayer = False + start_frame = None + end_frame = None for i in instance.context: is_renderlayer = ( "renderlayer" in i.data.get("families", []) or @@ -28,6 +44,17 @@ class ExtractWorkfileXgen(publish.Extractor): ) if is_renderlayer and i.data["publish"]: renderlayer = True + + if start_frame is None: + start_frame = i.data["frameStart"] + if end_frame is None: + end_frame = i.data["frameEnd"] + + if i.data["frameStart"] < start_frame: + start_frame = i.data["frameStart"] + if i.data["frameEnd"] > end_frame: + end_frame = i.data["frameEnd"] + break if not renderlayer: @@ -37,6 +64,51 @@ class ExtractWorkfileXgen(publish.Extractor): ) return + # We decrement start frame and increment end frame so motion blur will + # render correctly. + start_frame -= 1 + end_frame += 1 + + # Extract patches alembic. + basename, _ = os.path.splitext(current_file()) + dirname = os.path.dirname(current_file()) + kwargs = {"attrPrefix": ["xgen"], "stripNamespaces": True} + alembic_files = [] + for palette in cmds.ls(type="xgmPalette"): + patch_names = [] + for description in xgenm.descriptions(palette): + for name in xgenm.boundGeometry(palette, description): + patch_names.append(name) + + alembic_file = os.path.join( + dirname, + "{}__{}.abc".format(basename, palette.replace(":", "__ns__")) + ) + extract_alembic( + alembic_file, + root=patch_names, + selection=False, + startFrame=float(start_frame), + endFrame=float(end_frame), + verbose=True, + **kwargs + ) + alembic_files.append(alembic_file) + + template_data = copy.deepcopy(instance.data["anatomyData"]) + published_maya_path = StringTemplate( + instance.context.data["anatomy"].templates["publish"]["file"] + ).format(template_data) + published_basename, _ = os.path.splitext(published_maya_path) + + for source in alembic_files: + destination = os.path.join( + os.path.dirname(instance.data["resourcesDir"]), + os.path.basename(source.replace(basename, published_basename)) + ) + transfers.append((source, destination)) + + # Validate that we are using the published workfile. deadline_settings = instance.context.get("deadline") if deadline_settings: publish_settings = deadline_settings["publish"] @@ -76,8 +148,9 @@ class ExtractWorkfileXgen(publish.Extractor): with open(destination, "r") as f: for line in [line.rstrip() for line in f]: if line.startswith("\txgProjectPath"): + path = os.path.dirname(instance.data["resourcesDir"]) line = "\txgProjectPath\t\t{}/".format( - instance.data["resourcesDir"].replace("\\", "/") + path.replace("\\", "/") ) lines.append(line) @@ -88,31 +161,30 @@ class ExtractWorkfileXgen(publish.Extractor): sources.append(destination) # Add resource files to workfile instance. - transfers = [] for source in sources: basename = os.path.basename(source) - destination = os.path.join(instance.data["resourcesDir"], basename) + destination = os.path.join( + os.path.dirname(instance.data["resourcesDir"]), basename + ) transfers.append((source, destination)) - import xgenm + destination_dir = os.path.join( + instance.data["resourcesDir"], "collections" + ) for palette in cmds.ls(type="xgmPalette"): - relative_data_path = xgenm.getAttr( - "xgDataPath", palette.replace("|", "") - ).split(os.pathsep)[0] - absolute_data_path = relative_data_path.replace( - "${PROJECT}", - xgenm.getAttr("xgProjectPath", palette.replace("|", "")) - ) - - for root, _, files in os.walk(absolute_data_path): - for file in files: - source = os.path.join(root, file).replace("\\", "/") - destination = os.path.join( - instance.data["resourcesDir"], - relative_data_path.replace("${PROJECT}", ""), - source.replace(absolute_data_path, "")[1:] - ) - transfers.append((source, destination.replace("\\", "/"))) + project_path = xgenm.getAttr("xgProjectPath", palette) + data_path = xgenm.getAttr("xgDataPath", palette) + data_path = data_path.replace("${PROJECT}", project_path) + for path in data_path.split(os.pathsep): + for root, _, files in os.walk(path): + for f in files: + source = os.path.join(root, f) + destination = "{}/{}{}".format( + destination_dir, + palette.replace(":", "__ns__"), + source.replace(path, "") + ) + transfers.append((source, destination)) for source, destination in transfers: self.log.debug("Transfer: {} > {}".format(source, destination)) @@ -120,21 +192,26 @@ class ExtractWorkfileXgen(publish.Extractor): instance.data["transfers"] = transfers # Set palette attributes in preparation for workfile publish. - attrs = ["xgFileName", "xgBaseFile"] + attrs = {"xgFileName": None, "xgBaseFile": ""} data = {} for palette in cmds.ls(type="xgmPalette"): - for attr in attrs: - value = cmds.getAttr(palette + "." + attr) - if value: - new_value = "resources/{}".format(value) - node_attr = "{}.{}".format(palette, attr) - self.log.info( - "Setting \"{}\" on \"{}\"".format(new_value, node_attr) - ) - cmds.setAttr(node_attr, new_value, type="string") - try: - data[palette][attr] = value - except KeyError: - data[palette] = {attr: value} + attrs["xgFileName"] = "resources/{}.xgen".format( + palette.replace(":", "__ns__") + ) + for attr, value in attrs.items(): + node_attr = palette + "." + attr + + old_value = cmds.getAttr(node_attr) + try: + data[palette][attr] = old_value + except KeyError: + data[palette] = {attr: old_value} + + cmds.setAttr(node_attr, value, type="string") + self.log.info( + "Setting \"{}\" on \"{}\"".format(value, node_attr) + ) + + cmds.setAttr(palette + "." + "xgExportAsDelta", False) instance.data["xgenAttributes"] = data diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 80b62275cd..fd85cadcac 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -1,5 +1,6 @@ import os import copy +import tempfile from maya import cmds import xgenm @@ -16,6 +17,7 @@ class ExtractXgenCache(publish.Extractor): - Duplicate nodes used for patches. - Export palette and import onto duplicate nodes. - Export/Publish duplicate nodes and palette. + - Export duplicate palette to .xgen file and add to publish. - Publish all xgen files as resources. """ @@ -32,29 +34,6 @@ class ExtractXgenCache(publish.Extractor): maya_filename = "{}.{}".format(instance.data["name"], self.scene_type) maya_filepath = os.path.join(staging_dir, maya_filename) - # Get published xgen file name. - template_data = copy.deepcopy(instance.data["anatomyData"]) - template_data.update({"ext": "xgen"}) - templates = instance.context.data["anatomy"].templates["publish"] - xgen_filename = StringTemplate(templates["file"]).format(template_data) - name = instance.data["xgmPalette"].replace(":", "__").replace("|", "") - xgen_filename = xgen_filename.replace(".xgen", "__" + name + ".xgen") - - # Export xgen palette files. - xgen_path = os.path.join(staging_dir, xgen_filename).replace("\\", "/") - xgenm.exportPalette( - instance.data["xgmPalette"].replace("|", ""), xgen_path - ) - self.log.info("Extracted to {}".format(xgen_path)) - - representation = { - "name": name, - "ext": "xgen", - "files": xgen_filename, - "stagingDir": staging_dir, - } - instance.data["representations"].append(representation) - # Collect nodes to export. duplicate_nodes = [] for node, connections in instance.data["xgenConnections"].items(): @@ -74,17 +53,43 @@ class ExtractXgenCache(publish.Extractor): duplicate_nodes.append(duplicate_transform) + # Export temp xgen palette files. + temp_xgen_path = os.path.join( + tempfile.gettempdir(), "temp.xgen" + ).replace("\\", "/") + xgenm.exportPalette( + instance.data["xgmPalette"].replace("|", ""), temp_xgen_path + ) + self.log.info("Extracted to {}".format(temp_xgen_path)) + # Import xgen onto the duplicate. with maintained_selection(): cmds.select(duplicate_nodes) - palette = xgenm.importPalette(xgen_path, []) + palette = xgenm.importPalette(temp_xgen_path, []) - attribute_data = { - "{}.xgFileName".format(palette): xgen_filename + # Get published xgen file name. + template_data = copy.deepcopy(instance.data["anatomyData"]) + template_data.update({"ext": "xgen"}) + templates = instance.context.data["anatomy"].templates["publish"] + xgen_filename = StringTemplate(templates["file"]).format(template_data) + + # Export duplicated palette. + xgen_path = os.path.join(staging_dir, xgen_filename).replace("\\", "/") + xgenm.exportPalette(palette, xgen_path) + + representation = { + "name": "xgen", + "ext": "xgen", + "files": xgen_filename, + "stagingDir": staging_dir, } + instance.data["representations"].append(representation) # Export Maya file. type = "mayaAscii" if self.scene_type == "ma" else "mayaBinary" + attribute_data = { + "{}.xgFileName".format(palette): xgen_filename + } with attribute_values(attribute_data): with maintained_selection(): cmds.select(duplicate_nodes + [palette]) @@ -106,12 +111,13 @@ class ExtractXgenCache(publish.Extractor): "name": self.scene_type, "ext": self.scene_type, "files": maya_filename, - "stagingDir": staging_dir, - "data": {"xgenName": palette} + "stagingDir": staging_dir } instance.data["representations"].append(representation) + # Clean up. cmds.delete(duplicate_nodes + [palette]) + os.remove(temp_xgen_path) # Collect all files under palette root as resources. data_path = xgenm.getAttr( From c908bd2abaeb2a44b8beb88a4ed30aa1f2dad56c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 30 Jan 2023 10:15:27 +0000 Subject: [PATCH 0302/1271] Workaround for motion blur --- openpype/hosts/maya/plugins/load/load_xgen.py | 76 +++++++------------ 1 file changed, 28 insertions(+), 48 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 8249c9092e..0f2e13dd79 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -1,5 +1,4 @@ import os -import shutil import maya.cmds as cmds import xgenm @@ -12,7 +11,6 @@ from openpype.hosts.maya.api.lib import ( ) from openpype.hosts.maya.api import current_file from openpype.hosts.maya.api.plugin import get_reference_node -from openpype.pipeline import get_representation_path class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): @@ -25,55 +23,19 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): icon = "code-fork" color = "orange" - def setup_xgen_palette_file(self, maya_filepath, namespace, name): - # Setup xgen palette file. - project_path = os.path.dirname(current_file()) - - # Copy the xgen palette file from published version. - _, maya_extension = os.path.splitext(maya_filepath) - source = maya_filepath.replace(maya_extension, ".xgen") - xgen_file = os.path.join( - project_path, - "{basename}__{namespace}__{name}.xgen".format( - basename=os.path.splitext(os.path.basename(current_file()))[0], - namespace=namespace, - name=name - ) - ).replace("\\", "/") - self.log.info("Copying {} to {}".format(source, xgen_file)) - shutil.copy(source, xgen_file) - - # Modify xgDataPath and xgProjectPath to have current workspace first - # and published version directory second. This ensure that any newly - # created xgen files are created in the current workspace. - resources_path = os.path.join(os.path.dirname(source), "resources") + def write_xgen_file(self, data, xgen_file): lines = [] with open(xgen_file, "r") as f: for line in [line.rstrip() for line in f]: - if line.startswith("\txgDataPath"): - data_path = line.split("\t")[-1] - line = "\txgDataPath\t\t{}{}{}".format( - data_path, - os.pathsep, - data_path.replace( - "${PROJECT}xgen", resources_path.replace("\\", "/") - ) - ) - - if line.startswith("\txgProjectPath"): - line = "\txgProjectPath\t\t{}/".format( - project_path.replace("\\", "/") - ) + for key, value in data.items(): + if line.startswith("\t{}".format(key)): + line = "\t{}\t\t{}".format(key, value) lines.append(line) with open(xgen_file, "w") as f: f.write("\n".join(lines)) - xgd_file = xgen_file.replace(".xgen", ".xgd") - - return xgen_file, xgd_file - def process_reference(self, context, name, namespace, options): # Validate workfile has a path. if current_file() is None: @@ -89,11 +51,6 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): self.fname, context["project"]["name"] ) - name = context["representation"]["data"]["xgenName"] - xgen_file, xgd_file = self.setup_xgen_palette_file( - maya_filepath, namespace, name - ) - # Reference xgen. Xgen does not like being referenced in under a group. new_nodes = [] @@ -106,9 +63,32 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): returnNewNodes=True ) - xgen_palette = cmds.ls(nodes, type="xgmPalette", long=True)[0] + xgen_palette = cmds.ls( + nodes, type="xgmPalette", long=True + )[0].replace("|", "") + + _, maya_extension = os.path.splitext(current_file()) + xgen_file = current_file().replace( + maya_extension, + "__{}.xgen".format(xgen_palette.replace(":", "__")) + ) + xgd_file = xgen_file.replace(".xgen", ".xgd") self.set_palette_attributes(xgen_palette, xgen_file, xgd_file) + # Change the cache and disk values of xgDataPath and xgProjectPath + # to ensure paths are setup correctly. + project_path = os.path.dirname(current_file()).replace("\\", "/") + xgenm.setAttr("xgProjectPath", project_path, xgen_palette) + data_path = "${{PROJECT}}xgen/collections/{}{}{}".format( + xgen_palette.replace(":", "__ns__"), + os.pathsep, + xgenm.getAttr("xgDataPath", xgen_palette) + ) + xgenm.setAttr("xgDataPath", data_path, xgen_palette) + + data = {"xgProjectPath": project_path, "xgDataPath": data_path} + self.write_xgen_file(data, xgen_file) + # This create an expression attribute of float. If we did not add # any changes to collection, then Xgen does not create an xgd file # on save. This gives errors when launching the workfile again due From 46996bb592bcfe054f19b979f94267cb026d9826 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 30 Jan 2023 18:51:03 +0800 Subject: [PATCH 0303/1271] add camera family in abc loader --- openpype/hosts/max/plugins/load/load_pointcache.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/max/plugins/load/load_pointcache.py b/openpype/hosts/max/plugins/load/load_pointcache.py index a2e567ed5d..65d0662faa 100644 --- a/openpype/hosts/max/plugins/load/load_pointcache.py +++ b/openpype/hosts/max/plugins/load/load_pointcache.py @@ -15,7 +15,10 @@ from openpype.hosts.max.api import lib class AbcLoader(load.LoaderPlugin): """Alembic loader.""" - families = ["model", "animation", "pointcache"] + families = ["model", + "camera", + "animation", + "pointcache"] label = "Load Alembic" representations = ["abc"] order = -10 From 0d527a477bfa3f6d66a4745b5424534bc575ecbd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 30 Jan 2023 12:07:24 +0100 Subject: [PATCH 0304/1271] fix super call --- openpype/lib/attribute_definitions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index a2fe8314b2..b5cd15f41a 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -465,7 +465,7 @@ class EnumDef(AbstractAttrDef): return self.default def serialize(self): - data = super(TextDef, self).serialize() + data = super(EnumDef, self).serialize() data["items"] = copy.deepcopy(self.items) return data From c8fb00c9c81c9a60f0436dbcb43b546060cf6664 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 30 Jan 2023 12:43:40 +0100 Subject: [PATCH 0305/1271] global: expanding staging dir maker abstraction so it supports `OPENPYPE_TEMP_DIR` with anatomy formatting keys --- openpype/pipeline/publish/lib.py | 55 ++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index c76671fa39..5591acf57d 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -10,7 +10,11 @@ import six import pyblish.plugin import pyblish.api -from openpype.lib import Logger, filter_profiles +from openpype.lib import ( + Logger, + filter_profiles, + StringTemplate +) from openpype.settings import ( get_project_settings, get_system_settings, @@ -623,12 +627,51 @@ def get_instance_staging_dir(instance): Returns: str: Path to staging dir of instance. """ + staging_dir = instance.data.get('stagingDir', None) + openpype_temp_dir = os.getenv("OPENPYPE_TEMP_DIR") - staging_dir = instance.data.get("stagingDir") if not staging_dir: - staging_dir = os.path.normpath( - tempfile.mkdtemp(prefix="pyblish_tmp_") - ) - instance.data["stagingDir"] = staging_dir + custom_temp_dir = None + if openpype_temp_dir: + if "{" in openpype_temp_dir: + anatomy = instance.context.data["anatomy"] + # get anatomy formating data + # so template formating is supported + anatomy_data = copy.deepcopy(instance.context.data["anatomyData"]) + anatomy_data["root"] = anatomy.roots + """Template path formating is supporting: + - optional key formating + - available tokens: + - root[work | ] + - project[name | code] + - asset + - hierarchy + - task + - username + - app + """ + custom_temp_dir = StringTemplate.format_template( + openpype_temp_dir, anatomy_data) + custom_temp_dir = os.path.normpath(custom_temp_dir) + # create the dir in case it doesnt exists + os.makedirs(os.path.dirname(custom_temp_dir)) + elif os.path.exists(openpype_temp_dir): + custom_temp_dir = openpype_temp_dir + + + if custom_temp_dir: + staging_dir = os.path.normpath( + tempfile.mkdtemp( + prefix="pyblish_tmp_", + dir=custom_temp_dir + ) + ) + else: + staging_dir = os.path.normpath( + tempfile.mkdtemp(prefix="pyblish_tmp_") + ) + instance.data['stagingDir'] = staging_dir + + instance.context.data["cleanupFullPaths"].append(staging_dir) return staging_dir From ef86f1451542a1c9a85e9472da9ced35f6b92d95 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 30 Jan 2023 12:51:13 +0100 Subject: [PATCH 0306/1271] global: update docstrings at `get_instance_staging_dir` --- openpype/pipeline/publish/lib.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 5591acf57d..cb01d4633e 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -613,8 +613,21 @@ def context_plugin_should_run(plugin, context): def get_instance_staging_dir(instance): """Unified way how staging dir is stored and created on instances. - First check if 'stagingDir' is already set in instance data. If there is - not create new in tempdir. + First check if 'stagingDir' is already set in instance data. + In case there already is new tempdir will not be created. + + It also supports `OPENPYPE_TEMP_DIR`, so studio can define own temp shared + repository per project or even per more granular context. Template formating + is supported also with optional keys. Folder is created in case it doesnt exists. + + Available anatomy formating keys: + - root[work | ] + - project[name | code] + - asset + - hierarchy + - task + - username + - app Note: Staging dir does not have to be necessarily in tempdir so be carefull @@ -641,7 +654,7 @@ def get_instance_staging_dir(instance): anatomy_data["root"] = anatomy.roots """Template path formating is supporting: - optional key formating - - available tokens: + - available keys: - root[work | ] - project[name | code] - asset From 43399a08c82393c8522b3b4e7f59f16be354dbe6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 30 Jan 2023 12:52:11 +0100 Subject: [PATCH 0307/1271] flame: removing class override for staging dir creation it is already available in more expanded feature at parent class --- .../publish/extract_subset_resources.py | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index d5294d61c2..c6148162a6 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -548,30 +548,3 @@ class ExtractSubsetResources(publish.Extractor): "Path `{}` is containing more that one clip".format(path) ) return clips[0] - - def staging_dir(self, instance): - """Provide a temporary directory in which to store extracted files - - Upon calling this method the staging directory is stored inside - the instance.data['stagingDir'] - """ - staging_dir = instance.data.get('stagingDir', None) - openpype_temp_dir = os.getenv("OPENPYPE_TEMP_DIR") - - if not staging_dir: - if openpype_temp_dir and os.path.exists(openpype_temp_dir): - staging_dir = os.path.normpath( - tempfile.mkdtemp( - prefix="pyblish_tmp_", - dir=openpype_temp_dir - ) - ) - else: - staging_dir = os.path.normpath( - tempfile.mkdtemp(prefix="pyblish_tmp_") - ) - instance.data['stagingDir'] = staging_dir - - instance.context.data["cleanupFullPaths"].append(staging_dir) - - return staging_dir From 4dc9fadc424222a3f99444aaea3df8a8fd701a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 30 Jan 2023 13:56:56 +0100 Subject: [PATCH 0308/1271] Update openpype/pipeline/publish/lib.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/pipeline/publish/lib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index cb01d4633e..33f23ddb97 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -640,7 +640,9 @@ def get_instance_staging_dir(instance): Returns: str: Path to staging dir of instance. """ - staging_dir = instance.data.get('stagingDir', None) + staging_dir = instance.data.get('stagingDir') + if staging_dir: + return staging_dir openpype_temp_dir = os.getenv("OPENPYPE_TEMP_DIR") if not staging_dir: From a9cc08120d7f6c47b65f22b64081c73d4d5e1804 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 30 Jan 2023 13:59:39 +0100 Subject: [PATCH 0309/1271] global: refactor code for better readibility --- openpype/pipeline/publish/lib.py | 94 +++++++++++++++++--------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 33f23ddb97..cc4304cebd 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -616,11 +616,12 @@ def get_instance_staging_dir(instance): First check if 'stagingDir' is already set in instance data. In case there already is new tempdir will not be created. - It also supports `OPENPYPE_TEMP_DIR`, so studio can define own temp shared - repository per project or even per more granular context. Template formating - is supported also with optional keys. Folder is created in case it doesnt exists. + It also supports `OPENPYPE_TEMP_DIR`, so studio can define own temp + shared repository per project or even per more granular context. + Template formating is supported also with optional keys. Folder is + created in case it doesnt exists. - Available anatomy formating keys: + Available anatomy formatting keys: - root[work | ] - project[name | code] - asset @@ -643,50 +644,55 @@ def get_instance_staging_dir(instance): staging_dir = instance.data.get('stagingDir') if staging_dir: return staging_dir + openpype_temp_dir = os.getenv("OPENPYPE_TEMP_DIR") - - if not staging_dir: - custom_temp_dir = None - if openpype_temp_dir: - if "{" in openpype_temp_dir: - anatomy = instance.context.data["anatomy"] - # get anatomy formating data - # so template formating is supported - anatomy_data = copy.deepcopy(instance.context.data["anatomyData"]) - anatomy_data["root"] = anatomy.roots - """Template path formating is supporting: - - optional key formating - - available keys: - - root[work | ] - - project[name | code] - - asset - - hierarchy - - task - - username - - app - """ - custom_temp_dir = StringTemplate.format_template( - openpype_temp_dir, anatomy_data) - custom_temp_dir = os.path.normpath(custom_temp_dir) - # create the dir in case it doesnt exists - os.makedirs(os.path.dirname(custom_temp_dir)) - elif os.path.exists(openpype_temp_dir): - custom_temp_dir = openpype_temp_dir - - - if custom_temp_dir: - staging_dir = os.path.normpath( - tempfile.mkdtemp( - prefix="pyblish_tmp_", - dir=custom_temp_dir - ) + custom_temp_dir = None + if openpype_temp_dir: + if "{" in openpype_temp_dir: + custom_temp_dir = _formated_staging_dir( + instance, openpype_temp_dir ) - else: - staging_dir = os.path.normpath( - tempfile.mkdtemp(prefix="pyblish_tmp_") + elif os.path.exists(openpype_temp_dir): + custom_temp_dir = openpype_temp_dir + + + if custom_temp_dir: + staging_dir = os.path.normpath( + tempfile.mkdtemp( + prefix="pyblish_tmp_", + dir=custom_temp_dir ) - instance.data['stagingDir'] = staging_dir + ) + else: + staging_dir = os.path.normpath( + tempfile.mkdtemp(prefix="pyblish_tmp_") + ) + instance.data['stagingDir'] = staging_dir instance.context.data["cleanupFullPaths"].append(staging_dir) return staging_dir + + +def _formated_staging_dir(instance, openpype_temp_dir): + anatomy = instance.context.data["anatomy"] + # get anatomy formating data + # so template formating is supported + anatomy_data = copy.deepcopy(instance.context.data["anatomyData"]) + anatomy_data["root"] = anatomy.roots + """Template path formatting is supporting: + - optional key formating + - available keys: + - root[work | ] + - project[name | code] + - asset + - hierarchy + - task + - username + - app + """ + result = StringTemplate.format_template(openpype_temp_dir, anatomy_data) + result = os.path.normpath(result) + # create the dir in case it doesnt exists + os.makedirs(os.path.dirname(result)) + return result From a5ca4f93b8cdf4102828d5fd91d563e808081440 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Mon, 30 Jan 2023 15:21:29 +0100 Subject: [PATCH 0310/1271] cancel recursivity removal --- .../workfile/workfile_template_builder.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index d262d6a771..fd5ac16579 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -521,6 +521,11 @@ class AbstractTemplateBuilder(object): if not level_limit: level_limit = 1000 + placeholder_by_scene_id = { + placeholder.scene_identifier: placeholder + for placeholder in placeholders + } + all_processed = len(placeholders) == 0 # Counter is checked at the ned of a loop so the loop happens at least # once. @@ -561,14 +566,24 @@ class AbstractTemplateBuilder(object): placeholder.set_finished() - # Clear shared data before getting new placeholders - self.clear_shared_populate_data() + # self.clear_shared_populate_data() iter_counter += 1 if iter_counter >= level_limit: break all_processed = True + + collected_placeholders = self.get_placeholders() + for placeholder in collected_placeholders: + identifier = placeholder.scene_identifier + if identifier in placeholder_by_scene_id: + continue + + all_processed = False + placeholder_by_scene_id[identifier] = placeholder + placeholders.append(placeholder) + self.refresh() def _get_build_profiles(self): @@ -988,7 +1003,7 @@ class PlaceholderItem(object): return self._log def __repr__(self): - return "< {} {} >".format(self.__class__.__name__, self.name) + return "< {} {} >".format(self.__class__.__name__, self.data['family']) @property def order(self): From a2176420b7ed584d10b7bff37d729a0924000b39 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 30 Jan 2023 17:20:39 +0000 Subject: [PATCH 0311/1271] Working extraction --- .../hosts/maya/plugins/publish/extract_xgen.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index fd85cadcac..9549aba76d 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -6,7 +6,9 @@ from maya import cmds import xgenm from openpype.pipeline import publish -from openpype.hosts.maya.api.lib import maintained_selection, attribute_values +from openpype.hosts.maya.api.lib import ( + maintained_selection, attribute_values, write_xgen_file +) from openpype.lib import StringTemplate @@ -77,6 +79,18 @@ class ExtractXgenCache(publish.Extractor): xgen_path = os.path.join(staging_dir, xgen_filename).replace("\\", "/") xgenm.exportPalette(palette, xgen_path) + data = { + "xgDataPath": os.path.join( + instance.data["resourcesDir"], + "collections", + palette.replace(":", "__ns__") + ).replace("\\", "/"), + "xgProjectPath": os.path.dirname( + instance.data["resourcesDir"] + ).replace("\\", "/") + } + write_xgen_file(data, xgen_path) + representation = { "name": "xgen", "ext": "xgen", @@ -136,7 +150,7 @@ class ExtractXgenCache(publish.Extractor): destination = os.path.join( instance.data["resourcesDir"], "collections", - os.path.basename(data_path), + palette, source.replace(data_path, "")[1:] ) transfers.append((source, destination.replace("\\", "/"))) From 5a280a32bbec68d6a9284f005f062bf387a2a476 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 30 Jan 2023 17:20:46 +0000 Subject: [PATCH 0312/1271] Working updating --- openpype/hosts/maya/plugins/load/load_xgen.py | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 0f2e13dd79..35f7c21c58 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -7,10 +7,13 @@ from Qt import QtWidgets import openpype.hosts.maya.api.plugin from openpype.hosts.maya.api.lib import ( - maintained_selection, get_container_members, attribute_values + maintained_selection, + get_container_members, + attribute_values, + write_xgen_file ) from openpype.hosts.maya.api import current_file -from openpype.hosts.maya.api.plugin import get_reference_node +from openpype.pipeline import get_representation_path class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): @@ -23,18 +26,14 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): icon = "code-fork" color = "orange" - def write_xgen_file(self, data, xgen_file): - lines = [] - with open(xgen_file, "r") as f: - for line in [line.rstrip() for line in f]: - for key, value in data.items(): - if line.startswith("\t{}".format(key)): - line = "\t{}\t\t{}".format(key, value) - - lines.append(line) - - with open(xgen_file, "w") as f: - f.write("\n".join(lines)) + def get_xgen_xgd_paths(self, palette): + _, maya_extension = os.path.splitext(current_file()) + xgen_file = current_file().replace( + maya_extension, + "__{}.xgen".format(palette.replace("|", "").replace(":", "__")) + ) + xgd_file = xgen_file.replace(".xgen", ".xgd") + return xgen_file, xgd_file def process_reference(self, context, name, namespace, options): # Validate workfile has a path. @@ -67,12 +66,7 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): nodes, type="xgmPalette", long=True )[0].replace("|", "") - _, maya_extension = os.path.splitext(current_file()) - xgen_file = current_file().replace( - maya_extension, - "__{}.xgen".format(xgen_palette.replace(":", "__")) - ) - xgd_file = xgen_file.replace(".xgen", ".xgd") + xgen_file, xgd_file = self.get_xgen_xgd_paths(xgen_palette) self.set_palette_attributes(xgen_palette, xgen_file, xgd_file) # Change the cache and disk values of xgDataPath and xgProjectPath @@ -87,7 +81,7 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): xgenm.setAttr("xgDataPath", data_path, xgen_palette) data = {"xgProjectPath": project_path, "xgDataPath": data_path} - self.write_xgen_file(data, xgen_file) + write_xgen_file(data, xgen_file) # This create an expression attribute of float. If we did not add # any changes to collection, then Xgen does not create an xgd file @@ -138,21 +132,35 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): container_node = container["objectName"] members = get_container_members(container_node) - xgen_palette = cmds.ls(members, type="xgmPalette", long=True)[0] - reference_node = get_reference_node(members, self.log) - namespace = cmds.referenceQuery(reference_node, namespace=True)[1:] - - xgen_file, xgd_file = self.setup_xgen_palette_file( - get_representation_path(representation), - namespace, - representation["data"]["xgenName"] - ) + xgen_palette = cmds.ls( + members, type="xgmPalette", long=True + )[0].replace("|", "") + xgen_file, xgd_file = self.get_xgen_xgd_paths(xgen_palette) # Export current changes to apply later. xgenm.createDelta(xgen_palette.replace("|", ""), xgd_file) self.set_palette_attributes(xgen_palette, xgen_file, xgd_file) + maya_file = get_representation_path(representation) + _, extension = os.path.splitext(maya_file) + new_xgen_file = maya_file.replace(extension, ".xgen") + data_path = "" + with open(new_xgen_file, "r") as f: + for line in [line.rstrip() for line in f]: + if line.startswith("\txgDataPath"): + data_path = line.split("\t")[-1] + break + + project_path = os.path.dirname(current_file()).replace("\\", "/") + data_path = "${{PROJECT}}xgen/collections/{}{}{}".format( + xgen_palette.replace(":", "__ns__"), + os.pathsep, + data_path + ) + data = {"xgProjectPath": project_path, "xgDataPath": data_path} + write_xgen_file(data, xgen_file) + attribute_data = { "{}.xgFileName".format(xgen_palette): os.path.basename(xgen_file), "{}.xgBaseFile".format(xgen_palette): "", From 22ddb58cca40a320af68688e073cf98bb9e73fbc Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 30 Jan 2023 17:20:58 +0000 Subject: [PATCH 0313/1271] Refactor to lib --- openpype/hosts/maya/api/lib.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index dd5da275e8..65f39270f5 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3446,3 +3446,17 @@ def iter_visible_nodes_in_range(nodes, start, end): def get_attribute_input(attr): connections = cmds.listConnections(attr, plugs=True, destination=False) return connections[0] if connections else None + + +def write_xgen_file(data, filepath): + lines = [] + with open(filepath, "r") as f: + for line in [line.rstrip() for line in f]: + for key, value in data.items(): + if line.startswith("\t{}".format(key)): + line = "\t{}\t\t{}".format(key, value) + + lines.append(line) + + with open(filepath, "w") as f: + f.write("\n".join(lines)) From 2e9f60693b2da0b824ae8ad5283f445673e0c29a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 30 Jan 2023 18:05:52 +0000 Subject: [PATCH 0314/1271] Fix Edge case of loading xgen while existing xgen in cache. --- openpype/hosts/maya/plugins/load/load_xgen.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 35f7c21c58..d0ed3e05b3 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -87,9 +87,11 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): # any changes to collection, then Xgen does not create an xgd file # on save. This gives errors when launching the workfile again due # to trying to find the xgd file. - xgenm.addCustomAttr( - "custom_float_ignore", xgen_palette.replace("|", "") - ) + name = "custom_float_ignore" + if name not in xgenm.customAttrs(xgen_palette): + xgenm.addCustomAttr( + "custom_float_ignore", xgen_palette + ) shapes = cmds.ls(nodes, shapes=True, long=True) From 8ec87cba01a8c0d91c67843d6c575cf647cd18e3 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 30 Jan 2023 18:12:53 +0000 Subject: [PATCH 0315/1271] Fix Resetting Xgen attributes after incremental save. --- .../maya/plugins/publish/reset_xgen_attributes.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py b/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py index 0f763613c9..d3408f2c76 100644 --- a/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py +++ b/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py @@ -7,8 +7,8 @@ class ResetXgenAttributes(pyblish.api.InstancePlugin): """Reset Xgen attributes.""" label = "Reset Xgen Attributes." - # Offset to run after global integrator. - order = pyblish.api.IntegratorOrder + 1.0 + # Offset to run after workfile increment plugin. + order = pyblish.api.IntegratorOrder + 10.0 families = ["workfile"] def process(self, instance): @@ -19,3 +19,9 @@ class ResetXgenAttributes(pyblish.api.InstancePlugin): "Setting \"{}\" on \"{}\"".format(value, node_attr) ) cmds.setAttr(node_attr, value, type="string") + + cmds.setAttr(palette + "." + "xgExportAsDelta", True) + + if instance.data.get("xgenAttributes", {}): + self.log.info("Saving changes.") + cmds.file(save=True) From f1e8803f590e90e0cf38e5601cad97b12a5f97ae Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 30 Jan 2023 18:18:37 +0000 Subject: [PATCH 0316/1271] Update docs --- website/docs/artist_hosts_maya_xgen.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/website/docs/artist_hosts_maya_xgen.md b/website/docs/artist_hosts_maya_xgen.md index fc75959f2b..191826f49c 100644 --- a/website/docs/artist_hosts_maya_xgen.md +++ b/website/docs/artist_hosts_maya_xgen.md @@ -8,7 +8,7 @@ sidebar_label: Xgen ### Settings -Go to project settings > Maya > enable "Open Workfile Post Initialization"; +Go to project settings > `Maya` > enable `Open Workfile Post Initialization`; `project_settings/maya/open_workfile_post_initialization` @@ -27,6 +27,12 @@ Importing XGen Collections... # Error: XGen: Failed to import collection from file P:/PROJECTS/OP01_CG_demo/shots/sh040/work/Lighting/cg_ball_xgenMain_v035__ball_rigMain_01___collection.xgen # ``` +Go to project settings > `Deadline` > `Publish plugins` > `Maya Submit to Deadline` > disable `Use Published scene`; + +`project_settings/deadline/publish/MayaSubmitDeadline/use_published` + +This is due to temporary workaround while fixing rendering with published scenes. + ## Create Create an Xgen instance to publish. This needs to contain only **one Xgen collection**. From b8c7f067c31d7dd1c7a18de65072e4fbdb1375a1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 31 Jan 2023 07:56:20 +0100 Subject: [PATCH 0317/1271] global: host settings should be optional --- openpype/pipeline/colorspace.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/pipeline/colorspace.py b/openpype/pipeline/colorspace.py index e1ffe9d333..a13e6df811 100644 --- a/openpype/pipeline/colorspace.py +++ b/openpype/pipeline/colorspace.py @@ -438,7 +438,8 @@ def get_imageio_file_rules(project_name, host_name, project_settings=None): # get file rules from global and host_name frules_global = imageio_global["file_rules"] - frules_host = imageio_host["file_rules"] + # host is optional, some might not have any settings + frules_host = imageio_host.get("file_rules", {}) # compile file rules dictionary file_rules = {} @@ -455,7 +456,7 @@ def _get_imageio_settings(project_settings, host_name): Args: project_settings (dict): project settings. - Defaults to None. + Defaults to None. host_name (str): host name Returns: @@ -463,6 +464,7 @@ def _get_imageio_settings(project_settings, host_name): """ # get image io from global and host_name imageio_global = project_settings["global"]["imageio"] - imageio_host = project_settings[host_name]["imageio"] + # host is optional, some might not have any settings + imageio_host = project_settings[host_name].get("imageio", {}) return imageio_global, imageio_host From a8fcd42b6afedbcc0bb82869d0c703edf09f30e2 Mon Sep 17 00:00:00 2001 From: Libor Batek Date: Tue, 31 Jan 2023 10:59:47 +0100 Subject: [PATCH 0318/1271] adjusted according comments --- website/docs/artist_getting_started.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index 40961dbc77..5ab89e31d8 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -19,19 +19,22 @@ If this is not the case, please contact your administrator to consult on how to If you are working from **home** though, you'll **need to install** it yourself. You should, however, receive the OpenPype installer files from your studio admin, supervisor or production, because OpenPype versions and executables might not be compatible between studios. -Installing OpenPype is possible by Windows installer or by unzipping it anywhere on the disk from downloaded ZIP archive. +Installing OpenPype is possible by Installer or by unzipping downloaded ZIP archive to any drive location. -For more detailed info about installation on different OS please visit [Installation section](artist_install.md). +> For more detailed info about installing OpenPype please visit [Installation section](artist_install.md). -There are two ways running OpenPype +--- -first most common one by using OP icon on the Desktop triggering +You can run OpenPype by desktop "OP" icon (if exists after installing) or by directly executing -**openpype_gui.exe** suitable **for artists**. It runs OpenPype GUI in the OS tray. From there you can run all the available tools. To use any of the features, OpenPype must be running in the tray. +**openpype_gui.exe** located in the OpenPype folder. This executable being suitable **for artists**. -or alternatively by using +or alternatively by + +**openpype_console.exe** which is more suitable for **TDs/Admin** for debugging and error reporting. This one runs with opened console window where all the necessary info will appear during user's work session. + +> By seeing the "OP" icon in the OS tray user can easily tell OpenPype already running. -**openpype_console.exe** located in the OpenPype folder which is suitable for **TDs/Admin** for debugging and error reporting. This one runs with opened console window where all the necessary info will appear during user's work session. ## First Launch From 25b7d69aabbb90dbbc328f98efafc64668dbe8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Tue, 31 Jan 2023 11:58:38 +0100 Subject: [PATCH 0319/1271] Update openpype/pipeline/colorspace.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/pipeline/colorspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/colorspace.py b/openpype/pipeline/colorspace.py index a13e6df811..15d545c49f 100644 --- a/openpype/pipeline/colorspace.py +++ b/openpype/pipeline/colorspace.py @@ -465,6 +465,6 @@ def _get_imageio_settings(project_settings, host_name): # get image io from global and host_name imageio_global = project_settings["global"]["imageio"] # host is optional, some might not have any settings - imageio_host = project_settings[host_name].get("imageio", {}) + imageio_host = project_settings.get(host_name, {}).get("imageio", {}) return imageio_global, imageio_host From 06863de23e512bdd0aac5787376c4b4724308dcf Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 31 Jan 2023 12:01:43 +0100 Subject: [PATCH 0320/1271] PR comments --- openpype/pipeline/colorspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/colorspace.py b/openpype/pipeline/colorspace.py index 15d545c49f..14daa44db8 100644 --- a/openpype/pipeline/colorspace.py +++ b/openpype/pipeline/colorspace.py @@ -445,7 +445,7 @@ def get_imageio_file_rules(project_name, host_name, project_settings=None): file_rules = {} if frules_global["enabled"]: file_rules.update(frules_global["rules"]) - if frules_host["enabled"]: + if frules_host.get("enabled"): file_rules.update(frules_host["rules"]) return file_rules From 153783c2fd290006e3ba5abda2cbf430137bee28 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 31 Jan 2023 12:17:52 +0100 Subject: [PATCH 0321/1271] Refactor `self.` to `cls.` --- .../hosts/maya/plugins/publish/validate_attributes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_attributes.py b/openpype/hosts/maya/plugins/publish/validate_attributes.py index 136c38bc1d..7a1f0cf086 100644 --- a/openpype/hosts/maya/plugins/publish/validate_attributes.py +++ b/openpype/hosts/maya/plugins/publish/validate_attributes.py @@ -58,23 +58,23 @@ class ValidateAttributes(pyblish.api.ContextPlugin): # Filter families. families = [instance.data["family"]] families += instance.data.get("families", []) - families = list(set(families) & set(self.attributes.keys())) + families = list(set(families) & set(cls.attributes.keys())) if not families: continue # Get all attributes to validate. attributes = {} for family in families: - for preset in self.attributes[family]: + for preset in cls.attributes[family]: [node_name, attribute_name] = preset.split(".") try: attributes[node_name].update( - {attribute_name: self.attributes[family][preset]} + {attribute_name: cls.attributes[family][preset]} ) except KeyError: attributes.update({ node_name: { - attribute_name: self.attributes[family][preset] + attribute_name: cls.attributes[family][preset] } }) From fe688bd2437304f95e729c5672e83b6e31b37c48 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 31 Jan 2023 12:34:33 +0100 Subject: [PATCH 0322/1271] Remove unused `version` attribute --- openpype/hosts/maya/plugins/publish/collect_maya_workspace.py | 1 - .../hosts/maya/plugins/publish/validate_mesh_lamina_faces.py | 1 - .../hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py | 1 - .../hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py | 1 - .../hosts/maya/plugins/publish/validate_mesh_single_uv_set.py | 1 - .../hosts/maya/plugins/publish/validate_no_default_camera.py | 1 - openpype/hosts/maya/plugins/publish/validate_no_namespace.py | 1 - .../hosts/maya/plugins/publish/validate_no_null_transforms.py | 1 - .../hosts/maya/plugins/publish/validate_rig_joints_hidden.py | 1 - .../hosts/maya/plugins/publish/validate_scene_set_workspace.py | 1 - .../hosts/maya/plugins/publish/validate_shape_default_names.py | 1 - .../maya/plugins/publish/validate_transform_naming_suffix.py | 1 - openpype/hosts/maya/plugins/publish/validate_transform_zero.py | 1 - 13 files changed, 13 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_maya_workspace.py b/openpype/hosts/maya/plugins/publish/collect_maya_workspace.py index 1250ea438f..122fabe8a1 100644 --- a/openpype/hosts/maya/plugins/publish/collect_maya_workspace.py +++ b/openpype/hosts/maya/plugins/publish/collect_maya_workspace.py @@ -12,7 +12,6 @@ class CollectMayaWorkspace(pyblish.api.ContextPlugin): label = "Maya Workspace" hosts = ['maya'] - version = (0, 1, 0) def process(self, context): workspace = cmds.workspace(rootDirectory=True, query=True) diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py b/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py index 4427c6eece..8cd33aa1be 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py @@ -16,7 +16,6 @@ class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin): hosts = ['maya'] families = ['model'] category = 'geometry' - version = (0, 1, 0) label = 'Mesh Lamina Faces' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py b/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py index 0ef2716559..fc6da82338 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py @@ -20,7 +20,6 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin): families = ['model'] hosts = ['maya'] category = 'geometry' - version = (0, 1, 0) label = 'Mesh Edge Length Non Zero' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] optional = True 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 c8892a8e59..2079c159c2 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py @@ -21,7 +21,6 @@ class ValidateMeshNormalsUnlocked(pyblish.api.Validator): hosts = ['maya'] families = ['model'] category = 'geometry' - version = (0, 1, 0) label = 'Mesh Normals Unlocked' actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py b/openpype/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py index 6ca8c06ba5..155ab23294 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py @@ -23,7 +23,6 @@ class ValidateMeshSingleUVSet(pyblish.api.InstancePlugin): families = ['model', 'pointcache'] category = 'uv' optional = True - version = (0, 1, 0) label = "Mesh Single UV Set" actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_no_default_camera.py b/openpype/hosts/maya/plugins/publish/validate_no_default_camera.py index 1a5773e6a7..a4fb938d43 100644 --- a/openpype/hosts/maya/plugins/publish/validate_no_default_camera.py +++ b/openpype/hosts/maya/plugins/publish/validate_no_default_camera.py @@ -16,7 +16,6 @@ class ValidateNoDefaultCameras(pyblish.api.InstancePlugin): order = ValidateContentsOrder hosts = ['maya'] families = ['camera'] - version = (0, 1, 0) label = "No Default Cameras" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_no_namespace.py b/openpype/hosts/maya/plugins/publish/validate_no_namespace.py index 01c77e5b2e..61b531aa0f 100644 --- a/openpype/hosts/maya/plugins/publish/validate_no_namespace.py +++ b/openpype/hosts/maya/plugins/publish/validate_no_namespace.py @@ -24,7 +24,6 @@ class ValidateNoNamespace(pyblish.api.InstancePlugin): hosts = ['maya'] families = ['model'] category = 'cleanup' - version = (0, 1, 0) label = 'No Namespaces' actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py b/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py index b430c2b63c..291fe6890a 100644 --- a/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py +++ b/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py @@ -44,7 +44,6 @@ class ValidateNoNullTransforms(pyblish.api.InstancePlugin): hosts = ['maya'] families = ['model'] category = 'cleanup' - version = (0, 1, 0) label = 'No Empty/Null Transforms' actions = [RepairAction, openpype.hosts.maya.api.action.SelectInvalidAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_rig_joints_hidden.py b/openpype/hosts/maya/plugins/publish/validate_rig_joints_hidden.py index d5bf7fd1cf..30d95128a2 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rig_joints_hidden.py +++ b/openpype/hosts/maya/plugins/publish/validate_rig_joints_hidden.py @@ -24,7 +24,6 @@ class ValidateRigJointsHidden(pyblish.api.InstancePlugin): order = ValidateContentsOrder hosts = ['maya'] families = ['rig'] - version = (0, 1, 0) label = "Joints Hidden" actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_scene_set_workspace.py b/openpype/hosts/maya/plugins/publish/validate_scene_set_workspace.py index ec2bea220d..91f30f2f4f 100644 --- a/openpype/hosts/maya/plugins/publish/validate_scene_set_workspace.py +++ b/openpype/hosts/maya/plugins/publish/validate_scene_set_workspace.py @@ -32,7 +32,6 @@ class ValidateSceneSetWorkspace(pyblish.api.ContextPlugin): order = ValidatePipelineOrder hosts = ['maya'] category = 'scene' - version = (0, 1, 0) label = 'Maya Workspace Set' def process(self, context): diff --git a/openpype/hosts/maya/plugins/publish/validate_shape_default_names.py b/openpype/hosts/maya/plugins/publish/validate_shape_default_names.py index 651c6bcec9..6b3375ae7e 100644 --- a/openpype/hosts/maya/plugins/publish/validate_shape_default_names.py +++ b/openpype/hosts/maya/plugins/publish/validate_shape_default_names.py @@ -40,7 +40,6 @@ class ValidateShapeDefaultNames(pyblish.api.InstancePlugin): families = ['model'] category = 'cleanup' optional = True - version = (0, 1, 0) label = "Shape Default Naming" actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py index 65551c8d5e..2bb8bca3e1 100644 --- a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py +++ b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py @@ -34,7 +34,6 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): families = ['model'] category = 'cleanup' optional = True - version = (0, 1, 0) label = 'Suffix Naming Conventions' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] SUFFIX_NAMING_TABLE = {"mesh": ["_GEO", "_GES", "_GEP", "_OSD"], diff --git a/openpype/hosts/maya/plugins/publish/validate_transform_zero.py b/openpype/hosts/maya/plugins/publish/validate_transform_zero.py index da569195e8..034d325091 100644 --- a/openpype/hosts/maya/plugins/publish/validate_transform_zero.py +++ b/openpype/hosts/maya/plugins/publish/validate_transform_zero.py @@ -19,7 +19,6 @@ class ValidateTransformZero(pyblish.api.Validator): hosts = ["maya"] families = ["model"] category = "geometry" - version = (0, 1, 0) label = "Transform Zero (Freeze)" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] From 7d81f1906c371e35c153c1fd78ab9cc491f0fee4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 31 Jan 2023 12:35:33 +0100 Subject: [PATCH 0323/1271] Remove unused `category` attribute --- openpype/hosts/maya/plugins/publish/validate_color_sets.py | 1 - .../maya/plugins/publish/validate_mesh_arnold_attributes.py | 1 - openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py | 1 - .../hosts/maya/plugins/publish/validate_mesh_lamina_faces.py | 1 - .../hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py | 1 - .../hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py | 1 - .../hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py | 1 - .../hosts/maya/plugins/publish/validate_mesh_single_uv_set.py | 1 - .../maya/plugins/publish/validate_mesh_vertices_have_edges.py | 1 - openpype/hosts/maya/plugins/publish/validate_no_namespace.py | 1 - .../hosts/maya/plugins/publish/validate_no_null_transforms.py | 1 - .../hosts/maya/plugins/publish/validate_scene_set_workspace.py | 1 - .../hosts/maya/plugins/publish/validate_shape_default_names.py | 1 - .../maya/plugins/publish/validate_transform_naming_suffix.py | 1 - openpype/hosts/maya/plugins/publish/validate_transform_zero.py | 1 - .../maya/plugins/publish/validate_unreal_mesh_triangulated.py | 1 - 16 files changed, 16 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_color_sets.py b/openpype/hosts/maya/plugins/publish/validate_color_sets.py index 905417bafa..7ce3cca61a 100644 --- a/openpype/hosts/maya/plugins/publish/validate_color_sets.py +++ b/openpype/hosts/maya/plugins/publish/validate_color_sets.py @@ -19,7 +19,6 @@ class ValidateColorSets(pyblish.api.Validator): order = ValidateMeshOrder hosts = ['maya'] families = ['model'] - category = 'geometry' label = 'Mesh ColorSets' actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_arnold_attributes.py b/openpype/hosts/maya/plugins/publish/validate_mesh_arnold_attributes.py index c1c0636b9e..fa4c66952c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_arnold_attributes.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_arnold_attributes.py @@ -19,7 +19,6 @@ class ValidateMeshArnoldAttributes(pyblish.api.InstancePlugin): order = ValidateMeshOrder hosts = ["maya"] families = ["model"] - category = "geometry" label = "Mesh Arnold Attributes" actions = [ openpype.hosts.maya.api.action.SelectInvalidAction, diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py b/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py index 36a0da7a59..0eece1014e 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py @@ -48,7 +48,6 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin): order = ValidateMeshOrder hosts = ['maya'] families = ['model'] - category = 'geometry' label = 'Mesh Has UVs' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] optional = True diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py b/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py index 8cd33aa1be..f120361583 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py @@ -15,7 +15,6 @@ class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin): order = ValidateMeshOrder hosts = ['maya'] families = ['model'] - category = 'geometry' label = 'Mesh Lamina Faces' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py b/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py index fc6da82338..78e844d201 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py @@ -19,7 +19,6 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin): order = ValidateMeshOrder families = ['model'] hosts = ['maya'] - category = 'geometry' label = 'Mesh Edge Length Non Zero' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] optional = True 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 2079c159c2..1b754a9829 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py @@ -20,7 +20,6 @@ class ValidateMeshNormalsUnlocked(pyblish.api.Validator): order = ValidateMeshOrder hosts = ['maya'] families = ['model'] - category = 'geometry' label = 'Mesh Normals Unlocked' actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py b/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py index be7324a68f..be23f61ec5 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py @@ -235,7 +235,6 @@ class ValidateMeshHasOverlappingUVs(pyblish.api.InstancePlugin): order = ValidateMeshOrder hosts = ['maya'] families = ['model'] - category = 'geometry' label = 'Mesh Has Overlapping UVs' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] optional = True diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py b/openpype/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py index 155ab23294..faa360380e 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_single_uv_set.py @@ -21,7 +21,6 @@ class ValidateMeshSingleUVSet(pyblish.api.InstancePlugin): order = ValidateMeshOrder hosts = ['maya'] families = ['model', 'pointcache'] - category = 'uv' optional = True label = "Mesh Single UV Set" actions = [openpype.hosts.maya.api.action.SelectInvalidAction, diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py b/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py index 1e6d290ae7..9ac7735501 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py @@ -63,7 +63,6 @@ class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin): order = ValidateMeshOrder hosts = ['maya'] families = ['model'] - category = 'geometry' label = 'Mesh Vertices Have Edges' actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_no_namespace.py b/openpype/hosts/maya/plugins/publish/validate_no_namespace.py index 61b531aa0f..e91b99359d 100644 --- a/openpype/hosts/maya/plugins/publish/validate_no_namespace.py +++ b/openpype/hosts/maya/plugins/publish/validate_no_namespace.py @@ -23,7 +23,6 @@ class ValidateNoNamespace(pyblish.api.InstancePlugin): order = ValidateContentsOrder hosts = ['maya'] families = ['model'] - category = 'cleanup' label = 'No Namespaces' actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py b/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py index 291fe6890a..f77fc81dc1 100644 --- a/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py +++ b/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py @@ -43,7 +43,6 @@ class ValidateNoNullTransforms(pyblish.api.InstancePlugin): order = ValidateContentsOrder hosts = ['maya'] families = ['model'] - category = 'cleanup' label = 'No Empty/Null Transforms' actions = [RepairAction, openpype.hosts.maya.api.action.SelectInvalidAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_scene_set_workspace.py b/openpype/hosts/maya/plugins/publish/validate_scene_set_workspace.py index 91f30f2f4f..f1fa4d3c4c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_scene_set_workspace.py +++ b/openpype/hosts/maya/plugins/publish/validate_scene_set_workspace.py @@ -31,7 +31,6 @@ class ValidateSceneSetWorkspace(pyblish.api.ContextPlugin): order = ValidatePipelineOrder hosts = ['maya'] - category = 'scene' label = 'Maya Workspace Set' def process(self, context): diff --git a/openpype/hosts/maya/plugins/publish/validate_shape_default_names.py b/openpype/hosts/maya/plugins/publish/validate_shape_default_names.py index 6b3375ae7e..4ab669f46b 100644 --- a/openpype/hosts/maya/plugins/publish/validate_shape_default_names.py +++ b/openpype/hosts/maya/plugins/publish/validate_shape_default_names.py @@ -38,7 +38,6 @@ class ValidateShapeDefaultNames(pyblish.api.InstancePlugin): order = ValidateContentsOrder hosts = ['maya'] families = ['model'] - category = 'cleanup' optional = True label = "Shape Default Naming" actions = [openpype.hosts.maya.api.action.SelectInvalidAction, diff --git a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py index 2bb8bca3e1..0147aa8a52 100644 --- a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py +++ b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py @@ -32,7 +32,6 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): order = ValidateContentsOrder hosts = ['maya'] families = ['model'] - category = 'cleanup' optional = True label = 'Suffix Naming Conventions' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_transform_zero.py b/openpype/hosts/maya/plugins/publish/validate_transform_zero.py index 034d325091..abd9e00af1 100644 --- a/openpype/hosts/maya/plugins/publish/validate_transform_zero.py +++ b/openpype/hosts/maya/plugins/publish/validate_transform_zero.py @@ -18,7 +18,6 @@ class ValidateTransformZero(pyblish.api.Validator): order = ValidateContentsOrder hosts = ["maya"] families = ["model"] - category = "geometry" label = "Transform Zero (Freeze)" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] diff --git a/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py b/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py index 4211e76a73..e78962bf97 100644 --- a/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py +++ b/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py @@ -13,7 +13,6 @@ class ValidateUnrealMeshTriangulated(pyblish.api.InstancePlugin): order = ValidateMeshOrder hosts = ["maya"] families = ["staticMesh"] - category = "geometry" label = "Mesh is Triangulated" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] active = False From aad5a4f6eb84f6d131dc0c7e9f3f2c66b9772837 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 31 Jan 2023 11:36:07 +0000 Subject: [PATCH 0324/1271] Update openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py Co-authored-by: Roy Nieterau --- .../plugins/publish/reset_xgen_attributes.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py b/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py index d3408f2c76..08f367f2d5 100644 --- a/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py +++ b/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py @@ -12,16 +12,18 @@ class ResetXgenAttributes(pyblish.api.InstancePlugin): families = ["workfile"] def process(self, instance): - for palette, data in instance.data.get("xgenAttributes", {}).items(): + xgen_attributes = instance.data.get("xgenAttributes", {}) + if not xgen_attributes : + return + + for palette, data in xgen_attributes.items(): for attr, value in data.items(): node_attr = "{}.{}".format(palette, attr) self.log.info( "Setting \"{}\" on \"{}\"".format(value, node_attr) ) cmds.setAttr(node_attr, value, type="string") - - cmds.setAttr(palette + "." + "xgExportAsDelta", True) - - if instance.data.get("xgenAttributes", {}): - self.log.info("Saving changes.") - cmds.file(save=True) + cmds.setAttr(palette + ".xgExportAsDelta", True) + + self.log.info("Saving changes.") + cmds.file(save=True) From ca6d6f7ccafa7b85d72f573c4a4519a326d60b54 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 31 Jan 2023 12:36:22 +0100 Subject: [PATCH 0325/1271] Remove unused `category` attribute from blender plug-ins too --- openpype/hosts/blender/plugins/publish/validate_mesh_has_uv.py | 1 - .../blender/plugins/publish/validate_mesh_no_negative_scale.py | 1 - 2 files changed, 2 deletions(-) diff --git a/openpype/hosts/blender/plugins/publish/validate_mesh_has_uv.py b/openpype/hosts/blender/plugins/publish/validate_mesh_has_uv.py index cee855671d..edf47193be 100644 --- a/openpype/hosts/blender/plugins/publish/validate_mesh_has_uv.py +++ b/openpype/hosts/blender/plugins/publish/validate_mesh_has_uv.py @@ -14,7 +14,6 @@ class ValidateMeshHasUvs(pyblish.api.InstancePlugin): order = ValidateContentsOrder hosts = ["blender"] families = ["model"] - category = "geometry" label = "Mesh Has UV's" actions = [openpype.hosts.blender.api.action.SelectInvalidAction] optional = True diff --git a/openpype/hosts/blender/plugins/publish/validate_mesh_no_negative_scale.py b/openpype/hosts/blender/plugins/publish/validate_mesh_no_negative_scale.py index 45ac08811d..618feb95c1 100644 --- a/openpype/hosts/blender/plugins/publish/validate_mesh_no_negative_scale.py +++ b/openpype/hosts/blender/plugins/publish/validate_mesh_no_negative_scale.py @@ -14,7 +14,6 @@ class ValidateMeshNoNegativeScale(pyblish.api.Validator): order = ValidateContentsOrder hosts = ["blender"] families = ["model"] - category = "geometry" label = "Mesh No Negative Scale" actions = [openpype.hosts.blender.api.action.SelectInvalidAction] From 529ab6ca9bf0de6656fa11ba5f4e38af4b048049 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 31 Jan 2023 12:37:28 +0100 Subject: [PATCH 0326/1271] Remove unused `version` attribute from blender plug-ins too --- .../blender/plugins/publish/validate_camera_zero_keyframe.py | 1 - .../hosts/blender/plugins/publish/validate_no_colons_in_name.py | 1 - .../hosts/blender/plugins/publish/validate_transform_zero.py | 1 - 3 files changed, 3 deletions(-) diff --git a/openpype/hosts/blender/plugins/publish/validate_camera_zero_keyframe.py b/openpype/hosts/blender/plugins/publish/validate_camera_zero_keyframe.py index 84b9dd1a6e..48c267fd18 100644 --- a/openpype/hosts/blender/plugins/publish/validate_camera_zero_keyframe.py +++ b/openpype/hosts/blender/plugins/publish/validate_camera_zero_keyframe.py @@ -19,7 +19,6 @@ class ValidateCameraZeroKeyframe(pyblish.api.InstancePlugin): order = ValidateContentsOrder hosts = ["blender"] families = ["camera"] - version = (0, 1, 0) label = "Zero Keyframe" actions = [openpype.hosts.blender.api.action.SelectInvalidAction] diff --git a/openpype/hosts/blender/plugins/publish/validate_no_colons_in_name.py b/openpype/hosts/blender/plugins/publish/validate_no_colons_in_name.py index f5dc9fdd5c..1a98ec4c1d 100644 --- a/openpype/hosts/blender/plugins/publish/validate_no_colons_in_name.py +++ b/openpype/hosts/blender/plugins/publish/validate_no_colons_in_name.py @@ -19,7 +19,6 @@ class ValidateNoColonsInName(pyblish.api.InstancePlugin): order = ValidateContentsOrder hosts = ["blender"] families = ["model", "rig"] - version = (0, 1, 0) label = "No Colons in names" actions = [openpype.hosts.blender.api.action.SelectInvalidAction] diff --git a/openpype/hosts/blender/plugins/publish/validate_transform_zero.py b/openpype/hosts/blender/plugins/publish/validate_transform_zero.py index 742826d3d9..66ef731e6e 100644 --- a/openpype/hosts/blender/plugins/publish/validate_transform_zero.py +++ b/openpype/hosts/blender/plugins/publish/validate_transform_zero.py @@ -21,7 +21,6 @@ class ValidateTransformZero(pyblish.api.InstancePlugin): order = ValidateContentsOrder hosts = ["blender"] families = ["model"] - version = (0, 1, 0) label = "Transform Zero" actions = [openpype.hosts.blender.api.action.SelectInvalidAction] From 768b3d8ac9d4ab72a8d776b115232df079802dda Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 31 Jan 2023 11:38:43 +0000 Subject: [PATCH 0327/1271] Improve docs --- .../maya/plugins/publish/reset_xgen_attributes.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py b/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py index 08f367f2d5..b90885663c 100644 --- a/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py +++ b/openpype/hosts/maya/plugins/publish/reset_xgen_attributes.py @@ -4,7 +4,11 @@ import pyblish.api class ResetXgenAttributes(pyblish.api.InstancePlugin): - """Reset Xgen attributes.""" + """Reset Xgen attributes. + + When the incremental save of the workfile triggers, the Xgen attributes + changes so this plugin will change it back to the values before publishing. + """ label = "Reset Xgen Attributes." # Offset to run after workfile increment plugin. @@ -13,7 +17,7 @@ class ResetXgenAttributes(pyblish.api.InstancePlugin): def process(self, instance): xgen_attributes = instance.data.get("xgenAttributes", {}) - if not xgen_attributes : + if not xgen_attributes: return for palette, data in xgen_attributes.items(): @@ -24,6 +28,9 @@ class ResetXgenAttributes(pyblish.api.InstancePlugin): ) cmds.setAttr(node_attr, value, type="string") cmds.setAttr(palette + ".xgExportAsDelta", True) - + + # Need to save the scene, cause the attribute changes above does not + # mark the scene as modified so user can exit without commiting the + # changes. self.log.info("Saving changes.") cmds.file(save=True) From 658ad2a0c2f34bdc14b4ac586ca88b08db4f8b57 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 31 Jan 2023 11:40:55 +0000 Subject: [PATCH 0328/1271] Update openpype/hosts/maya/plugins/load/load_xgen.py Co-authored-by: Roy Nieterau --- openpype/hosts/maya/plugins/load/load_xgen.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index d0ed3e05b3..81a525fe61 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -149,8 +149,9 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): new_xgen_file = maya_file.replace(extension, ".xgen") data_path = "" with open(new_xgen_file, "r") as f: - for line in [line.rstrip() for line in f]: + for line in f: if line.startswith("\txgDataPath"): + line = line.rstrip() data_path = line.split("\t")[-1] break From 67e438dcb90e9050c1d03a7de52489e678eebdf0 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 31 Jan 2023 11:56:55 +0000 Subject: [PATCH 0329/1271] Update openpype/hosts/maya/plugins/inventory/connect_geometry.py Co-authored-by: Roy Nieterau --- openpype/hosts/maya/plugins/inventory/connect_geometry.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/inventory/connect_geometry.py b/openpype/hosts/maya/plugins/inventory/connect_geometry.py index 9fe4f9e195..bcfc577104 100644 --- a/openpype/hosts/maya/plugins/inventory/connect_geometry.py +++ b/openpype/hosts/maya/plugins/inventory/connect_geometry.py @@ -89,9 +89,7 @@ class ConnectGeometry(InventoryAction): return # Setup live worldspace blendshape connection. - for match in matches: - source = match[0] - target = match[1] + for source, target in matches: blendshape = cmds.blendShape(source, target)[0] cmds.setAttr(blendshape + ".origin", 0) cmds.setAttr(blendshape + "." + target.split(":")[-1], 1) From 311944edb6ebf5e740a9a46830e4bf708b6ccc50 Mon Sep 17 00:00:00 2001 From: Libor Batek Date: Tue, 31 Jan 2023 13:25:38 +0100 Subject: [PATCH 0330/1271] updated 3dsmax int. article --- website/docs/artist_hosts_3dsmax.md | 62 ++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/website/docs/artist_hosts_3dsmax.md b/website/docs/artist_hosts_3dsmax.md index eac89f740b..baee07fbb0 100644 --- a/website/docs/artist_hosts_3dsmax.md +++ b/website/docs/artist_hosts_3dsmax.md @@ -47,7 +47,7 @@ This is the core functional area for you as a user. Most of your actions will ta ![Menu OpenPype](assets/3dsmax_menu_first_OP.png) :::note OpenPype Menu -User should use this menu for Opening / Saving when dealing with work files not standard 3dsmax ```File Menu``` even though still possible. +User should use this menu exclusively for **Opening/Saving** when dealing with work files not standard ```File Menu``` even though user still being able perform file operations via this menu but prefferably just performing quick saves during work session not saving actual workfile versions. ::: ## Working With Scene Files @@ -61,37 +61,68 @@ In OpenPype menu first go to ```Work Files``` menu item so **Work Files Window* You first choose particular asset and assigned task and corresponding workfile you would like to open. -If not any workfile present simply hit ```Save As``` and keep ```Subversion``` empty and hitting ```Ok```. +If not any workfile present simply hit ```Save As``` and keep ```Subversion``` empty and hit ```Ok```. ![Save As Dialog](assets/3dsmax_SavingFirstFile_OP.png) -OpenPype correctly names it and add version to the workfile. This basically happens whenever user trigger ```Save As``` action. Resulting into incremental version numbers like ```workfileName_v001``` ```workfileName_v002``` etc. +OpenPype correctly names it and add version to the workfile. This basically happens whenever user trigger ```Save As``` action. Resulting into incremental version numbers like -> There also additional tools for naming like ```Subversion``` in ```Save As``` dialog but we won't dive into it for now. +```workfileName_v001``` + +```workfileName_v002``` + + etc. + +Basically meaning user is free of guessing what is the correct naming and other neccessities to keep everthing in order and managed. + +> Note: user still has also other options for naming like ```Subversion```, ```Artist's Note``` but we won't dive into those now. + +Here you can see resulting work file after ```Save As``` action. ![Save As Dialog](assets/3dsmax_SavingFirstFile2_OP.png) ## Understanding Context -It is good to be aware that whenever you as a user choose ```asset``` and ```task``` you happen to be in so called **context** meaning that all user actions are in relation with particular ```asset```. This could be quickly seen in host application header and or ```OpenPype Menu``` and its accompanying tools. +As seen on our example OpenPype created pretty first workfile and named it ```220901_couch_modeling_v001.max``` meaning it sits in the Project ```220901``` being it ```couch``` asset and workfile being ```modeling``` task and obviously ```v001``` telling user its first existing version of this workfile. + +It is good to be aware that whenever you as a user choose ```asset``` and ```task``` you happen to be in so called **context** meaning that all user actions are in relation with particular ```asset```. This could be quickly seen in host application header and ```OpenPype Menu``` and its accompanying tools. ![Workfile Context](assets/3dsmax_context.png) +> Whenever you choose different ```asset``` and its ```task``` in **Work Files window** you are basically changing context to the current asset/task you have chosen. + + +This concludes the basics of working with workfiles in 3dsmax using OpenPype and its tools. Following chapters will cover other aspects like creating multiple assets types and their publishing for later usage in the production. + --- -# *...to be edited for 3dsmax* +## Creating and Publishing Instances -## ~~Setting scene data~~ +:::warning Important +Before proceeding further please check [Glossary](artist_concepts.md) and [What Is Publishing?](artist_publish.md) So you have clear idea about terminology. +::: -3dsmax settings concerning framerate, resolution and frame range are handled -by OpenPype. If set correctly in OP Project Manager/Ftrack, 3dsmax will automatically set the -values for you. - - -## ~~Publishing models~~ ### Intro +Current OpenPype integration (ver 3.15.0) supports only ```PointCache``` and ```Camera``` families now. + +**Pointcache** family being basically any geometry outputted as Alembic cache (.abc) format + +**Camera** family being 3dsmax Camera object with/without animation outputted as native .max, FBX, Alembic format + + +--- + +:::note Work in progress +This part of documentation is still work in progress. +::: + +## ...to be added + + + + + From 148c55361f13508fce1c2e9c82b7719be7b8a4a6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 31 Jan 2023 18:06:44 +0100 Subject: [PATCH 0331/1271] OP-4850 - fix AE tests 'png' extension have 2 representations instead 1. (One is regular, one is from review, with name 'png_png' --- .../aftereffects/test_deadline_publish_in_aftereffects.py | 4 ++-- .../test_deadline_publish_in_aftereffects_multicomposition.py | 2 +- .../hosts/aftereffects/test_publish_in_aftereffects.py | 4 ++-- .../aftereffects/test_publish_in_aftereffects_multiframe.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects.py b/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects.py index 04fe6cb9aa..30761693a8 100644 --- a/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects.py +++ b/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects.py @@ -62,7 +62,7 @@ class TestDeadlinePublishInAfterEffects(AEDeadlinePublishTestClass): failures.append( DBAssert.count_of_types(dbcon, "representation", 4)) - additional_args = {"context.subset": "renderTest_taskMain", + additional_args = {"context.subset": "workfileTest_task", "context.ext": "aep"} failures.append( DBAssert.count_of_types(dbcon, "representation", 1, @@ -71,7 +71,7 @@ class TestDeadlinePublishInAfterEffects(AEDeadlinePublishTestClass): additional_args = {"context.subset": "renderTest_taskMain", "context.ext": "png"} failures.append( - DBAssert.count_of_types(dbcon, "representation", 1, + DBAssert.count_of_types(dbcon, "representation", 2, additional_args=additional_args)) additional_args = {"context.subset": "renderTest_taskMain", diff --git a/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects_multicomposition.py b/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects_multicomposition.py index f009b45f4d..4adff6a815 100644 --- a/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects_multicomposition.py +++ b/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects_multicomposition.py @@ -80,7 +80,7 @@ class TestDeadlinePublishInAfterEffectsMultiComposition(AEDeadlinePublishTestCla additional_args = {"context.subset": "renderTest_taskMain", "context.ext": "png"} failures.append( - DBAssert.count_of_types(dbcon, "representation", 1, + DBAssert.count_of_types(dbcon, "representation", 2, additional_args=additional_args)) additional_args = {"context.subset": "renderTest_taskMain", diff --git a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py index 57d5a3e3f1..2e4f343a5a 100644 --- a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py +++ b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py @@ -60,7 +60,7 @@ class TestPublishInAfterEffects(AELocalPublishTestClass): failures.append( DBAssert.count_of_types(dbcon, "representation", 4)) - additional_args = {"context.subset": "renderTest_taskMain", + additional_args = {"context.subset": "workfileTest_task", "context.ext": "aep"} failures.append( DBAssert.count_of_types(dbcon, "representation", 1, @@ -69,7 +69,7 @@ class TestPublishInAfterEffects(AELocalPublishTestClass): additional_args = {"context.subset": "renderTest_taskMain", "context.ext": "png"} failures.append( - DBAssert.count_of_types(dbcon, "representation", 1, + DBAssert.count_of_types(dbcon, "representation", 2, additional_args=additional_args)) additional_args = {"context.subset": "renderTest_taskMain", diff --git a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects_multiframe.py b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects_multiframe.py index 2d95eada99..dcf34844d1 100644 --- a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects_multiframe.py +++ b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects_multiframe.py @@ -47,7 +47,7 @@ class TestPublishInAfterEffects(AELocalPublishTestClass): failures.append( DBAssert.count_of_types(dbcon, "representation", 4)) - additional_args = {"context.subset": "renderTest_taskMain", + additional_args = {"context.subset": "workfileTest_task", "context.ext": "aep"} failures.append( DBAssert.count_of_types(dbcon, "representation", 1, From 6704ba153126facf6ff3e34e2a6bfdfea96915c5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 31 Jan 2023 18:35:36 +0100 Subject: [PATCH 0332/1271] OP-4850 - fix AE multicomposition 3 subsets >> 3 versions --- .../test_deadline_publish_in_aftereffects_multicomposition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects_multicomposition.py b/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects_multicomposition.py index 4adff6a815..d372efcb9a 100644 --- a/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects_multicomposition.py +++ b/tests/integration/hosts/aftereffects/test_deadline_publish_in_aftereffects_multicomposition.py @@ -47,7 +47,7 @@ class TestDeadlinePublishInAfterEffectsMultiComposition(AEDeadlinePublishTestCla print("test_db_asserts") failures = [] - failures.append(DBAssert.count_of_types(dbcon, "version", 2)) + failures.append(DBAssert.count_of_types(dbcon, "version", 3)) failures.append( DBAssert.count_of_types(dbcon, "version", 0, name={"$ne": 1})) From ab0e3fab01f150cc963579e921c5f9547b276060 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 31 Jan 2023 17:47:12 +0000 Subject: [PATCH 0333/1271] BigRoy feedback --- openpype/hosts/maya/api/lib.py | 28 +++- openpype/hosts/maya/api/plugin.py | 30 ---- .../plugins/inventory/connect_geometry.py | 32 +++- openpype/hosts/maya/plugins/load/load_xgen.py | 31 ++++ .../maya/plugins/publish/collect_xgen.py | 32 ++++ .../plugins/publish/extract_workfile_xgen.py | 97 +++++++---- .../maya/plugins/publish/extract_xgen.py | 158 ++++++++---------- .../maya/plugins/publish/validate_xgen.py | 18 +- 8 files changed, 255 insertions(+), 171 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 65f39270f5..e9956762f2 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -5,6 +5,7 @@ import sys import platform import uuid import math +import re import json import logging @@ -3449,14 +3450,31 @@ def get_attribute_input(attr): def write_xgen_file(data, filepath): + """Overwrites data in .xgen files. + + Quite naive approach to mainly overwrite "xgDataPath" and "xgProjectPath". + + Args: + data (dict): Dictionary of key, value. Key matches with xgen file. + For example: + {"xgDataPath": "some/path"} + filepath (string): Absolute path of .xgen file. + """ + # Generate regex lookup for line to key basically + # match any of the keys in `\t{key}\t\t` + keys = "|".join(re.escape(key) for key in data.keys()) + re_keys = re.compile("^\t({})\t\t".format(keys)) + lines = [] with open(filepath, "r") as f: - for line in [line.rstrip() for line in f]: - for key, value in data.items(): - if line.startswith("\t{}".format(key)): - line = "\t{}\t\t{}".format(key, value) + for line in f: + match = re_keys.match(line) + if match: + key = match.group(1) + value = data[key] + line = "\t{}\t\t{}\n".format(key, value) lines.append(line) with open(filepath, "w") as f: - f.write("\n".join(lines)) + f.writelines(lines) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index b7adf6edfc..82df85a8be 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -300,36 +300,6 @@ class ReferenceLoader(Loader): str(representation["_id"]), type="string") - # Update any xgen containers. - compound_name = "xgenContainers" - if cmds.objExists("{}.{}".format(node, compound_name)): - import xgenm - container_amount = cmds.getAttr( - "{}.{}".format(node, compound_name), size=True - ) - # loop through all compound children - for i in range(container_amount): - attr = "{}.{}[{}].container".format(node, compound_name, i) - objectset = cmds.listConnections(attr)[0] - reference_node = cmds.sets(objectset, query=True)[0] - palettes = cmds.ls( - cmds.referenceQuery(reference_node, nodes=True), - type="xgmPalette" - ) - for palette in palettes: - for description in xgenm.descriptions(palette): - xgenm.setAttr( - "cacheFileName", - path.replace("\\", "/"), - palette, - description, - "SplinePrimitive" - ) - - # Refresh UI and viewport. - de = xgenm.xgGlobal.DescriptionEditor - de.refresh("Full") - def remove(self, container): """Remove an existing `container` from Maya scene diff --git a/openpype/hosts/maya/plugins/inventory/connect_geometry.py b/openpype/hosts/maya/plugins/inventory/connect_geometry.py index bcfc577104..a12487cf7e 100644 --- a/openpype/hosts/maya/plugins/inventory/connect_geometry.py +++ b/openpype/hosts/maya/plugins/inventory/connect_geometry.py @@ -1,6 +1,7 @@ from maya import cmds from openpype.pipeline import InventoryAction, get_representation_context +from openpype.hosts.maya.api.lib import get_id class ConnectGeometry(InventoryAction): @@ -64,12 +65,12 @@ class ConnectGeometry(InventoryAction): source_data = self.get_container_data(source_object) matches = [] - node_types = [] + node_types = set() for target_container in target_containers: target_data = self.get_container_data( target_container["objectName"] ) - node_types.extend(target_data["node_types"]) + node_types.update(target_data["node_types"]) for id, transform in target_data["ids"].items(): source_match = source_data["ids"].get(id) if source_match: @@ -99,15 +100,30 @@ class ConnectGeometry(InventoryAction): cmds.xgmPreview() def get_container_data(self, container): - data = {"node_types": [], "ids": {}} + """Collects data about the container nodes. + + Args: + container (dict): Container instance. + + Returns: + data (dict): + "node_types": All node types in container nodes. + "ids": If the node is a mesh, we collect its parent transform + id. + """ + data = {"node_types": set(), "ids": {}} ref_node = cmds.sets(container, query=True, nodesOnly=True)[0] for node in cmds.referenceQuery(ref_node, nodes=True): node_type = cmds.nodeType(node) - data["node_types"].append(node_type) - if node_type == "mesh": - transform = cmds.listRelatives(node, parent=True)[0] - id = cmds.getAttr(transform + ".cbId") - data["ids"][id] = transform + data["node_types"].add(node_type) + + # Only interested in mesh transforms for connecting geometry with + # blendshape. + if node_type != "mesh": + continue + + transform = cmds.listRelatives(node, parent=True)[0] + data["ids"][get_id(transform)] = transform return data diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 81a525fe61..5110d4ca05 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -173,3 +173,34 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): super().update(container, representation) xgenm.applyDelta(xgen_palette.replace("|", ""), xgd_file) + + # Update any xgen containers. + compound_name = "xgenContainers" + import xgenm + container_amount = cmds.getAttr( + "{}.{}".format(container_node, compound_name), size=True + ) + # loop through all compound children + for i in range(container_amount): + attr = "{}.{}[{}].container".format( + container_node, compound_name, i + ) + objectset = cmds.listConnections(attr)[0] + reference_node = cmds.sets(objectset, query=True)[0] + palettes = cmds.ls( + cmds.referenceQuery(reference_node, nodes=True), + type="xgmPalette" + ) + for palette in palettes: + for description in xgenm.descriptions(palette): + xgenm.setAttr( + "cacheFileName", + maya_file.replace("\\", "/"), + palette, + description, + "SplinePrimitive" + ) + + # Refresh UI and viewport. + de = xgenm.xgGlobal.DescriptionEditor + de.refresh("Full") diff --git a/openpype/hosts/maya/plugins/publish/collect_xgen.py b/openpype/hosts/maya/plugins/publish/collect_xgen.py index 5a48b1d221..da0549b2d8 100644 --- a/openpype/hosts/maya/plugins/publish/collect_xgen.py +++ b/openpype/hosts/maya/plugins/publish/collect_xgen.py @@ -1,3 +1,5 @@ +import os + from maya import cmds import pyblish.api @@ -35,5 +37,35 @@ class CollectXgen(pyblish.api.InstancePlugin): input = get_attribute_input("{}.{}".format(node, attr)) data["xgenConnections"][node][attr] = input + # Collect all files under palette root as resources. + import xgenm + + data_path = xgenm.getAttr( + "xgDataPath", data["xgmPalette"].replace("|", "") + ).split(os.pathsep)[0] + data_path = data_path.replace( + "${PROJECT}", + xgenm.getAttr("xgProjectPath", data["xgmPalette"].replace("|", "")) + ) + transfers = [] + + # Since we are duplicating this palette when extracting we predict that + # the name will be the basename without namespaces. + predicted_palette_name = data["xgmPalette"].split(":")[-1] + predicted_palette_name = predicted_palette_name.replace("|", "") + + for root, _, files in os.walk(data_path): + for file in files: + source = os.path.join(root, file).replace("\\", "/") + destination = os.path.join( + instance.data["resourcesDir"], + "collections", + predicted_palette_name, + source.replace(data_path, "")[1:] + ) + transfers.append((source, destination.replace("\\", "/"))) + + data["transfers"] = transfers + self.log.info(data) instance.data.update(data) diff --git a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py index 5847563e5b..fe28427ae7 100644 --- a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -5,14 +5,16 @@ import copy from maya import cmds import pyblish.api -from openpype.hosts.maya.api import current_file from openpype.hosts.maya.api.lib import extract_alembic from openpype.pipeline import publish from openpype.lib import StringTemplate class ExtractWorkfileXgen(publish.Extractor): - """Extract Workfile Xgen.""" + """Extract Workfile Xgen. + + When submitting a render, we need to prep Xgen side car files. + """ # Offset to run before workfile scene save. order = pyblish.api.ExtractorOrder - 0.499 @@ -20,13 +22,65 @@ class ExtractWorkfileXgen(publish.Extractor): families = ["workfile"] hosts = ["maya"] + def get_render_max_frame_range(self, context): + """Return start to end frame range including all renderlayers in + context. + + This will return the full frame range which includes all frames of the + renderlayer instances to be published/submitted. + + Args: + context (pyblish.api.Context): Current publishing context. + + Returns: + tuple or None: Start frame, end frame tuple if any renderlayers + found. Otherwise None is returned. + + """ + + def _is_active_renderlayer(i): + """Return whether instance is active renderlayer""" + if not i.data.get("publish", True): + return False + + is_renderlayer = ( + "renderlayer" in i.data.get("families", []) or + i.data["family"] == "renderlayer" + ) + return is_renderlayer + + start_frame = None + end_frame = None + for instance in context: + if not _is_active_renderlayer(instance): + # Only consider renderlyare instances + continue + + render_start_frame = instance.data["frameStart"] + render_end_frame = instance.data["frameStart"] + + if start_frame is None: + start_frame = render_start_frame + else: + start_frame = min(start_frame, render_start_frame) + + if end_frame is None: + end_frame = render_end_frame + else: + end_frame = max(end_frame, render_end_frame) + + if start_frame is None or end_frame is None: + return + + return start_frame, end_frame + def process(self, instance): transfers = [] # Validate there is any palettes in the scene. if not cmds.ls(type="xgmPalette"): self.log.debug( - "No collections found in the scene. Abort Xgen extraction." + "No collections found in the scene. Skipping Xgen extraction." ) return else: @@ -34,44 +88,24 @@ class ExtractWorkfileXgen(publish.Extractor): # Validate to extract only when we are publishing a renderlayer as # well. - renderlayer = False - start_frame = None - end_frame = None - for i in instance.context: - is_renderlayer = ( - "renderlayer" in i.data.get("families", []) or - i.data["family"] == "renderlayer" - ) - if is_renderlayer and i.data["publish"]: - renderlayer = True - - if start_frame is None: - start_frame = i.data["frameStart"] - if end_frame is None: - end_frame = i.data["frameEnd"] - - if i.data["frameStart"] < start_frame: - start_frame = i.data["frameStart"] - if i.data["frameEnd"] > end_frame: - end_frame = i.data["frameEnd"] - - break - - if not renderlayer: + render_range = self.get_render_max_frame_range(instance.context) + if not render_range: self.log.debug( - "No publishable renderlayers found in context. Abort Xgen" + "No publishable renderlayers found in context. Skipping Xgen" " extraction." ) return + start_frame, end_frame = render_range + # We decrement start frame and increment end frame so motion blur will # render correctly. start_frame -= 1 end_frame += 1 # Extract patches alembic. - basename, _ = os.path.splitext(current_file()) - dirname = os.path.dirname(current_file()) + basename, _ = os.path.splitext(instance.context.data["currentFile"]) + dirname = os.path.dirname(instance.context.data["currentFile"]) kwargs = {"attrPrefix": ["xgen"], "stripNamespaces": True} alembic_files = [] for palette in cmds.ls(type="xgmPalette"): @@ -121,8 +155,7 @@ class ExtractWorkfileXgen(publish.Extractor): # Collect Xgen and Delta files. xgen_files = [] sources = [] - file_path = current_file() - current_dir = os.path.dirname(file_path) + current_dir = os.path.dirname(instance.context.data["currentFile"]) attrs = ["xgFileName", "xgBaseFile"] for palette in cmds.ls(type="xgmPalette"): for attr in attrs: diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 9549aba76d..7428fab53f 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -7,7 +7,7 @@ import xgenm from openpype.pipeline import publish from openpype.hosts.maya.api.lib import ( - maintained_selection, attribute_values, write_xgen_file + maintained_selection, attribute_values, write_xgen_file, delete_after ) from openpype.lib import StringTemplate @@ -36,48 +36,81 @@ class ExtractXgenCache(publish.Extractor): maya_filename = "{}.{}".format(instance.data["name"], self.scene_type) maya_filepath = os.path.join(staging_dir, maya_filename) - # Collect nodes to export. - duplicate_nodes = [] - for node, connections in instance.data["xgenConnections"].items(): - transform_name = connections["transform"].split(".")[0] - - # Duplicate_transform subd patch geometry. - duplicate_transform = cmds.duplicate(transform_name)[0] - - # Discard the children. - shapes = cmds.listRelatives(duplicate_transform, shapes=True) - children = cmds.listRelatives(duplicate_transform, children=True) - cmds.delete(set(children) - set(shapes)) - - duplicate_transform = cmds.parent( - duplicate_transform, world=True - )[0] - - duplicate_nodes.append(duplicate_transform) - - # Export temp xgen palette files. - temp_xgen_path = os.path.join( - tempfile.gettempdir(), "temp.xgen" - ).replace("\\", "/") - xgenm.exportPalette( - instance.data["xgmPalette"].replace("|", ""), temp_xgen_path - ) - self.log.info("Extracted to {}".format(temp_xgen_path)) - - # Import xgen onto the duplicate. - with maintained_selection(): - cmds.select(duplicate_nodes) - palette = xgenm.importPalette(temp_xgen_path, []) - # Get published xgen file name. template_data = copy.deepcopy(instance.data["anatomyData"]) template_data.update({"ext": "xgen"}) templates = instance.context.data["anatomy"].templates["publish"] xgen_filename = StringTemplate(templates["file"]).format(template_data) - # Export duplicated palette. - xgen_path = os.path.join(staging_dir, xgen_filename).replace("\\", "/") - xgenm.exportPalette(palette, xgen_path) + xgen_path = os.path.join( + self.staging_dir(instance), xgen_filename + ).replace("\\", "/") + type = "mayaAscii" if self.scene_type == "ma" else "mayaBinary" + + # Duplicate xgen setup. + with delete_after() as delete_bin: + duplicate_nodes = [] + # Collect nodes to export. + for node, connections in instance.data["xgenConnections"].items(): + transform_name = connections["transform"].split(".")[0] + + # Duplicate_transform subd patch geometry. + duplicate_transform = cmds.duplicate(transform_name)[0] + delete_bin.append(duplicate_transform) + + # Discard the children. + shapes = cmds.listRelatives(duplicate_transform, shapes=True) + children = cmds.listRelatives( + duplicate_transform, children=True + ) + cmds.delete(set(children) - set(shapes)) + + duplicate_transform = cmds.parent( + duplicate_transform, world=True + )[0] + + duplicate_nodes.append(duplicate_transform) + + # Export temp xgen palette files. + temp_xgen_path = os.path.join( + tempfile.gettempdir(), "temp.xgen" + ).replace("\\", "/") + xgenm.exportPalette( + instance.data["xgmPalette"].replace("|", ""), temp_xgen_path + ) + self.log.info("Extracted to {}".format(temp_xgen_path)) + + # Import xgen onto the duplicate. + with maintained_selection(): + cmds.select(duplicate_nodes) + palette = xgenm.importPalette(temp_xgen_path, []) + + delete_bin.append(palette) + + # Export duplicated palettes. + xgenm.exportPalette(palette, xgen_path) + + # Export Maya file. + attribute_data = {"{}.xgFileName".format(palette): xgen_filename} + with attribute_values(attribute_data): + with maintained_selection(): + cmds.select(duplicate_nodes + [palette]) + cmds.file( + maya_filepath, + force=True, + type=type, + exportSelected=True, + preserveReferences=False, + constructionHistory=True, + shader=True, + constraints=True, + expressions=True + ) + + self.log.info("Extracted to {}".format(maya_filepath)) + + if os.path.exists(temp_xgen_path): + os.remove(temp_xgen_path) data = { "xgDataPath": os.path.join( @@ -91,6 +124,7 @@ class ExtractXgenCache(publish.Extractor): } write_xgen_file(data, xgen_path) + # Adding representations. representation = { "name": "xgen", "ext": "xgen", @@ -99,28 +133,6 @@ class ExtractXgenCache(publish.Extractor): } instance.data["representations"].append(representation) - # Export Maya file. - type = "mayaAscii" if self.scene_type == "ma" else "mayaBinary" - attribute_data = { - "{}.xgFileName".format(palette): xgen_filename - } - with attribute_values(attribute_data): - with maintained_selection(): - cmds.select(duplicate_nodes + [palette]) - cmds.file( - maya_filepath, - force=True, - type=type, - exportSelected=True, - preserveReferences=False, - constructionHistory=True, - shader=True, - constraints=True, - expressions=True - ) - - self.log.info("Extracted to {}".format(maya_filepath)) - representation = { "name": self.scene_type, "ext": self.scene_type, @@ -128,31 +140,3 @@ class ExtractXgenCache(publish.Extractor): "stagingDir": staging_dir } instance.data["representations"].append(representation) - - # Clean up. - cmds.delete(duplicate_nodes + [palette]) - os.remove(temp_xgen_path) - - # Collect all files under palette root as resources. - data_path = xgenm.getAttr( - "xgDataPath", instance.data["xgmPalette"].replace("|", "") - ).split(os.pathsep)[0] - data_path = data_path.replace( - "${PROJECT}", - xgenm.getAttr( - "xgProjectPath", instance.data["xgmPalette"].replace("|", "") - ) - ) - transfers = [] - for root, _, files in os.walk(data_path): - for file in files: - source = os.path.join(root, file).replace("\\", "/") - destination = os.path.join( - instance.data["resourcesDir"], - "collections", - palette, - source.replace(data_path, "")[1:] - ) - transfers.append((source, destination.replace("\\", "/"))) - - instance.data["transfers"] = transfers diff --git a/openpype/hosts/maya/plugins/publish/validate_xgen.py b/openpype/hosts/maya/plugins/publish/validate_xgen.py index 19cf612848..2870909974 100644 --- a/openpype/hosts/maya/plugins/publish/validate_xgen.py +++ b/openpype/hosts/maya/plugins/publish/validate_xgen.py @@ -4,7 +4,7 @@ import maya.cmds as cmds import xgenm import pyblish.api -from openpype.pipeline.publish import KnownPublishError +from openpype.pipeline.publish import PublishValidationError class ValidateXgen(pyblish.api.InstancePlugin): @@ -20,7 +20,7 @@ class ValidateXgen(pyblish.api.InstancePlugin): # Only 1 collection/node per instance. if len(set_members) != 1: - raise KnownPublishError( + raise PublishValidationError( "Only one collection per instance is allowed." " Found:\n{}".format(set_members) ) @@ -28,7 +28,7 @@ class ValidateXgen(pyblish.api.InstancePlugin): # Only xgen palette node is allowed. node_type = cmds.nodeType(set_members[0]) if node_type != "xgmPalette": - raise KnownPublishError( + raise PublishValidationError( "Only node of type \"xgmPalette\" are allowed. Referred to as" " \"collection\" in the Maya UI." " Node type found: {}".format(node_type) @@ -50,10 +50,10 @@ class ValidateXgen(pyblish.api.InstancePlugin): except KeyError: inactive_modifiers[description] = [name] - msg = ( - "There are inactive modifiers on the collection. " - "Please delete these:\n{}".format( - json.dumps(inactive_modifiers, indent=4, sort_keys=True) + if inactive_modifiers: + raise PublishValidationError( + "There are inactive modifiers on the collection. " + "Please delete these:\n{}".format( + json.dumps(inactive_modifiers, indent=4, sort_keys=True) + ) ) - ) - assert not inactive_modifiers, msg From caf26b8ab671ee90977a78fdf9485381b1ae1f68 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 31 Jan 2023 17:49:10 +0000 Subject: [PATCH 0334/1271] Hound --- openpype/hosts/maya/plugins/publish/extract_xgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 7428fab53f..0719be3a1e 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -51,7 +51,7 @@ class ExtractXgenCache(publish.Extractor): with delete_after() as delete_bin: duplicate_nodes = [] # Collect nodes to export. - for node, connections in instance.data["xgenConnections"].items(): + for _, connections in instance.data["xgenConnections"].items(): transform_name = connections["transform"].split(".")[0] # Duplicate_transform subd patch geometry. From 3fa7610061298e4a3de96d176b74f5bd3ecca652 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 31 Jan 2023 18:35:50 +0000 Subject: [PATCH 0335/1271] Working version for Arnold. --- openpype/hosts/maya/api/lib.py | 13 +++++++++++-- openpype/hosts/maya/api/lib_renderproducts.py | 6 +++++- .../hosts/maya/plugins/publish/collect_render.py | 8 ++++---- .../deadline/plugins/publish/submit_publish_job.py | 10 +++++++--- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index f869dadaad..b31ab2408b 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -14,7 +14,7 @@ from math import ceil from six import string_types from maya import cmds, mel -import maya.api.OpenMaya as om +from maya.api import OpenMaya from openpype.client import ( get_project, @@ -3402,13 +3402,22 @@ def get_color_management_preferences(): ) } + # Split view and display from view_transform. view_transform comes in + # format of "{view} ({display})". + display = data["view_transform"].split("(")[-1].replace(")", "") + data.update({ + "display": display, + "view": data["view_transform"].replace("({})".format(display), "")[:-1] + }) + + # Get config absolute path. path = cmds.colorManagementPrefs( query=True, configFilePath=True ) # The OCIO config supports a custom token. maya_resources_token = "" - maya_resources_path = om.MGlobal.getAbsolutePathToResources() + maya_resources_path = OpenMaya.MGlobal.getAbsolutePathToResources() path = path.replace(maya_resources_token, maya_resources_path) data["config"] = path diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 0b585dc8cb..58ccbfd5a2 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -648,8 +648,12 @@ class RenderProductsArnold(ARenderProducts): preferences = lib.get_color_management_preferences() return preferences["view_transform"] + def _raw(): + preferences = lib.get_color_management_preferences() + return preferences["rendering_space"] + resolved_values = { - "Raw": lambda: "Raw", + "Raw": _raw, "Use View Transform": _view_transform, # Default. Same as Maya Preferences. "Use Output Transform": lib.get_color_management_output_transform diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index 2c89424381..d0164f30a8 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -264,7 +264,7 @@ class CollectMayaRender(pyblish.api.ContextPlugin): self.log.info(full_exp_files) self.log.info("collecting layer: {}".format(layer_name)) # Get layer specific settings, might be overrides - + colorspace_data = lib.get_color_management_preferences() data = { "subset": expected_layer_name, "attachTo": attach_to, @@ -318,9 +318,9 @@ class CollectMayaRender(pyblish.api.ContextPlugin): "renderSetupIncludeLights": render_instance.data.get( "renderSetupIncludeLights" ), - "colorspaceConfig": ( - lib.get_color_management_preferences()["config"] - ) + "colorspaceConfig": colorspace_data["config"], + "colorspaceDisplay": colorspace_data["display"], + "colorspaceView": colorspace_data["view"] } # Collect Deadline url if Deadline module is enabled diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 8811fa5d34..02aa1043d1 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -550,10 +550,12 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "tags": ["review"] if preview else [], "colorspaceData": { "colorspace": colorspace, - "configData": { + "config": { "path": additional_data["colorspaceConfig"], "template": "" - } + }, + "display": additional_data["display"], + "view": additional_data["view"] } } @@ -916,7 +918,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): additional_data = { "renderProducts": instance.data["renderProducts"], - "colorspaceConfig": instance.data["colorspaceConfig"] + "colorspaceConfig": instance.data["colorspaceConfig"], + "display": instance.data["colorspaceDisplay"], + "view": instance.data["colorspaceView"] } if isinstance(data.get("expectedFiles")[0], dict): From 06086b36bc0b84e70afc8b09232961811041d212 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 1 Feb 2023 03:32:20 +0000 Subject: [PATCH 0336/1271] [Automated] Bump version --- openpype/version.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/version.py b/openpype/version.py index ab61b16a14..3941912c6e 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.1-nightly.1" +__version__ = "3.15.1-nightly.2" diff --git a/pyproject.toml b/pyproject.toml index a872ed3609..634aeda5ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.0" # OpenPype +version = "3.15.1-nightly.2" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From a07aed0c40dfe7d8fc7c7ae83503020ef5238aec Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 1 Feb 2023 16:47:54 +0800 Subject: [PATCH 0337/1271] some edits on the color convert if the color workflow being linearized --- .../maya/plugins/publish/extract_look.py | 23 ++++++++++++++----- .../publish/validate_look_color_space.py | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 860872c3a6..22c95d04dc 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -385,6 +385,7 @@ class ExtractLook(publish.Extractor): source, mode, texture_hash = self._process_texture( filepath, + resource, do_maketx, staging=staging_dir, linearize=linearize, @@ -490,7 +491,7 @@ class ExtractLook(publish.Extractor): resources_dir, basename + ext ) - def _process_texture(self, filepath, do_maketx, staging, linearize, force): + def _process_texture(self, filepath, resource, do_maketx, staging, linearize, force): """Process a single texture file on disk for publishing. This will: 1. Check whether it's already published, if so it will do hardlink @@ -532,11 +533,21 @@ class ExtractLook(publish.Extractor): texture_hash ] if linearize: - self.log.info("tx: converting sRGB -> linear") - additional_args.extend(["--colorconvert", "sRGB", "linear"]) - - self.log.info("Using nuke-default ocio config instead of maya ocio config!") # noqa - self.log.info("The tx conversion is different from the maya tx conversion!") # noqa + if cmds.colorManagementPrefs(query=True, cmEnabled=True): + render_colorspace = cmds.colorManagementPrefs(query=True, + renderingSpaceName=True) + color_space_attr = resource["node"] + ".colorSpace" + try: + color_space = cmds.getAttr(color_space_attr) + except ValueError: + # node doesn't have color space attribute + color_space = "Raw" + self.log.info("tx: converting {0} -> {1}".format(color_space, + render_colorspace)) + additional_args.extend(["--colorconvert", color_space, render_colorspace]) + else: + self.log.info("tx: converting sRGB -> linear") + additional_args.extend(["--colorconvert", "sRGB", "Raw"]) config_path = get_ocio_config_path("nuke-default") additional_args.extend(["--colorconfig", config_path]) diff --git a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py index b354d51fef..103ade09b1 100644 --- a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py +++ b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py @@ -14,6 +14,7 @@ class ValidateMayaColorSpace(pyblish.api.InstancePlugin): families = ['look'] hosts = ['maya'] label = 'Color Management with maketx' + optional = True def process(self, instance): ocio_maya = cmds.colorManagementPrefs(q=True, From 8f83673f7f8dd93c063072437c8c32d8278677df Mon Sep 17 00:00:00 2001 From: Libor Batek <112623825+LiborBatek@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:12:02 +0100 Subject: [PATCH 0338/1271] Update website/docs/artist_getting_started.md Co-authored-by: Roy Nieterau --- website/docs/artist_getting_started.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index 5ab89e31d8..3db4421181 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -21,7 +21,9 @@ admin, supervisor or production, because OpenPype versions and executables might Installing OpenPype is possible by Installer or by unzipping downloaded ZIP archive to any drive location. -> For more detailed info about installing OpenPype please visit [Installation section](artist_install.md). +:::tip Using the OpenPype Installer +See the [Installation section](artist_install.md) for more information on how to use the OpenPype Installer +::: --- From 7ee305a17329892a28025d65fb906fd376a4d3a3 Mon Sep 17 00:00:00 2001 From: Libor Batek <112623825+LiborBatek@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:12:15 +0100 Subject: [PATCH 0339/1271] Update website/docs/artist_getting_started.md Co-authored-by: Roy Nieterau --- website/docs/artist_getting_started.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index 3db4421181..b9a75786cd 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -35,7 +35,9 @@ or alternatively by **openpype_console.exe** which is more suitable for **TDs/Admin** for debugging and error reporting. This one runs with opened console window where all the necessary info will appear during user's work session. -> By seeing the "OP" icon in the OS tray user can easily tell OpenPype already running. +:::tip Is OpenPype running? +OpenPype runs in the operating system's tray. If you see the OpenPype icon in the tray you can easily tell OpenPype is currently running. +::: From 4193ab3a9a8bc07f3cd21a03b434fcff5bf9f0dd Mon Sep 17 00:00:00 2001 From: Libor Batek <112623825+LiborBatek@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:12:41 +0100 Subject: [PATCH 0340/1271] Update website/docs/artist_getting_started.md Co-authored-by: Roy Nieterau --- website/docs/artist_getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index b9a75786cd..ef7e682c94 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -44,7 +44,7 @@ OpenPype runs in the operating system's tray. If you see the OpenPype icon in th ## First Launch -When you first start OpenPype, you will be asked to fill in some basic informations. +When you first start OpenPype, you will be asked to fill in some basic information. ### MongoDB From 51ad73c7b3f8b3376693fd946cefb38d716ad180 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Feb 2023 09:12:47 +0000 Subject: [PATCH 0341/1271] BigRoy feedback --- openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py | 4 ++-- website/docs/artist_hosts_maya_xgen.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py index fe28427ae7..49d724960f 100644 --- a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -83,8 +83,8 @@ class ExtractWorkfileXgen(publish.Extractor): "No collections found in the scene. Skipping Xgen extraction." ) return - else: - import xgenm + + import xgenm # Validate to extract only when we are publishing a renderlayer as # well. diff --git a/website/docs/artist_hosts_maya_xgen.md b/website/docs/artist_hosts_maya_xgen.md index 191826f49c..ec5f2ed921 100644 --- a/website/docs/artist_hosts_maya_xgen.md +++ b/website/docs/artist_hosts_maya_xgen.md @@ -4,6 +4,8 @@ title: Xgen for Maya sidebar_label: Xgen --- +OpenPype supports Xgen classic with the follow workflow. It eases the otherwise cumbersome issues around Xgen's side car files and hidden behaviour inside Maya. The workflow supports publishing, loading and updating of Xgen collections, along with connecting animation from geometry and (guide) curves. + ## Setup ### Settings From 56c7c00bf8ecefbb7330f41f4b50bc3145a9474d Mon Sep 17 00:00:00 2001 From: Libor Batek <112623825+LiborBatek@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:12:53 +0100 Subject: [PATCH 0342/1271] Update website/docs/artist_getting_started.md Co-authored-by: Roy Nieterau --- website/docs/artist_getting_started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index ef7e682c94..0ad98961d5 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -48,8 +48,8 @@ When you first start OpenPype, you will be asked to fill in some basic informati ### MongoDB -In most cases that will only be your studio MongoDB Address. -It's a URL that you should have received from your Studio admin and most often will look like this +In most cases you will only have to supply the MongoDB Address. +It's the database URL you should have received from your Studio admin and often will look like this `mongodb://username:passwword@mongo.mystudiodomain.com:12345` From 1ae99a2d2fae6274115aaeb0cd170587938972cb Mon Sep 17 00:00:00 2001 From: Libor Batek <112623825+LiborBatek@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:14:00 +0100 Subject: [PATCH 0343/1271] Update website/docs/artist_getting_started.md Co-authored-by: Roy Nieterau --- website/docs/artist_getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index 0ad98961d5..72a218f77e 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -62,7 +62,7 @@ asks for it, just put it in the corresponding text field and press `install` but ### OpenPype Version Repository -Sometimes your Studio might also ask you to fill in the path to it's version +Sometimes your Studio might also ask you to fill in the path to its version repository. This is a location where OpenPype will be looking for when checking if it's up to date and where updates are installed from automatically. From fae61816c60e2c2b351749ae240f279b00cce538 Mon Sep 17 00:00:00 2001 From: Libor Batek <112623825+LiborBatek@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:14:16 +0100 Subject: [PATCH 0344/1271] Update website/docs/artist_getting_started.md Co-authored-by: Roy Nieterau --- website/docs/artist_getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index 72a218f77e..1b32c6a985 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -27,7 +27,7 @@ See the [Installation section](artist_install.md) for more information on how to --- -You can run OpenPype by desktop "OP" icon (if exists after installing) or by directly executing +You can run OpenPype by desktop "OP" icon (if it exists after installing) or by directly executing **openpype_gui.exe** located in the OpenPype folder. This executable being suitable **for artists**. From 022e369d311dc09ca4404e7862f57e6b13323c4f Mon Sep 17 00:00:00 2001 From: Libor Batek <112623825+LiborBatek@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:15:36 +0100 Subject: [PATCH 0345/1271] Update website/docs/artist_getting_started.md Co-authored-by: Roy Nieterau --- website/docs/artist_getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index 1b32c6a985..8350333610 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -63,7 +63,7 @@ asks for it, just put it in the corresponding text field and press `install` but ### OpenPype Version Repository Sometimes your Studio might also ask you to fill in the path to its version -repository. This is a location where OpenPype will be looking for when checking +repository. This is a location where OpenPype will search for the latest versions, check if it's up to date and where updates are installed from automatically. This path is usually taken from the database directly, so you shouldn't need it. From 8351ce0fba1e09689f9e41339e90463a4fde419a Mon Sep 17 00:00:00 2001 From: Libor Batek <112623825+LiborBatek@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:17:04 +0100 Subject: [PATCH 0346/1271] grammar fixes Co-authored-by: Roy Nieterau --- website/docs/artist_getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index 8350333610..14951e599d 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -71,7 +71,7 @@ This path is usually taken from the database directly, so you shouldn't need it. ## Updates -If you're connected to your Studio, OpenPype will check for, and install updates automatically every time you run it. That's why during the first start, it will go through a quick update installation process, even though you might have just installed it. +If you're connected to your Studio, OpenPype will check for, and install updates automatically every time you run it. That's why during the first start it can go through a quick update installation process, even though you might have just installed it. ## Advanced Usage From aa2f845204b7b4f24e49811d4cb69ce4ccb3858e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 1 Feb 2023 17:38:18 +0800 Subject: [PATCH 0347/1271] hound fix --- openpype/hosts/maya/plugins/publish/extract_look.py | 13 +++++++------ .../plugins/publish/validate_look_color_space.py | 1 - 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 22c95d04dc..54b093f7c3 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -491,7 +491,8 @@ class ExtractLook(publish.Extractor): resources_dir, basename + ext ) - def _process_texture(self, filepath, resource, do_maketx, staging, linearize, force): + def _process_texture(self, filepath, resource, + do_maketx, staging, linearize, force): """Process a single texture file on disk for publishing. This will: 1. Check whether it's already published, if so it will do hardlink @@ -534,17 +535,17 @@ class ExtractLook(publish.Extractor): ] if linearize: if cmds.colorManagementPrefs(query=True, cmEnabled=True): - render_colorspace = cmds.colorManagementPrefs(query=True, - renderingSpaceName=True) + render_colorspace = cmds.colorManagementPrefs(query=True, renderingSpaceName=True) # noqa color_space_attr = resource["node"] + ".colorSpace" try: color_space = cmds.getAttr(color_space_attr) except ValueError: # node doesn't have color space attribute color_space = "Raw" - self.log.info("tx: converting {0} -> {1}".format(color_space, - render_colorspace)) - additional_args.extend(["--colorconvert", color_space, render_colorspace]) + self.log.info("tx: converting {0} -> {1}".format(color_space, render_colorspace)) # noqa + additional_args.extend(["--colorconvert", + color_space, + render_colorspace]) else: self.log.info("tx: converting sRGB -> linear") additional_args.extend(["--colorconvert", "sRGB", "Raw"]) diff --git a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py index 103ade09b1..b354d51fef 100644 --- a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py +++ b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py @@ -14,7 +14,6 @@ class ValidateMayaColorSpace(pyblish.api.InstancePlugin): families = ['look'] hosts = ['maya'] label = 'Color Management with maketx' - optional = True def process(self, instance): ocio_maya = cmds.colorManagementPrefs(q=True, From 062259fae27e75939fdedd202637bfdfe3844dca Mon Sep 17 00:00:00 2001 From: Libor Batek Date: Wed, 1 Feb 2023 10:52:54 +0100 Subject: [PATCH 0348/1271] removed commented parts --- website/docs/artist_hosts_3dsmax.md | 131 ---------------------------- 1 file changed, 131 deletions(-) diff --git a/website/docs/artist_hosts_3dsmax.md b/website/docs/artist_hosts_3dsmax.md index baee07fbb0..71ba8785dc 100644 --- a/website/docs/artist_hosts_3dsmax.md +++ b/website/docs/artist_hosts_3dsmax.md @@ -122,135 +122,4 @@ This part of documentation is still work in progress. - - - - - - From 9f17a4803f4f6fdfb8858a841e20dbe283b119f2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 11:14:07 +0100 Subject: [PATCH 0349/1271] OP-4663 - fix double dots in extension Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3d897c6d9f..bfed69c300 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -207,7 +207,7 @@ class ExtractOIIOTranscode(publish.Extractor): file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: - output_extension = input_extension + output_extension = input_extension.replace(".", "") new_file_name = '{}.{}'.format(file_name, output_extension) return os.path.join(output_dir, new_file_name) From e8d4a752a94e0f760bc2430536fdd8d87eda7636 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 11:22:20 +0100 Subject: [PATCH 0350/1271] Fix pyproject.toml version because of Poetry Automatization injects wrong format --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 634aeda5ac..2fc4f6fe39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.1-nightly.2" # OpenPype +version = "3.15.1" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 0fb0f74250761c4e6f1582b8ca64613caf0087a7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 11:23:52 +0100 Subject: [PATCH 0351/1271] Fix pyproject.toml version because of Poetry Automatization injects wrong format --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 634aeda5ac..2fc4f6fe39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.1-nightly.2" # OpenPype +version = "3.15.1" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From fb56e169dc745d6b934dcd509367dee27b91b0f3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 12:34:21 +0100 Subject: [PATCH 0352/1271] check for source class instead of for function by name availability --- openpype/pipeline/context_tools.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/openpype/pipeline/context_tools.py b/openpype/pipeline/context_tools.py index 06538dd91f..6610fd7da7 100644 --- a/openpype/pipeline/context_tools.py +++ b/openpype/pipeline/context_tools.py @@ -11,6 +11,7 @@ import pyblish.api from pyblish.lib import MessageHandler import openpype +from openpype.host import HostBase from openpype.client import ( get_project, get_asset_by_id, @@ -317,7 +318,7 @@ def get_current_host_name(): """ host = registered_host() - if host is not None and hasattr(host, "name"): + if isinstance(host, HostBase): return host.name return os.environ.get("AVALON_APP") @@ -332,28 +333,28 @@ def get_global_context(): def get_current_context(): host = registered_host() - if host is not None and hasattr(host, "get_current_context"): + if isinstance(host, HostBase): return host.get_current_context() return get_global_context() def get_current_project_name(): host = registered_host() - if host is not None and hasattr(host, "get_current_project_name"): + if isinstance(host, HostBase): return host.get_current_project_name() return get_global_context()["project_name"] def get_current_asset_name(): host = registered_host() - if host is not None and hasattr(host, "get_current_asset_name"): + if isinstance(host, HostBase): return host.get_current_asset_name() return get_global_context()["asset_name"] def get_current_task_name(): host = registered_host() - if host is not None and hasattr(host, "get_current_task_name"): + if isinstance(host, HostBase): return host.get_current_task_name() return get_global_context()["task_name"] From b37359979cd9f52034f77e5df250e7cefe535877 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 1 Feb 2023 12:38:02 +0100 Subject: [PATCH 0353/1271] :recycle: force modules to sys.path --- openpype/hosts/max/startup/startup.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/startup/startup.py b/openpype/hosts/max/startup/startup.py index 37bcef5db1..21da115baa 100644 --- a/openpype/hosts/max/startup/startup.py +++ b/openpype/hosts/max/startup/startup.py @@ -1,6 +1,20 @@ # -*- coding: utf-8 -*- -from openpype.hosts.max.api import MaxHost -from openpype.pipeline import install_host +import os +import sys + +# this might happen in some 3dsmax version where PYTHONPATH isn't added +# to sys.path automatically +try: + from openpype.hosts.max.api import MaxHost + from openpype.pipeline import install_host +except (ImportError, ModuleNotFoundError): + + for path in os.environ["PYTHONPATH"].split(os.pathsep): + if path and path not in sys.path: + sys.path.append(path) + + from openpype.hosts.max.api import MaxHost + from openpype.pipeline import install_host host = MaxHost() install_host(host) From 2313cd0507c73b5175cc5f353d74db64e016c367 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 12:38:54 +0100 Subject: [PATCH 0354/1271] added context getter functions to pipeline init --- openpype/pipeline/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index f5319c5a48..7a2ef59a5a 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -86,6 +86,12 @@ from .context_tools import ( registered_host, deregister_host, get_process_id, + + get_current_context, + get_current_host_name, + get_current_project_name, + get_current_asset_name, + get_current_task_name, ) install = install_host uninstall = uninstall_host @@ -176,6 +182,13 @@ __all__ = ( "register_host", "registered_host", "deregister_host", + "get_process_id", + + "get_current_context", + "get_current_host_name", + "get_current_project_name", + "get_current_asset_name", + "get_current_task_name", # Backwards compatible function names "install", From 981d454802dded37955a2c8c573c24173a5d5957 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Feb 2023 11:57:12 +0000 Subject: [PATCH 0355/1271] Remove old xgen cache plugin --- .../maya/plugins/publish/extract_xgen.py | 2 +- .../plugins/publish/extract_xgen_cache.py | 64 ------------------- 2 files changed, 1 insertion(+), 65 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/publish/extract_xgen_cache.py diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen.py b/openpype/hosts/maya/plugins/publish/extract_xgen.py index 0719be3a1e..0cc842b4ec 100644 --- a/openpype/hosts/maya/plugins/publish/extract_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_xgen.py @@ -12,7 +12,7 @@ from openpype.hosts.maya.api.lib import ( from openpype.lib import StringTemplate -class ExtractXgenCache(publish.Extractor): +class ExtractXgen(publish.Extractor): """Extract Xgen Workflow: diff --git a/openpype/hosts/maya/plugins/publish/extract_xgen_cache.py b/openpype/hosts/maya/plugins/publish/extract_xgen_cache.py deleted file mode 100644 index 77350f343e..0000000000 --- a/openpype/hosts/maya/plugins/publish/extract_xgen_cache.py +++ /dev/null @@ -1,64 +0,0 @@ -import os - -from maya import cmds - -from openpype.pipeline import publish -from openpype.hosts.maya.api.lib import ( - suspended_refresh, - maintained_selection -) - - -class ExtractXgenCache(publish.Extractor): - """Produce an alembic of just xgen interactive groom - - """ - - label = "Extract Xgen ABC Cache" - hosts = ["maya"] - families = ["xgen"] - optional = True - - def process(self, instance): - - # Collect the out set nodes - out_descriptions = [node for node in instance - if cmds.nodeType(node) == "xgmSplineDescription"] - - start = 1 - end = 1 - - self.log.info("Extracting Xgen Cache..") - dirname = self.staging_dir(instance) - - parent_dir = self.staging_dir(instance) - filename = "{name}.abc".format(**instance.data) - path = os.path.join(parent_dir, filename) - - with suspended_refresh(): - with maintained_selection(): - command = ( - '-file ' - + path - + ' -df "ogawa" -fr ' - + str(start) - + ' ' - + str(end) - + ' -step 1 -mxf -wfw' - ) - for desc in out_descriptions: - command += (" -obj " + desc) - cmds.xgmSplineCache(export=True, j=command) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - 'name': 'abc', - 'ext': 'abc', - 'files': filename, - "stagingDir": dirname, - } - instance.data["representations"].append(representation) - - self.log.info("Extracted {} to {}".format(instance, dirname)) From 5c4856d72a302db1132b21f8fac87e751e045899 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Feb 2023 12:05:38 +0000 Subject: [PATCH 0356/1271] BigRoy feedback --- openpype/hosts/maya/api/lib.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index b31ab2408b..d83f18bf30 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -5,6 +5,7 @@ import sys import platform import uuid import math +import re import json import logging @@ -3404,10 +3405,11 @@ def get_color_management_preferences(): # Split view and display from view_transform. view_transform comes in # format of "{view} ({display})". - display = data["view_transform"].split("(")[-1].replace(")", "") + regex = re.compile(r"^(?P.+) \((?P.+)\)$") + match = regex.match(data["view_transform"]) data.update({ - "display": display, - "view": data["view_transform"].replace("({})".format(display), "")[:-1] + "display": match.group("display"), + "view": match.group("view") }) # Get config absolute path. From 89d7edd8dfe0e6917c18870c92628659a8635541 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Feb 2023 12:06:26 +0000 Subject: [PATCH 0357/1271] Support for Maya Hardware --- openpype/hosts/maya/api/lib_renderproducts.py | 7 ++++++- openpype/hosts/maya/api/lib_rendersettings.py | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 58ccbfd5a2..6a607e7ff3 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -1354,7 +1354,12 @@ class RenderProductsMayaHardware(ARenderProducts): products = [] for cam in self.get_renderable_cameras(): - product = RenderProduct(productName="beauty", ext=ext, camera=cam) + product = RenderProduct( + productName="beauty", + ext=ext, + camera=cam, + colorspace=lib.get_color_management_output_transform() + ) products.append(product) return products diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 6190a49401..4710b10128 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -23,7 +23,8 @@ class RenderSettings(object): 'vray': 'vraySettings.fileNamePrefix', 'arnold': 'defaultRenderGlobals.imageFilePrefix', 'renderman': 'rmanGlobals.imageFileFormat', - 'redshift': 'defaultRenderGlobals.imageFilePrefix' + 'redshift': 'defaultRenderGlobals.imageFilePrefix', + 'mayahardware2': 'defaultRenderGlobals.imageFilePrefix' } _image_prefixes = { From 21d275d46ae2e7d8c1cc9fae67cd8512da46dec1 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Feb 2023 12:27:48 +0000 Subject: [PATCH 0358/1271] Support for Vray --- openpype/hosts/maya/api/lib_renderproducts.py | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 6a607e7ff3..38415c2ae2 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -841,9 +841,13 @@ class RenderProductsVray(ARenderProducts): if not dont_save_rgb: for camera in cameras: products.append( - RenderProduct(productName="", - ext=default_ext, - camera=camera)) + RenderProduct( + productName="", + ext=default_ext, + camera=camera, + colorspace=lib.get_color_management_output_transform() + ) + ) # separate alpha file separate_alpha = self._get_attr("vraySettings.separateAlpha") @@ -895,10 +899,13 @@ class RenderProductsVray(ARenderProducts): aov_name = self._get_vray_aov_name(aov) for camera in cameras: - product = RenderProduct(productName=aov_name, - ext=default_ext, - aov=aov, - camera=camera) + product = RenderProduct( + productName=aov_name, + ext=default_ext, + aov=aov, + camera=camera, + colorspace=lib.get_color_management_output_transform() + ) products.append(product) return products From 1690a64216b16e6305485d80b5710c8f2f1dab70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Wed, 1 Feb 2023 15:21:18 +0100 Subject: [PATCH 0359/1271] Update openpype/pipeline/colorspace.py Co-authored-by: Roy Nieterau --- openpype/pipeline/colorspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/colorspace.py b/openpype/pipeline/colorspace.py index 14daa44db8..cb37b2c4ae 100644 --- a/openpype/pipeline/colorspace.py +++ b/openpype/pipeline/colorspace.py @@ -445,7 +445,7 @@ def get_imageio_file_rules(project_name, host_name, project_settings=None): file_rules = {} if frules_global["enabled"]: file_rules.update(frules_global["rules"]) - if frules_host.get("enabled"): + if frules_host and frules_host["enabled"]: file_rules.update(frules_host["rules"]) return file_rules From e10859d322d675a6e0945e1e9958480c9ee33ca1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 1 Feb 2023 15:54:18 +0100 Subject: [PATCH 0360/1271] pr comments --- .../publish/extract_subset_resources.py | 3 ++ openpype/pipeline/publish/lib.py | 50 +++++++++++-------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index c6148162a6..5082217db0 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -143,6 +143,9 @@ class ExtractSubsetResources(publish.Extractor): # create staging dir path staging_dir = self.staging_dir(instance) + # append staging dir for later cleanup + instance.context.data["cleanupFullPaths"].append(staging_dir) + # add default preset type for thumbnail and reviewable video # update them with settings and override in case the same # are found in there diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index cc4304cebd..a32b076775 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -616,7 +616,7 @@ def get_instance_staging_dir(instance): First check if 'stagingDir' is already set in instance data. In case there already is new tempdir will not be created. - It also supports `OPENPYPE_TEMP_DIR`, so studio can define own temp + It also supports `OPENPYPE_TMPDIR`, so studio can define own temp shared repository per project or even per more granular context. Template formating is supported also with optional keys. Folder is created in case it doesnt exists. @@ -645,17 +645,16 @@ def get_instance_staging_dir(instance): if staging_dir: return staging_dir - openpype_temp_dir = os.getenv("OPENPYPE_TEMP_DIR") + openpype_temp_dir = os.getenv("OPENPYPE_TMPDIR") custom_temp_dir = None if openpype_temp_dir: if "{" in openpype_temp_dir: - custom_temp_dir = _formated_staging_dir( + custom_temp_dir = _format_staging_dir( instance, openpype_temp_dir ) elif os.path.exists(openpype_temp_dir): custom_temp_dir = openpype_temp_dir - if custom_temp_dir: staging_dir = os.path.normpath( tempfile.mkdtemp( @@ -669,30 +668,39 @@ def get_instance_staging_dir(instance): ) instance.data['stagingDir'] = staging_dir - instance.context.data["cleanupFullPaths"].append(staging_dir) - return staging_dir -def _formated_staging_dir(instance, openpype_temp_dir): +def _format_staging_dir(instance, openpype_temp_dir): + """ Formating template + + Template path formatting is supporting: + - optional key formating + - available keys: + - root[work | ] + - project[name | code] + - asset + - hierarchy + - task + - username + - app + + Args: + instance (pyblish.Instance): instance object + openpype_temp_dir (str): path string + + Returns: + str: formated path + """ anatomy = instance.context.data["anatomy"] # get anatomy formating data # so template formating is supported anatomy_data = copy.deepcopy(instance.context.data["anatomyData"]) anatomy_data["root"] = anatomy.roots - """Template path formatting is supporting: - - optional key formating - - available keys: - - root[work | ] - - project[name | code] - - asset - - hierarchy - - task - - username - - app - """ - result = StringTemplate.format_template(openpype_temp_dir, anatomy_data) - result = os.path.normpath(result) - # create the dir in case it doesnt exists + + result = StringTemplate.format_template( + openpype_temp_dir, anatomy_data).normalized() + + # create the dir in case it doesnt exists os.makedirs(os.path.dirname(result)) return result From c71fc217da17da5e93b7129240ffc6e418d7cd12 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Feb 2023 15:05:15 +0000 Subject: [PATCH 0361/1271] BigRoy feedback --- .../maya/plugins/publish/extract_workfile_xgen.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py index 49d724960f..b95add3306 100644 --- a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -104,7 +104,7 @@ class ExtractWorkfileXgen(publish.Extractor): end_frame += 1 # Extract patches alembic. - basename, _ = os.path.splitext(instance.context.data["currentFile"]) + path_no_ext, _ = os.path.splitext(instance.context.data["currentFile"]) dirname = os.path.dirname(instance.context.data["currentFile"]) kwargs = {"attrPrefix": ["xgen"], "stripNamespaces": True} alembic_files = [] @@ -116,7 +116,9 @@ class ExtractWorkfileXgen(publish.Extractor): alembic_file = os.path.join( dirname, - "{}__{}.abc".format(basename, palette.replace(":", "__ns__")) + "{}__{}.abc".format( + path_no_ext, palette.replace(":", "__ns__") + ) ) extract_alembic( alembic_file, @@ -138,7 +140,9 @@ class ExtractWorkfileXgen(publish.Extractor): for source in alembic_files: destination = os.path.join( os.path.dirname(instance.data["resourcesDir"]), - os.path.basename(source.replace(basename, published_basename)) + os.path.basename( + source.replace(path_no_ext, published_basename) + ) ) transfers.append((source, destination)) From b2ed65c17ad147e44fab334534f8fd1ad837d53c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 1 Feb 2023 16:06:01 +0100 Subject: [PATCH 0362/1271] pr comments --- openpype/pipeline/publish/lib.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index a32b076775..b3d273781e 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -649,12 +649,21 @@ def get_instance_staging_dir(instance): custom_temp_dir = None if openpype_temp_dir: if "{" in openpype_temp_dir: + # path is anatomy template custom_temp_dir = _format_staging_dir( instance, openpype_temp_dir ) - elif os.path.exists(openpype_temp_dir): + else: + # path is absolute custom_temp_dir = openpype_temp_dir + if not os.path.exists(custom_temp_dir): + try: + # create it if it doesnt exists + os.makedirs(custom_temp_dir) + except IOError as error: + raise IOError("Path couldn't be created: {}".format(error)) + if custom_temp_dir: staging_dir = os.path.normpath( tempfile.mkdtemp( From e7fbe105fdd312ecdf3560e1db5859dbbda39076 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:11:45 +0100 Subject: [PATCH 0363/1271] OP-4643 - update documentation in Settings schema --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 74b81b13af..3956f403f4 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -207,7 +207,7 @@ "children": [ { "type": "label", - "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + "label": "Configure Output Definition(s) for new representation(s). \nEmpty 'Extension' denotes keeping source extension. \nName(key) of output definition will be used as new representation name \nunless 'passthrough' value is used to keep existing name. \nFill either 'Colorspace' (for target colorspace) or \nboth 'Display' and 'View' (for display and viewer colorspaces)." }, { "type": "boolean", From ed95995fc7b688d7ea5a66e6a818364a1aadc551 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:13:59 +0100 Subject: [PATCH 0364/1271] OP-4643 - name of new representation from output definition key --- .../publish/extract_color_transcode.py | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index bfed69c300..e39ea3add9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -32,6 +32,25 @@ class ExtractOIIOTranscode(publish.Extractor): - task types - task names - subset names + + Can produce one or more representations (with different extensions) based + on output definition in format: + "output_name: { + "extension": "png", + "colorspace": "ACES - ACEScg", + "display": "", + "view": "", + "tags": [], + "custom_tags": [] + } + + If 'extension' is empty original representation extension is used. + 'output_name' will be used as name of new representation. In case of value + 'passthrough' name of original representation will be used. + + 'colorspace' denotes target colorspace to be transcoded into. Could be + empty if transcoding should be only into display and viewer colorspace. + (In that case both 'display' and 'view' must be filled.) """ label = "Transcode color spaces" @@ -78,7 +97,7 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - for _, output_def in profile.get("outputs", {}).items(): + for output_name, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) original_staging_dir = new_repre["stagingDir"] @@ -92,10 +111,10 @@ class ExtractOIIOTranscode(publish.Extractor): output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') - if output_extension: - self._rename_in_representation(new_repre, - files_to_convert, - output_extension) + self._rename_in_representation(new_repre, + files_to_convert, + output_name, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -154,10 +173,22 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _rename_in_representation(self, new_repre, files_to_convert, - output_extension): - """Replace old extension with new one everywhere in representation.""" - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension + output_name, output_extension): + """Replace old extension with new one everywhere in representation. + + Args: + new_repre (dict) + files_to_convert (list): of filenames from repre["files"], + standardized to always list + output_name (str): key of output definition from Settings, + if "" token used, keep original repre name + output_extension (str): extension from output definition + """ + if output_name != "passthrough": + new_repre["name"] = output_name + if not output_extension: + return + new_repre["ext"] = output_extension renamed_files = [] From 7a7102337bfe73ab7d7e8e805e681a7ec743fd40 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Feb 2023 15:19:38 +0000 Subject: [PATCH 0365/1271] BigRoy feedback --- .../hosts/maya/plugins/publish/extract_workfile_xgen.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py index b95add3306..c8d0d63344 100644 --- a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -105,7 +105,6 @@ class ExtractWorkfileXgen(publish.Extractor): # Extract patches alembic. path_no_ext, _ = os.path.splitext(instance.context.data["currentFile"]) - dirname = os.path.dirname(instance.context.data["currentFile"]) kwargs = {"attrPrefix": ["xgen"], "stripNamespaces": True} alembic_files = [] for palette in cmds.ls(type="xgmPalette"): @@ -114,11 +113,8 @@ class ExtractWorkfileXgen(publish.Extractor): for name in xgenm.boundGeometry(palette, description): patch_names.append(name) - alembic_file = os.path.join( - dirname, - "{}__{}.abc".format( - path_no_ext, palette.replace(":", "__ns__") - ) + alembic_file = "{}__{}.abc".format( + path_no_ext, palette.replace(":", "__ns__") ) extract_alembic( alembic_file, From 76ab705e0c33aa18b009725896d1375b5a9432a5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 1 Feb 2023 16:25:52 +0100 Subject: [PATCH 0366/1271] added documenation --- website/docs/admin_settings_system.md | 38 ++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/website/docs/admin_settings_system.md b/website/docs/admin_settings_system.md index 8aeb281109..39b58e6f81 100644 --- a/website/docs/admin_settings_system.md +++ b/website/docs/admin_settings_system.md @@ -13,18 +13,44 @@ Settings applicable to the full studio. ![general_settings](assets/settings/settings_system_general.png) -**`Studio Name`** - Full name of the studio (can be used as variable on some places) +### Studio Name + - Full name of the studio (can be used as variable on some places) -**`Studio Code`** - Studio acronym or a short code (can be used as variable on some places) +### Studio Code + - Studio acronym or a short code (can be used as variable on some places) -**`Admin Password`** - After setting admin password, normal user won't have access to OpenPype settings +### Admin Password + - After setting admin password, normal user won't have access to OpenPype settings and Project Manager GUI. Please keep in mind that this is a studio wide password and it is meant purely as a simple barrier to prevent artists from accidental setting changes. -**`Environment`** - Globally applied environment variables that will be appended to any OpenPype process in the studio. +### Environment + - Globally applied environment variables that will be appended to any OpenPype process in the studio. + - OpenPype is using some keys to configure some tools. Here are some: -**`Disk mapping`** - Platform dependent configuration for mapping of virtual disk(s) on an artist's OpenPype machines before OP starts up. -Uses `subst` command, if configured volume character in `Destination` field already exists, no re-mapping is done for that character(volume). +#### OPENPYPE_TMPDIR: + - Custom staging dir directory + - Supports anatomy keys formating. ex `{root[work]}/{project[name]}/temp` + - supported formating keys: + - root[work] + - project[name | code] + - asset + - hierarchy + - task + - username + - app + +#### OPENPYPE_DEBUG + - setting logger to debug mode + - example value: "1" (to activate) + +#### OPENPYPE_LOG_LEVEL + - stringified numeric value of log level. [Here for more info](https://docs.python.org/3/library/logging.html#logging-levels) + - example value: "10" + +### Disk mapping +- Platform dependent configuration for mapping of virtual disk(s) on an artist's OpenPype machines before OP starts up. +- Uses `subst` command, if configured volume character in `Destination` field already exists, no re-mapping is done for that character(volume). ### FFmpeg and OpenImageIO tools We bundle FFmpeg tools for all platforms and OpenImageIO tools for Windows and Linux. By default, bundled tools are used, but it is possible to set environment variables `OPENPYPE_FFMPEG_PATHS` and `OPENPYPE_OIIO_PATHS` in system settings environments to look for them in different directory. From a20646e82f5c39108c1ac5b0b9988226c49c1a56 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:42:48 +0100 Subject: [PATCH 0367/1271] OP-4643 - updated docstring for convert_colorspace --- openpype/lib/transcoding.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 6d91f514ec..18273dd432 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1054,8 +1054,11 @@ def convert_colorspace( Args: input_path (str): Path that should be converted. It is expected that contains single file or image sequence of same type - (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + (sequence in format 'file.FRAMESTART-FRAMEEND#.ext', see oiio docs, + eg `big.1-3#.tif`) output_path (str): Path to output filename. + (must follow format of 'input_path', eg. single file or + sequence in 'file.FRAMESTART-FRAMEEND#.ext', `output.1-3#.tif`) config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space From fce85d069471f9e5364e41b147f9ecabd1703af4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 17:22:40 +0100 Subject: [PATCH 0368/1271] check ftrack url before adding ftrackapp --- openpype/modules/ftrack/ftrack_module.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/modules/ftrack/ftrack_module.py b/openpype/modules/ftrack/ftrack_module.py index 6f14f8428d..853b7999da 100644 --- a/openpype/modules/ftrack/ftrack_module.py +++ b/openpype/modules/ftrack/ftrack_module.py @@ -510,7 +510,10 @@ def resolve_ftrack_url(url, logger=None): url = "https://" + url ftrack_url = None - if not url.endswith("ftrackapp.com"): + if url and _check_ftrack_url(url): + ftrack_url = url + + if not ftrack_url and not url.endswith("ftrackapp.com"): ftrackapp_url = url + ".ftrackapp.com" if _check_ftrack_url(ftrackapp_url): ftrack_url = ftrackapp_url From a2dbc6d51ddfdbdf27cca40472c46336d146eb86 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 17:22:53 +0100 Subject: [PATCH 0369/1271] added option to receive ftrack url from settings --- openpype/modules/ftrack/ftrack_module.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/modules/ftrack/ftrack_module.py b/openpype/modules/ftrack/ftrack_module.py index 853b7999da..269ec726ee 100644 --- a/openpype/modules/ftrack/ftrack_module.py +++ b/openpype/modules/ftrack/ftrack_module.py @@ -73,8 +73,19 @@ class FtrackModule( ftrack_url = property(get_ftrack_url) + @property + def settings_ftrack_url(self): + """Ftrack url from settings in a format as it is. + + Returns: + str: Ftrack url from settings. + """ + + return self._settings_ftrack_url + def get_global_environments(self): """Ftrack's global environments.""" + return { "FTRACK_SERVER": self.ftrack_url } From b661f16f88610fcbd02d2e7db01dcc4945f128e9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 17:23:11 +0100 Subject: [PATCH 0370/1271] added docstring for 'get_ftrack_url' --- openpype/modules/ftrack/ftrack_module.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openpype/modules/ftrack/ftrack_module.py b/openpype/modules/ftrack/ftrack_module.py index 269ec726ee..d61b5f0b26 100644 --- a/openpype/modules/ftrack/ftrack_module.py +++ b/openpype/modules/ftrack/ftrack_module.py @@ -64,6 +64,16 @@ class FtrackModule( self._timers_manager_module = None def get_ftrack_url(self): + """Resolved ftrack url. + + Resolving is trying to fill missing information in url and tried to + connect to the server. + + Returns: + Union[str, None]: Final variant of url or None if url could not be + reached. + """ + if self._ftrack_url is _URL_NOT_SET: self._ftrack_url = resolve_ftrack_url( self._settings_ftrack_url, From 7e213daca01dddfa3b0cf8d87d8870c4bf537f8d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 17:23:38 +0100 Subject: [PATCH 0371/1271] login url is not modifying the url from module --- openpype/modules/ftrack/tray/login_dialog.py | 27 ++++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/openpype/modules/ftrack/tray/login_dialog.py b/openpype/modules/ftrack/tray/login_dialog.py index fbb3455775..0e676545f7 100644 --- a/openpype/modules/ftrack/tray/login_dialog.py +++ b/openpype/modules/ftrack/tray/login_dialog.py @@ -139,8 +139,7 @@ class CredentialsDialog(QtWidgets.QDialog): self.fill_ftrack_url() def fill_ftrack_url(self): - url = os.getenv("FTRACK_SERVER") - checked_url = self.check_url(url) + checked_url = self.check_url() if checked_url == self.ftsite_input.text(): return @@ -154,7 +153,7 @@ class CredentialsDialog(QtWidgets.QDialog): self.api_input.setEnabled(enabled) self.user_input.setEnabled(enabled) - if not url: + if not checked_url: self.btn_advanced.hide() self.btn_simple.hide() self.btn_ftrack_login.hide() @@ -254,7 +253,7 @@ class CredentialsDialog(QtWidgets.QDialog): ) def _on_ftrack_login_clicked(self): - url = self.check_url(self.ftsite_input.text()) + url = self.check_url() if not url: return @@ -302,21 +301,21 @@ class CredentialsDialog(QtWidgets.QDialog): if is_logged is not None: self.set_is_logged(is_logged) - def check_url(self, url): - if url is not None: - url = url.strip("/ ") - - if not url: + def check_url(self): + settings_url = self._module.settings_ftrack_url + url = self._module.ftrack_url + if not settings_url: self.set_error( "Ftrack URL is not defined in settings!" ) return - if "http" not in url: - if url.endswith("ftrackapp.com"): - url = "https://" + url - else: - url = "https://{}.ftrackapp.com".format(url) + if url is None: + self.set_error( + "Specified URL does not lead to a valid Ftrack server." + ) + return + try: result = requests.get( url, From dc656050b7184c965774e289c5827c735533ab5d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 18:27:44 +0100 Subject: [PATCH 0372/1271] remove api.pi in openpype root --- openpype/api.py | 112 ------------------------------------------------ 1 file changed, 112 deletions(-) delete mode 100644 openpype/api.py diff --git a/openpype/api.py b/openpype/api.py deleted file mode 100644 index b60cd21d2b..0000000000 --- a/openpype/api.py +++ /dev/null @@ -1,112 +0,0 @@ -from .settings import ( - get_system_settings, - get_project_settings, - get_current_project_settings, - get_anatomy_settings, - - SystemSettings, - ProjectSettings -) -from .lib import ( - PypeLogger, - Logger, - Anatomy, - execute, - run_subprocess, - version_up, - get_asset, - get_workdir_data, - get_version_from_path, - get_last_version_from_path, - get_app_environments_for_context, - source_hash, - get_latest_version, - get_local_site_id, - change_openpype_mongo_url, - create_project_folders, - get_project_basic_paths -) - -from .lib.mongo import ( - get_default_components -) - -from .lib.applications import ( - ApplicationManager -) - -from .lib.avalon_context import ( - BuildWorkfile -) - -from . import resources - -from .plugin import ( - Extractor, - - ValidatePipelineOrder, - ValidateContentsOrder, - ValidateSceneOrder, - ValidateMeshOrder, -) - -# temporary fix, might -from .action import ( - get_errored_instances_from_context, - RepairAction, - RepairContextAction -) - - -__all__ = [ - "get_system_settings", - "get_project_settings", - "get_current_project_settings", - "get_anatomy_settings", - "get_project_basic_paths", - - "SystemSettings", - "ProjectSettings", - - "PypeLogger", - "Logger", - "Anatomy", - "execute", - "get_default_components", - "ApplicationManager", - "BuildWorkfile", - - # Resources - "resources", - - # plugin classes - "Extractor", - # ordering - "ValidatePipelineOrder", - "ValidateContentsOrder", - "ValidateSceneOrder", - "ValidateMeshOrder", - # action - "get_errored_instances_from_context", - "RepairAction", - "RepairContextAction", - - # get contextual data - "version_up", - "get_asset", - "get_workdir_data", - "get_version_from_path", - "get_last_version_from_path", - "get_app_environments_for_context", - "source_hash", - - "run_subprocess", - "get_latest_version", - - "get_local_site_id", - "change_openpype_mongo_url", - - "get_project_basic_paths", - "create_project_folders" - -] From 14ecf3aec18b2f89d6e0a53119d7e4adff3d5c84 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 18:33:32 +0100 Subject: [PATCH 0373/1271] remove last usage of openpype.api --- openpype/hosts/nuke/plugins/publish/collect_context_data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/collect_context_data.py b/openpype/hosts/nuke/plugins/publish/collect_context_data.py index 5a1cdcf49e..b487c946f0 100644 --- a/openpype/hosts/nuke/plugins/publish/collect_context_data.py +++ b/openpype/hosts/nuke/plugins/publish/collect_context_data.py @@ -1,7 +1,7 @@ import os import nuke import pyblish.api -import openpype.api as api +from openpype.lib import get_version_from_path import openpype.hosts.nuke.api as napi from openpype.pipeline import KnownPublishError @@ -57,7 +57,7 @@ class CollectContextData(pyblish.api.ContextPlugin): "fps": root_node['fps'].value(), "currentFile": current_file, - "version": int(api.get_version_from_path(current_file)), + "version": int(get_version_from_path(current_file)), "host": pyblish.api.current_host(), "hostVersion": nuke.NUKE_VERSION_STRING From 8fd4c06e712c9b8ffb606de5784e4172a4b0eaf3 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 1 Feb 2023 18:33:33 +0100 Subject: [PATCH 0374/1271] :heavy_minus_sign: pop QT_AUTO_SCREEN_SCALE_FACTOR --- openpype/hosts/max/addon.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/max/addon.py b/openpype/hosts/max/addon.py index d3245bbc7e..9d6ab5a8b3 100644 --- a/openpype/hosts/max/addon.py +++ b/openpype/hosts/max/addon.py @@ -12,6 +12,11 @@ class MaxAddon(OpenPypeModule, IHostAddon): def initialize(self, module_settings): self.enabled = True + def add_implementation_envs(self, env, _app): + # Remove auto screen scale factor for Qt + # - let 3dsmax decide it's value + env.pop("QT_AUTO_SCREEN_SCALE_FACTOR", None) + def get_workfile_extensions(self): return [".max"] From 6934233c25512bd9ace0e954a51e67aea3246d12 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 19:04:14 +0100 Subject: [PATCH 0375/1271] skip lock check if in untitled scene --- openpype/hosts/maya/api/pipeline.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 3798170671..7f31001cd0 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -514,6 +514,9 @@ def check_lock_on_current_file(): # add the lock file when opening the file filepath = current_file() + # Skip if current file is 'untitled' + if not filepath: + return if is_workfile_locked(filepath): # add lockfile dialog From fc2fd70f09b5d8024ee154203748b6ede94afb99 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 1 Feb 2023 19:04:33 +0100 Subject: [PATCH 0376/1271] remove unnecessary indentation --- openpype/hosts/maya/api/pipeline.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 7f31001cd0..5323717fa7 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -683,10 +683,12 @@ def before_workfile_save(event): def after_workfile_save(event): workfile_name = event["filename"] - if handle_workfile_locks(): - if workfile_name: - if not is_workfile_locked(workfile_name): - create_workfile_lock(workfile_name) + if ( + handle_workfile_locks() + and workfile_name + and not is_workfile_locked(workfile_name) + ): + create_workfile_lock(workfile_name) class MayaDirmap(HostDirmap): From e0f18363cd2017fecab4dae2eceb5f1029c15f21 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Feb 2023 20:57:47 +0000 Subject: [PATCH 0377/1271] Fix file paths. --- openpype/hosts/maya/plugins/load/load_xgen.py | 6 ++---- .../hosts/maya/plugins/publish/extract_workfile_xgen.py | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 5110d4ca05..fc86596208 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -73,9 +73,8 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): # to ensure paths are setup correctly. project_path = os.path.dirname(current_file()).replace("\\", "/") xgenm.setAttr("xgProjectPath", project_path, xgen_palette) - data_path = "${{PROJECT}}xgen/collections/{}{}{}".format( + data_path = "${{PROJECT}}xgen/collections/{};{}".format( xgen_palette.replace(":", "__ns__"), - os.pathsep, xgenm.getAttr("xgDataPath", xgen_palette) ) xgenm.setAttr("xgDataPath", data_path, xgen_palette) @@ -156,9 +155,8 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): break project_path = os.path.dirname(current_file()).replace("\\", "/") - data_path = "${{PROJECT}}xgen/collections/{}{}{}".format( + data_path = "${{PROJECT}}xgen/collections/{};{}".format( xgen_palette.replace(":", "__ns__"), - os.pathsep, data_path ) data = {"xgProjectPath": project_path, "xgDataPath": data_path} diff --git a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py index c8d0d63344..20e1bd37d8 100644 --- a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -208,7 +208,7 @@ class ExtractWorkfileXgen(publish.Extractor): project_path = xgenm.getAttr("xgProjectPath", palette) data_path = xgenm.getAttr("xgDataPath", palette) data_path = data_path.replace("${PROJECT}", project_path) - for path in data_path.split(os.pathsep): + for path in data_path.split(";"): for root, _, files in os.walk(path): for f in files: source = os.path.join(root, f) From 926f23d39275ece8fbdd04b97541cf874e9b22cd Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Feb 2023 20:57:53 +0000 Subject: [PATCH 0378/1271] Fix updating --- openpype/hosts/maya/api/plugin.py | 33 +++++++++++++++++++ openpype/hosts/maya/plugins/load/load_xgen.py | 31 ----------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 82df85a8be..916fddd923 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -300,6 +300,39 @@ class ReferenceLoader(Loader): str(representation["_id"]), type="string") + # When an animation or pointcache gets connected to an Xgen container, + # the compound attribute "xgenContainers" gets created. When animation + # containers gets updated we also need to update the cacheFileName on + # the Xgen collection. + compound_name = "xgenContainers" + if cmds.objExists("{}.{}".format(node, compound_name)): + import xgenm + container_amount = cmds.getAttr( + "{}.{}".format(node, compound_name), size=True + ) + # loop through all compound children + for i in range(container_amount): + attr = "{}.{}[{}].container".format(node, compound_name, i) + objectset = cmds.listConnections(attr)[0] + reference_node = cmds.sets(objectset, query=True)[0] + palettes = cmds.ls( + cmds.referenceQuery(reference_node, nodes=True), + type="xgmPalette" + ) + for palette in palettes: + for description in xgenm.descriptions(palette): + xgenm.setAttr( + "cacheFileName", + path.replace("\\", "/"), + palette, + description, + "SplinePrimitive" + ) + + # Refresh UI and viewport. + de = xgenm.xgGlobal.DescriptionEditor + de.refresh("Full") + def remove(self, container): """Remove an existing `container` from Maya scene diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index fc86596208..1600cd49bd 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -171,34 +171,3 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): super().update(container, representation) xgenm.applyDelta(xgen_palette.replace("|", ""), xgd_file) - - # Update any xgen containers. - compound_name = "xgenContainers" - import xgenm - container_amount = cmds.getAttr( - "{}.{}".format(container_node, compound_name), size=True - ) - # loop through all compound children - for i in range(container_amount): - attr = "{}.{}[{}].container".format( - container_node, compound_name, i - ) - objectset = cmds.listConnections(attr)[0] - reference_node = cmds.sets(objectset, query=True)[0] - palettes = cmds.ls( - cmds.referenceQuery(reference_node, nodes=True), - type="xgmPalette" - ) - for palette in palettes: - for description in xgenm.descriptions(palette): - xgenm.setAttr( - "cacheFileName", - maya_file.replace("\\", "/"), - palette, - description, - "SplinePrimitive" - ) - - # Refresh UI and viewport. - de = xgenm.xgGlobal.DescriptionEditor - de.refresh("Full") From 90ef934f495dd82606f7a7b702f6b3187622cfd7 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 2 Feb 2023 11:45:08 +0800 Subject: [PATCH 0379/1271] update the guess colorspace code reference from arnold --- openpype/hosts/maya/api/lib.py | 38 +++++++++++++++++++ .../maya/plugins/publish/extract_look.py | 18 +++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 25842a4776..51da7a4b98 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -15,6 +15,7 @@ from six import string_types from maya import cmds, mel import maya.api.OpenMaya as om +from arnold import * from openpype.client import ( get_project, @@ -3379,3 +3380,40 @@ def iter_visible_nodes_in_range(nodes, start, end): def get_attribute_input(attr): connections = cmds.listConnections(attr, plugs=True, destination=False) return connections[0] if connections else None + + +# Reference from Arnold +# Get Image Information for colorspace +def imageInfo(filepath): + """Take reference from makeTx.py + ImageInfo(filename): Get Image Information + AiTextureGetFormat(filename): Get Texture Format + AiTextureGetBitDepth(filename): Get Texture Bit Depth + """ + # Get Texture Information + img_info = {} + img_info['filename'] = filepath + if os.path.isfile(filepath): + img_info['bit_depth'] = AiTextureGetBitDepth(filepath) + img_info['format'] = AiTextureGetFormat(filepath) + else: + img_info['bit_depth'] = 8 + img_info['format'] = "unknown" + return img_info + +def guess_colorspace(img_info): + ''' Take reference from makeTx.py + Guess the colorspace of the input image filename. + @return: a string suitable for the --colorconvert option of maketx (linear, sRGB, Rec709) + ''' + try: + if img_info['bit_depth'] <= 16 and img_info['format'] in (AI_TYPE_BYTE, AI_TYPE_INT, AI_TYPE_UINT): + return 'sRGB' + else: + return 'linear' + + # now discard the image file as AiTextureGetFormat has loaded it + AiTextureInvalidate(img_info['filename']) + except: + print('[maketx] Error: Could not guess colorspace for "%s"' % img_info['filename']) + return 'linear' diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 54b093f7c3..5ac5d9c728 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -16,6 +16,7 @@ import pyblish.api from openpype.lib import source_hash, run_subprocess from openpype.pipeline import legacy_io, publish from openpype.hosts.maya.api import lib +from openpype.hosts.maya.api.lib import imageInfo, guess_colorspace # Modes for transfer COPY = 1 @@ -541,16 +542,25 @@ class ExtractLook(publish.Extractor): color_space = cmds.getAttr(color_space_attr) except ValueError: # node doesn't have color space attribute - color_space = "Raw" + img_info = imageInfo(filepath) + color_space = guess_colorspace(img_info) self.log.info("tx: converting {0} -> {1}".format(color_space, render_colorspace)) # noqa additional_args.extend(["--colorconvert", color_space, render_colorspace]) else: - self.log.info("tx: converting sRGB -> linear") - additional_args.extend(["--colorconvert", "sRGB", "Raw"]) + img_info = imageInfo(filepath) + color_space = guess_colorspace(img_info) + if color_space == "sRGB": + self.log.info("tx: converting sRGB -> linear") + additional_args.extend(["--colorconvert", "sRGB", "Raw"]) + else: + self.log.info("tx: texture's colorspace is already linear") - config_path = get_ocio_config_path("nuke-default") + + config_path = cmds.colorManagementPrefs(query=True, configFilePath=True) + if not os.path.exists(config_path): + raise RuntimeError("No OCIO config path found!") additional_args.extend(["--colorconfig", config_path]) # Ensure folder exists if not os.path.exists(os.path.dirname(converted)): From 1df0eb1fd82460298db2e279a83a299fe5504ee1 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 2 Feb 2023 12:00:49 +0800 Subject: [PATCH 0380/1271] update the guess colorspace code reference from arnold --- openpype/hosts/maya/api/lib.py | 26 +++++++++++-------- .../maya/plugins/publish/extract_look.py | 11 +++++--- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 51da7a4b98..e20ae15228 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -15,7 +15,7 @@ from six import string_types from maya import cmds, mel import maya.api.OpenMaya as om -from arnold import * +from arnold import * # noqa from openpype.client import ( get_project, @@ -3390,12 +3390,13 @@ def imageInfo(filepath): AiTextureGetFormat(filename): Get Texture Format AiTextureGetBitDepth(filename): Get Texture Bit Depth """ + # Get Texture Information img_info = {} img_info['filename'] = filepath if os.path.isfile(filepath): - img_info['bit_depth'] = AiTextureGetBitDepth(filepath) - img_info['format'] = AiTextureGetFormat(filepath) + img_info['bit_depth'] = AiTextureGetBitDepth(filepath) # noqa + img_info['format'] = AiTextureGetFormat(filepath) # noqa else: img_info['bit_depth'] = 8 img_info['format'] = "unknown" @@ -3404,16 +3405,19 @@ def imageInfo(filepath): def guess_colorspace(img_info): ''' Take reference from makeTx.py Guess the colorspace of the input image filename. - @return: a string suitable for the --colorconvert option of maketx (linear, sRGB, Rec709) + @return: a string suitable for the --colorconvert + option of maketx (linear, sRGB, Rec709) ''' try: - if img_info['bit_depth'] <= 16 and img_info['format'] in (AI_TYPE_BYTE, AI_TYPE_INT, AI_TYPE_UINT): - return 'sRGB' - else: - return 'linear' + if img_info['bit_depth'] <= 16: + if img_info['format'] in (AI_TYPE_BYTE, AI_TYPE_INT, AI_TYPE_UINT): # noqa + return 'sRGB' + else: + return 'linear' # now discard the image file as AiTextureGetFormat has loaded it - AiTextureInvalidate(img_info['filename']) - except: - print('[maketx] Error: Could not guess colorspace for "%s"' % img_info['filename']) + AiTextureInvalidate(img_info['filename']) # noqa + except ValueError: + print('[maketx] Error: Could not guess' + 'colorspace for "%s"' % img_info['filename']) return 'linear' diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 5ac5d9c728..46f7b0e03d 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -553,12 +553,15 @@ class ExtractLook(publish.Extractor): color_space = guess_colorspace(img_info) if color_space == "sRGB": self.log.info("tx: converting sRGB -> linear") - additional_args.extend(["--colorconvert", "sRGB", "Raw"]) + additional_args.extend(["--colorconvert", + "sRGB", + "Raw"]) else: - self.log.info("tx: texture's colorspace is already linear") + self.log.info("tx: texture's colorspace " + "is already linear") - - config_path = cmds.colorManagementPrefs(query=True, configFilePath=True) + config_path = cmds.colorManagementPrefs(query=True, + configFilePath=True) if not os.path.exists(config_path): raise RuntimeError("No OCIO config path found!") additional_args.extend(["--colorconfig", config_path]) From c1012bd03105d26c440a31797a0363180bfb0187 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 2 Feb 2023 12:01:54 +0800 Subject: [PATCH 0381/1271] hound fix --- openpype/hosts/maya/api/lib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index e20ae15228..d6a3988922 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3402,6 +3402,7 @@ def imageInfo(filepath): img_info['format'] = "unknown" return img_info + def guess_colorspace(img_info): ''' Take reference from makeTx.py Guess the colorspace of the input image filename. From 7217ba6b770d03c77736ba7e72cb2e04ff3b8dff Mon Sep 17 00:00:00 2001 From: Libor Batek Date: Thu, 2 Feb 2023 09:06:01 +0100 Subject: [PATCH 0382/1271] added pic for systray icon --- website/docs/artist_getting_started.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/website/docs/artist_getting_started.md b/website/docs/artist_getting_started.md index 14951e599d..301a58fa56 100644 --- a/website/docs/artist_getting_started.md +++ b/website/docs/artist_getting_started.md @@ -17,7 +17,7 @@ If this is not the case, please contact your administrator to consult on how to ## Working from home If you are working from **home** though, you'll **need to install** it yourself. You should, however, receive the OpenPype installer files from your studio -admin, supervisor or production, because OpenPype versions and executables might not be compatible between studios. +admin, supervisor or production, because OpenPype versions and executables might not be compatible between studios. Installing OpenPype is possible by Installer or by unzipping downloaded ZIP archive to any drive location. @@ -25,7 +25,6 @@ Installing OpenPype is possible by Installer or by unzipping downloaded ZIP arch See the [Installation section](artist_install.md) for more information on how to use the OpenPype Installer ::: ---- You can run OpenPype by desktop "OP" icon (if it exists after installing) or by directly executing @@ -33,12 +32,15 @@ You can run OpenPype by desktop "OP" icon (if it exists after installing) or by or alternatively by -**openpype_console.exe** which is more suitable for **TDs/Admin** for debugging and error reporting. This one runs with opened console window where all the necessary info will appear during user's work session. +**openpype_console.exe** which is more suitable for **TDs/Admin** for debugging and error reporting. This one runs with +opened console window where all the necessary info will appear during user's work session. :::tip Is OpenPype running? -OpenPype runs in the operating system's tray. If you see the OpenPype icon in the tray you can easily tell OpenPype is currently running. +OpenPype runs in the operating system's tray. If you see turquoise OpenPype icon in the tray you can easily tell OpenPype is currently running. +Keep in mind that on Windows this icon might be hidden by default, in which case, the artist can simply drag the icon down to the tray. ::: +![Systray](assets/artist_systray.png) ## First Launch @@ -49,7 +51,7 @@ When you first start OpenPype, you will be asked to fill in some basic informati ### MongoDB In most cases you will only have to supply the MongoDB Address. -It's the database URL you should have received from your Studio admin and often will look like this +It's the database URL you should have received from your Studio admin and often will look like this `mongodb://username:passwword@mongo.mystudiodomain.com:12345` @@ -64,14 +66,14 @@ asks for it, just put it in the corresponding text field and press `install` but Sometimes your Studio might also ask you to fill in the path to its version repository. This is a location where OpenPype will search for the latest versions, check -if it's up to date and where updates are installed from automatically. +if it's up to date and where updates are installed from automatically. -This path is usually taken from the database directly, so you shouldn't need it. +This path is usually taken from the database directly, so you shouldn't need it. ## Updates -If you're connected to your Studio, OpenPype will check for, and install updates automatically every time you run it. That's why during the first start it can go through a quick update installation process, even though you might have just installed it. +If you're connected to your Studio, OpenPype will check for, and install updates automatically every time you run it. That's why during the first start it can go through a quick update installation process, even though you might have just installed it. ## Advanced Usage From b8084babfd436cef14755de3161dd7fc64d154de Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 2 Feb 2023 18:00:03 +0800 Subject: [PATCH 0383/1271] update docstring --- openpype/hosts/maya/api/lib.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index d6a3988922..f33b0c8d8e 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3382,11 +3382,9 @@ def get_attribute_input(attr): return connections[0] if connections else None -# Reference from Arnold -# Get Image Information for colorspace def imageInfo(filepath): - """Take reference from makeTx.py - ImageInfo(filename): Get Image Information + """Take reference from makeTx.py in Arnold + ImageInfo(filename): Get Image Information for colorspace AiTextureGetFormat(filename): Get Texture Format AiTextureGetBitDepth(filename): Get Texture Bit Depth """ From df6031d810a8d3e6c222087f5149e86cc84d4a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Thu, 2 Feb 2023 11:40:06 +0100 Subject: [PATCH 0384/1271] Update openpype/hosts/max/startup/startup.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/max/startup/startup.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/max/startup/startup.py b/openpype/hosts/max/startup/startup.py index 21da115baa..0d3135a16f 100644 --- a/openpype/hosts/max/startup/startup.py +++ b/openpype/hosts/max/startup/startup.py @@ -4,17 +4,12 @@ import sys # this might happen in some 3dsmax version where PYTHONPATH isn't added # to sys.path automatically -try: - from openpype.hosts.max.api import MaxHost - from openpype.pipeline import install_host -except (ImportError, ModuleNotFoundError): +for path in os.environ["PYTHONPATH"].split(os.pathsep): + if path and path not in sys.path: + sys.path.append(path) - for path in os.environ["PYTHONPATH"].split(os.pathsep): - if path and path not in sys.path: - sys.path.append(path) - - from openpype.hosts.max.api import MaxHost - from openpype.pipeline import install_host +from openpype.hosts.max.api import MaxHost +from openpype.pipeline import install_host host = MaxHost() install_host(host) From 0dbb63df944d81d7318f0ea503f00cf7cca1f48d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 2 Feb 2023 11:54:55 +0100 Subject: [PATCH 0385/1271] modified change item a little --- openpype/pipeline/create/context.py | 324 ++++++++++++++++++++++------ 1 file changed, 254 insertions(+), 70 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 59314b9236..52e43f500e 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -183,92 +183,169 @@ def prepare_failed_creator_operation_info( } -class ChangedItem(object): +_EMPTY_VALUE = object() + + +class TrackChangesItem(object): + """Helper object to track changes in data. + + Has access to full old and new data and will create deep copy of them, + so it is not needed to create copy before passed in. + + Can work as a dictionary if old or new value is a dictionary. In + that case received object is another object of 'TrackChangesItem'. + + Goal is to be able to get old or new value as was or only changed values + or get information about removed/changed keys, and all of that on + any "dictionary level". + + ``` + # Example of possible usages + old_value = { + "key_1": "value_1", + "key_2": { + "key_sub_1": 1, + "key_sub_2": { + "enabled": True + } + }, + "key_3": "value_2" + } + + new_value = { + "key_1": "value_1", + "key_2": { + "key_sub_2": { + "enabled": False + }, + "key_sub_3": 3 + }, + "key_3": "value_3" + } + + changes = TrackChangesItem(old_value, new_value) + print(changes.changed) + >>> True + print(changes.changed_keys) + >>> {"key_2", "key_3"} + print(changes["key_2"]["key_sub_2"]["enabled"].changed) + >>> True + print(changes["key_2"].removed_keys) + >>> {"key_sub_1"} + print(changes["key_2"].available_keys) + >>> {"key_sub_1", "key_sub_2", "key_sub_3"} + print(changes.new_value == new_value) + >>> True + + only_changed_new_values = { + key: changes[key].new_value + for key in changes + } + ``` + + Args: + old_value (Any): Previous value. + new_value (Any): New value. + """ + def __init__(self, old_value, new_value): + self._changed = old_value != new_value + if old_value is _EMPTY_VALUE: + old_value = None + if new_value is _EMPTY_VALUE: + new_value = None self._old_value = copy.deepcopy(old_value) self._new_value = copy.deepcopy(new_value) - self._changed = self._old_value != self._new_value - old_is_dict = isinstance(old_value, dict) - new_is_dict = isinstance(new_value, dict) - children = {} - changed_keys = set() - available_keys = set() - new_keys = set() - old_keys = set() - if old_is_dict and new_is_dict: - old_keys = set(old_value.keys()) - new_keys = set(new_value.keys()) - available_keys = old_keys | new_keys - for key in available_keys: - item = ChangedItem( - old_value.get(key), new_value.get(key) - ) - children[key] = item - if item.changed or key not in old_keys or key not in new_keys: - changed_keys.add(key) + self._old_is_dict = isinstance(old_value, dict) + self._new_is_dict = isinstance(new_value, dict) - elif old_is_dict: - old_keys = set(old_value.keys()) - available_keys = set(old_keys) - changed_keys = set(available_keys) - for key in available_keys: - children[key] = ChangedItem(old_value.get(key), None) + self._old_keys = None + self._new_keys = None + self._available_keys = None + self._removed_keys = None - elif new_is_dict: - new_keys = set(new_value.keys()) - available_keys = set(new_keys) - changed_keys = set(available_keys) - for key in available_keys: - children[key] = ChangedItem(None, new_value.get(key)) + self._changed_keys = None - self._changed_keys = changed_keys - self._available_keys = available_keys - self._children = children - self._old_is_dict = old_is_dict - self._new_is_dict = new_is_dict - self._old_keys = old_keys - self._new_keys = new_keys + self._sub_items = None def __getitem__(self, key): - return self._children[key] + """Getter looks into subitems if object is dictionary.""" + + if self._sub_items is None: + self._prepare_sub_items() + return self._sub_items[key] def __bool__(self): + """Boolean of object is if old and new value are the same.""" + return self._changed - def __iter__(self): - for key in self.changed_keys: - yield key - def get(self, key, default=None): - return self._children.get(key, default) + """Try to get sub item.""" - def keys(self): - return self.changed_keys + if self._sub_items is None: + self._prepare_sub_items() + return self._sub_items.get(key, default) - def items(self): - if not self.is_dict: - yield None, self.changes - else: - for item in self.changes.items(): - yield item + @property + def old_value(self): + """Get copy of old value. + + Returns: + Any: Whatever old value was. + """ + + return copy.deepcopy(self._old_value) + + @property + def new_value(self): + """Get copy of new value. + + Returns: + Any: Whatever new value was. + """ + + return copy.deepcopy(self._new_value) @property def changed(self): + """Value changed. + + Returns: + bool: If data changed. + """ + return self._changed @property def is_dict(self): + """Object can be used as dictionary. + + Returns: + bool: When can be used that way. + """ + return self._old_is_dict or self._new_is_dict @property def changes(self): + """Get changes in raw data. + + This method should be used only if 'is_dict' value is 'True'. + + Returns: + Dict[str, Tuple[Any, Any]]: Changes are by key in tuple + (, ). If 'is_dict' is 'False' then + output is always empty dictionary. + """ + + output = {} if not self.is_dict: - return (self.old_value, self.new_value) + return output old_value = self.old_value new_value = self.new_value - output = {} for key in self.changed_keys: _old = None _new = None @@ -279,29 +356,135 @@ class ChangedItem(object): output[key] = (_old, _new) return output - @property - def changed_keys(self): - return set(self._changed_keys) - - @property - def available_keys(self): - return set(self._available_keys) - + # Methods/properties that can be used when 'is_dict' is 'True' @property def old_keys(self): + """Keys from old value. + + Empty set is returned if old value is not a dict. + + Returns: + Set[str]: Keys from old value. + """ + + if self._old_keys is None: + self._prepare_keys() return set(self._old_keys) @property def new_keys(self): + """Keys from new value. + + Empty set is returned if old value is not a dict. + + Returns: + Set[str]: Keys from new value. + """ + + if self._new_keys is None: + self._prepare_keys() return set(self._new_keys) @property - def old_value(self): - return copy.deepcopy(self._old_value) + def changed_keys(self): + """Keys that has changed from old to new value. + + Empty set is returned if both old and new value are not a dict. + + Returns: + Set[str]: Keys of changed keys. + """ + + if self._changed_keys is None: + self._prepare_sub_items() + return set(self._changed_keys) @property - def new_value(self): - return copy.deepcopy(self._new_value) + def available_keys(self): + """All keys that are available in old and new value. + + Empty set is returned if both old and new value are not a dict. + Output it is Union of 'old_keys' and 'new_keys'. + + Returns: + Set[str]: All keys from old and new value. + """ + + if self._available_keys is None: + self._prepare_keys() + return set(self._available_keys) + + @property + def removed_keys(self): + """Key that are not available in new value but were in old value. + + Returns: + Set[str]: All removed keys. + """ + + if self._removed_keys is None: + self._prepare_sub_items() + return set(self._removed_keys) + + def _prepare_keys(self): + old_keys = set() + new_keys = set() + if self._old_is_dict and self._new_is_dict: + old_keys = set(self._old_value.keys()) + new_keys = set(self._new_value.keys()) + + elif self._old_is_dict: + old_keys = set(self._old_value.keys()) + + elif self._new_is_dict: + new_keys = set(self._new_value.keys()) + + self._old_keys = old_keys + self._new_keys = new_keys + self._available_keys = old_keys | new_keys + self._removed_keys = old_keys - new_keys + + def _prepare_sub_items(self): + sub_items = {} + changed_keys = set() + + old_keys = self.old_keys + new_keys = self.new_keys + new_value = self.new_value + old_value = self.old_value + if self._old_is_dict and self._new_is_dict: + for key in self.available_keys: + item = TrackChangesItem( + old_value.get(key), new_value.get(key) + ) + sub_items[key] = item + if item.changed or key not in old_keys or key not in new_keys: + changed_keys.add(key) + + elif self._old_is_dict: + old_keys = set(old_value.keys()) + available_keys = set(old_keys) + changed_keys = set(available_keys) + for key in available_keys: + # NOTE Use '_EMPTY_VALUE' because old value could be 'None' + # which would result in "unchanged" item + sub_items[key] = TrackChangesItem( + old_value.get(key), _EMPTY_VALUE + ) + + elif self._new_is_dict: + new_keys = set(new_value.keys()) + available_keys = set(new_keys) + changed_keys = set(available_keys) + for key in available_keys: + # NOTE Use '_EMPTY_VALUE' because new value could be 'None' + # which would result in "unchanged" item + sub_items[key] = TrackChangesItem( + _EMPTY_VALUE, new_value.get(key) + ) + + self._sub_items = sub_items + self._changed_keys = changed_keys class InstanceMember: @@ -895,7 +1078,7 @@ class CreatedInstance: def changes(self): """Calculate and return changes.""" - return ChangedItem(self.origin_data, self.data_to_store()) + return TrackChangesItem(self._orig_data, self.data_to_store()) def mark_as_stored(self): """Should be called when instance data are stored. @@ -1519,8 +1702,9 @@ class CreateContext: def context_data_changes(self): """Changes of attributes.""" - old_value = copy.deepcopy(self._original_context_data) - return ChangedItem(old_value, self.context_data_to_store()) + return TrackChangesItem( + self._original_context_data, self.context_data_to_store() + ) def creator_adds_instance(self, instance): """Creator adds new instance to context. From 6ca987fdd259981f05bfcf0e2717a6e91682680e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 2 Feb 2023 11:56:51 +0100 Subject: [PATCH 0386/1271] modified houdini to use new changes object --- openpype/hosts/houdini/api/plugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/houdini/api/plugin.py b/openpype/hosts/houdini/api/plugin.py index 61127bda57..0b9940a993 100644 --- a/openpype/hosts/houdini/api/plugin.py +++ b/openpype/hosts/houdini/api/plugin.py @@ -225,12 +225,12 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): self._add_instance_to_context(created_instance) def update_instances(self, update_list): - for created_inst, _changes in update_list: + for created_inst, changes in update_list: instance_node = hou.node(created_inst.get("instance_node")) new_values = { - key: new_value - for key, (_old_value, new_value) in _changes.items() + key: changes[key].new_value + for key in changes } imprint( instance_node, From 0b65168688be847861d89b4e12547608f30c627c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 2 Feb 2023 12:17:07 +0100 Subject: [PATCH 0387/1271] fix other places using changes --- openpype/hosts/aftereffects/plugins/create/create_render.py | 4 ++-- openpype/hosts/max/api/plugin.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 8d38288257..5427edb44b 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -47,10 +47,10 @@ class RenderCreator(Creator): for created_inst, _changes in update_list: api.get_stub().imprint(created_inst.get("instance_id"), created_inst.data_to_store()) - subset_change = _changes.get("subset") + subset_change = _changes["subset"] if subset_change: api.get_stub().rename_item(created_inst.data["members"][0], - subset_change[1]) + subset_change.new_value) def remove_instances(self, instances): for instance in instances: diff --git a/openpype/hosts/max/api/plugin.py b/openpype/hosts/max/api/plugin.py index 4788bfd383..55603b26a0 100644 --- a/openpype/hosts/max/api/plugin.py +++ b/openpype/hosts/max/api/plugin.py @@ -78,12 +78,12 @@ class MaxCreator(Creator, MaxCreatorBase): self._add_instance_to_context(created_instance) def update_instances(self, update_list): - for created_inst, _changes in update_list: + for created_inst, changes in update_list: instance_node = created_inst.get("instance_node") new_values = { - key: new_value - for key, (_old_value, new_value) in _changes.items() + key: changes[key].new_value + for key in changes } imprint( instance_node, From e1f11abdf4d3beefedfffa56f21557d17cf1e953 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 2 Feb 2023 12:41:33 +0100 Subject: [PATCH 0388/1271] use AVALON_TASK to get current task name --- openpype/host/host.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/host/host.py b/openpype/host/host.py index 28d0a21b34..d2335c0062 100644 --- a/openpype/host/host.py +++ b/openpype/host/host.py @@ -123,7 +123,7 @@ class HostBase(object): Union[str, None]: Current task name. """ - return os.environ.get("AVALON_ASSET") + return os.environ.get("AVALON_TASK") def get_current_context(self): """Get current context information. From b83f0ac79948a26f1b757c0b965e4c138c271a11 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Thu, 2 Feb 2023 12:46:49 +0100 Subject: [PATCH 0389/1271] restore file as it was originally --- openpype/pipeline/workfile/workfile_template_builder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index fd5ac16579..42d8cc82e1 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -525,7 +525,6 @@ class AbstractTemplateBuilder(object): placeholder.scene_identifier: placeholder for placeholder in placeholders } - all_processed = len(placeholders) == 0 # Counter is checked at the ned of a loop so the loop happens at least # once. @@ -566,7 +565,8 @@ class AbstractTemplateBuilder(object): placeholder.set_finished() - # self.clear_shared_populate_data() + # Clear shared data before getting new placeholders + self.clear_shared_populate_data() iter_counter += 1 if iter_counter >= level_limit: @@ -1003,7 +1003,7 @@ class PlaceholderItem(object): return self._log def __repr__(self): - return "< {} {} >".format(self.__class__.__name__, self.data['family']) + return "< {} {} >".format(self.__class__.__name__, self.name) @property def order(self): From 77b55cca517705108e8d8ef7e0c0a577131309c3 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Thu, 2 Feb 2023 12:46:49 +0100 Subject: [PATCH 0390/1271] restore file as it was originally --- openpype/pipeline/workfile/workfile_template_builder.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index fd5ac16579..582657c735 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -525,7 +525,6 @@ class AbstractTemplateBuilder(object): placeholder.scene_identifier: placeholder for placeholder in placeholders } - all_processed = len(placeholders) == 0 # Counter is checked at the ned of a loop so the loop happens at least # once. @@ -566,14 +565,14 @@ class AbstractTemplateBuilder(object): placeholder.set_finished() - # self.clear_shared_populate_data() + # Clear shared data before getting new placeholders + self.clear_shared_populate_data() iter_counter += 1 if iter_counter >= level_limit: break all_processed = True - collected_placeholders = self.get_placeholders() for placeholder in collected_placeholders: identifier = placeholder.scene_identifier @@ -1003,7 +1002,7 @@ class PlaceholderItem(object): return self._log def __repr__(self): - return "< {} {} >".format(self.__class__.__name__, self.data['family']) + return "< {} {} >".format(self.__class__.__name__, self.name) @property def order(self): From f7fd0a53041cfa9c5a789ededd6b94dc56ba8d72 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 13:01:24 +0100 Subject: [PATCH 0391/1271] OP-4513 - fix for DL on MacOS This works if DL Openpype plugin Installation Directories is set to level of app bundle (eg. '/Applications/OpenPype 3.15.0.app') --- .../deadline/repository/custom/plugins/GlobalJobPreLoad.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 984590ddba..33d548d204 100644 --- a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -157,7 +157,7 @@ def get_openpype_version_from_path(path, build=True): # fix path for application bundle on macos if platform.system().lower() == "darwin": - path = os.path.join(path, "Contents", "MacOS", "lib", "Python") + path = os.path.join(path, "MacOS") version_file = os.path.join(path, "openpype", "version.py") if not os.path.isfile(version_file): From 638375c9db7caca607375ed8b3f29eba76e5349d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 2 Feb 2023 13:47:15 +0100 Subject: [PATCH 0392/1271] add missing attribute usage --- openpype/hosts/houdini/api/plugin.py | 2 +- openpype/hosts/max/api/plugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/api/plugin.py b/openpype/hosts/houdini/api/plugin.py index 0b9940a993..f0985973a6 100644 --- a/openpype/hosts/houdini/api/plugin.py +++ b/openpype/hosts/houdini/api/plugin.py @@ -230,7 +230,7 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): new_values = { key: changes[key].new_value - for key in changes + for key in changes.changed_keys } imprint( instance_node, diff --git a/openpype/hosts/max/api/plugin.py b/openpype/hosts/max/api/plugin.py index 55603b26a0..c16d9e61ec 100644 --- a/openpype/hosts/max/api/plugin.py +++ b/openpype/hosts/max/api/plugin.py @@ -83,7 +83,7 @@ class MaxCreator(Creator, MaxCreatorBase): new_values = { key: changes[key].new_value - for key in changes + for key in changes.changed_keys } imprint( instance_node, From a9e139998ca49b7dc6135858333923f223a92c3b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 2 Feb 2023 15:00:56 +0100 Subject: [PATCH 0393/1271] modified code example to also contain tests --- openpype/pipeline/create/context.py | 76 ++++++++++++++++------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 52e43f500e..f46b4eccdb 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -201,45 +201,53 @@ class TrackChangesItem(object): ``` # Example of possible usages - old_value = { - "key_1": "value_1", - "key_2": { - "key_sub_1": 1, - "key_sub_2": { - "enabled": True - } - }, - "key_3": "value_2" - } + >>> old_value = { + ... "key_1": "value_1", + ... "key_2": { + ... "key_sub_1": 1, + ... "key_sub_2": { + ... "enabled": True + ... } + ... }, + ... "key_3": "value_2" + ... } + >>> new_value = { + ... "key_1": "value_1", + ... "key_2": { + ... "key_sub_2": { + ... "enabled": False + ... }, + ... "key_sub_3": 3 + ... }, + ... "key_3": "value_3" + ... } - new_value = { - "key_1": "value_1", - "key_2": { - "key_sub_2": { - "enabled": False - }, - "key_sub_3": 3 - }, - "key_3": "value_3" - } + >>> changes = TrackChangesItem(old_value, new_value) + >>> changes.changed + True - changes = TrackChangesItem(old_value, new_value) - print(changes.changed) - >>> True - print(changes.changed_keys) - >>> {"key_2", "key_3"} - print(changes["key_2"]["key_sub_2"]["enabled"].changed) - >>> True - print(changes["key_2"].removed_keys) - >>> {"key_sub_1"} - print(changes["key_2"].available_keys) - >>> {"key_sub_1", "key_sub_2", "key_sub_3"} - print(changes.new_value == new_value) - >>> True + >>> changes["key_2"]["key_sub_1"].new_value is None + True + >>> list(sorted(changes.changed_keys)) + ['key_2', 'key_3'] + + >>> changes["key_2"]["key_sub_2"]["enabled"].changed + True + + >>> changes["key_2"].removed_keys + {'key_sub_1'} + + >>> list(sorted(changes["key_2"].available_keys)) + ['key_sub_1', 'key_sub_2', 'key_sub_3'] + + >>> changes.new_value == new_value + True + + # Get only changed values only_changed_new_values = { key: changes[key].new_value - for key in changes + for key in changes.changed_keys } ``` From 4a963e9aac1dc3ce8ada72e006f81fb5a4bf3f21 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 16:10:35 +0100 Subject: [PATCH 0394/1271] OP-4513 - fix valid logic 0 is false, None check is safer --- .../deadline/repository/custom/plugins/GlobalJobPreLoad.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 33d548d204..a5e48361c3 100644 --- a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -35,7 +35,7 @@ class OpenPypeVersion: self.prerelease = prerelease is_valid = True - if not major or not minor or not patch: + if major is None or minor is None or patch is None: is_valid = False self.is_valid = is_valid From d1a8744e5b1e72cf5323b58e2489e323967de8cb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 16:11:06 +0100 Subject: [PATCH 0395/1271] OP-4513 - better logging --- .../deadline/repository/custom/plugins/GlobalJobPreLoad.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index a5e48361c3..82865ed714 100644 --- a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -218,8 +218,8 @@ def get_requested_openpype_executable( requested_version_obj = OpenPypeVersion.from_string(requested_version) if not requested_version_obj: print(( - ">>> Requested version does not match version regex \"{}\"" - ).format(VERSION_REGEX)) + ">>> Requested version '{}' does not match version regex '{}'" + ).format(requested_version, VERSION_REGEX)) return None print(( From 63912a0772b7ef6c00e134f3bdaaba8508bcd444 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 17:18:10 +0100 Subject: [PATCH 0396/1271] OP-4513 - fix selection of openpype_console on Mac --- .../deadline/repository/custom/plugins/GlobalJobPreLoad.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 82865ed714..38eb163306 100644 --- a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -272,7 +272,8 @@ def get_requested_openpype_executable( # Deadline decide. exe_list = [ os.path.join(version_dir, "openpype_console.exe"), - os.path.join(version_dir, "openpype_console") + os.path.join(version_dir, "openpype_console"), + os.path.join(version_dir, "MacOS", "openpype_console") ] return FileUtils.SearchFileList(";".join(exe_list)) From 707c165f4f252afe4693ba71a0ae2dc9a74a62ea Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 17:29:37 +0100 Subject: [PATCH 0397/1271] OP-4513 - fix copy and paste artifact on MacOS clipboarch escapes whitespace with \ on MacOS, so for safety clean it here. Hopefully nobody starts folder name with space on Windows. --- .../deadline/repository/custom/plugins/GlobalJobPreLoad.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 38eb163306..108c418e7b 100644 --- a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -189,6 +189,12 @@ def get_openpype_executable(): exe_list = config.GetConfigEntryWithDefault("OpenPypeExecutable", "") dir_list = config.GetConfigEntryWithDefault( "OpenPypeInstallationDirs", "") + + # clean '\ ' for MacOS pasting + if exe_list: + exe_list = exe_list.replace("\\ ", " ") + if dir_list: + dir_list = dir_list.replace("\\ ", " ") return exe_list, dir_list From 27773fbbf16fa8c9fa48678f1ec438d5f65f3b8d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 18:23:51 +0100 Subject: [PATCH 0398/1271] OP-4822 - added profile to disble check to Settings --- .../defaults/project_settings/deadline.json | 3 +- .../schema_project_deadline.json | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index ceb0b2e39a..11bb1aee2e 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -33,7 +33,8 @@ "limit": [], "jobInfo": {}, "pluginInfo": {}, - "scene_patches": [] + "scene_patches": [], + "disable_strict_check_profiles": [] }, "NukeSubmitDeadline": { "enabled": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index 08a505bd47..d38358773a 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -195,6 +195,36 @@ ] } + }, + { + "type": "list", + "collapsible": true, + "key": "disable_strict_check_profiles", + "label": "Disable Strict Error Check profiles", + "use_label_wrap": true, + "docstring": "Set profile for disabling 'Strict Error Checking'", + "object_type": { + "type": "dict", + "children": [ + { + "key": "task_types", + "label": "Task types", + "type": "task-types-enum" + }, + { + "key": "task_names", + "label": "Task names", + "type": "list", + "object_type": "text" + }, + { + "key": "subsets", + "label": "Subset names", + "type": "list", + "object_type": "text" + } + ] + } } ] }, From bd77b30c5c3fc3f21d8732d9309ec6c8f5d776f4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 18:25:24 +0100 Subject: [PATCH 0399/1271] OP-4822 - added possibility to disable strict checking DL by default has Strict error checking, but some errors are not fatal. This allows to set profile based on Task and Subset values to temporarily disable Strict Error Checks --- .../plugins/publish/submit_maya_deadline.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 070d4eab18..a59738979c 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -35,6 +35,7 @@ from openpype.pipeline import legacy_io from openpype.hosts.maya.api.lib_rendersettings import RenderSettings from openpype.hosts.maya.api.lib import get_attr_in_layer +from openpype.lib.profiles_filtering import filter_profiles from openpype_modules.deadline import abstract_submit_deadline from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo from openpype.tests.lib import is_in_tests @@ -64,6 +65,7 @@ class MayaPluginInfo(object): # Include all lights flag RenderSetupIncludeLights = attr.ib( default="1", validator=_validate_deadline_bool_value) + StrictErrorChecking = attr.ib(default="1") @attr.s @@ -104,6 +106,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): jobInfo = {} pluginInfo = {} group = "none" + disable_strict_check_profiles = [] def get_job_info(self): job_info = DeadlineJobInfo(Plugin="MayaBatch") @@ -219,6 +222,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): "renderSetupIncludeLights", default_rs_include_lights) if rs_include_lights not in {"1", "0", True, False}: rs_include_lights = default_rs_include_lights + strict_checking = self._get_strict_checking(instance) plugin_info = MayaPluginInfo( SceneFile=self.scene_path, Version=cmds.about(version=True), @@ -227,6 +231,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): RenderSetupIncludeLights=rs_include_lights, # noqa ProjectPath=context.data["workspaceDir"], UsingRenderLayers=True, + StrictErrorChecking=strict_checking ) plugin_payload = attr.asdict(plugin_info) @@ -748,6 +753,32 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): for file in exp: yield file + def _get_strict_checking(self, plugin_info, instance): + """Find profile to disable Strict Error Checking + + Args: + instance (dict) + Returns: + (int) - 1 if Strict (DL default), 0 for disabled Strict + """ + strict_checking = 1 + if not self.disable_strict_check_profiles: + return strict_checking + + task_data = instance.data["anatomyData"].get("task", {}) + key_values = { + "task_names": task_data.get("name"), + "task_types": task_data.get("type"), + "subsets": instance.data["subset"] + } + profile = filter_profiles(self.profiles, key_values, + logger=self.log) + + if profile: + strict_checking = 0 + + return strict_checking + def _format_tiles( filename, index, tiles_x, tiles_y, From f29c46f7c1cea1b0a9f3cf5b889394ab8bf03afd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 18:44:00 +0100 Subject: [PATCH 0400/1271] OP-4822 - fix arguments --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index a59738979c..a2a80c319b 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -753,7 +753,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): for file in exp: yield file - def _get_strict_checking(self, plugin_info, instance): + def _get_strict_checking(self, instance): """Find profile to disable Strict Error Checking Args: From 8a2cf2e6d93d1d9ed0887a84315e13608e859060 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 18:44:24 +0100 Subject: [PATCH 0401/1271] OP-4822 - fix wrong variable --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index a2a80c319b..c353e0b109 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -771,7 +771,8 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): "task_types": task_data.get("type"), "subsets": instance.data["subset"] } - profile = filter_profiles(self.profiles, key_values, + profile = filter_profiles(self.disable_strict_check_profiles, + key_values, logger=self.log) if profile: From 5df388912abba15c5a477aa47f06807cce0cf8fb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Feb 2023 18:44:43 +0100 Subject: [PATCH 0402/1271] OP-4822 - added logging --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index c353e0b109..3d1b9bcbf6 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -776,6 +776,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): logger=self.log) if profile: + self.log.debug("Disabling Strict Error Checking") strict_checking = 0 return strict_checking From 280d977868d89edb3ecd15801f9bc7cf2c706d18 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 2 Feb 2023 18:39:41 +0000 Subject: [PATCH 0403/1271] Only parse focussed panel if its a modelPanel. --- openpype/hosts/maya/plugins/publish/extract_playblast.py | 4 ++-- openpype/hosts/maya/plugins/publish/extract_thumbnail.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_playblast.py b/openpype/hosts/maya/plugins/publish/extract_playblast.py index 1f9f9db99a..e4e44e4770 100644 --- a/openpype/hosts/maya/plugins/publish/extract_playblast.py +++ b/openpype/hosts/maya/plugins/publish/extract_playblast.py @@ -131,8 +131,8 @@ class ExtractPlayblast(publish.Extractor): # Update preset with current panel setting # if override_viewport_options is turned off - if not override_viewport_options: - panel = cmds.getPanel(withFocus=True) + panel = cmds.getPanel(withFocus=True) or "" + if not override_viewport_options and "modelPanel" in panel: panel_preset = capture.parse_active_view() preset.update(panel_preset) cmds.setFocus(panel) diff --git a/openpype/hosts/maya/plugins/publish/extract_thumbnail.py b/openpype/hosts/maya/plugins/publish/extract_thumbnail.py index 1edafeb926..1d94bd58c5 100644 --- a/openpype/hosts/maya/plugins/publish/extract_thumbnail.py +++ b/openpype/hosts/maya/plugins/publish/extract_thumbnail.py @@ -134,8 +134,8 @@ class ExtractThumbnail(publish.Extractor): # Update preset with current panel setting # if override_viewport_options is turned off - if not override_viewport_options: - panel = cmds.getPanel(withFocus=True) + panel = cmds.getPanel(withFocus=True) or "" + if not override_viewport_options and "modelPanel" in panel: panel_preset = capture.parse_active_view() preset.update(panel_preset) cmds.setFocus(panel) From 6a3d981c0fa7d081bee4fe89fd8a0df77c456bdc Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 2 Feb 2023 20:45:12 +0000 Subject: [PATCH 0404/1271] Fix updating VrayProxy --- openpype/hosts/maya/plugins/load/load_vrayproxy.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_vrayproxy.py b/openpype/hosts/maya/plugins/load/load_vrayproxy.py index 720a132aa7..64184f9e7b 100644 --- a/openpype/hosts/maya/plugins/load/load_vrayproxy.py +++ b/openpype/hosts/maya/plugins/load/load_vrayproxy.py @@ -81,10 +81,11 @@ class VRayProxyLoader(load.LoaderPlugin): c = colors.get(family) if c is not None: cmds.setAttr("{0}.useOutlinerColor".format(group_node), 1) - cmds.setAttr("{0}.outlinerColor".format(group_node), - (float(c[0])/255), - (float(c[1])/255), - (float(c[2])/255) + cmds.setAttr( + "{0}.outlinerColor".format(group_node), + (float(c[0]) / 255), + (float(c[1]) / 255), + (float(c[2]) / 255) ) return containerise( @@ -101,7 +102,7 @@ class VRayProxyLoader(load.LoaderPlugin): assert cmds.objExists(node), "Missing container" members = cmds.sets(node, query=True) or [] - vraymeshes = cmds.ls(members, type="VRayMesh") + vraymeshes = cmds.ls(members, type="VRayProxy") assert vraymeshes, "Cannot find VRayMesh in container" # get all representations for this version From c0439cae1aec3e7d60a9eb07267ac42b61309edc Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 2 Feb 2023 20:48:43 +0000 Subject: [PATCH 0405/1271] Validate vray plugin is loaded. Otherwise you can publish VrayProxy without needing to enable it and fail at extraction. --- .../maya/plugins/publish/validate_vray.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 openpype/hosts/maya/plugins/publish/validate_vray.py diff --git a/openpype/hosts/maya/plugins/publish/validate_vray.py b/openpype/hosts/maya/plugins/publish/validate_vray.py new file mode 100644 index 0000000000..045ac258a1 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_vray.py @@ -0,0 +1,18 @@ +from maya import cmds + +import pyblish.api +from openpype.pipeline import PublishValidationError + + +class ValidateVray(pyblish.api.InstancePlugin): + """Validate general Vray setup.""" + + order = pyblish.api.ValidatorOrder + label = 'VRay' + hosts = ["maya"] + families = ["vrayproxy"] + + def process(self, instance): + # Validate vray plugin is loaded. + if not cmds.pluginInfo("vrayformaya", query=True, loaded=True): + raise PublishValidationError("Vray plugin is not loaded.") From b00aab1eed7bf92347b64ed50e44340476b671a6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 2 Feb 2023 23:35:22 +0000 Subject: [PATCH 0406/1271] Fix rounding --- openpype/hosts/maya/api/lib.py | 149 +++++++++++++----- .../plugins/publish/validate_maya_units.py | 11 +- 2 files changed, 115 insertions(+), 45 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index e5fa883c99..887c04d257 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1969,8 +1969,6 @@ def get_id_from_sibling(node, history_only=True): return first_id - -# Project settings def set_scene_fps(fps, update=True): """Set FPS from project configuration @@ -1983,28 +1981,21 @@ def set_scene_fps(fps, update=True): """ - fps_mapping = {'15': 'game', - '24': 'film', - '25': 'pal', - '30': 'ntsc', - '48': 'show', - '50': 'palf', - '60': 'ntscf', - '23.98': '23.976fps', - '23.976': '23.976fps', - '29.97': '29.97fps', - '47.952': '47.952fps', - '47.95': '47.952fps', - '59.94': '59.94fps', - '44100': '44100fps', - '48000': '48000fps'} - - # pull from mapping - # this should convert float string to float and int to int - # so 25.0 is converted to 25, but 23.98 will be still float. - dec, ipart = math.modf(fps) - if dec == 0.0: - fps = int(ipart) + fps_mapping = { + '15': 'game', + '24': 'film', + '25': 'pal', + '30': 'ntsc', + '48': 'show', + '50': 'palf', + '60': 'ntscf', + '23.976023976023978': '23.976fps', + '29.97002997002997': '29.97fps', + '47.952047952047955': '47.952fps', + '59.94005994005994': '59.94fps', + '44100': '44100fps', + '48000': '48000fps' + } unit = fps_mapping.get(str(fps), None) if unit is None: @@ -2124,7 +2115,9 @@ def set_context_settings(): asset_data = asset_doc.get("data", {}) # Set project fps - fps = asset_data.get("fps", project_data.get("fps", 25)) + fps = convert_to_maya_fps( + asset_data.get("fps", project_data.get("fps", 25)) + ) legacy_io.Session["AVALON_FPS"] = str(fps) set_scene_fps(fps) @@ -2146,15 +2139,12 @@ def validate_fps(): """ - fps = get_current_project_asset(fields=["data.fps"])["data"]["fps"] - # TODO(antirotor): This is hack as for framerates having multiple - # decimal places. FTrack is ceiling decimal values on - # fps to two decimal places but Maya 2019+ is reporting those fps - # with much higher resolution. As we currently cannot fix Ftrack - # rounding, we have to round those numbers coming from Maya. - current_fps = float_round(mel.eval('currentTimeUnitToFPS()'), 2) + expected_fps = convert_to_maya_fps( + get_current_project_asset(fields=["data.fps"])["data"]["fps"] + ) + current_fps = mel.eval('currentTimeUnitToFPS()') - fps_match = current_fps == fps + fps_match = current_fps == expected_fps if not fps_match and not IS_HEADLESS: from openpype.widgets import popup @@ -2163,14 +2153,19 @@ def validate_fps(): dialog = popup.PopupUpdateKeys(parent=parent) dialog.setModal(True) dialog.setWindowTitle("Maya scene does not match project FPS") - dialog.setMessage("Scene %i FPS does not match project %i FPS" % - (current_fps, fps)) + dialog.setMessage( + "Scene {} FPS does not match project {} FPS".format( + current_fps, expected_fps + ) + ) dialog.setButtonText("Fix") # Set new text for button (add optional argument for the popup?) toggle = dialog.widgets["toggle"] update = toggle.isChecked() - dialog.on_clicked_state.connect(lambda: set_scene_fps(fps, update)) + dialog.on_clicked_state.connect( + lambda: set_scene_fps(expected_fps, update) + ) dialog.show() @@ -3353,3 +3348,85 @@ def iter_visible_nodes_in_range(nodes, start, end): def get_attribute_input(attr): connections = cmds.listConnections(attr, plugs=True, destination=False) return connections[0] if connections else None + + +def convert_to_maya_fps(fps): + """Convert any fps to supported Maya framerates.""" + float_framerates = [ + 23.976023976023978, + # WTF is 29.97 df vs fps? + 29.97002997002997, + 47.952047952047955, + 59.94005994005994 + ] + # 44100 fps evaluates as 41000.0. Why? Omitting for now. + int_framerates = [ + 2, + 3, + 4, + 5, + 6, + 8, + 10, + 12, + 15, + 16, + 20, + 24, + 25, + 30, + 40, + 48, + 50, + 60, + 75, + 80, + 90, + 100, + 120, + 125, + 150, + 200, + 240, + 250, + 300, + 375, + 400, + 500, + 600, + 750, + 1200, + 1500, + 2000, + 3000, + 6000, + 48000 + ] + + # If input fps is a whole number we'll return. + if float(fps).is_integer(): + # Validate fps is part of Maya's fps selection. + if fps not in int_framerates: + raise ValueError( + "Framerate \"{}\" is not supported in Maya".format(fps) + ) + return fps + else: + # Differences to supported float frame rates. + differences = [] + for i in float_framerates: + differences.append(abs(i - fps)) + + # Validate difference does not stray too far from supported framerates. + min_difference = min(differences) + min_index = differences.index(min_difference) + supported_framerate = float_framerates[min_index] + if round(min_difference) != 0: + raise ValueError( + "Framerate \"{}\" strays too far from any supported framerate" + " in Maya. Closest supported framerate is \"{}\"".format( + fps, supported_framerate + ) + ) + + return supported_framerate diff --git a/openpype/hosts/maya/plugins/publish/validate_maya_units.py b/openpype/hosts/maya/plugins/publish/validate_maya_units.py index e6fabb1712..ad256b6a72 100644 --- a/openpype/hosts/maya/plugins/publish/validate_maya_units.py +++ b/openpype/hosts/maya/plugins/publish/validate_maya_units.py @@ -33,18 +33,11 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): linearunits = context.data.get('linearUnits') angularunits = context.data.get('angularUnits') - # TODO(antirotor): This is hack as for framerates having multiple - # decimal places. FTrack is ceiling decimal values on - # fps to two decimal places but Maya 2019+ is reporting those fps - # with much higher resolution. As we currently cannot fix Ftrack - # rounding, we have to round those numbers coming from Maya. - # NOTE: this must be revisited yet again as it seems that Ftrack is - # now flooring the value? - fps = mayalib.float_round(context.data.get('fps'), 2, ceil) + fps = context.data.get('fps') # TODO repace query with using 'context.data["assetEntity"]' asset_doc = get_current_project_asset() - asset_fps = asset_doc["data"]["fps"] + asset_fps = mayalib.convert_to_maya_fps(asset_doc["data"]["fps"]) self.log.info('Units (linear): {0}'.format(linearunits)) self.log.info('Units (angular): {0}'.format(angularunits)) From 2c410e3d5435d661861acab1ea4f93c7de3387aa Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 3 Feb 2023 12:08:08 +0100 Subject: [PATCH 0407/1271] gracefully skip if label is not set --- openpype/hosts/houdini/api/shelves.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/api/shelves.py b/openpype/hosts/houdini/api/shelves.py index 3ccab964cd..3a6534d0eb 100644 --- a/openpype/hosts/houdini/api/shelves.py +++ b/openpype/hosts/houdini/api/shelves.py @@ -152,9 +152,13 @@ def get_or_create_tool(tool_definition, shelf): Returns: hou.Tool: The tool updated or the new one """ - existing_tools = shelf.tools() - tool_label = tool_definition.get('label') + tool_label = tool_definition.get("label") + if not tool_label: + log.warning("Skipped shelf without label") + return + + existing_tools = shelf.tools() existing_tool = next( (tool for tool in existing_tools if tool.label() == tool_label), None From 2c17a9bb4e866c3e85127acba40ade4f34eafdf6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 3 Feb 2023 12:08:20 +0100 Subject: [PATCH 0408/1271] fix mandatory keys --- openpype/hosts/houdini/api/shelves.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/houdini/api/shelves.py b/openpype/hosts/houdini/api/shelves.py index 3a6534d0eb..efbc0f5b04 100644 --- a/openpype/hosts/houdini/api/shelves.py +++ b/openpype/hosts/houdini/api/shelves.py @@ -66,7 +66,7 @@ def generate_shelves(): ) continue - mandatory_attributes = {'name', 'script'} + mandatory_attributes = {'label', 'script'} for tool_definition in shelf_definition.get('tools_list'): # We verify that the name and script attibutes of the tool # are set From 79e7b0e0d5052d5cb180b847fa20e2f2f04a6e3c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 3 Feb 2023 12:08:47 +0100 Subject: [PATCH 0409/1271] check script path and gracefully skip if was not found or not set --- openpype/hosts/houdini/api/shelves.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/houdini/api/shelves.py b/openpype/hosts/houdini/api/shelves.py index efbc0f5b04..f6295ddfe7 100644 --- a/openpype/hosts/houdini/api/shelves.py +++ b/openpype/hosts/houdini/api/shelves.py @@ -158,6 +158,11 @@ def get_or_create_tool(tool_definition, shelf): log.warning("Skipped shelf without label") return + script_path = tool_definition["script"] + if not script_path or not os.path.exists(script_path): + log.warning("This path doesn't exist - {}".format(script_path)) + return + existing_tools = shelf.tools() existing_tool = next( (tool for tool in existing_tools if tool.label() == tool_label), From 4c502ffe85af5d66ea2638d839b4487d0dfb2a42 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 3 Feb 2023 12:14:42 +0100 Subject: [PATCH 0410/1271] change how shelves are created --- openpype/hosts/houdini/api/shelves.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/openpype/hosts/houdini/api/shelves.py b/openpype/hosts/houdini/api/shelves.py index f6295ddfe7..ebd668e9e4 100644 --- a/openpype/hosts/houdini/api/shelves.py +++ b/openpype/hosts/houdini/api/shelves.py @@ -1,4 +1,5 @@ import os +import re import logging import platform @@ -168,24 +169,16 @@ def get_or_create_tool(tool_definition, shelf): (tool for tool in existing_tools if tool.label() == tool_label), None ) + + with open(script_path) as stream: + script = stream.read() + + tool_definition["script"] = script + if existing_tool: - tool_definition.pop('name', None) - tool_definition.pop('label', None) + tool_definition.pop("label", None) existing_tool.setData(**tool_definition) return existing_tool - tool_name = tool_label.replace(' ', '_').lower() - - if not os.path.exists(tool_definition['script']): - log.warning( - "This path doesn't exist - {}".format(tool_definition['script']) - ) - return - - with open(tool_definition['script']) as f: - script = f.read() - tool_definition.update({'script': script}) - - new_tool = hou.shelves.newTool(name=tool_name, **tool_definition) - - return new_tool + tool_name = re.sub(r"[^\w\d]+", "_", tool_label).lower() + return hou.shelves.newTool(name=tool_name, **tool_definition) From 1ac6d9eadb892ca24a7ab2e5c556ae23970c4da9 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 16:39:25 +0100 Subject: [PATCH 0411/1271] OP-4653 - added explicit 'use_layer_name' toggl Similar toggle is in AE, to keep same approach. This allow to create only single instance with layer name (previously only if multiple selected). --- .../hosts/photoshop/plugins/create/create_image.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index ca3bbfd27c..036a1127c1 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -73,13 +73,16 @@ class ImageCreator(Creator): groups_to_create.append(group) layer_name = '' - creating_multiple_groups = len(groups_to_create) > 1 + # use artist chosen option OR force layer if more subsets are created + # to differentiate them + use_layer_name = (pre_create_data.get("use_layer_name") or + len(groups_to_create) > 1) for group in groups_to_create: subset_name = subset_name_from_ui # reset to name from creator UI layer_names_in_hierarchy = [] created_group_name = self._clean_highlights(stub, group.name) - if creating_multiple_groups: + if use_layer_name: layer_name = re.sub( "[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS), "", @@ -137,7 +140,10 @@ class ImageCreator(Creator): label="Create only for selected"), BoolDef("create_multiple", default=True, - label="Create separate instance for each selected") + label="Create separate instance for each selected"), + BoolDef("use_layer_name", + default=False, + label="Use layer name in subset") ] return output From f0310e99f1799ec06c38e161ecd620cec6a1cbd3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 16:40:49 +0100 Subject: [PATCH 0412/1271] OP-4653 - updated description, fixed typos --- .../photoshop/plugins/create/create_image.py | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index 036a1127c1..d15d7b2ab5 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -13,7 +13,11 @@ from openpype.hosts.photoshop.api.pipeline import cache_and_get_instances class ImageCreator(Creator): - """Creates image instance for publishing.""" + """Creates image instance for publishing. + + Result of 'image' instance is image of all visible layers, or image(s) of + selected layers. + """ identifier = "image" label = "Image" family = "image" @@ -59,9 +63,10 @@ class ImageCreator(Creator): try: group = stub.group_selected_layers(subset_name_from_ui) except: - raise ValueError("Cannot group locked Bakcground layer!") + raise ValueError("Cannot group locked Background layer!") groups_to_create.append(group) + # create empty group if nothing selected if not groups_to_create and not top_layers_to_wrap: group = stub.create_group(subset_name_from_ui) groups_to_create.append(group) @@ -148,7 +153,34 @@ class ImageCreator(Creator): return output def get_detail_description(self): - return """Creator for Image instances""" + return """Creator for Image instances + + Main publishable item in Photoshop will be of `image` family. Result of + this item (instance) is picture that could be loaded and used + in another DCCs (for example as single layer in composition in + AfterEffects, reference in Maya etc). + + There are couple of options what to publish: + - separate image per selected layer (or group of layers) + - one image for all selected layers + - all visible layers (groups) flattened into single image + + In most cases you would like to keep `Create only for selected` + toggled on and select what you would like to publish. + Toggling this option off will allow you to create instance for all + visible layers without a need to select them explicitly. + + Use 'Create separate instance for each selected' to create separate + images per selected layer (group of layers). + + 'Use layer name in subset' will explicitly add layer name into subset + name. Position of this name is configurable in + `project_settings/global/tools/creator/subset_name_profiles`. + If layer placeholder ({layer}) is not used in `subset_name_profiles` + but layer name should be used (set explicitly in UI or implicitly if + multiple images should be created), it is added in capitalized form + as a suffix to subset name. + """ def _handle_legacy(self, instance_data): """Converts old instances to new format.""" From 53c1c842f573622d3c5704bfc06a2c6d56cdde19 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:02:33 +0100 Subject: [PATCH 0413/1271] OP-4653 - standardize use_composition_name Follow more closely login in PS, eg. if {composition} placeholder not present in subset template and should be used, add capitalized. Clean composition name --- .../aftereffects/plugins/create/create_render.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 8d38288257..4ed9192964 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -11,6 +11,7 @@ from openpype.pipeline import ( ) from openpype.hosts.aftereffects.api.pipeline import cache_and_get_instances from openpype.lib import prepare_template_data +from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS class RenderCreator(Creator): @@ -82,10 +83,19 @@ class RenderCreator(Creator): "if 'useSelection' or create at least " "one composition." ) - + use_composition_name = (pre_create_data.get("use_composition_name") or + len(comps) > 1) for comp in comps: - if pre_create_data.get("use_composition_name"): - composition_name = comp.name + if use_composition_name: + if "{composition}" not in subset_name.lower(): + subset_name += "{Composition}" + + composition_name = re.sub( + "[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS), + "", + comp.name + ) + dynamic_fill = prepare_template_data({"composition": composition_name}) subset_name = subset_name_from_ui.format(**dynamic_fill) From f2930ed156aa1e877fe0478e5539e4dd16aaff62 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:04:06 +0100 Subject: [PATCH 0414/1271] OP-4653 - updated description in AE creator --- .../plugins/create/create_render.py | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 4ed9192964..25dadbecfd 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -15,6 +15,11 @@ from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS class RenderCreator(Creator): + """Creates 'render' instance for publishing. + + Result of 'render' instance is video or sequence of images for particular + composition based of configuration in its RenderQueue. + """ identifier = "render" label = "Render" family = "render" @@ -140,7 +145,32 @@ class RenderCreator(Creator): return output def get_detail_description(self): - return """Creator for Render instances""" + return """Creator for Render instances + + Main publishable item in AfterEffects will be of `render` family. + Result of this item (instance) is picture sequence or video that could + be a final delivery product or loaded and used in another DCCs. + + Select single composition and create instance of 'render' family or + turn off 'Use selection' to create instance for all compositions. + + 'Use composition name in subset' allows to explicitly add composition + name into created subset name. + + Position of composition name could be set in + `project_settings/global/tools/creator/subset_name_profiles` with some + form of '{composition}' placeholder. + + Composition name will be used implicitly if multiple composition should + be handled at same time. + + If {composition} placeholder is not us 'subset_name_profiles' + composition name will be capitalized and set at the end of subset name + if necessary. + + If composition name should be used, it will be cleaned up of characters + that would cause an issue in published file names. + """ def get_dynamic_data(self, variant, task_name, asset_doc, project_name, host_name, instance): From 993145e6f8bf4b829401a89f428d8821108e54a6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:16:55 +0100 Subject: [PATCH 0415/1271] OP-4653 - fix wrong name --- openpype/hosts/aftereffects/plugins/create/create_render.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 25dadbecfd..c95270a3bb 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -92,8 +92,8 @@ class RenderCreator(Creator): len(comps) > 1) for comp in comps: if use_composition_name: - if "{composition}" not in subset_name.lower(): - subset_name += "{Composition}" + if "{composition}" not in subset_name_from_ui.lower(): + subset_name_from_ui += "{Composition}" composition_name = re.sub( "[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS), From 31b137fa1d6257d97e170f475cd6d11fbdbfa8f4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:17:27 +0100 Subject: [PATCH 0416/1271] OP-4653 - Hound --- .../plugins/create/create_render.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index c95270a3bb..a5f8dedace 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -146,28 +146,28 @@ class RenderCreator(Creator): def get_detail_description(self): return """Creator for Render instances - - Main publishable item in AfterEffects will be of `render` family. - Result of this item (instance) is picture sequence or video that could + + Main publishable item in AfterEffects will be of `render` family. + Result of this item (instance) is picture sequence or video that could be a final delivery product or loaded and used in another DCCs. - - Select single composition and create instance of 'render' family or + + Select single composition and create instance of 'render' family or turn off 'Use selection' to create instance for all compositions. - + 'Use composition name in subset' allows to explicitly add composition name into created subset name. - - Position of composition name could be set in + + Position of composition name could be set in `project_settings/global/tools/creator/subset_name_profiles` with some form of '{composition}' placeholder. - + Composition name will be used implicitly if multiple composition should be handled at same time. - - If {composition} placeholder is not us 'subset_name_profiles' + + If {composition} placeholder is not us 'subset_name_profiles' composition name will be capitalized and set at the end of subset name if necessary. - + If composition name should be used, it will be cleaned up of characters that would cause an issue in published file names. """ From 59010eb9a6c3a8abbabdd48dbb6c2a8563d9ac27 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:19:02 +0100 Subject: [PATCH 0417/1271] OP-4653 - refactor - move most important method higher --- .../plugins/create/create_render.py | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index a5f8dedace..10ded8b912 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -34,45 +34,6 @@ class RenderCreator(Creator): ["RenderCreator"] ["defaults"]) - def get_icon(self): - return resources.get_openpype_splash_filepath() - - def collect_instances(self): - for instance_data in cache_and_get_instances(self): - # legacy instances have family=='render' or 'renderLocal', use them - creator_id = (instance_data.get("creator_identifier") or - instance_data.get("family", '').replace("Local", '')) - if creator_id == self.identifier: - instance_data = self._handle_legacy(instance_data) - instance = CreatedInstance.from_existing( - instance_data, self - ) - self._add_instance_to_context(instance) - - def update_instances(self, update_list): - for created_inst, _changes in update_list: - api.get_stub().imprint(created_inst.get("instance_id"), - created_inst.data_to_store()) - subset_change = _changes.get("subset") - if subset_change: - api.get_stub().rename_item(created_inst.data["members"][0], - subset_change[1]) - - def remove_instances(self, instances): - for instance in instances: - self._remove_instance_from_context(instance) - self.host.remove_instance(instance) - - subset = instance.data["subset"] - comp_id = instance.data["members"][0] - comp = api.get_stub().get_item(comp_id) - if comp: - new_comp_name = comp.name.replace(subset, '') - if not new_comp_name: - new_comp_name = "dummyCompName" - api.get_stub().rename_item(comp_id, - new_comp_name) - def create(self, subset_name_from_ui, data, pre_create_data): stub = api.get_stub() # only after After Effects is up if pre_create_data.get("use_selection"): @@ -144,6 +105,45 @@ class RenderCreator(Creator): ] return output + def get_icon(self): + return resources.get_openpype_splash_filepath() + + def collect_instances(self): + for instance_data in cache_and_get_instances(self): + # legacy instances have family=='render' or 'renderLocal', use them + creator_id = (instance_data.get("creator_identifier") or + instance_data.get("family", '').replace("Local", '')) + if creator_id == self.identifier: + instance_data = self._handle_legacy(instance_data) + instance = CreatedInstance.from_existing( + instance_data, self + ) + self._add_instance_to_context(instance) + + def update_instances(self, update_list): + for created_inst, _changes in update_list: + api.get_stub().imprint(created_inst.get("instance_id"), + created_inst.data_to_store()) + subset_change = _changes.get("subset") + if subset_change: + api.get_stub().rename_item(created_inst.data["members"][0], + subset_change[1]) + + def remove_instances(self, instances): + for instance in instances: + self._remove_instance_from_context(instance) + self.host.remove_instance(instance) + + subset = instance.data["subset"] + comp_id = instance.data["members"][0] + comp = api.get_stub().get_item(comp_id) + if comp: + new_comp_name = comp.name.replace(subset, '') + if not new_comp_name: + new_comp_name = "dummyCompName" + api.get_stub().rename_item(comp_id, + new_comp_name) + def get_detail_description(self): return """Creator for Render instances From 0dd1a376b0463622dfe6aaab08057ce670524ee0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 17:19:52 +0100 Subject: [PATCH 0418/1271] OP-4653 - refactor - move most important method higher --- .../photoshop/plugins/create/create_image.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index d15d7b2ab5..8a103ea6c2 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -23,21 +23,6 @@ class ImageCreator(Creator): family = "image" description = "Image creator" - def collect_instances(self): - for instance_data in cache_and_get_instances(self): - # legacy instances have family=='image' - creator_id = (instance_data.get("creator_identifier") or - instance_data.get("family")) - - if creator_id == self.identifier: - instance_data = self._handle_legacy(instance_data) - layer = api.stub().get_layer(instance_data["members"][0]) - instance_data["layer"] = layer - instance = CreatedInstance.from_existing( - instance_data, self - ) - self._add_instance_to_context(instance) - def create(self, subset_name_from_ui, data, pre_create_data): groups_to_create = [] top_layers_to_wrap = [] @@ -120,6 +105,21 @@ class ImageCreator(Creator): stub.rename_layer(group.id, stub.PUBLISH_ICON + created_group_name) + def collect_instances(self): + for instance_data in cache_and_get_instances(self): + # legacy instances have family=='image' + creator_id = (instance_data.get("creator_identifier") or + instance_data.get("family")) + + if creator_id == self.identifier: + instance_data = self._handle_legacy(instance_data) + layer = api.stub().get_layer(instance_data["members"][0]) + instance_data["layer"] = layer + instance = CreatedInstance.from_existing( + instance_data, self + ) + self._add_instance_to_context(instance) + def update_instances(self, update_list): self.log.debug("update_list:: {}".format(update_list)) for created_inst, _changes in update_list: From 3a8c36e0570e5554d0d9ae7afd073fdde43294ff Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 18:28:29 +0100 Subject: [PATCH 0419/1271] OP-4822 - updated settings After discussion profile is too complicated, removed, replaced with simple boolean --- .../defaults/project_settings/deadline.json | 2 +- .../schema_project_deadline.json | 32 +++---------------- 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index 11bb1aee2e..0a4318a659 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -34,7 +34,7 @@ "jobInfo": {}, "pluginInfo": {}, "scene_patches": [], - "disable_strict_check_profiles": [] + "strict_error_checking": true }, "NukeSubmitDeadline": { "enabled": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index d38358773a..03f6489a41 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -197,34 +197,10 @@ } }, { - "type": "list", - "collapsible": true, - "key": "disable_strict_check_profiles", - "label": "Disable Strict Error Check profiles", - "use_label_wrap": true, - "docstring": "Set profile for disabling 'Strict Error Checking'", - "object_type": { - "type": "dict", - "children": [ - { - "key": "task_types", - "label": "Task types", - "type": "task-types-enum" - }, - { - "key": "task_names", - "label": "Task names", - "type": "list", - "object_type": "text" - }, - { - "key": "subsets", - "label": "Subset names", - "type": "list", - "object_type": "text" - } - ] - } + "type": "boolean", + "key": "strict_error_checking", + "label": "Strict Error Checking", + "default": true } ] }, From 214ef5104dfe17619c5f04e466d2bb70a9fbd195 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 18:33:40 +0100 Subject: [PATCH 0420/1271] OP-4822 - changed strict error checking to boolean flag Added boolean flag to render instance to allow artist to change it. --- .../maya/plugins/create/create_render.py | 4 +++ .../plugins/publish/submit_maya_deadline.py | 35 +++---------------- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_render.py b/openpype/hosts/maya/plugins/create/create_render.py index 8375149442..387b7321b9 100644 --- a/openpype/hosts/maya/plugins/create/create_render.py +++ b/openpype/hosts/maya/plugins/create/create_render.py @@ -54,6 +54,7 @@ class CreateRender(plugin.Creator): tileRendering (bool): Instance is set to tile rendering mode. We won't submit actual render, but we'll make publish job to wait for Tile Assembly job done and then publish. + strict_error_checking (bool): Enable/disable error checking on DL See Also: https://pype.club/docs/artist_hosts_maya#creating-basic-render-setup @@ -271,6 +272,9 @@ class CreateRender(plugin.Creator): secondary_pool = pool_setting["secondary_pool"] self.data["secondaryPool"] = self._set_default_pool(pool_names, secondary_pool) + strict_error_checking = maya_submit_dl.get("strict_error_checking", + True) + self.data["strict_error_checking"] = strict_error_checking if muster_enabled: self.log.info(">>> Loading Muster credentials ...") diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 3d1b9bcbf6..9fef816e1c 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -65,7 +65,7 @@ class MayaPluginInfo(object): # Include all lights flag RenderSetupIncludeLights = attr.ib( default="1", validator=_validate_deadline_bool_value) - StrictErrorChecking = attr.ib(default="1") + StrictErrorChecking = attr.ib(default=True) @attr.s @@ -222,7 +222,8 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): "renderSetupIncludeLights", default_rs_include_lights) if rs_include_lights not in {"1", "0", True, False}: rs_include_lights = default_rs_include_lights - strict_checking = self._get_strict_checking(instance) + strict_error_checking = instance.data.get("strict_error_checking", + True) plugin_info = MayaPluginInfo( SceneFile=self.scene_path, Version=cmds.about(version=True), @@ -231,7 +232,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): RenderSetupIncludeLights=rs_include_lights, # noqa ProjectPath=context.data["workspaceDir"], UsingRenderLayers=True, - StrictErrorChecking=strict_checking + StrictErrorChecking=strict_error_checking ) plugin_payload = attr.asdict(plugin_info) @@ -753,34 +754,6 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): for file in exp: yield file - def _get_strict_checking(self, instance): - """Find profile to disable Strict Error Checking - - Args: - instance (dict) - Returns: - (int) - 1 if Strict (DL default), 0 for disabled Strict - """ - strict_checking = 1 - if not self.disable_strict_check_profiles: - return strict_checking - - task_data = instance.data["anatomyData"].get("task", {}) - key_values = { - "task_names": task_data.get("name"), - "task_types": task_data.get("type"), - "subsets": instance.data["subset"] - } - profile = filter_profiles(self.disable_strict_check_profiles, - key_values, - logger=self.log) - - if profile: - self.log.debug("Disabling Strict Error Checking") - strict_checking = 0 - - return strict_checking - def _format_tiles( filename, index, tiles_x, tiles_y, From 463f770f2d19dcb1bfb184412d1664a8134b7502 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Feb 2023 18:40:53 +0100 Subject: [PATCH 0421/1271] OP-4822 - Hound --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 9fef816e1c..e7c1899513 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -35,7 +35,6 @@ from openpype.pipeline import legacy_io from openpype.hosts.maya.api.lib_rendersettings import RenderSettings from openpype.hosts.maya.api.lib import get_attr_in_layer -from openpype.lib.profiles_filtering import filter_profiles from openpype_modules.deadline import abstract_submit_deadline from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo from openpype.tests.lib import is_in_tests From 31173abd4fc75c6caca0be712f50faf06a24ed56 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 6 Feb 2023 13:39:04 +0100 Subject: [PATCH 0422/1271] OP-4702 - fix dirmap remote_site_dir started to return platform dict --- openpype/host/dirmap.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openpype/host/dirmap.py b/openpype/host/dirmap.py index 88d68f27bf..347c5fbf85 100644 --- a/openpype/host/dirmap.py +++ b/openpype/host/dirmap.py @@ -8,6 +8,7 @@ exists is used. import os from abc import ABCMeta, abstractmethod +import platform import six @@ -187,11 +188,19 @@ class HostDirmap(object): self.log.debug("local overrides {}".format(active_overrides)) self.log.debug("remote overrides {}".format(remote_overrides)) + current_platform = platform.system().lower() for root_name, active_site_dir in active_overrides.items(): remote_site_dir = ( remote_overrides.get(root_name) or sync_settings["sites"][remote_site]["root"][root_name] ) + + if isinstance(remote_site_dir, dict): + remote_site_dir = remote_site_dir.get(current_platform) + + if not remote_site_dir: + continue + if os.path.isdir(active_site_dir): if "destination-path" not in mapping: mapping["destination-path"] = [] From 9b6bd7954d99640db5391cce39e87d02dc0e557a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 6 Feb 2023 15:44:34 +0000 Subject: [PATCH 0423/1271] Working AssStandinLoader --- openpype/hosts/maya/plugins/load/load_ass.py | 119 +++++++++---------- 1 file changed, 55 insertions(+), 64 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_ass.py b/openpype/hosts/maya/plugins/load/load_ass.py index 5db6fc3dfa..6317c0a7ce 100644 --- a/openpype/hosts/maya/plugins/load/load_ass.py +++ b/openpype/hosts/maya/plugins/load/load_ass.py @@ -1,6 +1,9 @@ import os import clique +import maya.cmds as cmds +import mtoa.ui.arnoldmenu + from openpype.settings import get_project_settings from openpype.pipeline import ( load, @@ -15,6 +18,15 @@ from openpype.hosts.maya.api.lib import ( from openpype.hosts.maya.api.pipeline import containerise +def is_sequence(files): + sequence = False + collections, remainder = clique.assemble(files) + if collections: + sequence = True + + return sequence + + class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): """Load Arnold Proxy as reference""" @@ -27,16 +39,12 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): color = "orange" def process_reference(self, context, name, namespace, options): - - import maya.cmds as cmds - import pymel.core as pm - version = context['version'] version_data = version.get("data", {}) self.log.info("version_data: {}\n".format(version_data)) - frameStart = version_data.get("frameStart", None) + frame_start = version_data.get("frame_start", None) try: family = context["representation"]["context"]["family"] @@ -49,7 +57,7 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): path = self.fname proxyPath_base = os.path.splitext(path)[0] - if frameStart is not None: + if frame_start is not None: proxyPath_base = os.path.splitext(proxyPath_base)[0] publish_folder = os.path.split(path)[0] @@ -63,11 +71,13 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): path = os.path.join(publish_folder, filename) - proxyPath = proxyPath_base + ".ma" + proxyPath = proxyPath_base + ".ass" project_name = context["project"]["name"] - file_url = self.prepare_root_value(proxyPath, - project_name) + file_url = self.prepare_root_value( + proxyPath, project_name + ) + self.log.info(file_url) nodes = cmds.file(file_url, namespace=namespace, @@ -80,7 +90,7 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): translate=True, scale=True) # Set attributes - proxyShape = pm.ls(nodes, type="mesh")[0] + proxyShape = cmds.ls(nodes, type="mesh")[0] proxyShape.aiTranslator.set('procedural') proxyShape.dso.set(path) @@ -92,10 +102,11 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): c = colors.get(family) if c is not None: cmds.setAttr(groupName + ".useOutlinerColor", 1) - cmds.setAttr(groupName + ".outlinerColor", - (float(c[0])/255), - (float(c[1])/255), - (float(c[2])/255) + cmds.setAttr( + groupName + ".outlinerColor", + (float(c[0]) / 255), + (float(c[1]) / 255), + (float(c[2]) / 255) ) self[:] = nodes @@ -106,18 +117,11 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): self.update(container, representation) def update(self, container, representation): - from maya import cmds - import pymel.core as pm - node = container["objectName"] representation["context"].pop("frame", None) path = get_representation_path(representation) - print(path) - # path = self.fname - print(self.fname) proxyPath = os.path.splitext(path)[0] + ".ma" - print(proxyPath) # Get reference node from container members members = cmds.sets(node, query=True, nodesOnly=True) @@ -186,18 +190,11 @@ class AssStandinLoader(load.LoaderPlugin): color = "orange" def load(self, context, name, namespace, options): - - import maya.cmds as cmds - import mtoa.ui.arnoldmenu - import pymel.core as pm - version = context['version'] version_data = version.get("data", {}) self.log.info("version_data: {}\n".format(version_data)) - frameStart = version_data.get("frameStart", None) - asset = context['asset']['name'] namespace = namespace or unique_namespace( asset + "_", @@ -205,36 +202,34 @@ class AssStandinLoader(load.LoaderPlugin): suffix="_", ) - # cmds.loadPlugin("gpuCache", quiet=True) - # Root group label = "{}:{}".format(namespace, name) - root = pm.group(name=label, empty=True) + root = cmds.group(name=label, empty=True) settings = get_project_settings(os.environ['AVALON_PROJECT']) colors = settings['maya']['load']['colors'] - c = colors.get('ass') - if c is not None: - cmds.setAttr(root + ".useOutlinerColor", 1) - cmds.setAttr(root + ".outlinerColor", - c[0], c[1], c[2]) + color = colors.get('ass') + if color is not None: + cmds.setAttr(root + ".useOutlinerColor", True) + cmds.setAttr( + root + ".outlinerColor", color[0], color[1], color[2] + ) # Create transform with shape transform_name = label + "_ASS" - # transform = pm.createNode("transform", name=transform_name, - # parent=root) - standinShape = pm.PyNode(mtoa.ui.arnoldmenu.createStandIn()) - standin = standinShape.getParent() - standin.rename(transform_name) + standinShape = mtoa.ui.arnoldmenu.createStandIn() + standin = cmds.listRelatives(standinShape, parent=True)[0] + standin = cmds.rename(standin, transform_name) + standinShape = cmds.listRelatives(standin, shapes=True)[0] - pm.parent(standin, root) + cmds.parent(standin, root) # Set the standin filepath - standinShape.dso.set(self.fname) - if frameStart is not None: - standinShape.useFrameExtension.set(1) + cmds.setAttr(standinShape + ".dso", self.fname, type="string") + sequence = is_sequence(os.listdir(os.path.dirname(self.fname))) + cmds.setAttr(standinShape + ".useFrameExtension", sequence) nodes = [root, standin] self[:] = nodes @@ -247,31 +242,27 @@ class AssStandinLoader(load.LoaderPlugin): loader=self.__class__.__name__) def update(self, container, representation): - - import pymel.core as pm - - path = get_representation_path(representation) - - files_in_path = os.listdir(os.path.split(path)[0]) - sequence = 0 - collections, remainder = clique.assemble(files_in_path) - if collections: - sequence = 1 - # Update the standin standins = list() - members = pm.sets(container['objectName'], query=True) + members = cmds.sets(container['objectName'], query=True) for member in members: - shape = member.getShape() - if (shape and shape.type() == "aiStandIn"): - standins.append(shape) + shapes = cmds.listRelatives(member, shapes=True) + if not shapes: + continue + if cmds.nodeType(shapes[0]) == "aiStandIn": + standins.append(shapes[0]) + path = get_representation_path(representation) + sequence = is_sequence(os.listdir(os.path.dirname(path))) for standin in standins: - standin.dso.set(path) - standin.useFrameExtension.set(sequence) + cmds.setAttr(standin + ".dso", path, type="string") + cmds.setAttr(standin + ".useFrameExtension", sequence) - container = pm.PyNode(container["objectName"]) - container.representation.set(str(representation["_id"])) + cmds.setAttr( + container["objectName"] + ".representation", + str(representation["_id"]), + type="string" + ) def switch(self, container, representation): self.update(container, representation) From 23987420a375c199b095b295282728e913bde75a Mon Sep 17 00:00:00 2001 From: Seyedmohammadreza Hashemizadeh Date: Mon, 6 Feb 2023 19:03:06 +0100 Subject: [PATCH 0424/1271] update asset info of imported sets --- .../maya/api/workfile_template_builder.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index ef043ed0f4..56a53c070c 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -2,7 +2,7 @@ import json from maya import cmds -from openpype.pipeline import registered_host +from openpype.pipeline import registered_host, legacy_io from openpype.pipeline.workfile.workfile_template_builder import ( TemplateAlreadyImported, AbstractTemplateBuilder, @@ -41,10 +41,26 @@ class MayaTemplateBuilder(AbstractTemplateBuilder): )) cmds.sets(name=PLACEHOLDER_SET, empty=True) - cmds.file(path, i=True, returnNewNodes=True) + new_nodes = cmds.file(path, i=True, returnNewNodes=True) cmds.setAttr(PLACEHOLDER_SET + ".hiddenInOutliner", True) + imported_sets = cmds.ls(new_nodes, set=True) + if not imported_sets: + return True + + # update imported sets information + for node in imported_sets: + if not cmds.attributeQuery("id", node=node, exists=True): + continue + if cmds.getAttr("{}.id".format(node)) != "pyblish.avalon.instance": + continue + if not cmds.attributeQuery("asset", node=node, exists=True): + continue + asset = legacy_io.Session["AVALON_ASSET"] + + cmds.setAttr("{}.asset".format(node), asset, type="string") + return True From 3e25f2cddac284ed4583f751687525f1e2471e4a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 6 Feb 2023 18:08:25 +0000 Subject: [PATCH 0425/1271] Working AssProxyLoader --- openpype/hosts/maya/plugins/load/load_ass.py | 97 +++++++++----------- 1 file changed, 44 insertions(+), 53 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_ass.py b/openpype/hosts/maya/plugins/load/load_ass.py index 6317c0a7ce..ada65998a5 100644 --- a/openpype/hosts/maya/plugins/load/load_ass.py +++ b/openpype/hosts/maya/plugins/load/load_ass.py @@ -27,6 +27,18 @@ def is_sequence(files): return sequence +def set_color(node, context): + project_name = context["project"]["name"] + settings = get_project_settings(project_name) + colors = settings['maya']['load']['colors'] + color = colors.get('ass') + if color is not None: + cmds.setAttr(node + ".useOutlinerColor", True) + cmds.setAttr( + node + ".outlinerColor", color[0], color[1], color[2] + ) + + class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): """Load Arnold Proxy as reference""" @@ -46,19 +58,14 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): frame_start = version_data.get("frame_start", None) - try: - family = context["representation"]["context"]["family"] - except ValueError: - family = "ass" - with maintained_selection(): groupName = "{}:{}".format(namespace, name) path = self.fname - proxyPath_base = os.path.splitext(path)[0] + proxy_path_base = os.path.splitext(path)[0] if frame_start is not None: - proxyPath_base = os.path.splitext(proxyPath_base)[0] + proxy_path_base = os.path.splitext(proxy_path_base)[0] publish_folder = os.path.split(path)[0] files_in_folder = os.listdir(publish_folder) @@ -71,43 +78,33 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): path = os.path.join(publish_folder, filename) - proxyPath = proxyPath_base + ".ass" + proxy_path = proxy_path_base + ".ma" + msg = proxy_path + " does not exist." + assert os.path.exists(proxy_path), msg - project_name = context["project"]["name"] - file_url = self.prepare_root_value( - proxyPath, project_name + nodes = cmds.file( + proxy_path, + namespace=namespace, + reference=True, + returnNewNodes=True, + groupReference=True, + groupName=groupName ) - self.log.info(file_url) - nodes = cmds.file(file_url, - namespace=namespace, - reference=True, - returnNewNodes=True, - groupReference=True, - groupName=groupName) - - cmds.makeIdentity(groupName, apply=False, rotate=True, - translate=True, scale=True) + cmds.makeIdentity( + groupName, apply=False, rotate=True, translate=True, scale=True + ) # Set attributes - proxyShape = cmds.ls(nodes, type="mesh")[0] + proxy_shape = cmds.ls(nodes, type="mesh")[0] - proxyShape.aiTranslator.set('procedural') - proxyShape.dso.set(path) - proxyShape.aiOverrideShaders.set(0) + cmds.setAttr( + proxy_shape + ".aiTranslator", "procedural", type="string" + ) + cmds.setAttr(proxy_shape + ".dso", self.fname, type="string") + cmds.setAttr(proxy_shape + ".aiOverrideShaders", 0) - settings = get_project_settings(project_name) - colors = settings['maya']['load']['colors'] - - c = colors.get(family) - if c is not None: - cmds.setAttr(groupName + ".useOutlinerColor", 1) - cmds.setAttr( - groupName + ".outlinerColor", - (float(c[0]) / 255), - (float(c[1]) / 255), - (float(c[2]) / 255) - ) + set_color(groupName, context) self[:] = nodes @@ -121,16 +118,16 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): representation["context"].pop("frame", None) path = get_representation_path(representation) - proxyPath = os.path.splitext(path)[0] + ".ma" + proxy_path = os.path.splitext(path)[0] + ".ma" # Get reference node from container members members = cmds.sets(node, query=True, nodesOnly=True) reference_node = get_reference_node(members) - assert os.path.exists(proxyPath), "%s does not exist." % proxyPath + assert os.path.exists(proxy_path), "%s does not exist." % proxy_path try: - file_url = self.prepare_root_value(proxyPath, + file_url = self.prepare_root_value(proxy_path, representation["context"] ["project"] ["name"]) @@ -140,11 +137,13 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): returnNewNodes=True) # Set attributes - proxyShape = pm.ls(content, type="mesh")[0] + proxy_shape = cmds.ls(content, type="mesh")[0] - proxyShape.aiTranslator.set('procedural') - proxyShape.dso.set(path) - proxyShape.aiOverrideShaders.set(0) + cmds.setAttr( + proxy_shape + ".aiTranslator", "procedural", type="string" + ) + cmds.setAttr(proxy_shape + ".dso", self.fname, type="string") + cmds.setAttr(proxy_shape + ".aiOverrideShaders", 0) except RuntimeError as exc: # When changing a reference to a file that has load errors the @@ -206,15 +205,7 @@ class AssStandinLoader(load.LoaderPlugin): label = "{}:{}".format(namespace, name) root = cmds.group(name=label, empty=True) - settings = get_project_settings(os.environ['AVALON_PROJECT']) - colors = settings['maya']['load']['colors'] - - color = colors.get('ass') - if color is not None: - cmds.setAttr(root + ".useOutlinerColor", True) - cmds.setAttr( - root + ".outlinerColor", color[0], color[1], color[2] - ) + set_color(root, context) # Create transform with shape transform_name = label + "_ASS" From ecef4cbb4691ff7ea8320fda55e3ac4a8d70c4f2 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 Feb 2023 08:57:10 +0000 Subject: [PATCH 0426/1271] More information about issues with publishing. --- openpype/hosts/maya/plugins/load/load_ass.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_ass.py b/openpype/hosts/maya/plugins/load/load_ass.py index ada65998a5..e4e0b0da84 100644 --- a/openpype/hosts/maya/plugins/load/load_ass.py +++ b/openpype/hosts/maya/plugins/load/load_ass.py @@ -79,7 +79,10 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): path = os.path.join(publish_folder, filename) proxy_path = proxy_path_base + ".ma" - msg = proxy_path + " does not exist." + msg = ( + proxy_path + " does not exist.\nThere are most likely no " + + "proxy shapes in the \"proxy_SET\" when publishing." + ) assert os.path.exists(proxy_path), msg nodes = cmds.file( From 662d68daec5c0b786489e7035bd75be77a2cdd48 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 Feb 2023 10:06:56 +0000 Subject: [PATCH 0427/1271] Support for multiple proxy meshes. --- openpype/hosts/maya/plugins/load/load_ass.py | 39 +++++++++++++++++-- .../hosts/maya/plugins/publish/collect_ass.py | 4 -- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_ass.py b/openpype/hosts/maya/plugins/load/load_ass.py index e4e0b0da84..58abfa964e 100644 --- a/openpype/hosts/maya/plugins/load/load_ass.py +++ b/openpype/hosts/maya/plugins/load/load_ass.py @@ -60,7 +60,7 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): with maintained_selection(): - groupName = "{}:{}".format(namespace, name) + group_name = "{}:{}".format(namespace, name) path = self.fname proxy_path_base = os.path.splitext(path)[0] @@ -91,13 +91,36 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): reference=True, returnNewNodes=True, groupReference=True, - groupName=groupName + groupName=group_name ) + # Reset group to world zero. + transform_data = {} + for node in nodes: + if cmds.nodeType(node) != "transform": + continue + + transform_data[node] = {} + attrs = ["translate", "rotate", "scale"] + parameters = ["X", "Y", "Z"] + for attr in attrs: + for parameter in parameters: + transform_data[node][attr + parameter] = cmds.getAttr( + "{}.{}{}".format(node, attr, parameter) + ) + cmds.makeIdentity( - groupName, apply=False, rotate=True, translate=True, scale=True + group_name, + apply=False, + rotate=True, + translate=True, + scale=True ) + for node, data in transform_data.items(): + for attr, value in data.items(): + cmds.setAttr("{}.{}".format(node, attr), value) + # Set attributes proxy_shape = cmds.ls(nodes, type="mesh")[0] @@ -107,7 +130,15 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): cmds.setAttr(proxy_shape + ".dso", self.fname, type="string") cmds.setAttr(proxy_shape + ".aiOverrideShaders", 0) - set_color(groupName, context) + # Hides all other meshes at render time. + remaining_meshes = cmds.ls(nodes, type="mesh") + remaining_meshes.remove(proxy_shape) + for node in remaining_meshes: + cmds.setAttr( + node + ".aiTranslator", "procedural", type="string" + ) + + set_color(group_name, context) self[:] = nodes diff --git a/openpype/hosts/maya/plugins/publish/collect_ass.py b/openpype/hosts/maya/plugins/publish/collect_ass.py index b5e05d6665..7b5d1a00c7 100644 --- a/openpype/hosts/maya/plugins/publish/collect_ass.py +++ b/openpype/hosts/maya/plugins/publish/collect_ass.py @@ -1,5 +1,4 @@ from maya import cmds -from openpype.pipeline.publish import KnownPublishError import pyblish.api @@ -25,9 +24,6 @@ class CollectAssData(pyblish.api.InstancePlugin): instance.data['setMembers'] = members self.log.debug('content members: {}'.format(members)) elif objset.startswith("proxy_SET"): - if len(members) != 1: - msg = "You have multiple proxy meshes, please only use one" - raise KnownPublishError(msg) instance.data['proxy'] = members self.log.debug('proxy members: {}'.format(members)) From 77a6139c777d25b7bfd05eff55af94184de716ab Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 Feb 2023 10:13:22 +0000 Subject: [PATCH 0428/1271] Fix updating proxy --- openpype/hosts/maya/plugins/load/load_ass.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_ass.py b/openpype/hosts/maya/plugins/load/load_ass.py index 58abfa964e..59cfae7cdb 100644 --- a/openpype/hosts/maya/plugins/load/load_ass.py +++ b/openpype/hosts/maya/plugins/load/load_ass.py @@ -148,14 +148,14 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): self.update(container, representation) def update(self, container, representation): - node = container["objectName"] + container_node = container["objectName"] representation["context"].pop("frame", None) path = get_representation_path(representation) proxy_path = os.path.splitext(path)[0] + ".ma" # Get reference node from container members - members = cmds.sets(node, query=True, nodesOnly=True) + members = cmds.sets(container_node, query=True, nodesOnly=True) reference_node = get_reference_node(members) assert os.path.exists(proxy_path), "%s does not exist." % proxy_path @@ -195,18 +195,26 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): self.log.warning("Ignoring file read error:\n%s", exc) + # Hides all other meshes at render time. + remaining_meshes = cmds.ls(content, type="mesh") + remaining_meshes.remove(proxy_shape) + for node in remaining_meshes: + cmds.setAttr( + node + ".aiTranslator", "procedural", type="string" + ) + # Add new nodes of the reference to the container - cmds.sets(content, forceElement=node) + cmds.sets(content, forceElement=container_node) # Remove any placeHolderList attribute entries from the set that # are remaining from nodes being removed from the referenced file. - members = cmds.sets(node, query=True) + members = cmds.sets(container_node, query=True) invalid = [x for x in members if ".placeHolderList" in x] if invalid: - cmds.sets(invalid, remove=node) + cmds.sets(invalid, remove=container_node) # Update metadata - cmds.setAttr("{}.representation".format(node), + cmds.setAttr("{}.representation".format(container_node), str(representation["_id"]), type="string") From 9733b07f6dd383a63c00d51312bf700a475aa5d8 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 Feb 2023 15:21:58 +0000 Subject: [PATCH 0429/1271] Remove AssProxyLoader --- openpype/hosts/maya/plugins/load/load_ass.py | 195 +------------------ 1 file changed, 5 insertions(+), 190 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_ass.py b/openpype/hosts/maya/plugins/load/load_ass.py index 59cfae7cdb..50b72d87e8 100644 --- a/openpype/hosts/maya/plugins/load/load_ass.py +++ b/openpype/hosts/maya/plugins/load/load_ass.py @@ -9,12 +9,7 @@ from openpype.pipeline import ( load, get_representation_path ) -import openpype.hosts.maya.api.plugin -from openpype.hosts.maya.api.plugin import get_reference_node -from openpype.hosts.maya.api.lib import ( - maintained_selection, - unique_namespace -) +from openpype.hosts.maya.api.lib import unique_namespace from openpype.hosts.maya.api.pipeline import containerise @@ -39,193 +34,13 @@ def set_color(node, context): ) -class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): - """Load Arnold Proxy as reference""" +class ArnoldStandinLoader(load.LoaderPlugin): + """Load file as Arnold standin""" families = ["ass"] representations = ["ass"] - label = "Reference .ASS standin with Proxy" - order = -10 - icon = "code-fork" - color = "orange" - - def process_reference(self, context, name, namespace, options): - version = context['version'] - version_data = version.get("data", {}) - - self.log.info("version_data: {}\n".format(version_data)) - - frame_start = version_data.get("frame_start", None) - - with maintained_selection(): - - group_name = "{}:{}".format(namespace, name) - path = self.fname - proxy_path_base = os.path.splitext(path)[0] - - if frame_start is not None: - proxy_path_base = os.path.splitext(proxy_path_base)[0] - - publish_folder = os.path.split(path)[0] - files_in_folder = os.listdir(publish_folder) - collections, remainder = clique.assemble(files_in_folder) - - if collections: - hashes = collections[0].padding * '#' - coll = collections[0].format('{head}[index]{tail}') - filename = coll.replace('[index]', hashes) - - path = os.path.join(publish_folder, filename) - - proxy_path = proxy_path_base + ".ma" - msg = ( - proxy_path + " does not exist.\nThere are most likely no " + - "proxy shapes in the \"proxy_SET\" when publishing." - ) - assert os.path.exists(proxy_path), msg - - nodes = cmds.file( - proxy_path, - namespace=namespace, - reference=True, - returnNewNodes=True, - groupReference=True, - groupName=group_name - ) - - # Reset group to world zero. - transform_data = {} - for node in nodes: - if cmds.nodeType(node) != "transform": - continue - - transform_data[node] = {} - attrs = ["translate", "rotate", "scale"] - parameters = ["X", "Y", "Z"] - for attr in attrs: - for parameter in parameters: - transform_data[node][attr + parameter] = cmds.getAttr( - "{}.{}{}".format(node, attr, parameter) - ) - - cmds.makeIdentity( - group_name, - apply=False, - rotate=True, - translate=True, - scale=True - ) - - for node, data in transform_data.items(): - for attr, value in data.items(): - cmds.setAttr("{}.{}".format(node, attr), value) - - # Set attributes - proxy_shape = cmds.ls(nodes, type="mesh")[0] - - cmds.setAttr( - proxy_shape + ".aiTranslator", "procedural", type="string" - ) - cmds.setAttr(proxy_shape + ".dso", self.fname, type="string") - cmds.setAttr(proxy_shape + ".aiOverrideShaders", 0) - - # Hides all other meshes at render time. - remaining_meshes = cmds.ls(nodes, type="mesh") - remaining_meshes.remove(proxy_shape) - for node in remaining_meshes: - cmds.setAttr( - node + ".aiTranslator", "procedural", type="string" - ) - - set_color(group_name, context) - - self[:] = nodes - - return nodes - - def switch(self, container, representation): - self.update(container, representation) - - def update(self, container, representation): - container_node = container["objectName"] - - representation["context"].pop("frame", None) - path = get_representation_path(representation) - proxy_path = os.path.splitext(path)[0] + ".ma" - - # Get reference node from container members - members = cmds.sets(container_node, query=True, nodesOnly=True) - reference_node = get_reference_node(members) - - assert os.path.exists(proxy_path), "%s does not exist." % proxy_path - - try: - file_url = self.prepare_root_value(proxy_path, - representation["context"] - ["project"] - ["name"]) - content = cmds.file(file_url, - loadReference=reference_node, - type="mayaAscii", - returnNewNodes=True) - - # Set attributes - proxy_shape = cmds.ls(content, type="mesh")[0] - - cmds.setAttr( - proxy_shape + ".aiTranslator", "procedural", type="string" - ) - cmds.setAttr(proxy_shape + ".dso", self.fname, type="string") - cmds.setAttr(proxy_shape + ".aiOverrideShaders", 0) - - except RuntimeError as exc: - # When changing a reference to a file that has load errors the - # command will raise an error even if the file is still loaded - # correctly (e.g. when raising errors on Arnold attributes) - # When the file is loaded and has content, we consider it's fine. - if not cmds.referenceQuery(reference_node, isLoaded=True): - raise - - content = cmds.referenceQuery(reference_node, - nodes=True, - dagPath=True) - if not content: - raise - - self.log.warning("Ignoring file read error:\n%s", exc) - - # Hides all other meshes at render time. - remaining_meshes = cmds.ls(content, type="mesh") - remaining_meshes.remove(proxy_shape) - for node in remaining_meshes: - cmds.setAttr( - node + ".aiTranslator", "procedural", type="string" - ) - - # Add new nodes of the reference to the container - cmds.sets(content, forceElement=container_node) - - # Remove any placeHolderList attribute entries from the set that - # are remaining from nodes being removed from the referenced file. - members = cmds.sets(container_node, query=True) - invalid = [x for x in members if ".placeHolderList" in x] - if invalid: - cmds.sets(invalid, remove=container_node) - - # Update metadata - cmds.setAttr("{}.representation".format(container_node), - str(representation["_id"]), - type="string") - - -class AssStandinLoader(load.LoaderPlugin): - """Load .ASS file as standin""" - - families = ["ass"] - representations = ["ass"] - - label = "Load .ASS file as standin" + label = "Load file as Arnold standin" order = -5 icon = "code-fork" color = "orange" @@ -250,7 +65,7 @@ class AssStandinLoader(load.LoaderPlugin): set_color(root, context) # Create transform with shape - transform_name = label + "_ASS" + transform_name = label + "_standin" standinShape = mtoa.ui.arnoldmenu.createStandIn() standin = cmds.listRelatives(standinShape, parent=True)[0] From 01d763fe991f1ecdc83b1ce8b6ee002beead7dea Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 Feb 2023 16:29:02 +0000 Subject: [PATCH 0430/1271] Extract Ass proxy. --- .../hosts/maya/plugins/publish/extract_ass.py | 87 ++++++++++++++----- .../maya/plugins/publish/extract_assproxy.py | 81 ----------------- 2 files changed, 63 insertions(+), 105 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/publish/extract_assproxy.py diff --git a/openpype/hosts/maya/plugins/publish/extract_ass.py b/openpype/hosts/maya/plugins/publish/extract_ass.py index 049f256a7a..4cff9d0183 100644 --- a/openpype/hosts/maya/plugins/publish/extract_ass.py +++ b/openpype/hosts/maya/plugins/publish/extract_ass.py @@ -1,16 +1,18 @@ import os +import copy from maya import cmds import arnold from openpype.pipeline import publish from openpype.hosts.maya.api.lib import maintained_selection, attribute_values +from openpype.lib import StringTemplate -class ExtractAssStandin(publish.Extractor): - """Extract the content of the instance to a ass file""" +class ExtractArnoldSceneSource(publish.Extractor): + """Extract the content of the instance to an Arnold Scene Source file.""" - label = "Arnold Scene Source (.ass)" + label = "Arnold Scene Source" hosts = ["maya"] families = ["ass"] asciiAss = False @@ -18,7 +20,6 @@ class ExtractAssStandin(publish.Extractor): def process(self, instance): staging_dir = self.staging_dir(instance) filename = "{}.ass".format(instance.name) - filenames = [] file_path = os.path.join(staging_dir, filename) # Mask @@ -42,7 +43,7 @@ class ExtractAssStandin(publish.Extractor): mask = mask ^ node_types[key] # Motion blur - values = { + attribute_data = { "defaultArnoldRenderOptions.motion_blur_enable": instance.data.get( "motionBlur", True ), @@ -70,13 +71,65 @@ class ExtractAssStandin(publish.Extractor): "mask": mask } - self.log.info("Writing: '%s'" % file_path) - with attribute_values(values): + filenames = self._extract( + instance.data["setMembers"], attribute_data, kwargs + ) + + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + "name": "ass", + "ext": "ass", + "files": filenames if len(filenames) > 1 else filenames[0], + "stagingDir": staging_dir, + "frameStart": kwargs["startFrame"] + } + + instance.data["representations"].append(representation) + + self.log.info( + "Extracted instance {} to: {}".format(instance.name, staging_dir) + ) + + # Extract proxy. + kwargs["filename"] = file_path.replace(".ass", "_proxy.ass") + filenames = self._extract( + instance.data["proxy"], attribute_data, kwargs + ) + + template_data = copy.deepcopy(instance.data["anatomyData"]) + template_data.update({"ext": "ass"}) + templates = instance.context.data["anatomy"].templates["publish"] + published_filename_without_extension = StringTemplate( + templates["file"] + ).format(template_data).replace(".ass", "_proxy") + transfers = [] + for filename in filenames: + source = os.path.join(staging_dir, filename) + destination = os.path.join( + instance.data["resourcesDir"], + filename.replace( + filename.split(".")[0], + published_filename_without_extension + ) + ) + transfers.append((source, destination)) + + for source, destination in transfers: + self.log.debug("Transfer: {} > {}".format(source, destination)) + + instance.data["transfers"] = transfers + + def _extract(self, nodes, attribute_data, kwargs): + self.log.info("Writing: " + kwargs["filename"]) + filenames = [] + with attribute_values(attribute_data): with maintained_selection(): self.log.info( - "Writing: {}".format(instance.data["setMembers"]) + "Writing: {}".format(nodes) ) - cmds.select(instance.data["setMembers"], noExpand=True) + cmds.select(nodes, noExpand=True) self.log.info( "Extracting ass sequence with: {}".format(kwargs) @@ -89,18 +142,4 @@ class ExtractAssStandin(publish.Extractor): self.log.info("Exported: {}".format(filenames)) - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - 'name': 'ass', - 'ext': 'ass', - 'files': filenames if len(filenames) > 1 else filenames[0], - "stagingDir": staging_dir, - 'frameStart': kwargs["startFrame"] - } - - instance.data["representations"].append(representation) - - self.log.info("Extracted instance '%s' to: %s" - % (instance.name, staging_dir)) + return filenames diff --git a/openpype/hosts/maya/plugins/publish/extract_assproxy.py b/openpype/hosts/maya/plugins/publish/extract_assproxy.py deleted file mode 100644 index 4937a28a9e..0000000000 --- a/openpype/hosts/maya/plugins/publish/extract_assproxy.py +++ /dev/null @@ -1,81 +0,0 @@ -import os -import contextlib - -from maya import cmds - -from openpype.pipeline import publish -from openpype.hosts.maya.api.lib import maintained_selection - - -class ExtractAssProxy(publish.Extractor): - """Extract proxy model as Maya Ascii to use as arnold standin - - - """ - - order = publish.Extractor.order + 0.2 - label = "Ass Proxy (Maya ASCII)" - hosts = ["maya"] - families = ["ass"] - - def process(self, instance): - - @contextlib.contextmanager - def unparent(root): - """Temporarily unparent `root`""" - parent = cmds.listRelatives(root, parent=True) - if parent: - cmds.parent(root, world=True) - yield - self.log.info("{} - {}".format(root, parent)) - cmds.parent(root, parent) - else: - yield - - # Define extract output file path - stagingdir = self.staging_dir(instance) - filename = "{0}.ma".format(instance.name) - path = os.path.join(stagingdir, filename) - - # Perform extraction - self.log.info("Performing extraction..") - - # Get only the shape contents we need in such a way that we avoid - # taking along intermediateObjects - proxy = instance.data.get('proxy', None) - - if not proxy: - self.log.info("no proxy mesh") - return - - members = cmds.ls(proxy, - dag=True, - transforms=True, - noIntermediate=True) - self.log.info(members) - - with maintained_selection(): - with unparent(members[0]): - cmds.select(members, noExpand=True) - cmds.file(path, - force=True, - typ="mayaAscii", - exportSelected=True, - preserveReferences=False, - channels=False, - constraints=False, - expressions=False, - constructionHistory=False) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - 'name': 'ma', - 'ext': 'ma', - 'files': filename, - "stagingDir": stagingdir - } - instance.data["representations"].append(representation) - - self.log.info("Extracted instance '%s' to: %s" % (instance.name, path)) From 1d6206d41456df1dec2f60617ba54390d1857dfd Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 Feb 2023 16:39:57 +0000 Subject: [PATCH 0431/1271] Rename plugin --- .../maya/plugins/load/{load_ass.py => load_arnold_standin.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename openpype/hosts/maya/plugins/load/{load_ass.py => load_arnold_standin.py} (100%) diff --git a/openpype/hosts/maya/plugins/load/load_ass.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py similarity index 100% rename from openpype/hosts/maya/plugins/load/load_ass.py rename to openpype/hosts/maya/plugins/load/load_arnold_standin.py From e24665cf536aa4d48538f229718de57a731e75f1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:22:10 +0100 Subject: [PATCH 0432/1271] OP-4643 - remove review from old representation If new representation gets created and adds 'review' tag it becomes new reviewable representation. --- .../publish/extract_color_transcode.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index e39ea3add9..d10b887a0b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -89,6 +89,7 @@ class ExtractOIIOTranscode(publish.Extractor): continue added_representations = False + added_review = False colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] @@ -166,11 +167,15 @@ class ExtractOIIOTranscode(publish.Extractor): if tag not in new_repre["tags"]: new_repre["tags"].append(tag) + if tag == "review": + added_review = True + instance.data["representations"].append(new_repre) added_representations = True if added_representations: - self._mark_original_repre_for_deletion(repre, profile) + self._mark_original_repre_for_deletion(repre, profile, + added_review) def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): @@ -300,15 +305,16 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _mark_original_repre_for_deletion(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile, added_review): """If new transcoded representation created, delete old.""" + if not repre.get("tags"): + repre["tags"] = [] + delete_original = profile["delete_original"] if delete_original: - if not repre.get("tags"): - repre["tags"] = [] - - if "review" in repre["tags"]: - repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") + + if added_review and "review" in repre["tags"]: + repre["tags"].remove("review") From 3aa74b231c2e7116ea792901ac53cfcd848513fc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:23:42 +0100 Subject: [PATCH 0433/1271] OP-4643 - remove representation that should be deleted Or old revieable representation would be reviewed too. --- openpype/plugins/publish/extract_color_transcode.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index d10b887a0b..93ee1ec44d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -177,6 +177,11 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile, added_review) + for repre in tuple(instance.data["representations"]): + tags = repre.get("tags") or [] + if "delete" in tags and "thumbnail" not in tags: + instance.data["representations"].remove(repre) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From 9110f7055415995637039c2da8ac2c1afe2bf9c5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:02:09 +0100 Subject: [PATCH 0434/1271] use safe access to "subset" in AE --- openpype/hosts/aftereffects/plugins/create/create_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 5427edb44b..f661b3bfd7 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -47,7 +47,7 @@ class RenderCreator(Creator): for created_inst, _changes in update_list: api.get_stub().imprint(created_inst.get("instance_id"), created_inst.data_to_store()) - subset_change = _changes["subset"] + subset_change = _changes.get("subset") if subset_change: api.get_stub().rename_item(created_inst.data["members"][0], subset_change.new_value) From 22e4b9862b7aaf1e30b1d1d86fcccff2d2ec6799 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:05:27 +0100 Subject: [PATCH 0435/1271] added 'order' attribute to creators which is used to process them in specific order --- openpype/pipeline/create/creator_plugins.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 1f92056b23..1aba3f8770 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -107,7 +107,11 @@ class SubsetConvertorPlugin(object): @property def create_context(self): - """Quick access to create context.""" + """Quick access to create context. + + Returns: + CreateContext: Context which initialized the plugin. + """ return self._create_context @@ -157,6 +161,10 @@ class BaseCreator: # Cached group label after first call 'get_group_label' _cached_group_label = None + # Order in which will be plugin executed (collect & update instances) + # less == earlier -> Order '90' will be processed before '100' + order = 100 + # Variable to store logger _log = None From fd31d7815a69b51cfe7bc3ecc0d6a48f13c6e817 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:05:46 +0100 Subject: [PATCH 0436/1271] added helper methods to get sorted creators --- openpype/pipeline/create/context.py | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index f260e483d9..04db1df790 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1237,6 +1237,37 @@ class CreateContext: """Access to global publish attributes.""" return self._publish_attributes + def get_sorted_creators(self, identifiers=None): + """Sorted creators by 'order' attribute. + + Returns: + List[BaseCreator]: Sorted creator plugins by 'order' value. + """ + + if identifiers is not None: + identifiers = set(identifiers) + creators = [ + creator + for identifier, creator in self.creators.items() + if identifier in identifiers + ] + else: + creators = self.creators.values() + + return sorted( + creators, key=lambda creator: creator.order + ) + + @property + def sorted_creators(self): + return self.get_sorted_creators() + + @property + def sorted_autocreators(self): + return sorted( + self.autocreators.values(), key=lambda creator: creator.order + ) + @classmethod def get_host_misssing_methods(cls, host): """Collect missing methods from host. From 101bbef42d0532dcca235e145cfdae7dc7d22436 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:07:16 +0100 Subject: [PATCH 0437/1271] added helper method to remove instance --- openpype/pipeline/create/context.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 04db1df790..9d8bb63804 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1630,6 +1630,9 @@ class CreateContext: ) ]) + def _remove_instance(self, instance): + self._instances_by_id.pop(instance.id, None) + def creator_removed_instance(self, instance): """When creator removes instance context should be acknowledged. @@ -1641,7 +1644,7 @@ class CreateContext: from scene metadata. """ - self._instances_by_id.pop(instance.id, None) + self._remove_instance(instance) def add_convertor_item(self, convertor_identifier, label): self.convertor_items_by_id[convertor_identifier] = ConvertorItem( From 3316417173b1f0bdd9a49f91cd210ca4f85236ef Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:07:50 +0100 Subject: [PATCH 0438/1271] process creators in their order --- openpype/pipeline/create/context.py | 56 ++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 9d8bb63804..5682fb3115 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1688,7 +1688,7 @@ class CreateContext: # Collect instances error_message = "Collection of instances for creator {} failed. {}" failed_info = [] - for creator in self.creators.values(): + for creator in self.sorted_creators: label = creator.label identifier = creator.identifier failed = False @@ -1760,7 +1760,8 @@ class CreateContext: error_message = "Failed to run AutoCreator with identifier \"{}\". {}" failed_info = [] - for identifier, creator in self.autocreators.items(): + for creator in self.sorted_autocreators: + identifier = creator.identifier label = creator.label failed = False add_traceback = False @@ -1865,19 +1866,26 @@ class CreateContext: """Save instance specific values.""" instances_by_identifier = collections.defaultdict(list) for instance in self._instances_by_id.values(): + instance_changes = instance.changes() + if not instance_changes: + continue + identifier = instance.creator_identifier - instances_by_identifier[identifier].append(instance) + instances_by_identifier[identifier].append( + UpdateData(instance, instance_changes) + ) + + if not instances_by_identifier: + return error_message = "Instances update of creator \"{}\" failed. {}" failed_info = [] - for identifier, creator_instances in instances_by_identifier.items(): - update_list = [] - for instance in creator_instances: - instance_changes = instance.changes() - if instance_changes: - update_list.append(UpdateData(instance, instance_changes)) - creator = self.creators[identifier] + for creator in self.get_sorted_creators( + instances_by_identifier.keys() + ): + identifier = creator.identifier + update_list = instances_by_identifier[identifier] if not update_list: continue @@ -1913,9 +1921,13 @@ class CreateContext: def remove_instances(self, instances): """Remove instances from context. + All instances that don't have creator identifier leading to existing + creator are just removed from context. + Args: - instances(list): Instances that should be removed - from context. + instances(List[CreatedInstance]): Instances that should be removed. + Remove logic is done using creator, which may require to + do other cleanup than just remove instance from context. """ instances_by_identifier = collections.defaultdict(list) @@ -1925,8 +1937,13 @@ class CreateContext: error_message = "Instances removement of creator \"{}\" failed. {}" failed_info = [] - for identifier, creator_instances in instances_by_identifier.items(): - creator = self.creators.get(identifier) + # Remove instances by creator plugin order + for creator in self.get_sorted_creators( + instances_by_identifier.keys() + ): + identifier = creator.identifier + creator_instances = instances_by_identifier[identifier] + label = creator.label failed = False add_traceback = False @@ -1969,6 +1986,7 @@ class CreateContext: family(str): Instance family for which should be attribute definitions returned. """ + if family not in self._attr_plugins_by_family: import pyblish.logic @@ -1984,7 +2002,13 @@ class CreateContext: return self._attr_plugins_by_family[family] def _get_publish_plugins_with_attr_for_context(self): - """Publish plugins attributes for Context plugins.""" + """Publish plugins attributes for Context plugins. + + Returns: + List[pyblish.api.Plugin]: Publish plugins that have attribute + definitions for context. + """ + plugins = [] for plugin in self.plugins_with_defs: if not plugin.__instanceEnabled__: @@ -2009,7 +2033,7 @@ class CreateContext: return self._collection_shared_data def run_convertor(self, convertor_identifier): - """Run convertor plugin by it's idenfitifier. + """Run convertor plugin by identifier. Conversion is skipped if convertor is not available. From c5be74156652323a43f05e1d486ed77dfd0f0987 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:08:07 +0100 Subject: [PATCH 0439/1271] handle instances without available creator --- openpype/pipeline/create/context.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 5682fb3115..c978fcc2e1 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1935,6 +1935,12 @@ class CreateContext: identifier = instance.creator_identifier instances_by_identifier[identifier].append(instance) + # Just remove instances from context if creator is not available + missing_creators = set(instances_by_identifier) - set(self.creators) + for identifier in missing_creators: + for instance in instances_by_identifier[identifier]: + self._remove_instance(instance) + error_message = "Instances removement of creator \"{}\" failed. {}" failed_info = [] # Remove instances by creator plugin order @@ -2046,7 +2052,7 @@ class CreateContext: convertor.convert() def run_convertors(self, convertor_identifiers): - """Run convertor plugins by idenfitifiers. + """Run convertor plugins by identifiers. Conversion is skipped if convertor is not available. It is recommended to trigger reset after conversion to reload instances. From c7f051db20c45ab9ba8b835fea3f858051a93df3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:08:42 +0100 Subject: [PATCH 0440/1271] added 'show_order' attribute to 'Creator' so show order can be different than processing --- openpype/pipeline/create/creator_plugins.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 1aba3f8770..53acb618ed 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -497,6 +497,17 @@ class Creator(BaseCreator): # - similar to instance attribute definitions pre_create_attr_defs = [] + @property + def show_order(self): + """Order in which is creator shown in UI. + + Returns: + int: Order in which is creator shown (less == earlier). By default + is using Creator's 'order' or processing. + """ + + return self.order + @abstractmethod def create(self, subset_name, instance_data, pre_create_data): """Create new instance and store it. From b67181c4e012ac370a1ca284e2112e92589772d9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:09:32 +0100 Subject: [PATCH 0441/1271] added show order to publisher UI --- openpype/tools/publisher/constants.py | 2 ++ openpype/tools/publisher/control.py | 10 ++++++++-- openpype/tools/publisher/widgets/create_widget.py | 10 +++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/openpype/tools/publisher/constants.py b/openpype/tools/publisher/constants.py index e9fdd4774a..b2bfd7dd5c 100644 --- a/openpype/tools/publisher/constants.py +++ b/openpype/tools/publisher/constants.py @@ -24,6 +24,7 @@ CREATOR_THUMBNAIL_ENABLED_ROLE = QtCore.Qt.UserRole + 5 FAMILY_ROLE = QtCore.Qt.UserRole + 6 GROUP_ROLE = QtCore.Qt.UserRole + 7 CONVERTER_IDENTIFIER_ROLE = QtCore.Qt.UserRole + 8 +CREATOR_SORT_ROLE = QtCore.Qt.UserRole + 9 __all__ = ( @@ -36,6 +37,7 @@ __all__ = ( "IS_GROUP_ROLE", "CREATOR_IDENTIFIER_ROLE", "CREATOR_THUMBNAIL_ENABLED_ROLE", + "CREATOR_SORT_ROLE", "FAMILY_ROLE", "GROUP_ROLE", "CONVERTER_IDENTIFIER_ROLE", diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 7c8da66744..d1ee3ea8aa 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -832,7 +832,8 @@ class CreatorItem: default_variants, create_allow_context_change, create_allow_thumbnail, - pre_create_attributes_defs + show_order, + pre_create_attributes_defs, ): self.identifier = identifier self.creator_type = creator_type @@ -846,6 +847,7 @@ class CreatorItem: self.default_variants = default_variants self.create_allow_context_change = create_allow_context_change self.create_allow_thumbnail = create_allow_thumbnail + self.show_order = show_order self.pre_create_attributes_defs = pre_create_attributes_defs def get_group_label(self): @@ -869,6 +871,7 @@ class CreatorItem: pre_create_attr_defs = None create_allow_context_change = None create_allow_thumbnail = None + show_order = creator.order if creator_type is CreatorTypes.artist: description = creator.get_description() detail_description = creator.get_detail_description() @@ -877,6 +880,7 @@ class CreatorItem: pre_create_attr_defs = creator.get_pre_create_attr_defs() create_allow_context_change = creator.create_allow_context_change create_allow_thumbnail = creator.create_allow_thumbnail + show_order = creator.show_order identifier = creator.identifier return cls( @@ -892,7 +896,8 @@ class CreatorItem: default_variants, create_allow_context_change, create_allow_thumbnail, - pre_create_attr_defs + show_order, + pre_create_attr_defs, ) def to_data(self): @@ -915,6 +920,7 @@ class CreatorItem: "default_variants": self.default_variants, "create_allow_context_change": self.create_allow_context_change, "create_allow_thumbnail": self.create_allow_thumbnail, + "show_order": self.show_order, "pre_create_attributes_defs": pre_create_attributes_defs, } diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index 07b124f616..994b9ac912 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -18,9 +18,10 @@ from .tasks_widget import CreateWidgetTasksWidget from .precreate_widget import PreCreateWidget from ..constants import ( VARIANT_TOOLTIP, - CREATOR_IDENTIFIER_ROLE, FAMILY_ROLE, + CREATOR_IDENTIFIER_ROLE, CREATOR_THUMBNAIL_ENABLED_ROLE, + CREATOR_SORT_ROLE, ) SEPARATORS = ("---separator---", "---") @@ -441,7 +442,8 @@ class CreateWidget(QtWidgets.QWidget): # Add new families new_creators = set() - for identifier, creator_item in self._controller.creator_items.items(): + creator_items_by_identifier = self._controller.creator_items + for identifier, creator_item in creator_items_by_identifier.items(): if creator_item.creator_type != "artist": continue @@ -457,6 +459,7 @@ class CreateWidget(QtWidgets.QWidget): self._creators_model.appendRow(item) item.setData(creator_item.label, QtCore.Qt.DisplayRole) + item.setData(creator_item.show_order, CREATOR_SORT_ROLE) item.setData(identifier, CREATOR_IDENTIFIER_ROLE) item.setData( creator_item.create_allow_thumbnail, @@ -482,8 +485,9 @@ class CreateWidget(QtWidgets.QWidget): index = indexes[0] identifier = index.data(CREATOR_IDENTIFIER_ROLE) + create_item = creator_items_by_identifier.get(identifier) - self._set_creator_by_identifier(identifier) + self._set_creator(create_item) def _on_plugins_refresh(self): # Trigger refresh only if is visible From 7e1450e95d1cbc2d3df206235a13f280c3c642cb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:09:53 +0100 Subject: [PATCH 0442/1271] modified proxy filter for sorting of creators --- openpype/tools/publisher/widgets/create_widget.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index 994b9ac912..bc1f1ec4f7 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -91,6 +91,15 @@ class CreatorShortDescWidget(QtWidgets.QWidget): self._description_label.setText(description) +class CreatorsProxyModel(QtCore.QSortFilterProxyModel): + def lessThan(self, left, right): + l_show_order = left.data(CREATOR_SORT_ROLE) + r_show_order = right.data(CREATOR_SORT_ROLE) + if l_show_order == r_show_order: + return super(CreatorsProxyModel, self).lessThan(left, right) + return l_show_order < r_show_order + + class CreateWidget(QtWidgets.QWidget): def __init__(self, controller, parent=None): super(CreateWidget, self).__init__(parent) @@ -142,7 +151,7 @@ class CreateWidget(QtWidgets.QWidget): creators_view = QtWidgets.QListView(creators_view_widget) creators_model = QtGui.QStandardItemModel() - creators_sort_model = QtCore.QSortFilterProxyModel() + creators_sort_model = CreatorsProxyModel() creators_sort_model.setSourceModel(creators_model) creators_view.setModel(creators_sort_model) From dfb718741355d5b0c4cad8a9a4947d2dce2eb606 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:10:07 +0100 Subject: [PATCH 0443/1271] removed window title from creator widget --- openpype/tools/publisher/widgets/create_widget.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index bc1f1ec4f7..dbf075c216 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -104,8 +104,6 @@ class CreateWidget(QtWidgets.QWidget): def __init__(self, controller, parent=None): super(CreateWidget, self).__init__(parent) - self.setWindowTitle("Create new instance") - self._controller = controller self._asset_name = None From 310456ec137c621554b91d2a831f875489059106 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:10:24 +0100 Subject: [PATCH 0444/1271] fix creator_items cache cleanup --- openpype/tools/publisher/control.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index d1ee3ea8aa..435db5fcb3 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -1508,9 +1508,6 @@ class BasePublisherController(AbstractPublisherController): def _reset_attributes(self): """Reset most of attributes that can be reset.""" - # Reset creator items - self._creator_items = None - self.publish_is_running = False self.publish_has_validated = False self.publish_has_crashed = False @@ -1766,6 +1763,8 @@ class PublisherController(BasePublisherController): self._resetting_plugins = True self._create_context.reset_plugins() + # Reset creator items + self._creator_items = None self._resetting_plugins = False From e3c58662b9cf05211d35c5282e5d25a2fb5b46f0 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 Feb 2023 18:13:25 +0000 Subject: [PATCH 0445/1271] Working loading proxy --- .../maya/plugins/load/load_arnold_standin.py | 117 ++++++++++++++---- 1 file changed, 91 insertions(+), 26 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index 50b72d87e8..57e1d8a6e0 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -9,7 +9,9 @@ from openpype.pipeline import ( load, get_representation_path ) -from openpype.hosts.maya.api.lib import unique_namespace +from openpype.hosts.maya.api.lib import ( + unique_namespace, get_attribute_input, maintained_selection +) from openpype.hosts.maya.api.pipeline import containerise @@ -22,18 +24,6 @@ def is_sequence(files): return sequence -def set_color(node, context): - project_name = context["project"]["name"] - settings = get_project_settings(project_name) - colors = settings['maya']['load']['colors'] - color = colors.get('ass') - if color is not None: - cmds.setAttr(node + ".useOutlinerColor", True) - cmds.setAttr( - node + ".outlinerColor", color[0], color[1], color[2] - ) - - class ArnoldStandinLoader(load.LoaderPlugin): """Load file as Arnold standin""" @@ -62,24 +52,35 @@ class ArnoldStandinLoader(load.LoaderPlugin): label = "{}:{}".format(namespace, name) root = cmds.group(name=label, empty=True) - set_color(root, context) + # Set color. + project_name = context["project"]["name"] + settings = get_project_settings(project_name) + colors = settings['maya']['load']['colors'] + color = colors.get('ass') + if color is not None: + cmds.setAttr(root + ".useOutlinerColor", True) + cmds.setAttr( + root + ".outlinerColor", color[0], color[1], color[2] + ) - # Create transform with shape - transform_name = label + "_standin" + with maintained_selection(): + # Create transform with shape + transform_name = label + "_standin" - standinShape = mtoa.ui.arnoldmenu.createStandIn() - standin = cmds.listRelatives(standinShape, parent=True)[0] - standin = cmds.rename(standin, transform_name) - standinShape = cmds.listRelatives(standin, shapes=True)[0] + standinShape = mtoa.ui.arnoldmenu.createStandIn() + standin = cmds.listRelatives(standinShape, parent=True)[0] + standin = cmds.rename(standin, transform_name) + standinShape = cmds.listRelatives(standin, shapes=True)[0] - cmds.parent(standin, root) + cmds.parent(standin, root) - # Set the standin filepath - cmds.setAttr(standinShape + ".dso", self.fname, type="string") - sequence = is_sequence(os.listdir(os.path.dirname(self.fname))) - cmds.setAttr(standinShape + ".useFrameExtension", sequence) + # Set the standin filepath + dso_path, operator = self._setup_proxy(standinShape, self.fname) + cmds.setAttr(standinShape + ".dso", dso_path, type="string") + sequence = is_sequence(os.listdir(os.path.dirname(self.fname))) + cmds.setAttr(standinShape + ".useFrameExtension", sequence) - nodes = [root, standin] + nodes = [root, standin, operator] self[:] = nodes return containerise( @@ -89,6 +90,70 @@ class ArnoldStandinLoader(load.LoaderPlugin): context=context, loader=self.__class__.__name__) + def get_next_free_multi_index(self, attr_name): + """Find the next unconnected multi index at the input attribute.""" + + start_index = 0 + # Assume a max of 10 million connections + while start_index < 10000000: + connection_info = cmds.connectionInfo( + "{}[{}]".format(attr_name, start_index), + sourceFromDestination=True + ) + if len(connection_info or []) == 0: + return start_index + start_index += 1 + + def _setup_proxy(self, shape, path): + basename_split = os.path.basename(path).split(".") + proxy_basename = ( + basename_split[0] + "_proxy." + ".".join(basename_split[1:]) + ) + proxy_path = "/".join( + [os.path.dirname(path), "resources", proxy_basename] + ) + + if not os.path.exists(proxy_path): + self.log.error("Proxy files do not exist. Skipping proxy setup.") + return path + + options_node = "defaultArnoldRenderOptions" + merge_operator = get_attribute_input(options_node + ".operator") + if merge_operator is None: + merge_operator = cmds.createNode("aiMerge") + cmds.connectAttr( + merge_operator + ".message", options_node + ".operator" + ) + + merge_operator = merge_operator.split(".")[0] + + string_replace_operator = cmds.createNode("aiStringReplace") + cmds.setAttr( + string_replace_operator + ".selection", + "*.(@node=='procedural')", + type="string" + ) + cmds.setAttr( + string_replace_operator + ".match", + "resources/" + proxy_basename, + type="string" + ) + cmds.setAttr( + string_replace_operator + ".replace", + os.path.basename(path), + type="string" + ) + + cmds.connectAttr( + string_replace_operator + ".out", + "{}.inputs[{}]".format( + merge_operator, + self.get_next_free_multi_index(merge_operator + ".inputs") + ) + ) + + return proxy_path, string_replace_operator + def update(self, container, representation): # Update the standin standins = list() From cd1b02c59504dab76294663b326851cf70920d7e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:14:53 +0100 Subject: [PATCH 0446/1271] move batch creator after simple creators --- .../hosts/traypublisher/plugins/create/create_movie_batch.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py b/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py index 1dc4bad9b3..d077131e4c 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py +++ b/openpype/hosts/traypublisher/plugins/create/create_movie_batch.py @@ -33,6 +33,8 @@ class BatchMovieCreator(TrayPublishCreator): create_allow_context_change = False version_regex = re.compile(r"^(.+)_v([0-9]+)$") + # Position batch creator after simple creators + order = 110 def __init__(self, project_settings, *args, **kwargs): super(BatchMovieCreator, self).__init__(project_settings, From af752ae60663cd64f038ab154216a52e61d6dcb2 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 Feb 2023 18:23:04 +0000 Subject: [PATCH 0447/1271] Working proxy update --- .../maya/plugins/load/load_arnold_standin.py | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index 57e1d8a6e0..a90aa02d4d 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -104,7 +104,7 @@ class ArnoldStandinLoader(load.LoaderPlugin): return start_index start_index += 1 - def _setup_proxy(self, shape, path): + def _get_proxy_path(self, path): basename_split = os.path.basename(path).split(".") proxy_basename = ( basename_split[0] + "_proxy." + ".".join(basename_split[1:]) @@ -112,10 +112,14 @@ class ArnoldStandinLoader(load.LoaderPlugin): proxy_path = "/".join( [os.path.dirname(path), "resources", proxy_basename] ) + return proxy_basename, proxy_path + + def _setup_proxy(self, shape, path): + proxy_basename, proxy_path = self._get_proxy_path(path) if not os.path.exists(proxy_path): self.log.error("Proxy files do not exist. Skipping proxy setup.") - return path + return os.path.basename(path), path options_node = "defaultArnoldRenderOptions" merge_operator = get_attribute_input(options_node + ".operator") @@ -156,20 +160,33 @@ class ArnoldStandinLoader(load.LoaderPlugin): def update(self, container, representation): # Update the standin - standins = list() members = cmds.sets(container['objectName'], query=True) for member in members: + if cmds.nodeType(member) == "aiStringReplace": + string_replace_operator = member + shapes = cmds.listRelatives(member, shapes=True) if not shapes: continue if cmds.nodeType(shapes[0]) == "aiStandIn": - standins.append(shapes[0]) + standin = shapes[0] path = get_representation_path(representation) + proxy_basename, proxy_path = self._get_proxy_path(path) + cmds.setAttr( + string_replace_operator + ".match", + "resources/" + proxy_basename, + type="string" + ) + cmds.setAttr( + string_replace_operator + ".replace", + os.path.basename(path), + type="string" + ) + cmds.setAttr(standin + ".dso", proxy_path, type="string") + sequence = is_sequence(os.listdir(os.path.dirname(path))) - for standin in standins: - cmds.setAttr(standin + ".dso", path, type="string") - cmds.setAttr(standin + ".useFrameExtension", sequence) + cmds.setAttr(standin + ".useFrameExtension", sequence) cmds.setAttr( container["objectName"] + ".representation", From b51a2a72579966e721e268de68ad1d01e62c93a6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Feb 2023 19:24:30 +0100 Subject: [PATCH 0448/1271] added more docstrings --- openpype/pipeline/create/context.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index c978fcc2e1..0ee70f39cb 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1240,6 +1240,10 @@ class CreateContext: def get_sorted_creators(self, identifiers=None): """Sorted creators by 'order' attribute. + Args: + identifiers (Iterable[str]): Filter creators by identifiers. All + creators are returned if 'None' is passed. + Returns: List[BaseCreator]: Sorted creator plugins by 'order' value. """ @@ -1260,10 +1264,22 @@ class CreateContext: @property def sorted_creators(self): + """Sorted creators by 'order' attribute. + + Returns: + List[BaseCreator]: Sorted creator plugins by 'order' value. + """ + return self.get_sorted_creators() @property def sorted_autocreators(self): + """Sorted auto-creators by 'order' attribute. + + Returns: + List[AutoCreator]: Sorted plugins by 'order' value. + """ + return sorted( self.autocreators.values(), key=lambda creator: creator.order ) From bb4a44fe33d0a6e1518179f19efbf309969a3d28 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 7 Feb 2023 18:30:34 +0000 Subject: [PATCH 0449/1271] Clean up string replace operator --- .../hosts/maya/plugins/load/load_arnold_standin.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index a90aa02d4d..635e86708b 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -75,7 +75,9 @@ class ArnoldStandinLoader(load.LoaderPlugin): cmds.parent(standin, root) # Set the standin filepath - dso_path, operator = self._setup_proxy(standinShape, self.fname) + dso_path, operator = self._setup_proxy( + standinShape, self.fname, namespace + ) cmds.setAttr(standinShape + ".dso", dso_path, type="string") sequence = is_sequence(os.listdir(os.path.dirname(self.fname))) cmds.setAttr(standinShape + ".useFrameExtension", sequence) @@ -114,7 +116,7 @@ class ArnoldStandinLoader(load.LoaderPlugin): ) return proxy_basename, proxy_path - def _setup_proxy(self, shape, path): + def _setup_proxy(self, shape, path, namespace): proxy_basename, proxy_path = self._get_proxy_path(path) if not os.path.exists(proxy_path): @@ -131,7 +133,9 @@ class ArnoldStandinLoader(load.LoaderPlugin): merge_operator = merge_operator.split(".")[0] - string_replace_operator = cmds.createNode("aiStringReplace") + string_replace_operator = cmds.createNode( + "aiStringReplace", name=namespace + ":string_replace_operator" + ) cmds.setAttr( string_replace_operator + ".selection", "*.(@node=='procedural')", @@ -198,7 +202,6 @@ class ArnoldStandinLoader(load.LoaderPlugin): self.update(container, representation) def remove(self, container): - import maya.cmds as cmds members = cmds.sets(container['objectName'], query=True) cmds.lockNode(members, lock=False) cmds.delete([container['objectName']] + members) From d819a0c8c8882d325b708529205adf2315099625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Tue, 7 Feb 2023 19:37:11 +0100 Subject: [PATCH 0450/1271] :art: allow underscore in branch names allow underscore in branch names in regex for pre-commit hook - no-commit-branch --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 890df4613e..eec388924e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,4 +9,4 @@ repos: - id: check-yaml - id: check-added-large-files - id: no-commit-to-branch - args: [ '--pattern', '^(?!((release|enhancement|feature|bugfix|documentation|tests|local|chore)\/[a-zA-Z0-9\-]+)$).*' ] + args: [ '--pattern', '^(?!((release|enhancement|feature|bugfix|documentation|tests|local|chore)\/[a-zA-Z0-9\-_]+)$).*' ] From cba888e66e5e874cfa84add8f396f5a72d1b3e40 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Feb 2023 20:02:36 +0100 Subject: [PATCH 0451/1271] small docstring/comments enhancements --- openpype/pipeline/create/context.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index f46b4eccdb..427cabce67 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -252,12 +252,13 @@ class TrackChangesItem(object): ``` Args: - old_value (Any): Previous value. + old_value (Any): Old value. new_value (Any): New value. """ def __init__(self, old_value, new_value): self._changed = old_value != new_value + # Resolve if value is '_EMPTY_VALUE' after comparison of the values if old_value is _EMPTY_VALUE: old_value = None if new_value is _EMPTY_VALUE: @@ -412,7 +413,7 @@ class TrackChangesItem(object): """All keys that are available in old and new value. Empty set is returned if both old and new value are not a dict. - Output it is Union of 'old_keys' and 'new_keys'. + Output is Union of 'old_keys' and 'new_keys'. Returns: Set[str]: All keys from old and new value. From e1befa7ae3a01be70577539b747445377b09f28f Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 8 Feb 2023 03:28:55 +0000 Subject: [PATCH 0452/1271] [Automated] Bump version --- openpype/version.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/version.py b/openpype/version.py index 3941912c6e..c65d1197c1 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.1-nightly.2" +__version__ = "3.15.1-nightly.3" diff --git a/pyproject.toml b/pyproject.toml index 2fc4f6fe39..6e88404700 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.1" # OpenPype +version = "3.15.1-nightly.3" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 0921cef11ebdf3c4236c5b5b263e1352daf64721 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 03:29:43 +0000 Subject: [PATCH 0453/1271] Bump http-cache-semantics from 4.1.0 to 4.1.1 in /website Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/kornelski/http-cache-semantics/releases) - [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1) --- updated-dependencies: - dependency-name: http-cache-semantics 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 9af21c7500..0a56928cd9 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -4273,9 +4273,9 @@ htmlparser2@^6.1.0: entities "^2.0.0" http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-deceiver@^1.2.7: version "1.2.7" From 87712716d047716800bf27a934a9648b9024bed5 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 8 Feb 2023 07:48:45 +0000 Subject: [PATCH 0454/1271] Combine Alembic standin --- .../maya/plugins/load/load_abc_to_standin.py | 132 ------------------ .../maya/plugins/load/load_arnold_standin.py | 18 +-- 2 files changed, 10 insertions(+), 140 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/load/load_abc_to_standin.py diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py deleted file mode 100644 index 70866a3ba6..0000000000 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ /dev/null @@ -1,132 +0,0 @@ -import os - -from openpype.pipeline import ( - legacy_io, - load, - get_representation_path -) -from openpype.settings import get_project_settings - - -class AlembicStandinLoader(load.LoaderPlugin): - """Load Alembic as Arnold Standin""" - - families = ["animation", "model", "proxyAbc", "pointcache"] - representations = ["abc"] - - label = "Import Alembic as Arnold Standin" - order = -5 - icon = "code-fork" - color = "orange" - - def load(self, context, name, namespace, options): - - import maya.cmds as cmds - import mtoa.ui.arnoldmenu - from openpype.hosts.maya.api.pipeline import containerise - from openpype.hosts.maya.api.lib import unique_namespace - - version = context["version"] - version_data = version.get("data", {}) - family = version["data"]["families"] - self.log.info("version_data: {}\n".format(version_data)) - self.log.info("family: {}\n".format(family)) - frameStart = version_data.get("frameStart", None) - - asset = context["asset"]["name"] - namespace = namespace or unique_namespace( - asset + "_", - prefix="_" if asset[0].isdigit() else "", - suffix="_", - ) - - # Root group - label = "{}:{}".format(namespace, name) - root = cmds.group(name=label, empty=True) - - settings = get_project_settings(os.environ['AVALON_PROJECT']) - colors = settings["maya"]["load"]["colors"] - fps = legacy_io.Session["AVALON_FPS"] - c = colors.get(family[0]) - if c is not None: - r = (float(c[0]) / 255) - g = (float(c[1]) / 255) - b = (float(c[2]) / 255) - cmds.setAttr(root + ".useOutlinerColor", 1) - cmds.setAttr(root + ".outlinerColor", - r, g, b) - - transform_name = label + "_ABC" - - standinShape = cmds.ls(mtoa.ui.arnoldmenu.createStandIn())[0] - standin = cmds.listRelatives(standinShape, parent=True, - typ="transform") - standin = cmds.rename(standin, transform_name) - standinShape = cmds.listRelatives(standin, children=True)[0] - - cmds.parent(standin, root) - - # Set the standin filepath - cmds.setAttr(standinShape + ".dso", self.fname, type="string") - cmds.setAttr(standinShape + ".abcFPS", float(fps)) - - if frameStart is None: - cmds.setAttr(standinShape + ".useFrameExtension", 0) - - elif "model" in family: - cmds.setAttr(standinShape + ".useFrameExtension", 0) - - else: - cmds.setAttr(standinShape + ".useFrameExtension", 1) - - nodes = [root, standin] - self[:] = nodes - - return containerise( - name=name, - namespace=namespace, - nodes=nodes, - context=context, - loader=self.__class__.__name__) - - def update(self, container, representation): - - import pymel.core as pm - - path = get_representation_path(representation) - fps = legacy_io.Session["AVALON_FPS"] - # Update the standin - standins = list() - members = pm.sets(container['objectName'], query=True) - self.log.info("container:{}".format(container)) - for member in members: - shape = member.getShape() - if (shape and shape.type() == "aiStandIn"): - standins.append(shape) - - for standin in standins: - standin.dso.set(path) - standin.abcFPS.set(float(fps)) - if "modelMain" in container['objectName']: - standin.useFrameExtension.set(0) - else: - standin.useFrameExtension.set(1) - - container = pm.PyNode(container["objectName"]) - container.representation.set(str(representation["_id"])) - - def switch(self, container, representation): - self.update(container, representation) - - def remove(self, container): - import maya.cmds as cmds - members = cmds.sets(container['objectName'], query=True) - cmds.lockNode(members, lock=False) - cmds.delete([container['objectName']] + members) - - # Clean up the namespace - try: - cmds.namespace(removeNamespace=container['namespace'], - deleteNamespaceContent=True) - except RuntimeError: - pass diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index 635e86708b..3cfc5b71b3 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -25,12 +25,12 @@ def is_sequence(files): class ArnoldStandinLoader(load.LoaderPlugin): - """Load file as Arnold standin""" + """Load as Arnold standin""" - families = ["ass"] - representations = ["ass"] + families = ["ass", "animation", "model", "proxyAbc", "pointcache"] + representations = ["ass", "abc"] - label = "Load file as Arnold standin" + label = "Load as Arnold standin" order = -5 icon = "code-fork" color = "orange" @@ -75,14 +75,16 @@ class ArnoldStandinLoader(load.LoaderPlugin): cmds.parent(standin, root) # Set the standin filepath - dso_path, operator = self._setup_proxy( + path, operator = self._setup_proxy( standinShape, self.fname, namespace ) - cmds.setAttr(standinShape + ".dso", dso_path, type="string") + cmds.setAttr(standinShape + ".dso", path, type="string") sequence = is_sequence(os.listdir(os.path.dirname(self.fname))) cmds.setAttr(standinShape + ".useFrameExtension", sequence) - nodes = [root, standin, operator] + nodes = [root, standin] + if operator is not None: + nodes.append(operator) self[:] = nodes return containerise( @@ -121,7 +123,7 @@ class ArnoldStandinLoader(load.LoaderPlugin): if not os.path.exists(proxy_path): self.log.error("Proxy files do not exist. Skipping proxy setup.") - return os.path.basename(path), path + return path, None options_node = "defaultArnoldRenderOptions" merge_operator = get_attribute_input(options_node + ".operator") From 067a6c7c93ba3b55debf0a6deae39218871bee76 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 8 Feb 2023 08:19:11 +0000 Subject: [PATCH 0455/1271] Code cosmetics --- ...ect_ass.py => collect_arnold_scene_source.py} | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) rename openpype/hosts/maya/plugins/publish/{collect_ass.py => collect_arnold_scene_source.py} (72%) diff --git a/openpype/hosts/maya/plugins/publish/collect_ass.py b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py similarity index 72% rename from openpype/hosts/maya/plugins/publish/collect_ass.py rename to openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py index 7b5d1a00c7..06d0786665 100644 --- a/openpype/hosts/maya/plugins/publish/collect_ass.py +++ b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py @@ -3,16 +3,16 @@ from maya import cmds import pyblish.api -class CollectAssData(pyblish.api.InstancePlugin): - """Collect Ass data.""" +class CollectArnoldSceneSource(pyblish.api.InstancePlugin): + """Collect Arnold Scene Source data.""" # Offset to be after renderable camera collection. order = pyblish.api.CollectorOrder + 0.2 - label = 'Collect Ass' + label = "Collect Arnold Scene Source" families = ["ass"] def process(self, instance): - objsets = instance.data['setMembers'] + objsets = instance.data["setMembers"] for objset in objsets: objset = str(objset) @@ -21,11 +21,11 @@ class CollectAssData(pyblish.api.InstancePlugin): self.log.warning("Skipped empty instance: \"%s\" " % objset) continue if "content_SET" in objset: - instance.data['setMembers'] = members - self.log.debug('content members: {}'.format(members)) + instance.data["setMembers"] = members + self.log.debug("content members: {}".format(members)) elif objset.startswith("proxy_SET"): - instance.data['proxy'] = members - self.log.debug('proxy members: {}'.format(members)) + instance.data["proxy"] = members + self.log.debug("proxy members: {}".format(members)) # Use camera in object set if present else default to render globals # camera. From 7d5ede8ae51e5de7d22c8b6dfd3270638502dd98 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 8 Feb 2023 08:27:10 +0000 Subject: [PATCH 0456/1271] Fix creating multiple ass instances --- .../{create_ass.py => create_arnold_scene_source.py} | 10 +++++----- .../plugins/publish/collect_arnold_scene_source.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) rename openpype/hosts/maya/plugins/create/{create_ass.py => create_arnold_scene_source.py} (84%) diff --git a/openpype/hosts/maya/plugins/create/create_ass.py b/openpype/hosts/maya/plugins/create/create_arnold_scene_source.py similarity index 84% rename from openpype/hosts/maya/plugins/create/create_ass.py rename to openpype/hosts/maya/plugins/create/create_arnold_scene_source.py index 935a068ca5..2afb897e94 100644 --- a/openpype/hosts/maya/plugins/create/create_ass.py +++ b/openpype/hosts/maya/plugins/create/create_arnold_scene_source.py @@ -6,7 +6,7 @@ from openpype.hosts.maya.api import ( from maya import cmds -class CreateAss(plugin.Creator): +class CreateArnoldSceneSource(plugin.Creator): """Arnold Scene Source""" name = "ass" @@ -29,7 +29,7 @@ class CreateAss(plugin.Creator): maskOperator = False def __init__(self, *args, **kwargs): - super(CreateAss, self).__init__(*args, **kwargs) + super(CreateArnoldSceneSource, self).__init__(*args, **kwargs) # Add animation data self.data.update(lib.collect_animation_data()) @@ -52,7 +52,7 @@ class CreateAss(plugin.Creator): self.data["maskOperator"] = self.maskOperator def process(self): - instance = super(CreateAss, self).process() + instance = super(CreateArnoldSceneSource, self).process() nodes = [] @@ -61,6 +61,6 @@ class CreateAss(plugin.Creator): cmds.sets(nodes, rm=instance) - assContent = cmds.sets(name="content_SET") - assProxy = cmds.sets(name="proxy_SET", empty=True) + assContent = cmds.sets(name=instance + "_content_SET") + assProxy = cmds.sets(name=instance + "_proxy_SET", empty=True) cmds.sets([assContent, assProxy], forceElement=instance) diff --git a/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py index 06d0786665..c0275eef7b 100644 --- a/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py @@ -20,10 +20,10 @@ class CollectArnoldSceneSource(pyblish.api.InstancePlugin): if members is None: self.log.warning("Skipped empty instance: \"%s\" " % objset) continue - if "content_SET" in objset: + if objset.endswith("content_SET"): instance.data["setMembers"] = members self.log.debug("content members: {}".format(members)) - elif objset.startswith("proxy_SET"): + elif objset.endswith("proxy_SET"): instance.data["proxy"] = members self.log.debug("proxy members: {}".format(members)) From 33f2168e785206bd6eb3096fa52789f6cc7d737f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 8 Feb 2023 08:27:17 +0000 Subject: [PATCH 0457/1271] Code cosmetics --- .../publish/{extract_ass.py => extract_arnold_scene_source.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename openpype/hosts/maya/plugins/publish/{extract_ass.py => extract_arnold_scene_source.py} (100%) diff --git a/openpype/hosts/maya/plugins/publish/extract_ass.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py similarity index 100% rename from openpype/hosts/maya/plugins/publish/extract_ass.py rename to openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py From f03cb52538b048167ce5a6f994953094b58d88c5 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 8 Feb 2023 09:40:25 +0000 Subject: [PATCH 0458/1271] Proxy workflow for pointcache --- .../maya/plugins/create/create_pointcache.py | 8 +++ .../maya/plugins/load/load_arnold_standin.py | 3 +- .../plugins/publish/collect_pointcache.py | 30 ++++++++++ .../plugins/publish/extract_pointcache.py | 55 +++++++++++++++++-- 4 files changed, 89 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_pointcache.py b/openpype/hosts/maya/plugins/create/create_pointcache.py index cdec140ea8..63c0490dc7 100644 --- a/openpype/hosts/maya/plugins/create/create_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_pointcache.py @@ -1,3 +1,5 @@ +from maya import cmds + from openpype.hosts.maya.api import ( lib, plugin @@ -37,3 +39,9 @@ class CreatePointCache(plugin.Creator): # Default to not send to farm. self.data["farm"] = False self.data["priority"] = 50 + + def process(self): + instance = super(CreatePointCache, self).process() + + assProxy = cmds.sets(name=instance + "_proxy_SET", empty=True) + cmds.sets(assProxy, forceElement=instance) diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index 3cfc5b71b3..e2bb89ed77 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -138,9 +138,10 @@ class ArnoldStandinLoader(load.LoaderPlugin): string_replace_operator = cmds.createNode( "aiStringReplace", name=namespace + ":string_replace_operator" ) + node_type = "alembic" if path.endswith(".abc") else "procedural" cmds.setAttr( string_replace_operator + ".selection", - "*.(@node=='procedural')", + "*.(@node=='{}')".format(node_type), type="string" ) cmds.setAttr( diff --git a/openpype/hosts/maya/plugins/publish/collect_pointcache.py b/openpype/hosts/maya/plugins/publish/collect_pointcache.py index a841341f72..332992ca92 100644 --- a/openpype/hosts/maya/plugins/publish/collect_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/collect_pointcache.py @@ -1,3 +1,5 @@ +from maya import cmds + import pyblish.api @@ -12,3 +14,31 @@ class CollectPointcache(pyblish.api.InstancePlugin): def process(self, instance): if instance.data.get("farm"): instance.data["families"].append("publish.farm") + + proxy_set = None + for node in instance.data["setMembers"]: + if cmds.nodeType(node) != "objectSet": + continue + members = cmds.sets(node, query=True) + if members is None: + self.log.warning("Skipped empty objectset: \"%s\" " % node) + continue + if node.endswith("proxy_SET"): + proxy_set = node + instance.data["proxy"] = [] + instance.data["proxyRoots"] = [] + for member in members: + instance.data["proxy"].extend(cmds.ls(member, long=True)) + instance.data["proxyRoots"].extend( + cmds.ls(member, long=True) + ) + instance.data["proxy"].extend( + cmds.listRelatives(member, shapes=True, fullPath=True) + ) + self.log.debug( + "proxy members: {}".format(instance.data["proxy"]) + ) + + if proxy_set: + instance.remove(proxy_set) + instance.data["setMembers"].remove(proxy_set) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 7ed73fd5b0..0eb65e4226 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -1,4 +1,5 @@ import os +import copy from maya import cmds @@ -9,6 +10,7 @@ from openpype.hosts.maya.api.lib import ( maintained_selection, iter_visible_nodes_in_range ) +from openpype.lib import StringTemplate class ExtractAlembic(publish.Extractor): @@ -23,9 +25,7 @@ class ExtractAlembic(publish.Extractor): label = "Extract Pointcache (Alembic)" hosts = ["maya"] - families = ["pointcache", - "model", - "vrayproxy"] + families = ["pointcache", "model", "vrayproxy"] targets = ["local", "remote"] def process(self, instance): @@ -87,6 +87,7 @@ class ExtractAlembic(publish.Extractor): end=end)) suspend = not instance.data.get("refresh", False) + self.log.info(nodes) with suspended_refresh(suspend=suspend): with maintained_selection(): cmds.select(nodes, noExpand=True) @@ -101,9 +102,9 @@ class ExtractAlembic(publish.Extractor): instance.data["representations"] = [] representation = { - 'name': 'abc', - 'ext': 'abc', - 'files': filename, + "name": "abc", + "ext": "abc", + "files": filename, "stagingDir": dirname } instance.data["representations"].append(representation) @@ -112,6 +113,48 @@ class ExtractAlembic(publish.Extractor): self.log.info("Extracted {} to {}".format(instance, dirname)) + # Extract proxy. + if not instance.data.get("proxy"): + return + + path = path.replace(".abc", "_proxy.abc") + if not instance.data.get("includeParentHierarchy", True): + # Set the root nodes if we don't want to include parents + # The roots are to be considered the ones that are the actual + # direct members of the set + options["root"] = instance.data["proxyRoots"] + + with suspended_refresh(suspend=suspend): + with maintained_selection(): + cmds.select(instance.data["proxy"]) + extract_alembic( + file=path, + startFrame=start, + endFrame=end, + **options + ) + + template_data = copy.deepcopy(instance.data["anatomyData"]) + template_data.update({"ext": "abc"}) + templates = instance.context.data["anatomy"].templates["publish"] + published_filename_without_extension = StringTemplate( + templates["file"] + ).format(template_data).replace(".abc", "_proxy") + transfers = [] + destination = os.path.join( + instance.data["resourcesDir"], + filename.replace( + filename.split(".")[0], + published_filename_without_extension + ) + ) + transfers.append((path, destination)) + + for source, destination in transfers: + self.log.debug("Transfer: {} > {}".format(source, destination)) + + instance.data["transfers"] = transfers + def get_members_and_roots(self, instance): return instance[:], instance.data.get("setMembers") From 713ede50049b3df473fb66d2ce8e556bce46df5c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 8 Feb 2023 10:11:10 +0000 Subject: [PATCH 0459/1271] Documentation --- website/docs/artist_hosts_maya.md | 3 +++ website/docs/artist_hosts_maya_arnold.md | 16 ++++++++++++++++ website/docs/assets/maya-pointcache_setup.png | Bin 49860 -> 88602 bytes website/sidebars.js | 1 + 4 files changed, 20 insertions(+) create mode 100644 website/docs/artist_hosts_maya_arnold.md diff --git a/website/docs/artist_hosts_maya.md b/website/docs/artist_hosts_maya.md index 14619e52a1..9fab845e62 100644 --- a/website/docs/artist_hosts_maya.md +++ b/website/docs/artist_hosts_maya.md @@ -308,6 +308,8 @@ Select its root and Go **OpenPype → Create...** and select **Point Cache**. After that, publishing will create corresponding **abc** files. +When creating the instance, a objectset child `proxy` will be created. Meshes in the `proxy` objectset will be the viewport representation where loading supports proxies. Proxy representations are stored as `resources` of the subset. + Example setup: ![Maya - Point Cache Example](assets/maya-pointcache_setup.png) @@ -315,6 +317,7 @@ Example setup: :::note Publish on farm If your studio has Deadline configured, artists could choose to offload potentially long running export of pointache and publish it to the farm. Only thing that is necessary is to toggle `Farm` property in created pointcache instance to True. +::: ### Loading Point Caches diff --git a/website/docs/artist_hosts_maya_arnold.md b/website/docs/artist_hosts_maya_arnold.md new file mode 100644 index 0000000000..b8b8da6d57 --- /dev/null +++ b/website/docs/artist_hosts_maya_arnold.md @@ -0,0 +1,16 @@ +--- +id: artist_hosts_maya_arnold +title: Arnold for Maya +sidebar_label: Arnold +--- +## Arnold Scene Source (.ass) +Arnold Scene Source can be published as a single file or a sequence of files, determined by the frame range. + +When creating the instance, two objectsets are created; `content` and `proxy`. Meshes in the `proxy` objectset will be the viewport representation when loading as `standin`. Proxy representations are stored as `resources` of the subset. + +## Standin +Arnold Scene Source `ass` and Alembic `abc` are supported to load as standins. + +If a subset has a proxy representation, this will be used as display in the viewport. At render time the standin path will be replaced using the recommended string replacement workflow; + +https://help.autodesk.com/view/ARNOL/ENU/?guid=arnold_for_maya_operators_am_Updating_procedural_file_paths_with_string_replace_html diff --git a/website/docs/assets/maya-pointcache_setup.png b/website/docs/assets/maya-pointcache_setup.png index 8904baa239f90f342f7252f5732ccf57dccf2ad7..b2dc12690199c264e6a8ceebbbff4308e08764be 100644 GIT binary patch literal 88602 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%0DJ*m$sy4TvzuBkclL5Ev|*snH)H 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 Zt#D4oNKPwk<*K!O4Q;!AW?@DqLa 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 Date: Wed, 8 Feb 2023 12:07:00 +0100 Subject: [PATCH 0460/1271] Fix - added missed scopes for Slack bot --- openpype/modules/slack/manifest.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 7a65cc5915..233c39fbaf 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,6 +19,8 @@ oauth_config: - chat:write.public - files:write - channels:read + - users:read + - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From e7d60288a12a86e028b692da455ae5ca94ade552 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 12:15:43 +0100 Subject: [PATCH 0461/1271] Fix - added missed scopes for Slack bot --- openpype/modules/slack/manifest.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 7a65cc5915..233c39fbaf 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,6 +19,8 @@ oauth_config: - chat:write.public - files:write - channels:read + - users:read + - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From 1a7f7d310ec02be47e6e95e5e8781d72ba181a33 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 12:21:55 +0100 Subject: [PATCH 0462/1271] OP-4653 - changed exception Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/photoshop/plugins/create/create_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index 8a103ea6c2..9bf47733f5 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -48,7 +48,7 @@ class ImageCreator(Creator): try: group = stub.group_selected_layers(subset_name_from_ui) except: - raise ValueError("Cannot group locked Background layer!") + raise CreatorError("Cannot group locked Background layer!") groups_to_create.append(group) # create empty group if nothing selected From 21d745cc3cc9ad288a1aa0d7eb6329a5aeea7f9e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 12:25:29 +0100 Subject: [PATCH 0463/1271] OP-4822 - remove unwanted variable Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index e7c1899513..ed37ff1897 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -105,7 +105,6 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): jobInfo = {} pluginInfo = {} group = "none" - disable_strict_check_profiles = [] def get_job_info(self): job_info = DeadlineJobInfo(Plugin="MayaBatch") From 3156cc542508949c7786809db94feb0de4c7da35 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 12:39:28 +0100 Subject: [PATCH 0464/1271] OP-4653 - added missed import for CreatorError --- openpype/hosts/photoshop/plugins/create/create_image.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index 9bf47733f5..198c1586dc 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -5,7 +5,8 @@ from openpype.lib import BoolDef from openpype.pipeline import ( Creator, CreatedInstance, - legacy_io + legacy_io, + CreatorError ) from openpype.lib import prepare_template_data from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS From b28f91769367ad9b64eadb9d04f18bb2b4529c20 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 13:37:50 +0100 Subject: [PATCH 0465/1271] Fix - remove minor part in toml Causes issue in create_env and new Poetry --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6e88404700..2fc4f6fe39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.1-nightly.3" # OpenPype +version = "3.15.1" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 6e55202349b5ea4f62001a586e44e8306143fbee Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 8 Feb 2023 14:23:10 +0100 Subject: [PATCH 0466/1271] change used method names from 'isAlive' to 'is_alive' --- openpype/modules/ftrack/ftrack_server/event_server_cli.py | 6 +++--- openpype/modules/ftrack/tray/login_dialog.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/modules/ftrack/ftrack_server/event_server_cli.py b/openpype/modules/ftrack/ftrack_server/event_server_cli.py index 25ebad6658..ad7ffd8e25 100644 --- a/openpype/modules/ftrack/ftrack_server/event_server_cli.py +++ b/openpype/modules/ftrack/ftrack_server/event_server_cli.py @@ -316,7 +316,7 @@ def main_loop(ftrack_url): statuser_failed_count = 0 # If thread failed test Ftrack and Mongo connection - elif not statuser_thread.isAlive(): + elif not statuser_thread.is_alive(): statuser_thread.join() statuser_thread = None ftrack_accessible = False @@ -359,7 +359,7 @@ def main_loop(ftrack_url): storer_failed_count = 0 # If thread failed test Ftrack and Mongo connection - elif not storer_thread.isAlive(): + elif not storer_thread.is_alive(): if storer_thread.mongo_error: raise MongoPermissionsError() storer_thread.join() @@ -396,7 +396,7 @@ def main_loop(ftrack_url): processor_failed_count = 0 # If thread failed test Ftrack and Mongo connection - elif not processor_thread.isAlive(): + elif not processor_thread.is_alive(): if processor_thread.mongo_error: raise Exception( "Exiting because have issue with acces to MongoDB" diff --git a/openpype/modules/ftrack/tray/login_dialog.py b/openpype/modules/ftrack/tray/login_dialog.py index 0e676545f7..f374a71178 100644 --- a/openpype/modules/ftrack/tray/login_dialog.py +++ b/openpype/modules/ftrack/tray/login_dialog.py @@ -259,7 +259,7 @@ class CredentialsDialog(QtWidgets.QDialog): # If there is an existing server thread running we need to stop it. if self._login_server_thread: - if self._login_server_thread.isAlive(): + if self._login_server_thread.is_alive(): self._login_server_thread.stop() self._login_server_thread.join() self._login_server_thread = None From 2b78c9404c587e4a3f3f19402e30c4741a07b3db Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 15:01:02 +0100 Subject: [PATCH 0467/1271] OP-4822 - push property to render instance render instance is created from collected instance explicitly. --- openpype/hosts/maya/plugins/publish/collect_render.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index b1ad3ca58e..fc297ef612 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -318,7 +318,9 @@ class CollectMayaRender(pyblish.api.ContextPlugin): "aovSeparator": layer_render_products.layer_data.aov_separator, # noqa: E501 "renderSetupIncludeLights": render_instance.data.get( "renderSetupIncludeLights" - ) + ), + "strict_error_checking": render_instance.data.get( + "strict_error_checking") } # Collect Deadline url if Deadline module is enabled From 95aff1808fdb27d77b647f5b373c80e27eee56a1 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 8 Feb 2023 22:03:05 +0800 Subject: [PATCH 0468/1271] setting up deadline for 3dsmax --- openpype/hosts/max/api/lib.py | 33 +++++ openpype/hosts/max/api/lib_renderproducts.py | 102 +++++++++++++ openpype/hosts/max/api/lib_rendersettings.py | 125 ++++++++++++++++ .../hosts/max/plugins/create/create_render.py | 33 +++++ .../max/plugins/publish/collect_render.py | 72 +++++++++ .../maya/plugins/publish/collect_render.py | 2 - .../plugins/publish/submit_3dmax_deadline.py | 137 ++++++++++++++++++ .../defaults/project_settings/max.json | 7 + .../schemas/projects_schema/schema_main.json | 4 + .../projects_schema/schema_project_max.json | 52 +++++++ 10 files changed, 565 insertions(+), 2 deletions(-) create mode 100644 openpype/hosts/max/api/lib_renderproducts.py create mode 100644 openpype/hosts/max/api/lib_rendersettings.py create mode 100644 openpype/hosts/max/plugins/create/create_render.py create mode 100644 openpype/hosts/max/plugins/publish/collect_render.py create mode 100644 openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py create mode 100644 openpype/settings/defaults/project_settings/max.json create mode 100644 openpype/settings/entities/schemas/projects_schema/schema_project_max.json diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index 9256ca9ac1..8c421b2f9b 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -120,3 +120,36 @@ def get_all_children(parent, node_type=None): return ([x for x in child_list if rt.superClassOf(x) == node_type] if node_type else child_list) + + +def get_current_renderer(): + """get current renderer""" + return rt.renderers.production + + +def get_default_render_folder(project_setting=None): + return (project_setting["max"] + ["RenderSettings"] + ["default_render_image_folder"] + ) + + +def set_framerange(startFrame, endFrame): + """Get/set the type of time range to be rendered. + + Possible values are: + + 1 -Single frame. + + 2 -Active time segment ( animationRange ). + + 3 -User specified Range. + + 4 -User specified Frame pickup string (for example "1,3,5-12"). + """ + # hard-code, there should be a custom setting for this + rt.rendTimeType = 4 + if startFrame is not None and endFrame is not None: + frameRange = "{0}-{1}".format(startFrame, endFrame) + rt.rendPickupFrames = frameRange + diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py new file mode 100644 index 0000000000..f3bb8bdad1 --- /dev/null +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -0,0 +1,102 @@ +# Render Element Example : For scanline render, VRay +# https://help.autodesk.com/view/MAXDEV/2022/ENU/?guid=GUID-E8F75D47-B998-4800-A3A5-610E22913CFC +# arnold +# https://help.autodesk.com/view/ARNOL/ENU/?guid=arnold_for_3ds_max_ax_maxscript_commands_ax_renderview_commands_html +import os +from pymxs import runtime as rt +from openpype.hosts.max.api.lib import ( + get_current_renderer, + get_default_render_folder +) +from openpype.pipeline.context_tools import get_current_project_asset +from openpype.settings import get_project_settings +from openpype.pipeline import legacy_io + + +class RenderProducts(object): + + @classmethod + def __init__(self, project_settings=None): + self._project_settings = project_settings + if not self._project_settings: + self._project_settings = get_project_settings( + legacy_io.Session["AVALON_PROJECT"] + ) + + def render_product(self, container): + folder = rt.maxFilePath + folder = folder.replace("\\", "/") + setting = self._project_settings + render_folder = get_default_render_folder(setting) + + output_file = os.path.join(folder, render_folder, container) + context = get_current_project_asset() + startFrame = context["data"].get("frameStart") + endFrame = context["data"].get("frameEnd") + 1 + + img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] + full_render_list = self.beauty_render_product(output_file, + startFrame, + endFrame, + img_fmt) + renderer_class = get_current_renderer() + renderer = str(renderer_class).split(":")[0] + + if renderer == "VUE_File_Renderer": + return full_render_list + + if ( + renderer == "ART_Renderer" or + renderer == "Redshift Renderer" or + renderer == "V_Ray_6_Hotfix_3" or + renderer == "V_Ray_GPU_6_Hotfix_3" or + renderer == "Default_Scanline_Renderer" or + renderer == "Quicksilver_Hardware_Renderer" + ): + render_elem_list = self.render_elements_product(output_file, + startFrame, + endFrame, + img_fmt) + for render_elem in render_elem_list: + full_render_list.append(render_elem) + return full_render_list + + if renderer == "Arnold": + return full_render_list + + + def beauty_render_product(self, folder, startFrame, endFrame, fmt): + # get the beauty + beauty_frame_range = list() + + for f in range(startFrame, endFrame): + beauty = "{0}.{1}.{2}".format(folder, str(f), fmt) + beauty = beauty.replace("\\", "/") + beauty_frame_range.append(beauty) + + return beauty_frame_range + + # TODO: Get the arnold render product + def render_elements_product(self, folder, startFrame, endFrame, fmt): + """Get all the render element output files. """ + render_dirname = list() + + render_elem = rt.maxOps.GetCurRenderElementMgr() + render_elem_num = render_elem.NumRenderElements() + # get render elements from the renders + for i in range(render_elem_num): + renderlayer_name = render_elem.GetRenderElement(i) + target, renderpass = str(renderlayer_name).split(":") + + render_dir = os.path.join(folder, renderpass) + if renderlayer_name.enabled: + for f in range(startFrame, endFrame): + render_element = "{0}.{1}.{2}".format(render_dir, str(f), fmt) + render_element = render_element.replace("\\", "/") + render_dirname.append(render_element) + + return render_dirname + + def image_format(self): + img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] + return img_fmt diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py new file mode 100644 index 0000000000..8c8a82ae66 --- /dev/null +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -0,0 +1,125 @@ +import os +from pymxs import runtime as rt +from openpype.lib import Logger +from openpype.settings import get_project_settings +from openpype.pipeline import legacy_io +from openpype.pipeline.context_tools import get_current_project_asset + +from openpype.hosts.max.api.lib import ( + set_framerange, + get_current_renderer, + get_default_render_folder +) + + +class RenderSettings(object): + + log = Logger.get_logger("RenderSettings") + + _aov_chars = { + "dot": ".", + "dash": "-", + "underscore": "_" + } + + @classmethod + def __init__(self, project_settings=None): + self._project_settings = project_settings + if not self._project_settings: + self._project_settings = get_project_settings( + legacy_io.Session["AVALON_PROJECT"] + ) + + def set_render_camera(self, selection): + for sel in selection: + # to avoid Attribute Error from pymxs wrapper + found = False + if rt.classOf(sel) in rt.Camera.classes: + found = True + rt.viewport.setCamera(sel) + break + if not found: + raise RuntimeError("Camera not found") + + + def set_renderoutput(self, container): + folder = rt.maxFilePath + # hard-coded, should be customized in the setting + folder = folder.replace("\\", "/") + # hard-coded, set the renderoutput path + setting = self._project_settings + render_folder = get_default_render_folder(setting) + output_dir = os.path.join(folder, render_folder) + if not os.path.exists(output_dir): + os.makedirs(output_dir) + # hard-coded, should be customized in the setting + context = get_current_project_asset() + + # get project reoslution + width = context["data"].get("resolutionWidth") + height = context["data"].get("resolutionHeight") + # Set Frame Range + startFrame = context["data"].get("frameStart") + endFrame = context["data"].get("frameEnd") + set_framerange(startFrame, endFrame) + # get the production render + renderer_class = get_current_renderer() + renderer = str(renderer_class).split(":")[0] + + img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] + output = os.path.join(output_dir, container) + try: + aov_separator = self._aov_chars[( + self._project_settings["maya"] + ["RenderSettings"] + ["aov_separator"] + )] + except KeyError: + aov_separator = "." + outputFilename = "{0}.{1}".format(output, img_fmt) + outputFilename = outputFilename.replace("{aov_separator}", aov_separator) + rt.rendOutputFilename = outputFilename + if renderer == "VUE_File_Renderer": + return + # TODO: Finish the arnold render setup + if renderer == "Arnold": + return + + if ( + renderer == "ART_Renderer" or + renderer == "Redshift Renderer" or + renderer == "V_Ray_6_Hotfix_3" or + renderer == "V_Ray_GPU_6_Hotfix_3" or + renderer == "Default_Scanline_Renderer" or + renderer == "Quicksilver_Hardware_Renderer" + ): + self.render_element_layer(output, width, height, img_fmt) + + rt.rendSaveFile= True + + + def render_element_layer(self, dir, width, height, ext): + """For Renderers with render elements""" + rt.renderWidth = width + rt.renderHeight = height + render_elem = rt.maxOps.GetCurRenderElementMgr() + render_elem_num = render_elem.NumRenderElements() + if render_elem_num < 0: + return + + for i in range(render_elem_num): + renderlayer_name = render_elem.GetRenderElement(i) + target, renderpass = str(renderlayer_name).split(":") + render_element = os.path.join(dir, renderpass) + aov_name = "{0}.{1}".format(render_element, ext) + try: + aov_separator = self._aov_chars[( + self._project_settings["maya"] + ["RenderSettings"] + ["aov_separator"] + )] + except KeyError: + aov_separator = "." + + aov_name = aov_name.replace("{aov_separator}", aov_separator) + render_elem.SetRenderElementFileName(i, aov_name) diff --git a/openpype/hosts/max/plugins/create/create_render.py b/openpype/hosts/max/plugins/create/create_render.py new file mode 100644 index 0000000000..76c10ca4a9 --- /dev/null +++ b/openpype/hosts/max/plugins/create/create_render.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +"""Creator plugin for creating camera.""" +from openpype.hosts.max.api import plugin +from openpype.pipeline import CreatedInstance +from openpype.hosts.max.api.lib_rendersettings import RenderSettings + + +class CreateRender(plugin.MaxCreator): + identifier = "io.openpype.creators.max.render" + label = "Render" + family = "maxrender" + icon = "gear" + + def create(self, subset_name, instance_data, pre_create_data): + from pymxs import runtime as rt + sel_obj = list(rt.selection) + instance = super(CreateRender, self).create( + subset_name, + instance_data, + pre_create_data) # type: CreatedInstance + container_name = instance.data.get("instance_node") + container = rt.getNodeByName(container_name) + # TODO: Disable "Add to Containers?" Panel + # parent the selected cameras into the container + for obj in sel_obj: + obj.parent = container + # for additional work on the node: + # instance_node = rt.getNodeByName(instance.get("instance_node")) + + # set viewport camera for rendering(mandatory for deadline) + RenderSettings().set_render_camera(sel_obj) + # set output paths for rendering(mandatory for deadline) + RenderSettings().set_renderoutput(container_name) diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py new file mode 100644 index 0000000000..fc44c01206 --- /dev/null +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +"""Collect Render""" +import os +import pyblish.api + +from pymxs import runtime as rt +from openpype.pipeline import legacy_io +from openpype.hosts.max.api.lib import get_current_renderer +from openpype.hosts.max.api.lib_renderproducts import RenderProducts + + +class CollectRender(pyblish.api.InstancePlugin): + """Collect Render for Deadline""" + + order = pyblish.api.CollectorOrder + 0.01 + label = "Collect 3dmax Render Layers" + hosts = ['max'] + families = ["maxrender"] + + def process(self, instance): + context = instance.context + folder = rt.maxFilePath + file = rt.maxFileName + current_file = os.path.join(folder, file) + filepath = current_file.replace("\\", "/") + + context.data['currentFile'] = current_file + asset = legacy_io.Session["AVALON_ASSET"] + + render_layer_files = RenderProducts().render_product(instance.name) + folder = folder.replace("\\", "/") + + imgFormat = RenderProducts().image_format() + renderer_class = get_current_renderer() + renderer_name = str(renderer_class).split(":")[0] + # setup the plugin as 3dsmax for the internal renderer + if ( + renderer_name == "ART_Renderer" or + renderer_name == "Default_Scanline_Renderer" or + renderer_name == "Quicksilver_Hardware_Renderer" + ): + plugin = "3dsmax" + + if ( + renderer_name == "V_Ray_6_Hotfix_3" or + renderer_name == "V_Ray_GPU_6_Hotfix_3" + ): + plugin = "Vray" + + if renderer_name == "Redshift Renderer": + plugin = "redshift" + + if renderer_name == "Arnold": + plugin = "arnold" + + # https://forums.autodesk.com/t5/3ds-max-programming/pymxs-quickrender-animation-range/td-p/11216183 + + data = { + "subset": instance.name, + "asset": asset, + "publish": True, + "imageFormat": imgFormat, + "family": 'maxrender', + "families": ['maxrender'], + "source": filepath, + "files": render_layer_files, + "plugin": plugin, + "frameStart": context.data['frameStart'], + "frameEnd": context.data['frameEnd'] + } + self.log.info("data: {0}".format(data)) + instance.data.update(data) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index b1ad3ca58e..c5fce219fa 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -184,7 +184,6 @@ class CollectMayaRender(pyblish.api.ContextPlugin): self.log.info("multipart: {}".format( multipart)) assert exp_files, "no file names were generated, this is bug" - self.log.info(exp_files) # if we want to attach render to subset, check if we have AOV's # in expectedFiles. If so, raise error as we cannot attach AOV @@ -320,7 +319,6 @@ class CollectMayaRender(pyblish.api.ContextPlugin): "renderSetupIncludeLights" ) } - # Collect Deadline url if Deadline module is enabled deadline_settings = ( context.data["system_settings"]["modules"]["deadline"] diff --git a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py new file mode 100644 index 0000000000..7e7173e4ce --- /dev/null +++ b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py @@ -0,0 +1,137 @@ +import os +import json +import getpass + +import requests +import pyblish.api + + +from openpype.pipeline import legacy_io + + +class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): + """ + 3DMax File Submit Render Deadline + + """ + + label = "Submit 3DsMax Render to Deadline" + order = pyblish.api.IntegratorOrder + hosts = ["max"] + families = ["maxrender"] + targets = ["local"] + + def process(self, instance): + context = instance.context + filepath = context.data["currentFile"] + filename = os.path.basename(filepath) + comment = context.data.get("comment", "") + deadline_user = context.data.get("deadlineUser", getpass.getuser()) + jobname ="{0} - {1}".format(filename, instance.name) + + # StartFrame to EndFrame + frames = "{start}-{end}".format( + start=int(instance.data["frameStart"]), + end=int(instance.data["frameEnd"]) + ) + + payload = { + "JobInfo": { + # Top-level group name + "BatchName": filename, + + # Job name, as seen in Monitor + "Name": jobname, + + # Arbitrary username, for visualisation in Monitor + "UserName": deadline_user, + + "Plugin": instance.data["plugin"], + "Pool": instance.data.get("primaryPool"), + "secondaryPool": instance.data.get("secondaryPool"), + "Frames": frames, + "ChunkSize" : instance.data.get("chunkSize", 10), + "Comment": comment + }, + "PluginInfo": { + # Input + "SceneFile": instance.data["source"], + "Version": "2023", + "SaveFile" : True, + # Mandatory for Deadline + # Houdini version without patch number + + "IgnoreInputs": True + }, + + # Mandatory for Deadline, may be empty + "AuxFiles": [] + } + # Include critical environment variables with submission + api.Session + keys = [ + # Submit along the current Avalon tool setup that we launched + # this application with so the Render Slave can build its own + # similar environment using it, e.g. "maya2018;vray4.x;yeti3.1.9" + "AVALON_TOOLS", + "OPENPYPE_VERSION" + ] + # Add mongo url if it's enabled + if context.data.get("deadlinePassMongoUrl"): + keys.append("OPENPYPE_MONGO") + + environment = dict({key: os.environ[key] for key in keys + if key in os.environ}, **legacy_io.Session) + + payload["JobInfo"].update({ + "EnvironmentKeyValue%d" % index: "{key}={value}".format( + key=key, + value=environment[key] + ) for index, key in enumerate(environment) + }) + + # Include OutputFilename entries + # The first entry also enables double-click to preview rendered + # frames from Deadline Monitor + output_data = {} + # need to be fixed + for i, filepath in enumerate(instance.data["files"]): + dirname = os.path.dirname(filepath) + fname = os.path.basename(filepath) + output_data["OutputDirectory%d" % i] = dirname.replace("\\", "/") + output_data["OutputFilename%d" % i] = fname + + if not os.path.exists(dirname): + self.log.info("Ensuring output directory exists: %s" % + dirname) + os.makedirs(dirname) + + payload["JobInfo"].update(output_data) + + self.submit(instance, payload) + + def submit(self, instance, payload): + + context = instance.context + deadline_url = context.data.get("defaultDeadline") + deadline_url = instance.data.get( + "deadlineUrl", deadline_url) + + assert deadline_url, "Requires Deadline Webservice URL" + + plugin = payload["JobInfo"]["Plugin"] + self.log.info("Using Render Plugin : {}".format(plugin)) + + self.log.info("Submitting..") + self.log.debug(json.dumps(payload, indent=4, sort_keys=True)) + + # E.g. http://192.168.0.1:8082/api/jobs + url = "{}/api/jobs".format(deadline_url) + response = requests.post(url, json=payload, verify=False) + if not response.ok: + raise Exception(response.text) + # Store output dir for unified publisher (filesequence) + expected_files = instance.data["files"] + self.log.info("exp:{}".format(expected_files)) + output_dir = os.path.dirname(expected_files[0]) + instance.data["outputDir"] = output_dir + instance.data["deadlineSubmissionJob"] = response.json() diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json new file mode 100644 index 0000000000..651a074a08 --- /dev/null +++ b/openpype/settings/defaults/project_settings/max.json @@ -0,0 +1,7 @@ +{ + "RenderSettings": { + "default_render_image_folder": "renders/max", + "aov_separator": "underscore", + "image_format": "exr" + } +} \ 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 0b9fbf7470..ebe59c7942 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_main.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_main.json @@ -82,6 +82,10 @@ "type": "schema", "name": "schema_project_slack" }, + { + "type": "schema", + "name": "schema_project_max" + }, { "type": "schema", "name": "schema_project_maya" diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_max.json b/openpype/settings/entities/schemas/projects_schema/schema_project_max.json new file mode 100644 index 0000000000..3d4cd5c54a --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_max.json @@ -0,0 +1,52 @@ +{ + "type": "dict", + "collapsible": true, + "key": "max", + "label": "Max", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "RenderSettings", + "label": "Render Settings", + "children": [ + { + "type": "text", + "key": "default_render_image_folder", + "label": "Default render image folder" + }, + { + "key": "aov_separator", + "label": "AOV Separator character", + "type": "enum", + "multiselection": false, + "default": "underscore", + "enum_items": [ + {"dash": "- (dash)"}, + {"underscore": "_ (underscore)"}, + {"dot": ". (dot)"} + ] + }, + { + "key": "image_format", + "label": "Output Image Format", + "type": "enum", + "multiselection": false, + "defaults": "exr", + "enum_items": [ + {"avi": "avi"}, + {"bmp": "bmp"}, + {"exr": "exr"}, + {"tif": "tif"}, + {"tiff": "tiff"}, + {"jpg": "jpg"}, + {"png": "png"}, + {"tga": "tga"}, + {"dds": "dds"} + ] + } + ] + } + ] +} \ No newline at end of file From 91e1d28a9fdc15e7841cd55237f81607dfe0a7b8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 15:09:26 +0100 Subject: [PATCH 0469/1271] OP-4513 - platform specific logic Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../deadline/repository/custom/plugins/GlobalJobPreLoad.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 108c418e7b..b0560ce1e8 100644 --- a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -191,9 +191,8 @@ def get_openpype_executable(): "OpenPypeInstallationDirs", "") # clean '\ ' for MacOS pasting - if exe_list: + if platform.system().lower() == "darwin": exe_list = exe_list.replace("\\ ", " ") - if dir_list: dir_list = dir_list.replace("\\ ", " ") return exe_list, dir_list From 1e5ec12070c36da7dc16c0eb2fbf0646424eb4ed Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 8 Feb 2023 22:23:47 +0800 Subject: [PATCH 0470/1271] hound fix --- openpype/hosts/max/api/lib.py | 6 ++---- openpype/hosts/max/api/lib_renderproducts.py | 15 +++++++++------ openpype/hosts/max/api/lib_rendersettings.py | 18 +++++++++--------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index 8c421b2f9b..0477b43182 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -130,8 +130,7 @@ def get_current_renderer(): def get_default_render_folder(project_setting=None): return (project_setting["max"] ["RenderSettings"] - ["default_render_image_folder"] - ) + ["default_render_image_folder"]) def set_framerange(startFrame, endFrame): @@ -147,9 +146,8 @@ def set_framerange(startFrame, endFrame): 4 -User specified Frame pickup string (for example "1,3,5-12"). """ - # hard-code, there should be a custom setting for this + # hard-code, there should be a custom setting for this rt.rendTimeType = 4 if startFrame is not None and endFrame is not None: frameRange = "{0}-{1}".format(startFrame, endFrame) rt.rendPickupFrames = frameRange - diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index f3bb8bdad1..3b7767478d 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -34,7 +34,7 @@ class RenderProducts(object): startFrame = context["data"].get("frameStart") endFrame = context["data"].get("frameEnd") + 1 - img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] + img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa full_render_list = self.beauty_render_product(output_file, startFrame, endFrame, @@ -52,7 +52,7 @@ class RenderProducts(object): renderer == "V_Ray_GPU_6_Hotfix_3" or renderer == "Default_Scanline_Renderer" or renderer == "Quicksilver_Hardware_Renderer" - ): + ): render_elem_list = self.render_elements_product(output_file, startFrame, endFrame, @@ -64,13 +64,14 @@ class RenderProducts(object): if renderer == "Arnold": return full_render_list - def beauty_render_product(self, folder, startFrame, endFrame, fmt): # get the beauty beauty_frame_range = list() for f in range(startFrame, endFrame): - beauty = "{0}.{1}.{2}".format(folder, str(f), fmt) + beauty = "{0}.{1}.{2}".format(folder, + str(f), + fmt) beauty = beauty.replace("\\", "/") beauty_frame_range.append(beauty) @@ -83,7 +84,7 @@ class RenderProducts(object): render_elem = rt.maxOps.GetCurRenderElementMgr() render_elem_num = render_elem.NumRenderElements() - # get render elements from the renders + # get render elements from the renders for i in range(render_elem_num): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") @@ -91,7 +92,9 @@ class RenderProducts(object): render_dir = os.path.join(folder, renderpass) if renderlayer_name.enabled: for f in range(startFrame, endFrame): - render_element = "{0}.{1}.{2}".format(render_dir, str(f), fmt) + render_element = "{0}.{1}.{2}".format(render_dir, + str(f), + fmt) render_element = render_element.replace("\\", "/") render_dirname.append(render_element) diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 8c8a82ae66..11dd005ad7 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -41,7 +41,6 @@ class RenderSettings(object): if not found: raise RuntimeError("Camera not found") - def set_renderoutput(self, container): folder = rt.maxFilePath # hard-coded, should be customized in the setting @@ -66,7 +65,7 @@ class RenderSettings(object): renderer_class = get_current_renderer() renderer = str(renderer_class).split(":")[0] - img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] + img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa output = os.path.join(output_dir, container) try: aov_separator = self._aov_chars[( @@ -77,7 +76,8 @@ class RenderSettings(object): except KeyError: aov_separator = "." outputFilename = "{0}.{1}".format(output, img_fmt) - outputFilename = outputFilename.replace("{aov_separator}", aov_separator) + outputFilename = outputFilename.replace("{aov_separator}", + aov_separator) rt.rendOutputFilename = outputFilename if renderer == "VUE_File_Renderer": return @@ -92,11 +92,10 @@ class RenderSettings(object): renderer == "V_Ray_GPU_6_Hotfix_3" or renderer == "Default_Scanline_Renderer" or renderer == "Quicksilver_Hardware_Renderer" - ): + ): self.render_element_layer(output, width, height, img_fmt) - rt.rendSaveFile= True - + rt.rendSaveFile = True def render_element_layer(self, dir, width, height, ext): """For Renderers with render elements""" @@ -115,11 +114,12 @@ class RenderSettings(object): try: aov_separator = self._aov_chars[( self._project_settings["maya"] - ["RenderSettings"] - ["aov_separator"] + ["RenderSettings"] + ["aov_separator"] )] except KeyError: aov_separator = "." - aov_name = aov_name.replace("{aov_separator}", aov_separator) + aov_name = aov_name.replace("{aov_separator}", + aov_separator) render_elem.SetRenderElementFileName(i, aov_name) From 0ea62e664f5f0c54c5593147f69ae6740e07380b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 8 Feb 2023 22:29:28 +0800 Subject: [PATCH 0471/1271] hound fix --- openpype/hosts/max/api/lib_rendersettings.py | 6 +++--- openpype/hosts/max/plugins/publish/collect_render.py | 3 ++- .../deadline/plugins/publish/submit_3dmax_deadline.py | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 11dd005ad7..90398e841c 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -35,9 +35,9 @@ class RenderSettings(object): # to avoid Attribute Error from pymxs wrapper found = False if rt.classOf(sel) in rt.Camera.classes: - found = True - rt.viewport.setCamera(sel) - break + found = True + rt.viewport.setCamera(sel) + break if not found: raise RuntimeError("Camera not found") diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index fc44c01206..cda774bf11 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -53,7 +53,8 @@ class CollectRender(pyblish.api.InstancePlugin): if renderer_name == "Arnold": plugin = "arnold" - # https://forums.autodesk.com/t5/3ds-max-programming/pymxs-quickrender-animation-range/td-p/11216183 + # https://forums.autodesk.com/t5/3ds-max-programming/ + # pymxs-quickrender-animation-range/td-p/11216183 data = { "subset": instance.name, diff --git a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py index 7e7173e4ce..faeb071524 100644 --- a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py @@ -27,7 +27,7 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): filename = os.path.basename(filepath) comment = context.data.get("comment", "") deadline_user = context.data.get("deadlineUser", getpass.getuser()) - jobname ="{0} - {1}".format(filename, instance.name) + jobname = "{0} - {1}".format(filename, instance.name) # StartFrame to EndFrame frames = "{start}-{end}".format( @@ -50,14 +50,14 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): "Pool": instance.data.get("primaryPool"), "secondaryPool": instance.data.get("secondaryPool"), "Frames": frames, - "ChunkSize" : instance.data.get("chunkSize", 10), + "ChunkSize": instance.data.get("chunkSize", 10), "Comment": comment }, "PluginInfo": { # Input "SceneFile": instance.data["source"], "Version": "2023", - "SaveFile" : True, + "SaveFile": True, # Mandatory for Deadline # Houdini version without patch number @@ -67,7 +67,7 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): # Mandatory for Deadline, may be empty "AuxFiles": [] } - # Include critical environment variables with submission + api.Session + # Include critical environment variables with submission + api.Session keys = [ # Submit along the current Avalon tool setup that we launched # this application with so the Render Slave can build its own From 6a55e2a9a3472214c077a66954ce50d1665b0bfa Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 8 Feb 2023 22:33:10 +0800 Subject: [PATCH 0472/1271] hound fix --- openpype/hosts/max/api/lib_renderproducts.py | 2 +- openpype/hosts/max/plugins/publish/collect_render.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index 3b7767478d..a7361a5a25 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -101,5 +101,5 @@ class RenderProducts(object): return render_dirname def image_format(self): - img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] + img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa return img_fmt diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index cda774bf11..dd85afd586 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -53,9 +53,6 @@ class CollectRender(pyblish.api.InstancePlugin): if renderer_name == "Arnold": plugin = "arnold" - # https://forums.autodesk.com/t5/3ds-max-programming/ - # pymxs-quickrender-animation-range/td-p/11216183 - data = { "subset": instance.name, "asset": asset, From 6894e17bbc9881b847a3a141574d336441b35f84 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 16:18:45 +0100 Subject: [PATCH 0473/1271] Refactor - use values from context if available Remove unnecessary keys from data --- openpype/plugins/load/add_site.py | 33 +++++++++++++++++++++++-------- openpype/tools/loader/widgets.py | 16 +++++++-------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/openpype/plugins/load/add_site.py b/openpype/plugins/load/add_site.py index 860e0ef15e..f0c9887b66 100644 --- a/openpype/plugins/load/add_site.py +++ b/openpype/plugins/load/add_site.py @@ -40,16 +40,29 @@ class AddSyncSite(load.LoaderPlugin): return self._sync_server def load(self, context, name=None, namespace=None, data=None): + """"Adds site skeleton information on representation_id + + Looks for loaded containers for workfile, adds them site skeleton too + (eg. they should be downloaded too). + Handles hero versions (for representation_id and referenced subsets) + Args: + context (dict): + name (str): + namespace (str): + data (dict): expects {"site_name": SITE_NAME_TO_ADD} + """ # self.log wont propagate print("Adding {} to representation: {}".format( data["site_name"], data["_id"])) - family = context["representation"]["context"]["family"] - project_name = data["project_name"] - repre_id = data["_id"] + project_name = context["project"]["name"] + repre_doc = context["representation"] + family = repre_doc["context"]["family"] + repre_id = [repre_doc["_id"]] site_name = data["site_name"] representation_ids = self._add_hero_representation_ids(project_name, - repre_id) + repre_id, + repre_doc) for repre_id in representation_ids: self.sync_server.add_site(project_name, repre_id, site_name, @@ -79,20 +92,24 @@ class AddSyncSite(load.LoaderPlugin): """No real file loading""" return "" - def _add_hero_representation_ids(self, project_name, repre_id): + def _add_hero_representation_ids(self, project_name, repre_id, + repre_doc=None): """Find hero version if exists for repre_id. Args: project_name (str) repre_id (ObjectId) + repre_doc (dict): repre document for 'repre_id', might be collected + previously Returns: (list): at least [repre_id] if no hero version found """ representation_ids = [repre_id] - repre_doc = get_representation_by_id( - project_name, repre_id, fields=["_id", "parent", "name"] - ) + if not repre_doc: + repre_doc = get_representation_by_id( + project_name, repre_id, fields=["_id", "parent", "name"] + ) version_doc = get_version_by_id(project_name, repre_doc["parent"]) if version_doc["type"] != "hero_version": diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index faef6c8a26..dbf2feb624 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -1468,23 +1468,21 @@ class RepresentationWidget(QtWidgets.QWidget): repre_ids = [] data_by_repre_id = {} selected_side = action_representation.get("selected_side") + site_name = "{}_site_name".format(selected_side) is_sync_loader = tools_lib.is_sync_loader(loader) for item in items: - item_id = item.get("_id") - repre_ids.append(item_id) + repre_id = item["_id"] + repre_ids.append(repre_id) if not is_sync_loader: continue - site_name = "{}_site_name".format(selected_side) data_site_name = item.get(site_name) if not data_site_name: continue - data_by_repre_id[item_id] = { - "_id": item_id, - "site_name": data_site_name, - "project_name": self.dbcon.active_project() + data_by_repre_id[repre_id] = { + "site_name": data_site_name } repre_contexts = get_repres_contexts(repre_ids, self.dbcon) @@ -1574,8 +1572,8 @@ def _load_representations_by_loader(loader, repre_contexts, version_name = version_doc.get("name") try: if data_by_repre_id: - _id = repre_context["representation"]["_id"] - data = data_by_repre_id.get(_id) + repre_id = repre_context["representation"]["_id"] + data = data_by_repre_id.get(repre_id) options.update(data) load_with_repre_context( loader, From d9877ae62323cea4099e9c01f2e47caffa4f5d4d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 16:54:20 +0100 Subject: [PATCH 0474/1271] Refactor - remove explict handling of hero version If representation_id should be added(downloaded) it shouldn't download hero version if downloaded repre is actually latest, eg hero version. They files are completely separate. Hero version should be downloaded explicitly in Loader. --- openpype/plugins/load/add_site.py | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/openpype/plugins/load/add_site.py b/openpype/plugins/load/add_site.py index f0c9887b66..98a390fb30 100644 --- a/openpype/plugins/load/add_site.py +++ b/openpype/plugins/load/add_site.py @@ -44,7 +44,6 @@ class AddSyncSite(load.LoaderPlugin): Looks for loaded containers for workfile, adds them site skeleton too (eg. they should be downloaded too). - Handles hero versions (for representation_id and referenced subsets) Args: context (dict): name (str): @@ -60,13 +59,8 @@ class AddSyncSite(load.LoaderPlugin): repre_id = [repre_doc["_id"]] site_name = data["site_name"] - representation_ids = self._add_hero_representation_ids(project_name, - repre_id, - repre_doc) - - for repre_id in representation_ids: - self.sync_server.add_site(project_name, repre_id, site_name, - force=True) + self.sync_server.add_site(project_name, repre_id, site_name, + force=True) if family == "workfile": links = get_linked_representation_id( @@ -76,12 +70,9 @@ class AddSyncSite(load.LoaderPlugin): ) for link_repre_id in links: try: - representation_ids = self._add_hero_representation_ids( - project_name, link_repre_id) - for repre_id in representation_ids: - self.sync_server.add_site(project_name, repre_id, - site_name, - force=False) + self.sync_server.add_site(project_name, link_repre_id, + site_name, + force=False) except SiteAlreadyPresentError: # do not add/reset working site for references self.log.debug("Site present", exc_info=True) @@ -92,24 +83,20 @@ class AddSyncSite(load.LoaderPlugin): """No real file loading""" return "" - def _add_hero_representation_ids(self, project_name, repre_id, - repre_doc=None): + def _add_hero_representation_ids(self, project_name, repre_id): """Find hero version if exists for repre_id. Args: project_name (str) repre_id (ObjectId) - repre_doc (dict): repre document for 'repre_id', might be collected - previously Returns: (list): at least [repre_id] if no hero version found """ representation_ids = [repre_id] - if not repre_doc: - repre_doc = get_representation_by_id( - project_name, repre_id, fields=["_id", "parent", "name"] - ) + repre_doc = get_representation_by_id( + project_name, repre_id, fields=["_id", "parent", "name"] + ) version_doc = get_version_by_id(project_name, repre_doc["parent"]) if version_doc["type"] != "hero_version": From 4edf16cad6e9b3cab480c57068783af57d0ccd69 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 17:26:48 +0100 Subject: [PATCH 0475/1271] Fix - updated usage of correct variables --- openpype/plugins/load/add_site.py | 8 +++++--- openpype/plugins/load/remove_site.py | 22 +++++++++++++++------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/openpype/plugins/load/add_site.py b/openpype/plugins/load/add_site.py index 98a390fb30..38c27f9079 100644 --- a/openpype/plugins/load/add_site.py +++ b/openpype/plugins/load/add_site.py @@ -51,13 +51,13 @@ class AddSyncSite(load.LoaderPlugin): data (dict): expects {"site_name": SITE_NAME_TO_ADD} """ # self.log wont propagate - print("Adding {} to representation: {}".format( - data["site_name"], data["_id"])) project_name = context["project"]["name"] repre_doc = context["representation"] family = repre_doc["context"]["family"] - repre_id = [repre_doc["_id"]] + repre_id = repre_doc["_id"] site_name = data["site_name"] + print("Adding {} to representation: {}".format( + data["site_name"], repre_id)) self.sync_server.add_site(project_name, repre_id, site_name, force=True) @@ -70,6 +70,8 @@ class AddSyncSite(load.LoaderPlugin): ) for link_repre_id in links: try: + print("Adding {} to linked representation: {}".format( + data["site_name"], link_repre_id)) self.sync_server.add_site(project_name, link_repre_id, site_name, force=False) diff --git a/openpype/plugins/load/remove_site.py b/openpype/plugins/load/remove_site.py index c5f442b2f5..bea8b1b346 100644 --- a/openpype/plugins/load/remove_site.py +++ b/openpype/plugins/load/remove_site.py @@ -3,7 +3,10 @@ from openpype.pipeline import load class RemoveSyncSite(load.LoaderPlugin): - """Remove sync site and its files on representation""" + """Remove sync site and its files on representation. + + Removes files only on local site! + """ representations = ["*"] families = ["*"] @@ -24,13 +27,18 @@ class RemoveSyncSite(load.LoaderPlugin): return self._sync_server def load(self, context, name=None, namespace=None, data=None): - self.log.info("Removing {} on representation: {}".format( - data["site_name"], data["_id"])) - self.sync_server.remove_site(data["project_name"], - data["_id"], - data["site_name"], + project_name = context["project"]["name"] + repre_doc = context["representation"] + repre_id = repre_doc["_id"] + site_name = data["site_name"] + + print("Removing {} on representation: {}".format(site_name, repre_id)) + + self.sync_server.remove_site(project_name, + repre_id, + site_name, True) - self.log.debug("Site added.") + self.log.debug("Site removed.") def filepath_from_context(self, context): """No real file loading""" From f346ace6a12557879cb753c972cead6ea1f4e510 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 17:36:51 +0100 Subject: [PATCH 0476/1271] OP-4653 - removed obsolete import Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/photoshop/plugins/create/create_image.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index 198c1586dc..cdea82cb05 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -5,7 +5,6 @@ from openpype.lib import BoolDef from openpype.pipeline import ( Creator, CreatedInstance, - legacy_io, CreatorError ) from openpype.lib import prepare_template_data From b1ef1b751f5bdf3df1274b524ac55f34f4b1c51d Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Wed, 8 Feb 2023 19:11:20 +0100 Subject: [PATCH 0477/1271] Abstract get_representation_path function and use it on shotgrid to fix remote errors with data instances not having 'published_path' --- .../publish/integrate_ftrack_instances.py | 52 ++----------------- .../publish/integrate_shotgrid_publish.py | 4 +- .../publish/integrate_shotgrid_version.py | 48 ++++++++++++----- .../plugins/publish/integrate_slack_api.py | 11 ++-- openpype/plugins/publish/integrate.py | 46 ++++++++++++++++ 5 files changed, 92 insertions(+), 69 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py index 2d06e2ab02..c3baecec67 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py @@ -10,6 +10,7 @@ from openpype.lib.transcoding import ( ) from openpype.lib.profiles_filtering import filter_profiles from openpype.lib.transcoding import VIDEO_EXTENSIONS +from openpype.plugins.publish.integrate import get_representation_path class IntegrateFtrackInstance(pyblish.api.InstancePlugin): @@ -153,7 +154,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): if not review_representations or has_movie_review: for repre in thumbnail_representations: - repre_path = self._get_repre_path(instance, repre, False) + repre_path = get_representation_path(instance, repre, False) if not repre_path: self.log.warning( "Published path is not set and source was removed." @@ -210,7 +211,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): "from {}".format(repre)) continue - repre_path = self._get_repre_path(instance, repre, False) + repre_path = get_representation_path(instance, repre, False) if not repre_path: self.log.warning( "Published path is not set and source was removed." @@ -324,7 +325,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): # Add others representations as component for repre in other_representations: - published_path = self._get_repre_path(instance, repre, True) + published_path = get_representation_path(instance, repre, True) if not published_path: continue # Create copy of base comp item and append it @@ -364,51 +365,6 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): def _collect_additional_metadata(self, streams): pass - def _get_repre_path(self, instance, repre, only_published): - """Get representation path that can be used for integration. - - When 'only_published' is set to true the validation of path is not - relevant. In that case we just need what is set in 'published_path' - as "reference". The reference is not used to get or upload the file but - for reference where the file was published. - - Args: - instance (pyblish.Instance): Processed instance object. Used - for source of staging dir if representation does not have - filled it. - repre (dict): Representation on instance which could be and - could not be integrated with main integrator. - only_published (bool): Care only about published paths and - ignore if filepath is not existing anymore. - - Returns: - str: Path to representation file. - None: Path is not filled or does not exists. - """ - - published_path = repre.get("published_path") - if published_path: - published_path = os.path.normpath(published_path) - if os.path.exists(published_path): - return published_path - - if only_published: - return published_path - - comp_files = repre["files"] - if isinstance(comp_files, (tuple, list, set)): - filename = comp_files[0] - else: - filename = comp_files - - staging_dir = repre.get("stagingDir") - if not staging_dir: - staging_dir = instance.data["stagingDir"] - src_path = os.path.normpath(os.path.join(staging_dir, filename)) - if os.path.exists(src_path): - return src_path - return None - def _get_asset_version_status_name(self, instance): if not self.asset_versions_status_profiles: return None diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py index cfd2d10fd9..ee6ece2e67 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py @@ -1,6 +1,8 @@ import os import pyblish.api +from openpype.plugins.publish.integrate import get_representation_path + class IntegrateShotgridPublish(pyblish.api.InstancePlugin): """ @@ -22,7 +24,7 @@ class IntegrateShotgridPublish(pyblish.api.InstancePlugin): for representation in instance.data.get("representations", []): - local_path = representation.get("published_path") + local_path = get_representation_path(instance, representation, False) code = os.path.basename(local_path) if representation.get("tags", []): diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py index a1b7140e22..60ad1ff91d 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py @@ -1,6 +1,7 @@ -import os import pyblish.api +from openpype.plugins.publish.integrate import get_representation_path + class IntegrateShotgridVersion(pyblish.api.InstancePlugin): """Integrate Shotgrid Version""" @@ -17,15 +18,37 @@ class IntegrateShotgridVersion(pyblish.api.InstancePlugin): # TODO: Use path template solver to build version code from settings anatomy = instance.data.get("anatomyData", {}) - code = "_".join( - [ - anatomy["project"]["code"], - anatomy["parent"], - anatomy["asset"], - anatomy["task"]["name"], - "v{:03}".format(int(anatomy["version"])), - ] - ) + ### Starts Alkemy-X Override ### + # code = "_".join( + # [ + # anatomy["project"]["code"], + # anatomy["parent"], + # anatomy["asset"], + # anatomy["task"]["name"], + # "v{:03}".format(int(anatomy["version"])), + # ] + # ) + # Initial editorial Shotgrid versions don't need task in name + if anatomy["app"] == "hiero": + code = "_".join( + [ + anatomy["project"]["code"], + anatomy["parent"], + anatomy["asset"], + "v{:03}".format(int(anatomy["version"])), + ] + ) + else: + code = "_".join( + [ + anatomy["project"]["code"], + anatomy["parent"], + anatomy["asset"], + anatomy["task"]["name"], + "v{:03}".format(int(anatomy["version"])), + ] + ) + ### Ends Alkemy-X Override ### version = self._find_existing_version(code, context) @@ -41,8 +64,9 @@ class IntegrateShotgridVersion(pyblish.api.InstancePlugin): data_to_update["sg_status_list"] = status for representation in instance.data.get("representations", []): - local_path = representation.get("published_path") - code = os.path.basename(local_path) + # Get representation path from published_path or create it from stagingDir if not existent + local_path = get_representation_path(instance, representation, False) + self.log.info("Local path: %s", local_path) if "shotgridreview" in representation.get("tags", []): diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index 612031efac..ac918381c0 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -9,6 +9,7 @@ import time from openpype.client import OpenPypeMongoConnection from openpype.lib.plugin_tools import prepare_template_data +from openpype.plugins.publish.integrate import get_representation_path class IntegrateSlackAPI(pyblish.api.InstancePlugin): @@ -167,10 +168,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): thumbnail_path = None for repre in instance.data.get("representations", []): if repre.get('thumbnail') or "thumbnail" in repre.get('tags', []): - repre_thumbnail_path = ( - repre.get("published_path") or - os.path.join(repre["stagingDir"], repre["files"]) - ) + repre_thumbnail_path = get_representation_path(instance, repre, False) if os.path.exists(repre_thumbnail_path): thumbnail_path = repre_thumbnail_path break @@ -184,10 +182,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): if (repre.get("review") or "review" in tags or "burnin" in tags): - repre_review_path = ( - repre.get("published_path") or - os.path.join(repre["stagingDir"], repre["files"]) - ) + repre_review_path = get_representation_path(instance, repre, False) if os.path.exists(repre_review_path): review_path = repre_review_path if "burnin" in tags: # burnin has precedence if exists diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 7b73943c37..854cf8b9ec 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -53,6 +53,52 @@ def get_frame_padded(frame, padding): return "{frame:0{padding}d}".format(padding=padding, frame=frame) +def get_representation_path(instance, repre, only_published): + """Get representation path that can be used for integration. + + When 'only_published' is set to true the validation of path is not + relevant. In that case we just need what is set in 'published_path' + as "reference". The reference is not used to get or upload the file but + for reference where the file was published. + + Args: + instance (pyblish.Instance): Processed instance object. Used + for source of staging dir if representation does not have + filled it. + repre (dict): Representation on instance which could be and + could not be integrated with main integrator. + only_published (bool): Care only about published paths and + ignore if filepath is not existing anymore. + + Returns: + str: Path to representation file. + None: Path is not filled or does not exists. + """ + + published_path = repre.get("published_path") + if published_path: + published_path = os.path.normpath(published_path) + if os.path.exists(published_path): + return published_path + + if only_published: + return published_path + + comp_files = repre["files"] + if isinstance(comp_files, (tuple, list, set)): + filename = comp_files[0] + else: + filename = comp_files + + staging_dir = repre.get("stagingDir") + if not staging_dir: + staging_dir = instance.data["stagingDir"] + src_path = os.path.normpath(os.path.join(staging_dir, filename)) + if os.path.exists(src_path): + return src_path + return None + + class IntegrateAsset(pyblish.api.InstancePlugin): """Register publish in the database and transfer files to destinations. From c13416b68570365b9ce3b247979f79f721cc3031 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Wed, 8 Feb 2023 19:39:47 +0100 Subject: [PATCH 0478/1271] Remove unintended code block and fix Hound lint warnings for <79 chars width --- .../publish/integrate_shotgrid_publish.py | 4 +- .../publish/integrate_shotgrid_version.py | 46 +++++-------------- .../plugins/publish/integrate_slack_api.py | 8 +++- 3 files changed, 21 insertions(+), 37 deletions(-) diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py index ee6ece2e67..7789a47074 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py @@ -24,7 +24,9 @@ class IntegrateShotgridPublish(pyblish.api.InstancePlugin): for representation in instance.data.get("representations", []): - local_path = get_representation_path(instance, representation, False) + local_path = get_representation_path( + instance, representation, False + ) code = os.path.basename(local_path) if representation.get("tags", []): diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py index 60ad1ff91d..94fc4ae9e8 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py @@ -18,37 +18,15 @@ class IntegrateShotgridVersion(pyblish.api.InstancePlugin): # TODO: Use path template solver to build version code from settings anatomy = instance.data.get("anatomyData", {}) - ### Starts Alkemy-X Override ### - # code = "_".join( - # [ - # anatomy["project"]["code"], - # anatomy["parent"], - # anatomy["asset"], - # anatomy["task"]["name"], - # "v{:03}".format(int(anatomy["version"])), - # ] - # ) - # Initial editorial Shotgrid versions don't need task in name - if anatomy["app"] == "hiero": - code = "_".join( - [ - anatomy["project"]["code"], - anatomy["parent"], - anatomy["asset"], - "v{:03}".format(int(anatomy["version"])), - ] - ) - else: - code = "_".join( - [ - anatomy["project"]["code"], - anatomy["parent"], - anatomy["asset"], - anatomy["task"]["name"], - "v{:03}".format(int(anatomy["version"])), - ] - ) - ### Ends Alkemy-X Override ### + code = "_".join( + [ + anatomy["project"]["code"], + anatomy["parent"], + anatomy["asset"], + anatomy["task"]["name"], + "v{:03}".format(int(anatomy["version"])), + ] + ) version = self._find_existing_version(code, context) @@ -64,9 +42,9 @@ class IntegrateShotgridVersion(pyblish.api.InstancePlugin): data_to_update["sg_status_list"] = status for representation in instance.data.get("representations", []): - # Get representation path from published_path or create it from stagingDir if not existent - local_path = get_representation_path(instance, representation, False) - self.log.info("Local path: %s", local_path) + local_path = get_representation_path( + instance, representation, False + ) if "shotgridreview" in representation.get("tags", []): diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index ac918381c0..d486b2179a 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -168,7 +168,9 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): thumbnail_path = None for repre in instance.data.get("representations", []): if repre.get('thumbnail') or "thumbnail" in repre.get('tags', []): - repre_thumbnail_path = get_representation_path(instance, repre, False) + repre_thumbnail_path = get_representation_path( + instance, repre, False + ) if os.path.exists(repre_thumbnail_path): thumbnail_path = repre_thumbnail_path break @@ -182,7 +184,9 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): if (repre.get("review") or "review" in tags or "burnin" in tags): - repre_review_path = get_representation_path(instance, repre, False) + repre_review_path = get_representation_path( + instance, repre, False + ) if os.path.exists(repre_review_path): review_path = repre_review_path if "burnin" in tags: # burnin has precedence if exists From 27150e4abb148e7dfc4eb673ea4affa9c080f55f Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Wed, 8 Feb 2023 20:23:26 +0100 Subject: [PATCH 0479/1271] Address feedback on PR and move function to pipeline.publish.lib --- .../publish/integrate_ftrack_instances.py | 8 ++-- .../publish/integrate_shotgrid_publish.py | 4 +- .../publish/integrate_shotgrid_version.py | 4 +- .../plugins/publish/integrate_slack_api.py | 6 +-- openpype/pipeline/publish/__init__.py | 2 + openpype/pipeline/publish/lib.py | 46 +++++++++++++++++++ openpype/plugins/publish/integrate.py | 46 ------------------- 7 files changed, 59 insertions(+), 57 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py index c3baecec67..d6cb3daf0d 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py @@ -3,6 +3,7 @@ import json import copy import pyblish.api +from openpype.pipeline.publish import get_publish_repre_path from openpype.lib.openpype_version import get_openpype_version from openpype.lib.transcoding import ( get_ffprobe_streams, @@ -10,7 +11,6 @@ from openpype.lib.transcoding import ( ) from openpype.lib.profiles_filtering import filter_profiles from openpype.lib.transcoding import VIDEO_EXTENSIONS -from openpype.plugins.publish.integrate import get_representation_path class IntegrateFtrackInstance(pyblish.api.InstancePlugin): @@ -154,7 +154,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): if not review_representations or has_movie_review: for repre in thumbnail_representations: - repre_path = get_representation_path(instance, repre, False) + repre_path = get_publish_repre_path(instance, repre, False) if not repre_path: self.log.warning( "Published path is not set and source was removed." @@ -211,7 +211,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): "from {}".format(repre)) continue - repre_path = get_representation_path(instance, repre, False) + repre_path = get_publish_repre_path(instance, repre, False) if not repre_path: self.log.warning( "Published path is not set and source was removed." @@ -325,7 +325,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): # Add others representations as component for repre in other_representations: - published_path = get_representation_path(instance, repre, True) + published_path = get_publish_repre_path(instance, repre, True) if not published_path: continue # Create copy of base comp item and append it diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py index 7789a47074..fc15d5515f 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py @@ -1,7 +1,7 @@ import os import pyblish.api -from openpype.plugins.publish.integrate import get_representation_path +from openpype.pipeline.publish import get_publish_repre_path class IntegrateShotgridPublish(pyblish.api.InstancePlugin): @@ -24,7 +24,7 @@ class IntegrateShotgridPublish(pyblish.api.InstancePlugin): for representation in instance.data.get("representations", []): - local_path = get_representation_path( + local_path = get_publish_repre_path( instance, representation, False ) code = os.path.basename(local_path) diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py index 94fc4ae9e8..adfdca718c 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py @@ -1,6 +1,6 @@ import pyblish.api -from openpype.plugins.publish.integrate import get_representation_path +from openpype.pipeline.publish import get_publish_repre_path class IntegrateShotgridVersion(pyblish.api.InstancePlugin): @@ -42,7 +42,7 @@ class IntegrateShotgridVersion(pyblish.api.InstancePlugin): data_to_update["sg_status_list"] = status for representation in instance.data.get("representations", []): - local_path = get_representation_path( + local_path = get_publish_repre_path( instance, representation, False ) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index d486b2179a..4e2557ccc7 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -8,8 +8,8 @@ from abc import ABCMeta, abstractmethod import time from openpype.client import OpenPypeMongoConnection +from openpype.pipeline.publish import get_publish_repre_path from openpype.lib.plugin_tools import prepare_template_data -from openpype.plugins.publish.integrate import get_representation_path class IntegrateSlackAPI(pyblish.api.InstancePlugin): @@ -168,7 +168,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): thumbnail_path = None for repre in instance.data.get("representations", []): if repre.get('thumbnail') or "thumbnail" in repre.get('tags', []): - repre_thumbnail_path = get_representation_path( + repre_thumbnail_path = get_publish_repre_path( instance, repre, False ) if os.path.exists(repre_thumbnail_path): @@ -184,7 +184,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): if (repre.get("review") or "review" in tags or "burnin" in tags): - repre_review_path = get_representation_path( + repre_review_path = get_publish_repre_path( instance, repre, False ) if os.path.exists(repre_review_path): diff --git a/openpype/pipeline/publish/__init__.py b/openpype/pipeline/publish/__init__.py index dc6fc0f97a..5be973ad86 100644 --- a/openpype/pipeline/publish/__init__.py +++ b/openpype/pipeline/publish/__init__.py @@ -36,6 +36,7 @@ from .lib import ( filter_instances_for_context_plugin, context_plugin_should_run, get_instance_staging_dir, + get_publish_repre_path, ) from .abstract_expected_files import ExpectedFiles @@ -79,6 +80,7 @@ __all__ = ( "filter_instances_for_context_plugin", "context_plugin_should_run", "get_instance_staging_dir", + "get_publish_repre_path", "ExpectedFiles", diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index c76671fa39..e206c4552c 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -632,3 +632,49 @@ def get_instance_staging_dir(instance): instance.data["stagingDir"] = staging_dir return staging_dir + + +def get_publish_repre_path(instance, repre, only_published): + """Get representation path that can be used for integration. + + When 'only_published' is set to true the validation of path is not + relevant. In that case we just need what is set in 'published_path' + as "reference". The reference is not used to get or upload the file but + for reference where the file was published. + + Args: + instance (pyblish.Instance): Processed instance object. Used + for source of staging dir if representation does not have + filled it. + repre (dict): Representation on instance which could be and + could not be integrated with main integrator. + only_published (bool): Care only about published paths and + ignore if filepath is not existing anymore. + + Returns: + str: Path to representation file. + None: Path is not filled or does not exists. + """ + + published_path = repre.get("published_path") + if published_path: + published_path = os.path.normpath(published_path) + if os.path.exists(published_path): + return published_path + + if only_published: + return published_path + + comp_files = repre["files"] + if isinstance(comp_files, (tuple, list, set)): + filename = comp_files[0] + else: + filename = comp_files + + staging_dir = repre.get("stagingDir") + if not staging_dir: + staging_dir = get_instance_staging_dir(instance) + src_path = os.path.normpath(os.path.join(staging_dir, filename)) + if os.path.exists(src_path): + return src_path + return None diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 854cf8b9ec..7b73943c37 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -53,52 +53,6 @@ def get_frame_padded(frame, padding): return "{frame:0{padding}d}".format(padding=padding, frame=frame) -def get_representation_path(instance, repre, only_published): - """Get representation path that can be used for integration. - - When 'only_published' is set to true the validation of path is not - relevant. In that case we just need what is set in 'published_path' - as "reference". The reference is not used to get or upload the file but - for reference where the file was published. - - Args: - instance (pyblish.Instance): Processed instance object. Used - for source of staging dir if representation does not have - filled it. - repre (dict): Representation on instance which could be and - could not be integrated with main integrator. - only_published (bool): Care only about published paths and - ignore if filepath is not existing anymore. - - Returns: - str: Path to representation file. - None: Path is not filled or does not exists. - """ - - published_path = repre.get("published_path") - if published_path: - published_path = os.path.normpath(published_path) - if os.path.exists(published_path): - return published_path - - if only_published: - return published_path - - comp_files = repre["files"] - if isinstance(comp_files, (tuple, list, set)): - filename = comp_files[0] - else: - filename = comp_files - - staging_dir = repre.get("stagingDir") - if not staging_dir: - staging_dir = instance.data["stagingDir"] - src_path = os.path.normpath(os.path.join(staging_dir, filename)) - if os.path.exists(src_path): - return src_path - return None - - class IntegrateAsset(pyblish.api.InstancePlugin): """Register publish in the database and transfer files to destinations. From abc4ecf59d938201478019fe1e9619a5600c9f70 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 16:22:22 +0800 Subject: [PATCH 0480/1271] hound fix --- openpype/hosts/max/api/lib_renderproducts.py | 3 +-- openpype/hosts/max/api/lib_rendersettings.py | 3 +-- openpype/hosts/max/plugins/publish/collect_render.py | 6 ++---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index a7361a5a25..ddc5d8111f 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -51,8 +51,7 @@ class RenderProducts(object): renderer == "V_Ray_6_Hotfix_3" or renderer == "V_Ray_GPU_6_Hotfix_3" or renderer == "Default_Scanline_Renderer" or - renderer == "Quicksilver_Hardware_Renderer" - ): + renderer == "Quicksilver_Hardware_Renderer"): render_elem_list = self.render_elements_product(output_file, startFrame, endFrame, diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 90398e841c..aa523348dc 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -91,8 +91,7 @@ class RenderSettings(object): renderer == "V_Ray_6_Hotfix_3" or renderer == "V_Ray_GPU_6_Hotfix_3" or renderer == "Default_Scanline_Renderer" or - renderer == "Quicksilver_Hardware_Renderer" - ): + renderer == "Quicksilver_Hardware_Renderer"): self.render_element_layer(output, width, height, img_fmt) rt.rendSaveFile = True diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index dd85afd586..549c784e56 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -37,14 +37,12 @@ class CollectRender(pyblish.api.InstancePlugin): if ( renderer_name == "ART_Renderer" or renderer_name == "Default_Scanline_Renderer" or - renderer_name == "Quicksilver_Hardware_Renderer" - ): + renderer_name == "Quicksilver_Hardware_Renderer"): plugin = "3dsmax" if ( renderer_name == "V_Ray_6_Hotfix_3" or - renderer_name == "V_Ray_GPU_6_Hotfix_3" - ): + renderer_name == "V_Ray_GPU_6_Hotfix_3"): plugin = "Vray" if renderer_name == "Redshift Renderer": From 81b894ed13e5dc5d0743313f814ec9639f4d516a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 16:24:20 +0800 Subject: [PATCH 0481/1271] hound fix --- openpype/hosts/max/api/lib_renderproducts.py | 3 +-- openpype/hosts/max/api/lib_rendersettings.py | 3 +-- openpype/hosts/max/plugins/publish/collect_render.py | 6 ++---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index ddc5d8111f..9becd2b5e5 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -45,8 +45,7 @@ class RenderProducts(object): if renderer == "VUE_File_Renderer": return full_render_list - if ( - renderer == "ART_Renderer" or + if (renderer == "ART_Renderer" or renderer == "Redshift Renderer" or renderer == "V_Ray_6_Hotfix_3" or renderer == "V_Ray_GPU_6_Hotfix_3" or diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index aa523348dc..176c797405 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -85,8 +85,7 @@ class RenderSettings(object): if renderer == "Arnold": return - if ( - renderer == "ART_Renderer" or + if (renderer == "ART_Renderer" or renderer == "Redshift Renderer" or renderer == "V_Ray_6_Hotfix_3" or renderer == "V_Ray_GPU_6_Hotfix_3" or diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index 549c784e56..cd991b36eb 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -34,14 +34,12 @@ class CollectRender(pyblish.api.InstancePlugin): renderer_class = get_current_renderer() renderer_name = str(renderer_class).split(":")[0] # setup the plugin as 3dsmax for the internal renderer - if ( - renderer_name == "ART_Renderer" or + if (renderer_name == "ART_Renderer" or renderer_name == "Default_Scanline_Renderer" or renderer_name == "Quicksilver_Hardware_Renderer"): plugin = "3dsmax" - if ( - renderer_name == "V_Ray_6_Hotfix_3" or + if (renderer_name == "V_Ray_6_Hotfix_3" or renderer_name == "V_Ray_GPU_6_Hotfix_3"): plugin = "Vray" From 8f016413d1075d027b863765e0a4595393b48ae8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 16:26:30 +0800 Subject: [PATCH 0482/1271] hound fix --- openpype/hosts/max/plugins/publish/collect_render.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index cd991b36eb..f5d99af63e 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -34,9 +34,9 @@ class CollectRender(pyblish.api.InstancePlugin): renderer_class = get_current_renderer() renderer_name = str(renderer_class).split(":")[0] # setup the plugin as 3dsmax for the internal renderer - if (renderer_name == "ART_Renderer" or - renderer_name == "Default_Scanline_Renderer" or - renderer_name == "Quicksilver_Hardware_Renderer"): + if (renderer_name == "ART_Renderer" + or renderer_name == "Default_Scanline_Renderer" + or renderer_name == "Quicksilver_Hardware_Renderer"): plugin = "3dsmax" if (renderer_name == "V_Ray_6_Hotfix_3" or From 98d05db60b1ec4fbfa4e870bf471e5f3b4844063 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 16:29:26 +0800 Subject: [PATCH 0483/1271] hound fix --- openpype/hosts/max/api/lib_renderproducts.py | 12 ++++++------ openpype/hosts/max/api/lib_rendersettings.py | 12 ++++++------ openpype/hosts/max/plugins/publish/collect_render.py | 4 ++-- .../plugins/publish/submit_3dmax_deadline.py | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index 9becd2b5e5..fbd1f3d50e 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -45,12 +45,12 @@ class RenderProducts(object): if renderer == "VUE_File_Renderer": return full_render_list - if (renderer == "ART_Renderer" or - renderer == "Redshift Renderer" or - renderer == "V_Ray_6_Hotfix_3" or - renderer == "V_Ray_GPU_6_Hotfix_3" or - renderer == "Default_Scanline_Renderer" or - renderer == "Quicksilver_Hardware_Renderer"): + if (renderer == "ART_Renderer" + or renderer == "Redshift Renderer" + or renderer == "V_Ray_6_Hotfix_3" + or renderer == "V_Ray_GPU_6_Hotfix_3" + or renderer == "Default_Scanline_Renderer" + or renderer == "Quicksilver_Hardware_Renderer"): render_elem_list = self.render_elements_product(output_file, startFrame, endFrame, diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 176c797405..c1e376746a 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -85,12 +85,12 @@ class RenderSettings(object): if renderer == "Arnold": return - if (renderer == "ART_Renderer" or - renderer == "Redshift Renderer" or - renderer == "V_Ray_6_Hotfix_3" or - renderer == "V_Ray_GPU_6_Hotfix_3" or - renderer == "Default_Scanline_Renderer" or - renderer == "Quicksilver_Hardware_Renderer"): + if (renderer == "ART_Renderer" + or renderer == "Redshift Renderer" + or renderer == "V_Ray_6_Hotfix_3" + or renderer == "V_Ray_GPU_6_Hotfix_3" + or renderer == "Default_Scanline_Renderer" + or renderer == "Quicksilver_Hardware_Renderer"): self.render_element_layer(output, width, height, img_fmt) rt.rendSaveFile = True diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index f5d99af63e..c4b8ac4985 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -39,8 +39,8 @@ class CollectRender(pyblish.api.InstancePlugin): or renderer_name == "Quicksilver_Hardware_Renderer"): plugin = "3dsmax" - if (renderer_name == "V_Ray_6_Hotfix_3" or - renderer_name == "V_Ray_GPU_6_Hotfix_3"): + if (renderer_name == "V_Ray_6_Hotfix_3" + or renderer_name == "V_Ray_GPU_6_Hotfix_3"): plugin = "Vray" if renderer_name == "Redshift Renderer": diff --git a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py index faeb071524..88834e4a91 100644 --- a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py @@ -126,7 +126,7 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): # E.g. http://192.168.0.1:8082/api/jobs url = "{}/api/jobs".format(deadline_url) - response = requests.post(url, json=payload, verify=False) + response = requests.post(url, json=payload) if not response.ok: raise Exception(response.text) # Store output dir for unified publisher (filesequence) From fe7b6fbd315fcc7b13803ee7944ce37f46c9051c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 16:43:53 +0800 Subject: [PATCH 0484/1271] hound fix --- openpype/hosts/max/api/lib_renderproducts.py | 14 ++++++++------ openpype/hosts/max/api/lib_rendersettings.py | 14 ++++++++------ .../hosts/max/plugins/publish/collect_render.py | 14 +++++++++----- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index fbd1f3d50e..4ba92f06bd 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -45,12 +45,14 @@ class RenderProducts(object): if renderer == "VUE_File_Renderer": return full_render_list - if (renderer == "ART_Renderer" - or renderer == "Redshift Renderer" - or renderer == "V_Ray_6_Hotfix_3" - or renderer == "V_Ray_GPU_6_Hotfix_3" - or renderer == "Default_Scanline_Renderer" - or renderer == "Quicksilver_Hardware_Renderer"): + if ( + renderer == "ART_Renderer" or + renderer == "Redshift Renderer" or + renderer == "V_Ray_6_Hotfix_3" or + renderer == "V_Ray_GPU_6_Hotfix_3" or + renderer == "Default_Scanline_Renderer" or + renderer == "Quicksilver_Hardware_Renderer"\ + ): render_elem_list = self.render_elements_product(output_file, startFrame, endFrame, diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index c1e376746a..ef8ad6bdc5 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -85,12 +85,14 @@ class RenderSettings(object): if renderer == "Arnold": return - if (renderer == "ART_Renderer" - or renderer == "Redshift Renderer" - or renderer == "V_Ray_6_Hotfix_3" - or renderer == "V_Ray_GPU_6_Hotfix_3" - or renderer == "Default_Scanline_Renderer" - or renderer == "Quicksilver_Hardware_Renderer"): + if ( + renderer == "ART_Renderer" or + renderer == "Redshift Renderer" or + renderer == "V_Ray_6_Hotfix_3" or + renderer == "V_Ray_GPU_6_Hotfix_3" or + renderer == "Default_Scanline_Renderer" or + renderer == "Quicksilver_Hardware_Renderer" + ): self.render_element_layer(output, width, height, img_fmt) rt.rendSaveFile = True diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index c4b8ac4985..59a1450691 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -34,13 +34,17 @@ class CollectRender(pyblish.api.InstancePlugin): renderer_class = get_current_renderer() renderer_name = str(renderer_class).split(":")[0] # setup the plugin as 3dsmax for the internal renderer - if (renderer_name == "ART_Renderer" - or renderer_name == "Default_Scanline_Renderer" - or renderer_name == "Quicksilver_Hardware_Renderer"): + if ( + renderer_name == "ART_Renderer" or + renderer_name == "Default_Scanline_Renderer" or + renderer_name == "Quicksilver_Hardware_Renderer" + ): plugin = "3dsmax" - if (renderer_name == "V_Ray_6_Hotfix_3" - or renderer_name == "V_Ray_GPU_6_Hotfix_3"): + if ( + renderer_name == "V_Ray_6_Hotfix_3" or + renderer_name == "V_Ray_GPU_6_Hotfix_3" + ): plugin = "Vray" if renderer_name == "Redshift Renderer": From 07e14442b172a23a7c019be10a135333526d9ee2 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 16:44:41 +0800 Subject: [PATCH 0485/1271] hound fix --- openpype/hosts/max/api/lib_renderproducts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index 4ba92f06bd..44efed2c36 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -51,7 +51,7 @@ class RenderProducts(object): renderer == "V_Ray_6_Hotfix_3" or renderer == "V_Ray_GPU_6_Hotfix_3" or renderer == "Default_Scanline_Renderer" or - renderer == "Quicksilver_Hardware_Renderer"\ + renderer == "Quicksilver_Hardware_Renderer" ): render_elem_list = self.render_elements_product(output_file, startFrame, From c54ead7ad2b2110d7a4786e986002dd8de270cf5 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 18:37:37 +0800 Subject: [PATCH 0486/1271] add arnold camera and add 3dmax as renderer --- openpype/hosts/max/api/lib_rendersettings.py | 14 ++++++++++++- .../max/plugins/publish/collect_render.py | 21 +------------------ .../projects_schema/schema_project_max.json | 1 - 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index ef8ad6bdc5..db4e53720e 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -83,7 +83,7 @@ class RenderSettings(object): return # TODO: Finish the arnold render setup if renderer == "Arnold": - return + self.arnold_setup() if ( renderer == "ART_Renderer" or @@ -97,6 +97,18 @@ class RenderSettings(object): rt.rendSaveFile = True + def arnold_setup(self): + # get Arnold RenderView run in the background + # for setting up renderable camera + arv = rt.MAXToAOps.ArnoldRenderView() + render_camera = rt.viewport.GetCamera() + arv.setOption("Camera", str(render_camera)) + + aovmgr = rt.renderers.current.AOVManager + aovmgr.drivers = "#()" + + arv.close() + def render_element_layer(self, dir, width, height, ext): """For Renderers with render elements""" rt.renderWidth = width diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index 59a1450691..ce8d62e089 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -34,25 +34,6 @@ class CollectRender(pyblish.api.InstancePlugin): renderer_class = get_current_renderer() renderer_name = str(renderer_class).split(":")[0] # setup the plugin as 3dsmax for the internal renderer - if ( - renderer_name == "ART_Renderer" or - renderer_name == "Default_Scanline_Renderer" or - renderer_name == "Quicksilver_Hardware_Renderer" - ): - plugin = "3dsmax" - - if ( - renderer_name == "V_Ray_6_Hotfix_3" or - renderer_name == "V_Ray_GPU_6_Hotfix_3" - ): - plugin = "Vray" - - if renderer_name == "Redshift Renderer": - plugin = "redshift" - - if renderer_name == "Arnold": - plugin = "arnold" - data = { "subset": instance.name, "asset": asset, @@ -62,7 +43,7 @@ class CollectRender(pyblish.api.InstancePlugin): "families": ['maxrender'], "source": filepath, "files": render_layer_files, - "plugin": plugin, + "plugin": "3dsmax", "frameStart": context.data['frameStart'], "frameEnd": context.data['frameEnd'] } diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_max.json b/openpype/settings/entities/schemas/projects_schema/schema_project_max.json index 3d4cd5c54a..fbd9358c74 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_max.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_max.json @@ -35,7 +35,6 @@ "multiselection": false, "defaults": "exr", "enum_items": [ - {"avi": "avi"}, {"bmp": "bmp"}, {"exr": "exr"}, {"tif": "tif"}, From 819148cfc059fb852b28c96d721dffbbf25dc995 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 18:38:42 +0800 Subject: [PATCH 0487/1271] remove unused variable --- openpype/hosts/max/plugins/publish/collect_render.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index ce8d62e089..d8b0312d43 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -31,8 +31,6 @@ class CollectRender(pyblish.api.InstancePlugin): folder = folder.replace("\\", "/") imgFormat = RenderProducts().image_format() - renderer_class = get_current_renderer() - renderer_name = str(renderer_class).split(":")[0] # setup the plugin as 3dsmax for the internal renderer data = { "subset": instance.name, From 1b1f92ba5a74c5c2dac1c941df33ae61903ec3f5 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 18:39:42 +0800 Subject: [PATCH 0488/1271] remove unused variable --- openpype/hosts/max/plugins/publish/collect_render.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index d8b0312d43..857ac88ea6 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -5,7 +5,6 @@ import pyblish.api from pymxs import runtime as rt from openpype.pipeline import legacy_io -from openpype.hosts.max.api.lib import get_current_renderer from openpype.hosts.max.api.lib_renderproducts import RenderProducts From e1aff812525d8d2bd4aa0308e68db68994ade388 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 18:56:37 +0800 Subject: [PATCH 0489/1271] correct separator issue in naming convention --- openpype/hosts/max/api/lib_rendersettings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index db4e53720e..8f7e91822c 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -75,7 +75,7 @@ class RenderSettings(object): )] except KeyError: aov_separator = "." - outputFilename = "{0}.{1}".format(output, img_fmt) + outputFilename = "{0}..{1}".format(output, img_fmt) outputFilename = outputFilename.replace("{aov_separator}", aov_separator) rt.rendOutputFilename = outputFilename @@ -122,7 +122,7 @@ class RenderSettings(object): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") render_element = os.path.join(dir, renderpass) - aov_name = "{0}.{1}".format(render_element, ext) + aov_name = "{0}..{1}".format(render_element, ext) try: aov_separator = self._aov_chars[( self._project_settings["maya"] From 31424672aa22d08629e87f49dfadcdd2a8ba3e19 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 18:59:35 +0800 Subject: [PATCH 0490/1271] correct separator issue in naming convention --- openpype/hosts/max/api/lib_renderproducts.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index 44efed2c36..fd0eb947af 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -92,9 +92,10 @@ class RenderProducts(object): render_dir = os.path.join(folder, renderpass) if renderlayer_name.enabled: for f in range(startFrame, endFrame): - render_element = "{0}.{1}.{2}".format(render_dir, - str(f), - fmt) + render_element = "{0}_{1}..{2}.{2}".format(folder, + renderpass, + str(f), + fmt) render_element = render_element.replace("\\", "/") render_dirname.append(render_element) From 536cbd2913d89b64acc8b41b58bd77efeefb51c4 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 18:59:58 +0800 Subject: [PATCH 0491/1271] correct separator issue in naming convention --- openpype/hosts/max/api/lib_renderproducts.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index fd0eb947af..83b5a0bc35 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -89,7 +89,6 @@ class RenderProducts(object): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") - render_dir = os.path.join(folder, renderpass) if renderlayer_name.enabled: for f in range(startFrame, endFrame): render_element = "{0}_{1}..{2}.{2}".format(folder, From 4bd05046236759e6209a256af639ee943e091406 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 19:00:23 +0800 Subject: [PATCH 0492/1271] correct separator issue in naming convention --- openpype/hosts/max/api/lib_renderproducts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index 83b5a0bc35..a924505d7e 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -91,7 +91,7 @@ class RenderProducts(object): if renderlayer_name.enabled: for f in range(startFrame, endFrame): - render_element = "{0}_{1}..{2}.{2}".format(folder, + render_element = "{0}_{1}..{2}.{3}".format(folder, renderpass, str(f), fmt) From 15d7a7589fa119010510232e3ae243f0e6834383 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 12:04:06 +0100 Subject: [PATCH 0493/1271] fix context collection from create context --- .../publish/collect_from_create_context.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openpype/plugins/publish/collect_from_create_context.py b/openpype/plugins/publish/collect_from_create_context.py index d3398c885e..5fcf8feb56 100644 --- a/openpype/plugins/publish/collect_from_create_context.py +++ b/openpype/plugins/publish/collect_from_create_context.py @@ -32,7 +32,7 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin): thumbnail_paths_by_instance_id.get(None) ) - project_name = create_context.project_name + project_name = create_context.get_current_project_name() if project_name: context.data["projectName"] = project_name @@ -53,11 +53,15 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin): context.data.update(create_context.context_data_to_store()) context.data["newPublishing"] = True # Update context data - for key in ("AVALON_PROJECT", "AVALON_ASSET", "AVALON_TASK"): - value = create_context.dbcon.Session.get(key) - if value is not None: - legacy_io.Session[key] = value - os.environ[key] = value + asset_name = create_context.get_current_asset_name() + task_name = create_context.get_current_task_name() + for key, value in ( + ("AVALON_PROJECT", project_name), + ("AVALON_ASSET", asset_name), + ("AVALON_TASK", task_name) + ): + legacy_io.Session[key] = value + os.environ[key] = value def create_instance( self, From 0a7d7e45638987a53ac35c2dd9c69d38cfe9855e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 19:04:28 +0800 Subject: [PATCH 0494/1271] correct separator issue in naming convention --- openpype/hosts/max/api/lib_renderproducts.py | 2 +- openpype/hosts/max/api/lib_rendersettings.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index a924505d7e..4c9c9b8088 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -91,7 +91,7 @@ class RenderProducts(object): if renderlayer_name.enabled: for f in range(startFrame, endFrame): - render_element = "{0}_{1}..{2}.{3}".format(folder, + render_element = "{0}_{1}.{2}.{3}".format(folder, renderpass, str(f), fmt) diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 8f7e91822c..30a252e07a 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -121,8 +121,7 @@ class RenderSettings(object): for i in range(render_elem_num): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") - render_element = os.path.join(dir, renderpass) - aov_name = "{0}..{1}".format(render_element, ext) + aov_name = "{0}_{1}..{2}".format(dir, renderpass, ext) try: aov_separator = self._aov_chars[( self._project_settings["maya"] From a3bc0e6debfe0e0e1dabff1bba543b30af738e65 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 19:54:14 +0800 Subject: [PATCH 0495/1271] update aov naming convention --- openpype/hosts/max/api/lib_renderproducts.py | 9 ++++----- openpype/hosts/max/api/lib_rendersettings.py | 15 +++------------ 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index 4c9c9b8088..84cb0c1744 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -88,13 +88,12 @@ class RenderProducts(object): for i in range(render_elem_num): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") - + render_element = os.path.join(dir, renderpass) if renderlayer_name.enabled: for f in range(startFrame, endFrame): - render_element = "{0}_{1}.{2}.{3}".format(folder, - renderpass, - str(f), - fmt) + render_element = "{0}.{1}.{2}".format(render_element, + str(f), + fmt) render_element = render_element.replace("\\", "/") render_dirname.append(render_element) diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 30a252e07a..1188d77e29 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -121,16 +121,7 @@ class RenderSettings(object): for i in range(render_elem_num): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") - aov_name = "{0}_{1}..{2}".format(dir, renderpass, ext) - try: - aov_separator = self._aov_chars[( - self._project_settings["maya"] - ["RenderSettings"] - ["aov_separator"] - )] - except KeyError: - aov_separator = "." - - aov_name = aov_name.replace("{aov_separator}", - aov_separator) + render_element = os.path.join(dir, renderpass) + dir = dir.replace(".", " ") + aov_name = "{0}..{1}".format(render_element, ext) render_elem.SetRenderElementFileName(i, aov_name) From 189a842660436b23f67de81a1548683ca3b066f5 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 19:55:22 +0800 Subject: [PATCH 0496/1271] update aov naming convention --- openpype/hosts/max/api/lib_renderproducts.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index 84cb0c1744..912c0c89d7 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -88,12 +88,12 @@ class RenderProducts(object): for i in range(render_elem_num): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") - render_element = os.path.join(dir, renderpass) + render_name = os.path.join(dir, renderpass) if renderlayer_name.enabled: for f in range(startFrame, endFrame): - render_element = "{0}.{1}.{2}".format(render_element, - str(f), - fmt) + render_element = "{0}.{1}.{2}".format(render_name, + str(f), + fmt) render_element = render_element.replace("\\", "/") render_dirname.append(render_element) From b148dec04843137622960b50c484d94ad9b0e82b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 12:58:17 +0100 Subject: [PATCH 0497/1271] Added helper method to return unified information on create error --- openpype/pipeline/create/context.py | 70 +++++++++++++++++------------ 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 190d542724..dfe60d438b 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1538,6 +1538,44 @@ class CreateContext: pre_create_data ) + def _create_with_unified_error( + self, identifier, creator, *args, **kwargs + ): + error_message = "Failed to run Creator with identifier \"{}\". {}" + + label = None + add_traceback = False + result = None + fail_info = None + success = False + + try: + # Try to get creator and his label + if creator is None: + creator = self._get_creator_in_create(identifier) + label = getattr(creator, "label", label) + + # Run create + result = creator.create(*args, **kwargs) + success = True + + except CreatorError: + exc_info = sys.exc_info() + self.log.warning(error_message.format(identifier, exc_info[1])) + + except: + add_traceback = True + exc_info = sys.exc_info() + self.log.warning( + error_message.format(identifier, ""), + exc_info=True + ) + + if not success: + fail_info = prepare_failed_creator_operation_info( + identifier, label, exc_info, add_traceback + ) + return result, fail_info def creator_removed_instance(self, instance): """When creator removes instance context should be acknowledged. @@ -1663,37 +1701,11 @@ class CreateContext: Reset instances if any autocreator executed properly. """ - error_message = "Failed to run AutoCreator with identifier \"{}\". {}" failed_info = [] for identifier, creator in self.autocreators.items(): - label = creator.label - failed = False - add_traceback = False - try: - creator.create() - - except CreatorError: - failed = True - exc_info = sys.exc_info() - self.log.warning(error_message.format(identifier, exc_info[1])) - - # Use bare except because some hosts raise their exceptions that - # do not inherit from python's `BaseException` - except: - failed = True - add_traceback = True - exc_info = sys.exc_info() - self.log.warning( - error_message.format(identifier, ""), - exc_info=True - ) - - if failed: - failed_info.append( - prepare_failed_creator_operation_info( - identifier, label, exc_info, add_traceback - ) - ) + _, fail_info = self._create_with_unified_error(identifier, creator) + if fail_info is not None: + failed_info.append(fail_info) if failed_info: raise CreatorsCreateFailed(failed_info) From fac10d26337157bc253a1af90ae0a1040ff49533 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 12:58:51 +0100 Subject: [PATCH 0498/1271] added public method 'create_with_unified_error' used in publisher --- openpype/pipeline/create/context.py | 25 +++++++++++++++++++++++++ openpype/tools/publisher/control.py | 3 ++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index dfe60d438b..2a92d21225 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1576,6 +1576,31 @@ class CreateContext: identifier, label, exc_info, add_traceback ) return result, fail_info + + def create_with_unified_error(self, identifier, *args, **kwargs): + """Trigger create but raise only one error if anything fails. + + Added to raise unified exception. Capture any possible issues and + reraise it with unified information. + + Args: + identifier (str): Identifier of creator. + *args (Tuple[Any]): Arguments for create method. + **kwargs (Dict[Any, Any]): Keyword argument for create method. + + Raises: + CreatorsCreateFailed: When creation fails due to any possible + reason. If anything goes wrong this is only possible exception + the method should raise. + """ + + result, fail_info = self._create_with_unified_error( + identifier, None, *args, **kwargs + ) + if fail_info is not None: + raise CreatorsCreateFailed([fail_info]) + return result + def creator_removed_instance(self, instance): """When creator removes instance context should be acknowledged. diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 670c22a43e..11215b5ff8 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -2017,9 +2017,10 @@ class PublisherController(BasePublisherController): success = True try: - self._create_context.raw_create( + self._create_context.create_with_unified_error( creator_identifier, subset_name, instance_data, options ) + except CreatorsOperationFailed as exc: success = False self._emit_event( From cb84cf769eea9d5a018ef67dd6e4cb0d6d7276b8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 13:00:28 +0100 Subject: [PATCH 0499/1271] 'create' method is not triggering 'raw_create' --- openpype/pipeline/create/context.py | 34 ++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 2a92d21225..3287141970 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1415,6 +1415,30 @@ class CreateContext: with self.bulk_instances_collection(): self._bulk_instances_to_process.append(instance) + def _get_creator_in_create(self, identifier): + """Creator by identifier with unified error. + + Helper method to get creator by identifier with same error when creator + is not available. + + Args: + identifier (str): Identifier of creator plugin. + + Returns: + BaseCreator: Creator found by identifier. + + Raises: + CreatorError: When identifier is not known. + """ + + creator = self.creators.get(identifier) + # Fake CreatorError (Could be maybe specific exception?) + if creator is None: + raise CreatorError( + "Creator {} was not found".format(identifier) + ) + return creator + def raw_create(self, identifier, *args, **kwargs): """Wrapper for creators to trigger 'create' method. @@ -1497,14 +1521,9 @@ class CreateContext: Raises: CreatorError: If creator was not found or asset is empty. - CreatorsCreateFailed: When creation fails. """ - creator = self.creators.get(creator_identifier) - if creator is None: - raise CreatorError( - "Creator {} was not found".format(creator_identifier) - ) + creator = self._get_creator_in_create(creator_identifier) project_name = self.project_name if asset_doc is None: @@ -1531,8 +1550,7 @@ class CreateContext: "task": task_name, "variant": variant } - return self.raw_create( - creator_identifier, + return creator.create( subset_name, instance_data, pre_create_data From 0cb78a10e6cd7b0c7307c70be1827e2e8c1d1f2e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 13:00:49 +0100 Subject: [PATCH 0500/1271] removed unused 'raw_create' method --- openpype/pipeline/create/context.py | 53 ----------------------------- 1 file changed, 53 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 3287141970..078c50acc2 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1439,59 +1439,6 @@ class CreateContext: ) return creator - def raw_create(self, identifier, *args, **kwargs): - """Wrapper for creators to trigger 'create' method. - - Different types of creators may expect different arguments thus the - hints for args are blind. - - Args: - identifier (str): Creator's identifier. - *args (Tuple[Any]): Arguments for create method. - **kwargs (Dict[Any, Any]): Keyword argument for create method. - - Raises: - CreatorsCreateFailed: When creation fails. - """ - - error_message = "Failed to run Creator with identifier \"{}\". {}" - creator = self.creators.get(identifier) - label = getattr(creator, "label", None) - failed = False - add_traceback = False - exc_info = None - result = None - try: - # Fake CreatorError (Could be maybe specific exception?) - if creator is None: - raise CreatorError( - "Creator {} was not found".format(identifier) - ) - - result = creator.create(*args, **kwargs) - - except CreatorError: - failed = True - exc_info = sys.exc_info() - self.log.warning(error_message.format(identifier, exc_info[1])) - - except: - failed = True - add_traceback = True - exc_info = sys.exc_info() - self.log.warning( - error_message.format(identifier, ""), - exc_info=True - ) - - if failed: - raise CreatorsCreateFailed([ - prepare_failed_creator_operation_info( - identifier, label, exc_info, add_traceback - ) - ]) - return result - def create( self, creator_identifier, From 0d2b6da3ef7e012d00b938a9b47609e66bb928e5 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 20:02:28 +0800 Subject: [PATCH 0501/1271] update aov naming convention --- openpype/hosts/max/api/lib_renderproducts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index 912c0c89d7..5f96b13273 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -88,7 +88,7 @@ class RenderProducts(object): for i in range(render_elem_num): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") - render_name = os.path.join(dir, renderpass) + render_name = os.path.join(folder, renderpass) if renderlayer_name.enabled: for f in range(startFrame, endFrame): render_element = "{0}.{1}.{2}".format(render_name, From be573252486287a04dc37a5d0daa6edb13fb8ce5 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 20:22:19 +0800 Subject: [PATCH 0502/1271] update aov naming convention --- openpype/hosts/max/api/lib_renderproducts.py | 8 ++++---- openpype/hosts/max/api/lib_rendersettings.py | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index 5f96b13273..e3cccff982 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -88,12 +88,12 @@ class RenderProducts(object): for i in range(render_elem_num): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") - render_name = os.path.join(folder, renderpass) if renderlayer_name.enabled: for f in range(startFrame, endFrame): - render_element = "{0}.{1}.{2}".format(render_name, - str(f), - fmt) + render_element = "{0}_{1}.{2}.{3}".format(folder, + renderpass, + str(f), + fmt) render_element = render_element.replace("\\", "/") render_dirname.append(render_element) diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 1188d77e29..6d2aa678b7 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -122,6 +122,5 @@ class RenderSettings(object): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") render_element = os.path.join(dir, renderpass) - dir = dir.replace(".", " ") aov_name = "{0}..{1}".format(render_element, ext) render_elem.SetRenderElementFileName(i, aov_name) From 9ba3d144f381d62127c90542d280ad80c0306b18 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 9 Feb 2023 20:24:03 +0800 Subject: [PATCH 0503/1271] update aov naming convention --- openpype/hosts/max/api/lib_rendersettings.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 6d2aa678b7..2324d743eb 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -121,6 +121,5 @@ class RenderSettings(object): for i in range(render_elem_num): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") - render_element = os.path.join(dir, renderpass) - aov_name = "{0}..{1}".format(render_element, ext) + aov_name = "{0}_{1}..{2}".format(dir, renderpass, ext) render_elem.SetRenderElementFileName(i, aov_name) From a3c9f792c81ac6276594f73d6dc5152bce0b12cd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 9 Feb 2023 14:29:20 +0100 Subject: [PATCH 0504/1271] refactor tempdir creator function wip --- openpype/pipeline/publish/lib.py | 67 ++++++-------------------------- openpype/pipeline/tempdir.py | 62 +++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 56 deletions(-) create mode 100644 openpype/pipeline/tempdir.py diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index b3d273781e..380f0df91a 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -12,13 +12,15 @@ import pyblish.api from openpype.lib import ( Logger, - filter_profiles, - StringTemplate + filter_profiles ) from openpype.settings import ( get_project_settings, get_system_settings, ) +from openpype.pipeline import ( + tempdir +) from .contants import ( DEFAULT_PUBLISH_TEMPLATE, @@ -645,24 +647,12 @@ def get_instance_staging_dir(instance): if staging_dir: return staging_dir - openpype_temp_dir = os.getenv("OPENPYPE_TMPDIR") - custom_temp_dir = None - if openpype_temp_dir: - if "{" in openpype_temp_dir: - # path is anatomy template - custom_temp_dir = _format_staging_dir( - instance, openpype_temp_dir - ) - else: - # path is absolute - custom_temp_dir = openpype_temp_dir - - if not os.path.exists(custom_temp_dir): - try: - # create it if it doesnt exists - os.makedirs(custom_temp_dir) - except IOError as error: - raise IOError("Path couldn't be created: {}".format(error)) + anatomy_data = instance.data.get("anatomy_data") + project_name = + # get customized tempdir path from `OPENPYPE_TEMPDIR` env var + custom_temp_dir = tempdir.create_custom_tempdir( + instance.data["anatomy_data"]["project"]["name"] + ) if custom_temp_dir: staging_dir = os.path.normpath( @@ -677,39 +667,4 @@ def get_instance_staging_dir(instance): ) instance.data['stagingDir'] = staging_dir - return staging_dir - - -def _format_staging_dir(instance, openpype_temp_dir): - """ Formating template - - Template path formatting is supporting: - - optional key formating - - available keys: - - root[work | ] - - project[name | code] - - asset - - hierarchy - - task - - username - - app - - Args: - instance (pyblish.Instance): instance object - openpype_temp_dir (str): path string - - Returns: - str: formated path - """ - anatomy = instance.context.data["anatomy"] - # get anatomy formating data - # so template formating is supported - anatomy_data = copy.deepcopy(instance.context.data["anatomyData"]) - anatomy_data["root"] = anatomy.roots - - result = StringTemplate.format_template( - openpype_temp_dir, anatomy_data).normalized() - - # create the dir in case it doesnt exists - os.makedirs(os.path.dirname(result)) - return result + return staging_dir \ No newline at end of file diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py new file mode 100644 index 0000000000..c73fce2e9a --- /dev/null +++ b/openpype/pipeline/tempdir.py @@ -0,0 +1,62 @@ +import os + +from openpype.lib import ( + Anatomy, + StringTemplate +) + +def create_custom_tempdir(project_name, anatomy=None, formating_data=None): + """ Create custom tempdir + + Template path formatting is supporting: + - optional key formating + - available keys: + - root[work | ] + - project[name | code] + + Args: + instance (pyblish.Instance): instance object + openpype_temp_dir (str): path string + + Returns: + str: formated path + """ + openpype_tempdir = os.getenv("OPENPYPE_TMPDIR") + if not openpype_tempdir: + return + + custom_tempdir = None + if "{" in openpype_tempdir: + if anatomy is None: + anatomy = Anatomy(project_name) + # create base formate data + data = { + "root": anatomy.roots + } + if formating_data is None: + # We still don't have `project_code` on Anatomy... + project_doc = anatomy.get_project_doc_from_cache(project_name) + data["project"] = { + "name": project_name, + "code": project_doc["data"]["code"], + } + else: + data["project"] = formating_data["project"] + + # path is anatomy template + custom_tempdir = StringTemplate.format_template( + openpype_tempdir, data).normalized() + + else: + # path is absolute + custom_tempdir = openpype_tempdir + + # create he dir path if it doesnt exists + if not os.path.exists(custom_tempdir): + try: + # create it if it doesnt exists + os.makedirs(custom_tempdir) + except IOError as error: + raise IOError("Path couldn't be created: {}".format(error)) + + return custom_tempdir From 304d7584042454193be5b1c85f0dc87e06c1d4c3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 9 Feb 2023 14:40:40 +0100 Subject: [PATCH 0505/1271] renaming module for temporarydir, fixing docstring --- openpype/pipeline/publish/lib.py | 19 ++++++++++--------- .../pipeline/{tempdir.py => temporarydir.py} | 19 +++++++++++-------- 2 files changed, 21 insertions(+), 17 deletions(-) rename openpype/pipeline/{tempdir.py => temporarydir.py} (82%) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 380f0df91a..c6c8b71b24 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -19,7 +19,7 @@ from openpype.settings import ( get_system_settings, ) from openpype.pipeline import ( - tempdir + temporarydir ) from .contants import ( @@ -626,11 +626,6 @@ def get_instance_staging_dir(instance): Available anatomy formatting keys: - root[work | ] - project[name | code] - - asset - - hierarchy - - task - - username - - app Note: Staging dir does not have to be necessarily in tempdir so be carefull @@ -648,10 +643,16 @@ def get_instance_staging_dir(instance): return staging_dir anatomy_data = instance.data.get("anatomy_data") - project_name = + anatomy = instance.data.get("anatomy") + + if anatomy_data: + project_name = anatomy_data["project"]["name"] + else: + project_name = os.getenv("AVALON_PROJECT") + # get customized tempdir path from `OPENPYPE_TEMPDIR` env var - custom_temp_dir = tempdir.create_custom_tempdir( - instance.data["anatomy_data"]["project"]["name"] + custom_temp_dir = temporarydir.create_custom_tempdir( + project_name, anatomy=anatomy, formating_data=anatomy_data ) if custom_temp_dir: diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/temporarydir.py similarity index 82% rename from openpype/pipeline/tempdir.py rename to openpype/pipeline/temporarydir.py index c73fce2e9a..31586d82c8 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/temporarydir.py @@ -1,9 +1,11 @@ -import os +""" +Temporary folder operations +""" + +import os +from openpype.lib import StringTemplate +from openpype.pipeline import Anatomy -from openpype.lib import ( - Anatomy, - StringTemplate -) def create_custom_tempdir(project_name, anatomy=None, formating_data=None): """ Create custom tempdir @@ -15,11 +17,12 @@ def create_custom_tempdir(project_name, anatomy=None, formating_data=None): - project[name | code] Args: - instance (pyblish.Instance): instance object - openpype_temp_dir (str): path string + project_name (str): name of project + anatomy (openpype.pipeline.Anatomy): Anatomy object + formating_data (dict): formating data used for filling template. Returns: - str: formated path + bool | str: formated path or None """ openpype_tempdir = os.getenv("OPENPYPE_TMPDIR") if not openpype_tempdir: From 171b9bc3dbc079d08248cc2cafa40342b9bcd762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A0=20Serra=20Arrizabalaga?= Date: Thu, 9 Feb 2023 05:49:02 -0800 Subject: [PATCH 0506/1271] Make only_published argument optional Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- 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 e206c4552c..92c43c99e8 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -634,7 +634,7 @@ def get_instance_staging_dir(instance): return staging_dir -def get_publish_repre_path(instance, repre, only_published): +def get_publish_repre_path(instance, repre, only_published=False): """Get representation path that can be used for integration. When 'only_published' is set to true the validation of path is not From 5de967b6447fb2b7f7dc84ad84704857afa35af8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 9 Feb 2023 15:07:40 +0100 Subject: [PATCH 0507/1271] updated documentation --- openpype/pipeline/publish/lib.py | 2 +- website/docs/admin_environment.md | 30 +++++++++++++++++++++++++++ website/docs/admin_settings_system.md | 29 ++++---------------------- website/sidebars.js | 1 + 4 files changed, 36 insertions(+), 26 deletions(-) create mode 100644 website/docs/admin_environment.md diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index c6c8b71b24..aaa2dd444a 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -668,4 +668,4 @@ def get_instance_staging_dir(instance): ) instance.data['stagingDir'] = staging_dir - return staging_dir \ No newline at end of file + return staging_dir diff --git a/website/docs/admin_environment.md b/website/docs/admin_environment.md new file mode 100644 index 0000000000..2cc558b530 --- /dev/null +++ b/website/docs/admin_environment.md @@ -0,0 +1,30 @@ +--- +id: admin_environment +title: Environment +sidebar_label: Environment +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## OPENPYPE_TMPDIR: + - Custom staging dir directory + - Supports anatomy keys formating. ex `{root[work]}/{project[name]}/temp` + - supported formating keys: + - root[work] + - project[name | code] + +## OPENPYPE_DEBUG + - setting logger to debug mode + - example value: "1" (to activate) + +## OPENPYPE_LOG_LEVEL + - stringified numeric value of log level. [Here for more info](https://docs.python.org/3/library/logging.html#logging-levels) + - example value: "10" + +## OPENPYPE_MONGO +- If set it takes precedence over the one set in keyring +- for more details on how to use it go [here](admin_use#check-for-mongodb-database-connection) + +## OPENPYPE_USERNAME +- if set it overides system created username diff --git a/website/docs/admin_settings_system.md b/website/docs/admin_settings_system.md index 39b58e6f81..6a17844755 100644 --- a/website/docs/admin_settings_system.md +++ b/website/docs/admin_settings_system.md @@ -14,39 +14,18 @@ Settings applicable to the full studio. ![general_settings](assets/settings/settings_system_general.png) ### Studio Name - - Full name of the studio (can be used as variable on some places) +Full name of the studio (can be used as variable on some places) ### Studio Code - - Studio acronym or a short code (can be used as variable on some places) +Studio acronym or a short code (can be used as variable on some places) ### Admin Password - - After setting admin password, normal user won't have access to OpenPype settings +After setting admin password, normal user won't have access to OpenPype settings and Project Manager GUI. Please keep in mind that this is a studio wide password and it is meant purely as a simple barrier to prevent artists from accidental setting changes. ### Environment - - Globally applied environment variables that will be appended to any OpenPype process in the studio. - - OpenPype is using some keys to configure some tools. Here are some: - -#### OPENPYPE_TMPDIR: - - Custom staging dir directory - - Supports anatomy keys formating. ex `{root[work]}/{project[name]}/temp` - - supported formating keys: - - root[work] - - project[name | code] - - asset - - hierarchy - - task - - username - - app - -#### OPENPYPE_DEBUG - - setting logger to debug mode - - example value: "1" (to activate) - -#### OPENPYPE_LOG_LEVEL - - stringified numeric value of log level. [Here for more info](https://docs.python.org/3/library/logging.html#logging-levels) - - example value: "10" +Globally applied environment variables that will be appended to any OpenPype process in the studio. ### Disk mapping - Platform dependent configuration for mapping of virtual disk(s) on an artist's OpenPype machines before OP starts up. diff --git a/website/sidebars.js b/website/sidebars.js index cc945a019e..ed4ff45db8 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -85,6 +85,7 @@ module.exports = { type: "category", label: "Configuration", items: [ + "admin_environment", "admin_settings", "admin_settings_system", "admin_settings_project_anatomy", From d774eab62603a8356ed55b6c74255332b6c675ee Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 9 Feb 2023 15:08:18 +0100 Subject: [PATCH 0508/1271] end line added --- website/docs/admin_settings_system.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/admin_settings_system.md b/website/docs/admin_settings_system.md index 6a17844755..c39cac61f5 100644 --- a/website/docs/admin_settings_system.md +++ b/website/docs/admin_settings_system.md @@ -176,4 +176,4 @@ In the image before you can see that we set most of the environment variables in In this example MTOA will automatically will the `MAYA_VERSION`(which is set by Maya Application environment) and `MTOA_VERSION` into the `MTOA` variable. We then use the `MTOA` to set all the other variables needed for it to function within Maya. ![tools](assets/settings/tools_01.png) -All of the tools defined in here can then be assigned to projects. You can also change the tools versions on any project level all the way down to individual asset or shot overrides. So if you just need to upgrade you render plugin for a single shot, while not risking the incompatibilities on the rest of the project, it is possible. \ No newline at end of file +All of the tools defined in here can then be assigned to projects. You can also change the tools versions on any project level all the way down to individual asset or shot overrides. So if you just need to upgrade you render plugin for a single shot, while not risking the incompatibilities on the rest of the project, it is possible. From 3ac0a1cb6f8c96071f8f5e992bbb0bb868b84e3f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 9 Feb 2023 15:54:20 +0100 Subject: [PATCH 0509/1271] OP-4513 - fix Openpype plugin for MacOS --- .../repository/custom/plugins/OpenPype/OpenPype.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/openpype/modules/deadline/repository/custom/plugins/OpenPype/OpenPype.py b/openpype/modules/deadline/repository/custom/plugins/OpenPype/OpenPype.py index 6b0f69d98f..ab4a3d5e9b 100644 --- a/openpype/modules/deadline/repository/custom/plugins/OpenPype/OpenPype.py +++ b/openpype/modules/deadline/repository/custom/plugins/OpenPype/OpenPype.py @@ -73,7 +73,7 @@ class OpenPypeDeadlinePlugin(DeadlinePlugin): """ # fix path for application bundle on macos if platform.system().lower() == "darwin": - path = os.path.join(path, "Contents", "MacOS", "lib", "Python") + path = os.path.join(path, "MacOS") version_file = os.path.join(path, "openpype", "version.py") if not os.path.isfile(version_file): @@ -107,8 +107,11 @@ class OpenPypeDeadlinePlugin(DeadlinePlugin): "Scanning for compatible requested " f"version {requested_version}")) dir_list = self.GetConfigEntry("OpenPypeInstallationDirs") + # clean '\ ' for MacOS pasting + if platform.system().lower() == "darwin": + dir_list = dir_list.replace("\\ ", " ") install_dir = DirectoryUtils.SearchDirectoryList(dir_list) - if dir: + if install_dir: sub_dirs = [ f.path for f in os.scandir(install_dir) if f.is_dir() @@ -120,6 +123,9 @@ class OpenPypeDeadlinePlugin(DeadlinePlugin): openpype_versions.append((version, subdir)) exe_list = self.GetConfigEntry("OpenPypeExecutable") + # clean '\ ' for MacOS pasting + if platform.system().lower() == "darwin": + exe_list = exe_list.replace("\\ ", " ") exe = FileUtils.SearchFileList(exe_list) if openpype_versions: # if looking for requested compatible version, @@ -161,7 +167,9 @@ class OpenPypeDeadlinePlugin(DeadlinePlugin): os.path.join( compatible_versions[-1][1], "openpype_console.exe"), os.path.join( - compatible_versions[-1][1], "openpype_console") + compatible_versions[-1][1], "openpype_console"), + os.path.join( + compatible_versions[-1][1], "MacOS", "openpype_console") ] exe = FileUtils.SearchFileList(";".join(exe_list)) From b16af10670181630de0fb1bc700877f46dbfa18d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 9 Feb 2023 16:27:34 +0100 Subject: [PATCH 0510/1271] Removed obsolete method and imports Fixed in get_linked_representation_id instead --- openpype/plugins/load/add_site.py | 38 ------------------------------- 1 file changed, 38 deletions(-) diff --git a/openpype/plugins/load/add_site.py b/openpype/plugins/load/add_site.py index 38c27f9079..e31f746f51 100644 --- a/openpype/plugins/load/add_site.py +++ b/openpype/plugins/load/add_site.py @@ -2,12 +2,6 @@ from openpype.client import get_linked_representation_id from openpype.modules import ModulesManager from openpype.pipeline import load from openpype.modules.sync_server.utils import SiteAlreadyPresentError -from openpype.client.entities import ( - get_hero_version_by_subset_id, - get_representation_by_id, - get_version_by_id, - get_representation_by_name -) class AddSyncSite(load.LoaderPlugin): @@ -84,35 +78,3 @@ class AddSyncSite(load.LoaderPlugin): def filepath_from_context(self, context): """No real file loading""" return "" - - def _add_hero_representation_ids(self, project_name, repre_id): - """Find hero version if exists for repre_id. - - Args: - project_name (str) - repre_id (ObjectId) - Returns: - (list): at least [repre_id] if no hero version found - """ - representation_ids = [repre_id] - - repre_doc = get_representation_by_id( - project_name, repre_id, fields=["_id", "parent", "name"] - ) - - version_doc = get_version_by_id(project_name, repre_doc["parent"]) - if version_doc["type"] != "hero_version": - hero_version = get_hero_version_by_subset_id( - project_name, version_doc["parent"], - fields=["_id", "version_id"] - ) - if (hero_version and - hero_version["version_id"] == version_doc["_id"]): - hero_repre_doc = get_representation_by_name( - project_name, - repre_doc["name"], - hero_version["_id"] - ) - representation_ids.append(hero_repre_doc["_id"]) - - return representation_ids From af83e14dcea910b901969ed7590d6c72e749d2c8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 9 Feb 2023 16:35:14 +0100 Subject: [PATCH 0511/1271] updating workflows to use YNPUT_BOT_TOKEN also some housecleaning was needed --- .github/workflows/automate-projects.yml | 19 -------- .github/workflows/milestone_assign.yml | 4 +- .github/workflows/milestone_create.yml | 8 ++-- .github/workflows/nightly_merge.yml | 4 +- .github/workflows/prerelease.yml | 40 +++------------- .github/workflows/release.yml | 64 ++++--------------------- .github/workflows/test_build.yml | 26 +--------- 7 files changed, 23 insertions(+), 142 deletions(-) delete mode 100644 .github/workflows/automate-projects.yml diff --git a/.github/workflows/automate-projects.yml b/.github/workflows/automate-projects.yml deleted file mode 100644 index b605071c2d..0000000000 --- a/.github/workflows/automate-projects.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Automate Projects - -on: - issues: - types: [opened, labeled] -env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - -jobs: - assign_one_project: - runs-on: ubuntu-latest - name: Assign to One Project - steps: - - name: Assign NEW bugs to triage - uses: srggrs/assign-one-project-github-action@1.2.0 - if: contains(github.event.issue.labels.*.name, 'bug') - with: - project: 'https://github.com/pypeclub/pype/projects/2' - column_name: 'Needs triage' diff --git a/.github/workflows/milestone_assign.yml b/.github/workflows/milestone_assign.yml index 4b52dfc30d..d1490b64c0 100644 --- a/.github/workflows/milestone_assign.yml +++ b/.github/workflows/milestone_assign.yml @@ -13,7 +13,7 @@ jobs: if: github.event.pull_request.milestone == null uses: zoispag/action-assign-milestone@v1 with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" + repo-token: "${{ secrets.YNPUT_BOT_TOKEN }}" milestone: 'next-minor' run_if_develop: @@ -24,5 +24,5 @@ jobs: if: github.event.pull_request.milestone == null uses: zoispag/action-assign-milestone@v1 with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" + repo-token: "${{ secrets.YNPUT_BOT_TOKEN }}" milestone: 'next-patch' \ No newline at end of file diff --git a/.github/workflows/milestone_create.yml b/.github/workflows/milestone_create.yml index b56ca81dc1..cb459c7ae6 100644 --- a/.github/workflows/milestone_create.yml +++ b/.github/workflows/milestone_create.yml @@ -12,7 +12,7 @@ jobs: uses: "WyriHaximus/github-action-get-milestones@master" id: milestones env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + GITHUB_TOKEN: "${{ secrets.YNPUT_BOT_TOKEN }}" - run: printf "name=number::%s" $(printenv MILESTONES | jq --arg MILESTONE $(printenv MILESTONE) '.[] | select(.title == $MILESTONE) | .number') id: querymilestone @@ -31,7 +31,7 @@ jobs: with: title: 'next-patch' env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + GITHUB_TOKEN: "${{ secrets.YNPUT_BOT_TOKEN }}" generate-next-minor: runs-on: ubuntu-latest @@ -40,7 +40,7 @@ jobs: uses: "WyriHaximus/github-action-get-milestones@master" id: milestones env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + GITHUB_TOKEN: "${{ secrets.YNPUT_BOT_TOKEN }}" - run: printf "name=number::%s" $(printenv MILESTONES | jq --arg MILESTONE $(printenv MILESTONE) '.[] | select(.title == $MILESTONE) | .number') id: querymilestone @@ -59,4 +59,4 @@ jobs: with: title: 'next-minor' env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file + GITHUB_TOKEN: "${{ secrets.YNPUT_BOT_TOKEN }}" \ No newline at end of file diff --git a/.github/workflows/nightly_merge.yml b/.github/workflows/nightly_merge.yml index 1d36c89cc7..a50a5c2828 100644 --- a/.github/workflows/nightly_merge.yml +++ b/.github/workflows/nightly_merge.yml @@ -14,10 +14,10 @@ jobs: - name: 🚛 Checkout Code uses: actions/checkout@v2 - - name: 🔨 Merge develop to main + - name: 🔨 Merge develop to main uses: everlytic/branch-merge@1.1.0 with: - github_token: ${{ secrets.ADMIN_TOKEN }} + github_token: ${{ secrets.YNPUT_BOT_TOKEN }} source_ref: 'develop' target_branch: 'main' commit_message_template: '[Automated] Merged {source_ref} into {target_branch}' diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 94bbe48156..571b0339e1 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -25,43 +25,15 @@ jobs: - name: 🔎 Determine next version type id: version_type run: | - TYPE=$(python ./tools/ci_tools.py --bump --github_token ${{ secrets.GITHUB_TOKEN }}) - - echo ::set-output name=type::$TYPE + TYPE=$(python ./tools/ci_tools.py --bump --github_token ${{ secrets.YNPUT_BOT_TOKEN }}) + echo "type=${TYPE}" >> $GITHUB_OUTPUT - name: 💉 Inject new version into files id: version if: steps.version_type.outputs.type != 'skip' run: | - RESULT=$(python ./tools/ci_tools.py --nightly --github_token ${{ secrets.GITHUB_TOKEN }}) - - echo ::set-output name=next_tag::$RESULT - - # - name: "✏️ Generate full changelog" - # if: steps.version_type.outputs.type != 'skip' - # id: generate-full-changelog - # uses: heinrichreimer/github-changelog-generator-action@v2.3 - # with: - # token: ${{ secrets.ADMIN_TOKEN }} - # addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}, "refactor":{"prefix":"**🔀 Refactored code**", "labels":["refactor"]}}' - # issues: false - # issuesWoLabels: false - # sinceTag: "3.12.0" - # maxIssues: 100 - # pullRequests: true - # prWoLabels: false - # author: false - # unreleased: true - # compareLink: true - # stripGeneratorNotice: true - # verbose: true - # unreleasedLabel: ${{ steps.version.outputs.next_tag }} - # excludeTagsRegex: "CI/.+" - # releaseBranch: "main" - - - name: "🖨️ Print changelog to console" - if: steps.version_type.outputs.type != 'skip' - run: cat CHANGELOG.md + NEW_VERSION_TAG=$(python ./tools/ci_tools.py --nightly --github_token ${{ secrets.YNPUT_BOT_TOKEN }}) + echo "next_tag=${NEW_VERSION_TAG}" >> $GITHUB_OUTPUT - name: 💾 Commit and Tag id: git_commit @@ -80,7 +52,7 @@ jobs: - name: Push to protected main branch uses: CasperWA/push-protected@v2.10.0 with: - token: ${{ secrets.ADMIN_TOKEN }} + token: ${{ secrets.YNPUT_BOT_TOKEN }} branch: main tags: true unprotect_reviews: true @@ -89,7 +61,7 @@ jobs: uses: everlytic/branch-merge@1.1.0 if: steps.version_type.outputs.type != 'skip' with: - github_token: ${{ secrets.ADMIN_TOKEN }} + github_token: ${{ secrets.YNPUT_BOT_TOKEN }} source_ref: 'main' target_branch: 'develop' commit_message_template: '[Automated] Merged {source_ref} into {target_branch}' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e3b6eb05c..0b4c8af2c7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,34 +26,12 @@ jobs: - name: 💉 Inject new version into files id: version run: | - echo ::set-output name=current_version::${GITHUB_REF#refs/*/} - RESULT=$(python ./tools/ci_tools.py --finalize ${GITHUB_REF#refs/*/}) - LASTRELEASE=$(python ./tools/ci_tools.py --lastversion release) + NEW_VERSION=$(python ./tools/ci_tools.py --finalize ${GITHUB_REF#refs/*/}) + LAST_VERSION=$(python ./tools/ci_tools.py --lastversion release) - echo ::set-output name=last_release::$LASTRELEASE - echo ::set-output name=release_tag::$RESULT - - # - name: "✏️ Generate full changelog" - # if: steps.version.outputs.release_tag != 'skip' - # id: generate-full-changelog - # uses: heinrichreimer/github-changelog-generator-action@v2.3 - # with: - # token: ${{ secrets.ADMIN_TOKEN }} - # addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}, "refactor":{"prefix":"**🔀 Refactored code**", "labels":["refactor"]}}' - # issues: false - # issuesWoLabels: false - # sinceTag: "3.12.0" - # maxIssues: 100 - # pullRequests: true - # prWoLabels: false - # author: false - # unreleased: true - # compareLink: true - # stripGeneratorNotice: true - # verbose: true - # futureRelease: ${{ steps.version.outputs.release_tag }} - # excludeTagsRegex: "CI/.+" - # releaseBranch: "main" + echo "current_version=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + echo "last_release=${LAST_VERSION}" >> $GITHUB_OUTPUT + echo "release_tag=${NEW_VERSION}" >> $GITHUB_OUTPUT - name: 💾 Commit and Tag id: git_commit @@ -70,43 +48,17 @@ jobs: if: steps.version.outputs.release_tag != 'skip' uses: CasperWA/push-protected@v2.10.0 with: - token: ${{ secrets.ADMIN_TOKEN }} + token: ${{ secrets.YNPUT_BOT_TOKEN }} branch: main tags: true unprotect_reviews: true - - name: "✏️ Generate last changelog" - if: steps.version.outputs.release_tag != 'skip' - id: generate-last-changelog - uses: heinrichreimer/github-changelog-generator-action@v2.2 - with: - token: ${{ secrets.ADMIN_TOKEN }} - addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}, "refactor":{"prefix":"**🔀 Refactored code**", "labels":["refactor"]}}' - issues: false - issuesWoLabels: false - sinceTag: ${{ steps.version.outputs.last_release }} - maxIssues: 100 - pullRequests: true - prWoLabels: false - author: false - unreleased: true - compareLink: true - stripGeneratorNotice: true - verbose: true - futureRelease: ${{ steps.version.outputs.release_tag }} - excludeTagsRegex: "CI/.+" - releaseBranch: "main" - stripHeaders: true - base: 'none' - - - name: 🚀 Github Release if: steps.version.outputs.release_tag != 'skip' uses: ncipollo/release-action@v1 with: - body: ${{ steps.generate-last-changelog.outputs.changelog }} tag: ${{ steps.version.outputs.release_tag }} - token: ${{ secrets.ADMIN_TOKEN }} + token: ${{ secrets.YNPUT_BOT_TOKEN }} - name: ☠ Delete Pre-release if: steps.version.outputs.release_tag != 'skip' @@ -118,7 +70,7 @@ jobs: if: steps.version.outputs.release_tag != 'skip' uses: everlytic/branch-merge@1.1.0 with: - github_token: ${{ secrets.ADMIN_TOKEN }} + github_token: ${{ secrets.YNPUT_BOT_TOKEN }} source_ref: 'main' target_branch: 'develop' commit_message_template: '[Automated] Merged release {source_ref} into {target_branch}' diff --git a/.github/workflows/test_build.yml b/.github/workflows/test_build.yml index 0e6c242bd6..064a4d47e0 100644 --- a/.github/workflows/test_build.yml +++ b/.github/workflows/test_build.yml @@ -28,7 +28,7 @@ jobs: uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - + - name: 🧵 Install Requirements shell: pwsh run: | @@ -64,27 +64,3 @@ jobs: run: | export SKIP_THIRD_PARTY_VALIDATION="1" ./tools/build.sh - - # MacOS-latest: - - # runs-on: macos-latest - # strategy: - # matrix: - # python-version: [3.9] - - # steps: - # - name: 🚛 Checkout Code - # uses: actions/checkout@v2 - - # - name: Set up Python - # uses: actions/setup-python@v2 - # with: - # python-version: ${{ matrix.python-version }} - - # - name: 🧵 Install Requirements - # run: | - # ./tools/create_env.sh - - # - name: 🔨 Build - # run: | - # ./tools/build.sh From 240c77c9bbd49d5133c17717b6358769c2c63104 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 9 Feb 2023 16:43:18 +0100 Subject: [PATCH 0512/1271] fixing nightly versioning --- .github/workflows/milestone_assign.yml | 2 +- .github/workflows/milestone_create.yml | 2 +- .github/workflows/nightly_merge.yml | 2 +- pyproject.toml | 2 +- tools/ci_tools.py | 24 +++++++++++------------- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/.github/workflows/milestone_assign.yml b/.github/workflows/milestone_assign.yml index d1490b64c0..3cbee51472 100644 --- a/.github/workflows/milestone_assign.yml +++ b/.github/workflows/milestone_assign.yml @@ -25,4 +25,4 @@ jobs: uses: zoispag/action-assign-milestone@v1 with: repo-token: "${{ secrets.YNPUT_BOT_TOKEN }}" - milestone: 'next-patch' \ No newline at end of file + milestone: 'next-patch' diff --git a/.github/workflows/milestone_create.yml b/.github/workflows/milestone_create.yml index cb459c7ae6..632704e64a 100644 --- a/.github/workflows/milestone_create.yml +++ b/.github/workflows/milestone_create.yml @@ -59,4 +59,4 @@ jobs: with: title: 'next-minor' env: - GITHUB_TOKEN: "${{ secrets.YNPUT_BOT_TOKEN }}" \ No newline at end of file + GITHUB_TOKEN: "${{ secrets.YNPUT_BOT_TOKEN }}" diff --git a/.github/workflows/nightly_merge.yml b/.github/workflows/nightly_merge.yml index a50a5c2828..c5cd9f1466 100644 --- a/.github/workflows/nightly_merge.yml +++ b/.github/workflows/nightly_merge.yml @@ -26,4 +26,4 @@ jobs: uses: benc-uk/workflow-dispatch@v1 with: workflow: Nightly Prerelease - token: ${{ secrets.ADMIN_TOKEN }} \ No newline at end of file + token: ${{ secrets.ADMIN_TOKEN }} diff --git a/pyproject.toml b/pyproject.toml index 6e88404700..a872ed3609 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.1-nightly.3" # OpenPype +version = "3.15.0" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" diff --git a/tools/ci_tools.py b/tools/ci_tools.py index c8f0cd48b4..750bf8645d 100644 --- a/tools/ci_tools.py +++ b/tools/ci_tools.py @@ -7,16 +7,10 @@ from github import Github import os def get_release_type_github(Log, github_token): - # print(Log) minor_labels = ["Bump Minor"] - # patch_labels = [ - # "type: enhancement", - # "type: bug", - # "type: deprecated", - # "type: Feature"] g = Github(github_token) - repo = g.get_repo("pypeclub/OpenPype") + repo = g.get_repo("ynput/OpenPype") labels = set() for line in Log.splitlines(): @@ -35,12 +29,12 @@ def get_release_type_github(Log, github_token): else: return "patch" - # TODO: if all is working fine, this part can be cleaned up eventually + # TODO: if all is working fine, this part can be cleaned up eventually # if any(label in labels for label in patch_labels): # return "patch" - + return None - + def remove_prefix(text, prefix): return text[text.startswith(prefix) and len(prefix):] @@ -93,12 +87,16 @@ def file_regex_replace(filename, regex, version): f.truncate() -def bump_file_versions(version): +def bump_file_versions(version, nightly=False): filename = "./openpype/version.py" regex = "(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-((0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?" file_regex_replace(filename, regex, version) + if nightly: + # skip nightly reversion in pyproject.toml + return + # bump pyproject.toml filename = "pyproject.toml" regex = "version = \"(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(\+((0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?\" # OpenPype" @@ -196,7 +194,7 @@ def main(): if options.nightly: next_tag_v = calculate_next_nightly(github_token=options.github_token) print(next_tag_v) - bump_file_versions(next_tag_v) + bump_file_versions(next_tag_v, True) if options.finalize: new_release = finalize_prerelease(options.finalize) @@ -222,7 +220,7 @@ def main(): new_prerelease = current_prerelease.bump_prerelease().__str__() print(new_prerelease) bump_file_versions(new_prerelease) - + if options.version: bump_file_versions(options.version) print(f"Injected version {options.version} into the release") From f805e7501d488e82bffcf31aaaab2d63ae15d2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 9 Feb 2023 16:55:56 +0100 Subject: [PATCH 0513/1271] removing last reference of ADMIN_TOKEN --- .github/workflows/nightly_merge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightly_merge.yml b/.github/workflows/nightly_merge.yml index c5cd9f1466..1776d7a464 100644 --- a/.github/workflows/nightly_merge.yml +++ b/.github/workflows/nightly_merge.yml @@ -26,4 +26,4 @@ jobs: uses: benc-uk/workflow-dispatch@v1 with: workflow: Nightly Prerelease - token: ${{ secrets.ADMIN_TOKEN }} + token: ${{ secrets.YNPUT_BOT_TOKEN }} From 922fe5bdaf17d8e78b30b58b1ca41bbeb54f9307 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Thu, 9 Feb 2023 15:59:27 +0000 Subject: [PATCH 0514/1271] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index c65d1197c1..61339fb3dd 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.1-nightly.3" +__version__ = "3.15.1-nightly.4" From d516de4ecdfcf17b2d8f91d91535d22ef7c6ad13 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 9 Feb 2023 16:28:55 +0000 Subject: [PATCH 0515/1271] Ensure content and proxy hierarchy is the same. --- .../publish/collect_arnold_scene_source.py | 4 +- .../publish/extract_arnold_scene_source.py | 51 ++++++--- .../publish/validate_arnold_scene_source.py | 106 ++++++++++++++++++ 3 files changed, 145 insertions(+), 16 deletions(-) create mode 100644 openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py diff --git a/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py index c0275eef7b..0415808b7a 100644 --- a/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/collect_arnold_scene_source.py @@ -21,10 +21,10 @@ class CollectArnoldSceneSource(pyblish.api.InstancePlugin): self.log.warning("Skipped empty instance: \"%s\" " % objset) continue if objset.endswith("content_SET"): - instance.data["setMembers"] = members + instance.data["setMembers"] = cmds.ls(members, long=True) self.log.debug("content members: {}".format(members)) elif objset.endswith("proxy_SET"): - instance.data["proxy"] = members + instance.data["proxy"] = cmds.ls(members, long=True) self.log.debug("proxy members: {}".format(members)) # Use camera in object set if present else default to render globals diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 4cff9d0183..10943dd810 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -5,14 +5,16 @@ from maya import cmds import arnold from openpype.pipeline import publish -from openpype.hosts.maya.api.lib import maintained_selection, attribute_values +from openpype.hosts.maya.api.lib import ( + maintained_selection, attribute_values, delete_after +) from openpype.lib import StringTemplate class ExtractArnoldSceneSource(publish.Extractor): """Extract the content of the instance to an Arnold Scene Source file.""" - label = "Arnold Scene Source" + label = "Extract Arnold Scene Source" hosts = ["maya"] families = ["ass"] asciiAss = False @@ -124,22 +126,43 @@ class ExtractArnoldSceneSource(publish.Extractor): def _extract(self, nodes, attribute_data, kwargs): self.log.info("Writing: " + kwargs["filename"]) filenames = [] - with attribute_values(attribute_data): - with maintained_selection(): - self.log.info( - "Writing: {}".format(nodes) + # Duplicating nodes so they are direct children of the world. This + # makes the hierarchy of any exported ass file the same. + with delete_after() as delete_bin: + duplicate_nodes = [] + for node in nodes: + duplicate_transform = cmds.duplicate(node)[0] + delete_bin.append(duplicate_transform) + + # Discard the children. + shapes = cmds.listRelatives(duplicate_transform, shapes=True) + children = cmds.listRelatives( + duplicate_transform, children=True ) - cmds.select(nodes, noExpand=True) + cmds.delete(set(children) - set(shapes)) - self.log.info( - "Extracting ass sequence with: {}".format(kwargs) - ) + duplicate_transform = cmds.parent( + duplicate_transform, world=True + )[0] - exported_files = cmds.arnoldExportAss(**kwargs) + duplicate_nodes.append(duplicate_transform) - for file in exported_files: - filenames.append(os.path.split(file)[1]) + with attribute_values(attribute_data): + with maintained_selection(): + self.log.info( + "Writing: {}".format(duplicate_nodes) + ) + cmds.select(duplicate_nodes, noExpand=True) - self.log.info("Exported: {}".format(filenames)) + self.log.info( + "Extracting ass sequence with: {}".format(kwargs) + ) + + exported_files = cmds.arnoldExportAss(**kwargs) + + for file in exported_files: + filenames.append(os.path.split(file)[1]) + + self.log.info("Exported: {}".format(filenames)) return filenames diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py new file mode 100644 index 0000000000..ad00502d56 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py @@ -0,0 +1,106 @@ +import os +import types + +import maya.cmds as cmds +from mtoa.core import createOptions + +import pyblish.api +from openpype.pipeline.publish import ( + ValidateContentsOrder, PublishValidationError +) + + +class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): + """Validate Arnold Scene Source. + + If using proxies we need the nodes to share the same names and not be + parent to the world. This ends up needing at least two groups with content + nodes and proxy nodes in another. + """ + + order = ValidateContentsOrder + hosts = ["maya"] + families = ["ass"] + label = "Validate Arnold Scene Source" + + def _get_nodes_data(self, nodes): + ungrouped_nodes = [] + nodes_by_name = {} + parents = [] + for node in nodes: + node_split = node.split("|") + if len(node_split) == 2: + ungrouped_nodes.append(node) + + parent = "|".join(node_split[:-1]) + if parent: + parents.append(parent) + + nodes_by_name[node_split[-1]] = node + for shape in cmds.listRelatives(node, shapes=True): + nodes_by_name[shape.split("|")[-1]] = shape + + return ungrouped_nodes, nodes_by_name, parents + + def process(self, instance): + if not instance.data["proxy"]: + return + + ungrouped_nodes = [] + + nodes, content_nodes_by_name, content_parents = self._get_nodes_data( + instance.data["setMembers"] + ) + ungrouped_nodes.extend(nodes) + + nodes, proxy_nodes_by_name, proxy_parents = self._get_nodes_data( + instance.data["proxy"] + ) + ungrouped_nodes.extend(nodes) + + # Validate against nodes directly parented to world. + if ungrouped_nodes: + raise PublishValidationError( + "Found nodes parented to the world: {}\n" + "All nodes need to be grouped.".format(ungrouped_nodes) + ) + + # Validate for content and proxy nodes amount being the same. + if len(instance.data["setMembers"]) != len(instance.data["proxy"]): + raise PublishValidationError( + "Amount of content nodes ({}) and proxy nodes ({}) needs to " + "be the same.".format( + len(instance.data["setMembers"]), + len(instance.data["proxy"]) + ) + ) + + # Validate against content and proxy nodes sharing same parent. + if list(set(content_parents) & set(proxy_parents)): + raise PublishValidationError( + "Content and proxy nodes cannot share the same parent." + ) + + # Validate for content and proxy nodes sharing same names. + sorted_content_names = sorted(content_nodes_by_name.keys()) + sorted_proxy_names = sorted(proxy_nodes_by_name.keys()) + odd_content_names = list( + set(sorted_content_names) - set(sorted_proxy_names) + ) + odd_content_nodes = [ + content_nodes_by_name[x] for x in odd_content_names + ] + odd_proxy_names = list( + set(sorted_proxy_names) - set(sorted_content_names) + ) + odd_proxy_nodes = [ + proxy_nodes_by_name[x] for x in odd_proxy_names + ] + if not sorted_content_names == sorted_proxy_names: + raise PublishValidationError( + "Content and proxy nodes need to share the same names.\n" + "Content nodes not matching: {}\n" + "Proxy nodes not matching: {}".format( + odd_content_nodes, odd_proxy_nodes + ) + ) From 8ee1a3a2d2cb7b952f6aee1385b31abee5d9add7 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 9 Feb 2023 17:11:31 +0000 Subject: [PATCH 0516/1271] Document proxy workflow --- website/docs/artist_hosts_maya_arnold.md | 14 ++++++++++++++ .../docs/assets/maya-arnold_scene_source.png | Bin 0 -> 15948 bytes website/docs/assets/maya-arnold_standin.png | Bin 0 -> 42985 bytes 3 files changed, 14 insertions(+) create mode 100644 website/docs/assets/maya-arnold_scene_source.png create mode 100644 website/docs/assets/maya-arnold_standin.png diff --git a/website/docs/artist_hosts_maya_arnold.md b/website/docs/artist_hosts_maya_arnold.md index b8b8da6d57..b3c02a0894 100644 --- a/website/docs/artist_hosts_maya_arnold.md +++ b/website/docs/artist_hosts_maya_arnold.md @@ -8,9 +8,23 @@ Arnold Scene Source can be published as a single file or a sequence of files, de When creating the instance, two objectsets are created; `content` and `proxy`. Meshes in the `proxy` objectset will be the viewport representation when loading as `standin`. Proxy representations are stored as `resources` of the subset. +### Arnold Scene Source Proxy Workflow +In order to utilize operators and proxies, the content and proxy nodes need to share the same names (including the shape names). This is done by parenting the content and proxy nodes into separate groups. For example: + +![Arnold Scene Source](assets/maya-arnold_scene_source.png) + ## Standin Arnold Scene Source `ass` and Alembic `abc` are supported to load as standins. +### Standin Proxy Workflow If a subset has a proxy representation, this will be used as display in the viewport. At render time the standin path will be replaced using the recommended string replacement workflow; https://help.autodesk.com/view/ARNOL/ENU/?guid=arnold_for_maya_operators_am_Updating_procedural_file_paths_with_string_replace_html + +Since the content and proxy nodes share the same names and hierarchy, any manually shader assignments will be shared. + + +:::note for advanced users +You can stop the proxy swapping by disabling the string replacement operator found in the container. +![Arnold Standin](assets/maya-arnold_standin.png) +::: diff --git a/website/docs/assets/maya-arnold_scene_source.png b/website/docs/assets/maya-arnold_scene_source.png new file mode 100644 index 0000000000000000000000000000000000000000..4150b78aac7bfe776d9f4b4940a56d77176a2a57 GIT binary patch literal 15948 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

M-ZZ1puA}J8G$tO$CyZX_jwrh#*yh&^Hbc( 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

H1$S7zu1R^u(X=+a-N2qaE)ReLSRVk zwolWgju{L_j(TKq>9+empk%JPLx6J6p$k&D!9S7eKv}2bB=V;Dhud&__HkDIB})U@R4fxdG$Nz|Dx5M)w23o zQ@p)hQ&jA+;>$AR9oAEwu`L;n0Y*0O^RP`4ZL7dq7uOqqmqVOB%Q6G>L;$Z2{?pyI zS`+vV{F7iue~h0h#fl%UZb>LYswKYL%irIW^}=KEnb4b>$cFM|6o-pRQlMsODoQdd zk!~50JtZ!1$p*)N*XFd+|EkT==VvXfi8eD{T7rnB7gf=P6WkdtE0}6<-<`2Fex1mxnD+*Aq2^?=!1~zKPL{x_847xq)L9#8kg)#@IUmH&tQAQYcxy z^;jh7Olfs!rAZn^ZKU=1Qn{^@1Wc3B&ckKqpU&jMQziv{%sVr@h zXcUPgrohWy>5f$i6tEFGpFHJj#u`aT7us%7{PunJ%pIrR3j^U zni~^i5Vwiu;1kauz+|b^XqSRKMGY);!}KGnb1U-vmbrBimtq=F0aVBPXakboqW>RX zQZ@WK1In0M7np}h#e)N>@Ae`w&PzjJK;+^RN@6X1{KuDF)ZGVi!0vZxhviIq4!kzY zWWJQDHyeb_31go4{Yq1>R<9&DEzv=a%Tu2oJ7lr@L_=z!N6KWuiMaZkZvm40 zbR9mX8SrWGQCwl*Kt zc2?SsA>J86Cr5`vMX$yfW#*oFwmbZ{xQZBUNUn& z8!s;svsBpKtzxk1?H2dn4QdoxbSkA!F#3%IVm(|Wi$)T~K)>xoBy6K=mk>6+&f8f+}{&iR4W!yr1C`$6Q_I3x<-}ttg`79fv z1W6peoty!`3Xu?rx!A0Ava1{w)27Oma%-2GL-!0(q{0NtzGy#TsEFTlA67Wp!Z%d-cZ4G$*=2Zu3lBh9aMe?J=X-vywb z%B1H|XEwkGoB(-r^naFq<~`s5tAN*vJ$v@dW^;Y{_xGb8VC2Khd8YC)Mn%V6JRg(V zeQBLppVv4E)UR+L;ChJt5|;ketu@f(`|pMRuy8-66D~z*tZoBxJwykCxfhA=kMg@C zv;ber0`@Y#!x+EQvelJiLT@Ff3pRmTLP_j}tSDMvjzhh&3L(NjwK&H^L@Wa18C8_l zhco2v2xf#+!(-oKgx2E|6F)BW7rUh0`Ec*yoqQX8zna~Dm3`X8Po=5d`F`yy(YnZ< zYS)VB{yE2g)Ojj#AHd!`hyUysL<9xU;@bk)DL{Ch22KkfjN`W`<^6J~KSW^_@6go` zPwK?pAf9SY*7`8n7xHgigINdZ?(^5O{e|%Rxy~_S>2IGhy8C|7*lP^A+nfK(p!c`N zLRBVBBE12NIixq};)(|ja_o#Qpy$umRaZcK`UIo75tEkT)OECCpQ{D&${v@HILFEomW#^7Fz3j|(!)Ud z1RU0Xm4jY1C)hF3o2in@_R1BnWW>eZ6yZ(Ob2Akpx?Fe8Pb~3L>I8)3n*ehCEcm!v z!zJmRQ;?&nXO3zzBQxf8cXxn=SNWymMKQw8Nz9@t4mnw!aI&~C zuqaTILA1<$=0CH*Ewy&+UseXqH$aWEjrM|z& zfRUdyl?U3f%-h(*42e|upS$$H{4oFZ8pLVvgPB5TSXc;fQ0D1{N}vh;Co!5ljYlD$ zfBM-+HkaT=D(pJM#im%mst+a+8*F}ftFt}ef_S>*`O%HpaSx0ja7P8dSpV3ZGgK`M{k2V{+E1tVcATT!;RA&< zZEaayYJFH(V%?m1yJj4KAsUtGodKtS@eAd~ucNI2@-=J-9*7)nW8qS~Gtd@VowL2x zljTRNHsZjw&ZO|I^#lOozoBq7bf>;_`4xf0M|xNttF>8>^I;|zKi$E zeyryNQUNYD_U+GDmd|)lNyTKhT9NDUfBeQ}9Qa%yj9C`=ctAW0_HE#iYf3vHJ#1+MJYD=GzkEeJYicNCP@ym1`Nm0X zx_R;*Xvc;@*urn?6UHtVY}2|CJ-j@8_~ca`%k@j)uJ?@9PuZGj`wwk!KaxM}_ig>P zg+FFn;?L~ZdyTTU5sA`Vn7nP3e)5@xr>v8|$&{!PYS!r8)S%I@PubECmTRZnrbQUq zF~fa*@M6F7MS@LdKa@24^J&cG!do%Lp);eEc$SQlMo^3pO0Yi-vG2qin#yi`{ha;t zNA#+?J4F?DtTD{G$2VMyU)K!<6T{r&%vppkJ-_w<=DB*RRO$HAr%&vIr5d*FvnG&l zC9=-NVZzfii8wiL5jQO?596}ASDvrPw4m%7*o2eN;d=fnju=yX8pM9?v#B!Uu;XZj}#VsLKhY0C@snnK_EGT|Qs8Wt&B7!OJE4pnH z;ftcSgTZG}(FrZ}v8S4pJ&7KnKDDmQsf4s*zwRrEZg-wg!UV;{h+IsQpT~{iIOtxB zF$}SM?p?Pi;Tz4;r6jx5e*;W02-A~U9T=(6E36Bu@eEL`~5rm7A*!sJYD;qb4k^2MJ zrT5wP2kps}pEaJ56tyHP-5UJb2ztCd{3$ax$Q-hYBOrn8R+33V^e zJLJ(z^_G@R%RMF{x?(tz>|kc?X|SEoM$y~*88E^Wxm^A%Vk)-hN8Gi&_N+SnmD8n` zAI?&+Uxf$VruHOuH`j8P2%AZ>e*{fpre>`RZ?L3|!P&SJfuo8Ie|UO6UC%sUC1=S= zI4{L0QPCjF#?25LQ8cN0P=9l3$jvkrzh0d()?^}O0tBbmg4RqvW5aMnXQ`f#DmYiG zG4W9_@qiRvp~T~a$VPi8|Mr#2-zCAALKLC6KVev zpK*FKQyNo2Hxc4QUbtI|**N77e+(VJoUd9t`Nvka+#SWd&MX-%jzj@hfRh-xj#iVD z+k?$UA@B>_fMlh%@+Ulrz?fPuU2;VA)Y~)Hp9>VtRsrVDTFhPZS90#2ibA32WRkmw zZGV4hqjH3x_$Wz-%^3tWiN-~SgY!p8{<4)F!B`ex<_w?SBrLd?eQC|V&tDF#>et{; zng}6eDFLMvmC_JH2OmA3C+Upub3#nmOg0>W)5g9LD3&Ql;mNFTj!87vSqg1domX4U zr%k_@$$gLmr6S}RlA5tdsofIHnR)p|X+9Ru3MVECC(K4CZ1@VOa+tOcCs>9%JTISy zdCk%sN#nVhLb^?VA7Yc zJv0d*KxDX>#3?O&$>S?(33%iPpCWSK(t>XK2TFfP*u1PFG7r6`9Y6dB#!%PGjR`pMrI7xB}&-LHMYJP|LoHWlC&u8Da zy{|BUUe^--N7q6r_GeNkmw&sktj(fM!Yxh;Um;ieSk-vWm5%-6o1vBl*EW} zzuF5G^#|#niGXS#v4EyMWyvyE3ZTmR1wsmmg=9`WN#}5#UYqePC6+=39Md4{Emc&e zZNLer(nePJuOq@m1WAzu7N={0>O&T`#^X^u>8dDoJ zRRLn%Og$UFJxUChBx|5g+%7l;^&(bz{meSWR`-}hEmqnMKaYZM- zi<&<2ko0PJ1)pxQd@{29%0lGYq+-xcp~rAWfKKMpKLn7QkAtT|8W24QslG#Xm|4*} zgeak|-RQ#mmIj?I?PquFBsoOB>tgc= zGwk&M{N%ih?XDIIXZd*yM@f2H*XQ~@HAj}Kvr`3*E}zX1Z1=y{e^0b3v)^b##&JoU zAlhWG>ZEnBYg8ob5C@4B)Nm;9*%#$=_m?$2lG+61RVY75*g7Fe{Qh9Pq%AI@(G4}W zmxHA79fgA^@IGD|_LF#J-ZufnV7R#StCUT(Av3;T9dcLaW4Dwd7orhA1|~{3PFoU{ zU$maaSE`(6`=u$119+bA#`k=8rc-WND`e?PKr{D}p)FgE+l;0)9y4E@cqHr;o1*=} z6K}ABubH>|$RVa}#gCH08w1nkH>7_yYb<)r3sNAdj_B8+GskI_E~MO?9}#!y6#3~! zqK(s8LV<{|Jl039=vZH@v9gy}+T!w&ALLQZZDBp)9Sih&5*G75HsjoATHS1LyVx?* z*zY;_z0by*{UzG49OCiYiDq}v%G|NYnOT^0rOp6c@y#iPZ*{v;Hek0Z#oY;Z!)C5A z=+;cXv?-(K)I=}|c9kmTvJgyhhGA0-A)-Sz;6Q3(H9e4zLKUa^I&Q%Kmx(Bj0$ zh}{h1(V>WQqbeZa`A(fpFPk-$GGfLg**Sx0E#xL=N&~~OX^;K-VN4D=;FY4Zcrngy z<=x^H-^CDq)+IM@ZMtq>hB0DDZb1uBD3B2~WZB!tjbgCnb%pp)SW{TRGsQGg8QU}4 zX26L-P3+O`o6#3)@hMH#Vk??g32Z7!!bbJ`V`pc{PW;q^tk&x=y1Ern?fOFbedxDNW+~+V$Y3{%d2BAjnUM`*yUhn2D(b-P+ z+P?f(vHni}`Etjtvlw}oec?Tp(UHH59=xS#nQsWKJE|5D^Av6o&>i0)%ZY4xi6z%? z-&ZNTx0g4s9$!+IeTq;VJT=bAUQzB8_|ZBpl=a9LZ&tDur*xoi;f1`tYfh#=8MUAZ zvp5!tX)8`V!6pDZG!d7?X9bgbzSTitih&D1)pO?^DZ)sBzLOkO|y%d!kQgurM%p+LUWX{Jr5ss&r!RY>Df{ zW3cAa>oxe&rUqUJ)NaFV&RqFayy~IQb?uKJ{MvcG`ZMRRZ&}W$8@JpQ~!Zi(<{rQQr&F))j-FPOMy5ZKcB3 zuF07K=C;=wngfYeWN6)%<7lHhmw$Rbe2mIHu5Y*USVVOgVPO5z_TwMbvqD^cP!WaN zKW#TKr2%y4$3e|zjkK&U*hejhhw>3una#8hk!52s62wyMidcRc-S`3!>UqXR(UXEZ zp1sv!?kC|BAlkGbZAq}@WW*t1)7TedSTc|o5IpJI$WMu0Y&jm6*3>feR?*@gp*B7x z+|6oste(kO&M}YH?l?ZSkW{}gIGjOdIiAIR1OlYqfPL|dDlIT;tA&C0wGD5TBQ+b` z@9~{4Z6(LyR=?Y@B<5V~%F|~zY_vx^mmnXyAybWSy6Zm9O2PiP3fCTOIM-8`gOElA z2Oc&mvAm5&YI5cfMt)g+v8DaK)Y;c2K`c9X7gd-1bbPAVA0dO(eEtFXtmmDPJ%z zE>-deHPU1j@)9RCjt;SF9vRr3+J9~5<`VBxZS4irI=IKU>yKhI=3(Vny!Bs}!y!V8 z)qhJgw{HJ3Q~h33w!5m4=S+yaOfu2-SHE`n0>>+3&iX{%PF`I9Eh5}OWVh>^%`}pz zm_0qx%_T>+z+c*U^Sag%!5%k`C1S0!m`1NlyWYZb~Q~|gT&{y9P+c7u2!D-CvE+1 zWVDXCG4f4xMKmX71K@kro<^cJV!%G=#5#gB6&Ii<9Y>}>WlVkwPGH9}hlfv!8-s8H z6QLh&`Z323H)Z&GdOq!YyVw)Zi@X~ilYcv}&FxGKSb6k|0XM_@&D~^s*SXGu!SudA z(jbV40K1DjB9uE{4VxT`Bjc>BTzXu;Y7X7Zd>*>)pWHN)YP_G!3Sl6Clp=xhr8Gff zu5BSen$ML-ibj>?0EtG!PWqiksEyWF|H-R%Yxa2#^*<8RA*ASkv*Hs#(DzlE&qYmvXk@LX9}jNi>CJ4aop)%Cx( z9MOyv&+xi~zNgRsr(kQ;?|n{JEM})dbN;z{?mt&o|C96Q0k8>zw3RzLXHKkhdkndG}Cs! zyM{f#ke2&$pw#*1;-i)Pqc+04Vl~j=?lsH>pu^cKbeR41q^=zxkh>jv83>en6!?5| zx|+0wya;sflIru%V)@^~Mmuakd=DA3m#qr|<&dw{SP?MGKxfR<_-0dzHdO8o)$Q>X z{^-OJ!rVu~ImTkp8{RfD-HS;>*Bz|30)Z}LB2ggFCtE?X*tKa=+rtz(8_U{c0t*OO zIl6CqFwJ`v`IxJ@ZKMYp|qzfKYJ$o6r5231Ksmc9qEnl3ihEiZ3wRJK6Jf7 zZ@@n4W$c^H<2KmrF2~8t!=IbAHOT1}#8PT>5^nMy=5emoHXUZM)sj#0M(F?)B{vd_ zy9O1Z?9cgf_bL45v-nYKB9byL;jNA90s)MvjGmPusLmxqYnnidc7J~AvRr0?OV@O_Bn&zCyHDG)k zRuqYU=rpbB-gWdgbS<5H_7p{JjZ?Qtkp3ZjxB7SwRm{A5JNVH$V67|@eyQW9RUpvd zzx;I1^t0h2B;XRmI8oy(%j)ME*$|y+|}8VrR0|fCa7&s|@MG)_HT;Xig4dSc9&< z2n0Ss-s}F)wf|#rPkv{K{zJSm}XGw@4Um zip*BlpKSN)xtVS_vU?proNG1S8sT5_Xk}HX!$R`!XQKqM=0R2R7vk{6mG_nlYIJs! z=_e~9g}PQDNu`u=M6m&XGF1M*?{*IxU-&_spu;b~9qVPSA2r!Uwwih?alo7RNNRGp z?P*s*AXC8Kc<-&YGPSOj3RH)_I_4qm?e0~N1(=!tJgDVRH+im;xPzP?znotQ+)?9t z4$Vhdg}2Xy_iC*GfhruZ)#F-(*+|PUk^lGPyjTv?Z4Q2vd zv2EF&M0!4RVJEU8tp)cY*S<8lr>Cd7iStRAK5P*5vB$hiv;dS804%du9EXz)hNK=S z(_*&mh(ujAjTEV6MZ<=*w=p%pvgV}QAavCY+h zkai2$Ran|HD<2zKNNH?X&Xe8^e;2V!@L>bD5R}Zh84O^Wl~LEeJKaFEjX7_iouDr|zhteXjbDI3S#aV=|{c*^*iKR46FiYsxc4n$Dc;uwL=rejg! zVKPD?%Py!)T{41bdph(T5sLoun#^bYu}cf$T4A6ni}JE)OpFgsjQ zEpo)p)^vta@=e;Y@P^Q*8hv9;>ZKQByQJe8qW;{hVx6X!7Fwb7c3WL0G*wQP3>m^) z7LQ%W?rzQEWRF&N`Ev$H>d86KPDddYyVhbbVV3_IOg#v zRXjrYZp&=>d7un?v%zZV(8^f?$Qq)MX+J4pIlVR<=aQV-0&@Hn0D7c2qtX5CyKLv zPPzURK1*BPrP=rJ9>2>+vk{-8@~(^65o-QrCbqA%-dZ|G{`5e{c7McsG?;alo>~1W z-80$RP3+g=YW|BFR&8N6deN3Sju`U18t>XkZ}Wz=?%Wowt^X+}G3c%N3iycRBIe$w z#a33j5WimT>C15s(9DS~YIse!T!KX)oCY687 zE(sO-mT7vmW;n*A8N5ccv=@c&2#OczMW$F6m91&O>XTNS->3#V`-WHCt$sqzk;<^O z?URGIBJ@t7HBSE3SmNSi(8))K{_xy{h!`*HDAqA4WcPGB<(wy(G=UgI% zRGxbN_08-<$U+n;!X5UYg=IGr`rh%b4t4$am(sjq{Eq~dZHxkdCHsPJ_M4uOq`%Lw z9=KHF=1bKQZ|`hbFt<5s=5$giVx=ZCNb&*g8(A6v5Sx<8>(1pKO1QBu`nVBq8zV=H z=-M69Z-z)cGl=bWc&sAz^LxPrYZX+uZ!)xoWv&4Lv?c6_%b3>U(g!fXJ?&SNXMT$c zQS>tHOXFTOg&53-m-ZxZH0IW0^ZK!YX{m(XJnOEKjZhU0kF*ovp#&sB@cV`#s3_PgK<|Z-c?=3%=Cd zt6UY+w2Ks?m4OwpYHgW4J65@L^6|mYH9|SgzikCpR%+zA*BFxcfv7O=I zbNbm0cG7h3pgw6oT;UR08lml**bDtg+q?jigoQY>mI85Nr3#sN>jI%P`Z?++#dN9z!j#a5`b2gBqT!O>^NZ;yIH?-5vX+yq)f_|w-Xfm zR5uqN+{koFv-W9DJA~gxcdus^cs|99eXbh|R})>q0~wOL9%g9Geli$_eGSl&bUIa? zuUR>mpZ9zwpkic)uyJq8VquXvkT`|CJ`niMoZQmVq6NUe^Iq?2Naj#~B&GaToq3^8 z`~F*XvASLRd4t0enQnKE#;99RlX>9{#*V^+_|nkxq91vv7#G$DcM#~$x(H7H^Q(|D z1OVJ}m-DfvaOgG05`jg^Om|r;rPseVA!@SIGZlfq#0?(1>ymYf1kkCcas{gaAXAUn z8JG1GH1X|WxSQPrigt&zL>&a0(+A+_>XfM#pl~*JVW5l4Ep4_dZOR^B2)c(=;ju`7 zYr{H2{iStT#>U1!-b_#t%m^!r9+U1mfB->l>LzSE=!`-6!7HnsSXaRRv*xH9I+2A) z{1bA79Gf%PmH3jRu;OD@m%?qlkTG#bC8G|vTm$Lh@(-b(V?`MuS5(9%L{0+e(3AV! zuQ9mW-HSOHi%QBSQhWV>h5uyIP_p1?*flLU5KK(fu%lVn2J*kDf}s@|e!FVPXgH8(K<4v| zdA(J)Jd#yEC2WDQ@}SCwZ}R%*28)5IqJ#HTL|inik$!t=*wj<#_3~!9y2Q1_e^1;Y zQBo?@a3-HJ!%5L)G-h@xfJ`I*#XKRwK!5p(gz$RZ4vEKQvj? zlaO&vE|)Lo8{QTpgcFPr>0_~?w29=IZGjLbECAg*t5VoMRh?rR1M=3*u{HRnZYlM3 z-+7u4axLN+G>61VF)nicU$@#Ao0MrhVhk++(zJPwX6_(E=PL!nrV|3{wVI_24v_q4 zfGYof8T8^cz9=Z+w{!9AW2X)PX`qg!g%8&lJTK~ovKmrI+hS1V>Goz9oBxRX>L%b0 z_-ba0C3kaSKz~KU>E0*n0N1=3INLp+_o;>9^Nw$~kuDP+I+8&x3I&0Tqe7aee(OZe zU9|R z52YCrhm{_SM?KX9wIzVPv~KFPr2n^D4DmUS)*e)w=*GLozBYgQG^^>k zD_Ig2%UClz7*^9 z3pkI;<5fq!eslh&M8YK?yAyH4L)fAEH46bX(`_vg*g(Q!jjsJzA~GnQ)+0TiwI0)c z+p8f_>Jl^7eLToCbB3aCyv=g}Rn~h0-24C0;xfui_n4_w$e1a^JU7o+hZ!2w z6pOgk?u!6a@xw8+Mok!WUsyY&e%ZF6WefUuJqXoDLRawPNgx>_8v%*a?Gegmc^+Oi z%9QTQbp_m6?E*h5ggfZhL4esjz*T7lZSLA%z;v+aznO;NBwzLT!7Mv!uF}$L4)$my zP_x!#3J1WQF+8t!$dFW@Y|nOINW%2ccT*=9!LBlK!|}elpkFH4;s3Fnd#Q01PyM<` z7)F8m1H`Sm5{FyCo`+!1v=>CQ@28kgwMp~)+hTt$0+~uOx0A&S?~bbT{s!eP9ozC3 zm#6pqw(b%H?k;Tk)MQDzs0fjJ98Kf>ZPW-?#L(U@omI+Uinx#q9@)!4?keq4+2NYX zhlll+29)PgEhskXqk$j(V};0#27v!raU-;vv>fRJeHi{V3graN@p9XM@-K^hSknDS z`jce(hF)+Bez{bW?cVq7kT@Q+EPU|_V!0lDu`y&2$~I_^e^ zHz{&?y;Hl+YyVXC!{>FhFJ-TC68EEp z?l%K^lM84(F^KJsEpvc(+`jRUZX(W#P*1(|;vmTF5`w z$;g}UU3&44KS27bEdAG`z{jXFRNWrlFMF7*h67d1!HJ6{xM?{H8A4Oc_#OhOiW$EP zI<~`Jkn%5&W)D<81tuwIiK{imraHdYhAA4Q_;FleVA+g_8j{b1= zYUZ8*DRwUFWx9YROXIst?X+g=R&M$4HHW7@cav>bPTK268Il)mi2Sc-Klh!}_poKN z%5TM7$y)V)JZNJ%w3l<|>0CN6+*o|`m2^>05cGX)SJ$1gU17SX;4+Z%JD|o4N$=dQ zL3KW>$HjfejYYu5Kmn(m069hk$yP3pRMby zKE63)1Tg&7kfFt?Tj?z@WjKi{Gcpt#*u}0MSOEG3st%K$x|fg-(RY533%iCO zK@)X%gzhS!c1WeLxpdBXBD%KTZp>5plksbKr5eJ$X$q*pDyZp{HInnvlJ)eGl!&Z` z=W}T7g~H+YdBq#A zAoF?xV_XPsB~AWUa!r8bI{h#j?!gD@_)HVtrt+A+m=E-wWs$dF9dkoZnK#RIOkl6v zT~|#d2f8?yjIez93#lcPBnFlIaUh^JQQltC8P^dnl8J^_Ekg7sEfzQfN`7nv%%pFj zmZ3Ywrnsc56#IqcuAG-MDIL!}J<+V-jwN9jgTrA$P~|yq?oJ18HR*NI{{?dKs$gXr z0HjT~OFz?p6{%M~jJ+*3Fc{T*<>`6_Y}*tS)&rGLM44|r>MPFKjh@Dxhb=Q?JiC$E zf3|g6=T{w2MHg&tiOWQoqVBVrh}l}Okl>&iT(jOiTR`|b4wC(ZKSh_2y|+TfemoCM z9}hg7=;M<14YKw-y-QmN0*G79#0%}3hDCvDnP#K9aT}oVIEHi(Cb|*g`Vjksalq#- zb!5+N>5e*wQD5GH0#>i+M(!Vc%|55`y{<;Q&W?}n8kHY>xI3F(Jw~aqCQYWUq2aiu zRfUN5(eR!*zk)Y<5YjD*wFkl+apSlifI_yAx!ayqFzU9Ok_57!N)hd2)ww|%9Yr>5 z`dEps#pu{V+!>x-V!4II%`a{ftDoK2GuFTCP9GlY;=u!I;C^Iq1HGjZ*^k=21^{)9 ziG=uNroO$Fr}qxC-H)QIeJ37^P3xT#*QHNHS=R$lAR{S306Eecb<4sRqVKwplW)L1 z=S;oqQ0?2^;^kye?&%Sx^(EpK(=dHs%jkO;Lmy>Z&TCv0f=LYG_Kh&yx85YE09P3=B`Xdsg2em_!L)e`kI+e zd}9goV4HTAg#H2s1qz-^_ox9;x(wVHp~)E*p*J3KiWs!CwVOZ-kk1ocfs||GFu%Hv zDR^Y@W3|nSkJ-g)SMLB?0O zNsB;5$^Yd`_w1DHve@X3dK+PB)4d9)*8bPS`?j;)sIr!*5dC(Y{rh<*_pW zyZ;R--bb-4rgA+-vR+vdzUylEQusIhAesR#g1!pYA zm1>0lqT(#~1`*=`Eu(ueJg>MSx7TVN zR}Ogi3)DzQ+je7n01b3M-3 zen*Y>W;_aJK@~rty)P@eR0L^MOV{=UcNjw;DcVFDbLq5F(5+0N=z8eUX)W?p+KpA7 z-C3HWP+EARWd*unjPa@HyiuNEyIoUWLuk4dyv(8`q|`F-dZ|mc$uYa!{UoQ?rlf9P zx~a)OM=5;AcE|HqXfpX!X+YR@8alk=NDR7+<8p=TJ^>pDIb@>W4pYPzF@T@yCfSOYtC&ATyl zJMO_CbYP?YdFJZ_;i**r6VagribxhgnaFqX@5J6H|3~jK_p>RiBHQZOCJUCo(2ilDH zGvdXyYd$z{6muQ-6>}z`H^dEB6cJB*y4R~VpQvt!Ha`;P*B*s50N`11F8JaDr)}~&qk;YgslQsl9 zEgro39=6bOaMkDfYQImkNxYR;^Uam(k!9X> zTZf8Y#@~@D!f~WMxmaf0^d8~p^jzB3I=i{F&JoI!SEpjGW^tS&<;{yi+K@^N@euRx z0i=U8?0LxCG}QaXK4K8YVX?fvWDT`hDodTKyLpec#HSM3(L@WS8?6*Iw8l1@`%C0EU-|Pu^?k%~^`luOe=~ zn7Bq9PO<(%vRFN4t@emwdU^5Jehg#l3Ryv4XWYY2rDFoN8TwK}jKOVZsk>fccn`bA zuTleNMRNKX&(%?XHNC&?B%X`P|Cw ziAki13VJwaFX0rfCfw}qkalP1?Xzwlr1y8c@VYk^xOV3hVt6~!c^ib85nwvyfvbXK zFJt^MTXjM&jK^8`u=4EEEbPya!NH^y=Kup%Q#fr3G_)+?0&M?B#C{)x9G-X=QA&rR*H9zlwT;?2zDD2x>p7#fSE>6XS?mn<`fJI(dDY9zTu ziha&U4jtGRHuY@c@x%_^fk|>%>a~QIHgz-FdnCVZs9;e_3mg!e( zEGkId8SQ+`5q%a(Y(t#26)e0)Nh`RYGb{Z`ThCB^eHNOFu&6YR+jF2onSLq&gkvtsTF}bUWDu4AO81R85q)m|ww6 zs|1S0aaly;2gZ}QhoxB0O-W|}_H0Y%1~0Hq17^rO$Q2Cf z&x*zwVFaHxEJi#6k0uax6k} zv%kz+`uuU%D9>y^%cmFU#JpDCpUyPGAxm;AxLeH{x-n~dJu%obWX~9n$CIK#pQ~nT zm|4%kOkrJTOQMu*SsZIlvx)3xnbV@#{YzocXWB;;6*Z9Q;q+efNZ-;X@q@RpL!Uey znaf~npPKy;yH4noAmv>SO~7Dg@%cjI4HBYX&8eokqSoKB)WNShEGMA+_3JoYt1h4b z)Fp47s88(KZr88gsXl2P{z_YU%)?lEe{0#vI0j0Sp*HIKO7s(9@yEiIqP;zN1`!|e zT%^G$Y7pCX3SpE2l;7P6?>rBEq)H;%F7Uo_9w54H`1_^K$&NGbY%u<)4c)b_fw38m z2ES>k!XtVE6GNntC>(9D?g%cbWF^jn42IrX;Gs71ZTx-g6Z4g4c2rAW<-;#?qpX!d zqg7^NOW{uIY+l*S-;xt}QIMeLuHN_JKPP%Ws`DoAUI}NX?ReX~M0ERZ35tP2Mq0Rr zxUcW+AM{uq{qWj>RBFGqM(>=-P+P(0XV?TrKI;=9{Mhw-@{`TyIY!dHuyp;)>u2l| zmXt~jyLitf!@6t#V-7BD0%qJLr|L4<7#{q6zq-15gl)e)<{s33!-hxX%OYsw-YWCH zaGiq<=a%aAWEw>reLS3tWQel1m+VttXb!u%T{mg8*W@&ka9&6r2a?Uhu zaVh?kXxEhllaJ8_qRgk?c~A2~$&`vC_=pX3WjShb&vp50Sr%{u!KBtZP$uCG2B2uH(|Yt)T9@1MFD;sA4LpH^9ZWqgQ*G zEuxoVxn_y|#wry3Cp1Tx7_%MCkr%-iI*+)LH3HIjPEBUopU%sP3@@E8#h!sU*?4EJ zv!KQ%^Tj`S@scrF!`?=$73)ByUw7WLcIVsRF}zN(K@(6HJ%YCD2CzoVsr(g3 zJ#04hslV!aA3d<+!Niy6+}oQ(TEYJSD4^<(1pr|7wSLrS$H%Yf6E%~zjeoz;%hih? z?&D1P8<7X!e)Il$db2p=iZ7wAxMk2LVbB}577-gpTc9d7y90KAY_ADhH|e{;1E?&2 z#u9>o1Y!L6Zvy@Y;U2V@0cKq4?cr==)n-@A zuf3m_Rj6`npvyHp6t1Lbr-&)yqlKgI)SI9YQwxE~z<4ezi*c|(NK$Tu+ zIX`LfV)3W5k5Kw`m#svHyZEXJvTBGiAzDT3k|xzi;9VdMY5ypI7!Jd++cXBvOpeGVTYlZ|gGld|8|UGT zj(Y9cL^UOS2If=RhrA2GSBEFxCF8#_@c3O_&(}z!Tnf*wtezy3W601+*^<2$rLDZd zMz_x%s}+L{R7>u-{V6d(#)c{I!d(?{FSdK5}-*Z%nNsBog zX??mAFWzGl2z0kl5BZk_0;!OtDxvX18p5@cawO{9eW;WccOs*i&tgH>Ob`y5lQP z|8-va4thjAfnX*r38nk~Z}$2TJi)fBnL4;M%JRY^ADXMnVc#L!E--@UqP$_`I=Xx$ zGp^reXgZ`gR55ieQNmM|8SRgu>+bYa@Gpe)-|ZM1ZDRO}QXBFzVka81N+dst`=T>oUD({?(B);`%VpND}-&%p&0Zz>fZ36*R|7sIO7z(tr*R(>r=lr$|r!I58nYq|yX2f;`6YiI8 z1BN1_#Ohgd-k96;Fxvna0go4QYRA9by_}^e!Q{dfgJd`xzkKpwFZqMV_@0vphM`c^ zwNw!WjF&Cg_7Gz;hrR1;lN#5Y*zOReACy?Gq(hUmJ?bMQ3inzWsVN;A-qVLurneP@ z%kB7(QC6yna!P*7x%HNKUpc~N)%a1@mbTbKt^5B`O??>`JHahb4ovW4-hi#qt!a(@ zMFxhVMax!AtLUCNhSf8Fs@zbJpZlo&XAp;?Jlp|&NI#aZu)XKn?Ps9X=L>f+%*&oO zLcdm<8;Scx!fa&FQbIr8_;bWgaqD4+8q4(AZ_WZP#iiqs7=0X@Xt}%lbe1SNx2nSD zN^9e(tGJH7{;=0kB0GL_;;EC+gz*X`og$ekTOsT3oZic)_;p8@^kx0a@AGW;`g(~1 zc##}vST37-9v8lCb1>LIT8pGS7f$^0r7Zxx1WMV%`uE8#Kq2?`aVp_-Yf)8S3>DYY zN3-W>8}LPe`zSoD(9^0Hk}gmj?B8Fn-L+qB;^=?5rDrdNbudL>VVF3e(6@6p@Zt%s zH?6Gy4#pU81~~@2)4H^n$(^V6_D+1shwV}l(1_B_p92?64&J&kvXfyp*qX$!PrpqM_ z=pKS>ulcZ3S(v)3eO|q(Tgn(`XYaW1U4Q8;KMK6UGSD@g&u6eG*z}Tm^%12Z`+x3=Q=4qoo*ksGx zd!&^QG$h~5!_>zpefVL+1^qD<>dEWbiS0n^6tcmb%`N;_L1!u)91)AcXEdVqH!-E6uc$_0JEa3`nZ<9SYx z7om7a6=}jnK)Wpfh_o+93h0o+BG1Zw^JjU4QX9PU&gS;OJ)kq^HD*C)-L6^5X)q!d z@qaZnHvYgAjJ3W!CK_`5CrkmrLRC8h%7u_JuvJJSFo99+7FfHE(iTim1+-?FjGSvA zEH+n-XBeZ<-9We4G$2)tuVF1X48L+nZFrf6-aZYOEZ?Q@mIL$|UCq^iS6^Xv8X7z( z0)Y;bR4%Rk4WPz&zBhgQ2Oy3Suya0EpC;no)77K}hH0OkNr2$@ykhj1^s7r@e%aMq z7PdmW>t7qUGh!2(8k1{lYaig@&bZ51{1m#m@dHnC^H6tbbzgLWM`xoORZ%@=8;rpP z;^c#CoJbf4@vdP1c+XMc%X8i4pf}BT9s@(jmtFwjZZTWV0Ny*t{A8xa#}{ew&egKX ztfFVngCtukfIh|r+csV`DaR=IeV+LoH87;Av8l;K->u7M!|IpFEzt&x=MW8p*B(sR z?j^NutBiZ61a=f9e8;-?d-qf#1;!b0kZHfltIqSYtzH_>iFJzjRPZ9?y8EYVKr`Ug zFn(xQeWY)BMPy(yw2SPO=pt@4h!;S30W4~NSNur6yV_e#kHY+7Q$KSNhs*X02&gjd zm7LgsH?f1)9N*&Z;WcpvE$eBW~mZhSJem@A7AFv;Q zPs~Aq$5LW+q=oj-Zgw^>cvMg^Bg447ITMMaKWg;X%|6ADC#>A`Mi+SgPIs1@CG1n_ zdqsC{R#Ubs?wP0JpbPCGH>;1^D_;uQcl7^)!rIJ!gmCJzEn`ps5ke|%BDRz12%(`< zTC|POW2MK)^X`g~O!{b-u=ec_SGxzqO=G;?iBkW1U}o}ko^dw^TCNP{`3Oh5z&DeL z9LdPXYzj$KC^!;SL{tP61i!BvJL~=ajQ8j9`{(!HIUGFP z+`R7RxSrQ_JtaTMX&HtgtaLG)AL{!c1d*xgd+V*?Que2lfw_v0^JM%wT;@7QeU3C7 zw;#dGTu232^ie=LPgIst~!sZXNHS|7S;lsHm{ zLy2YAV-x(+_=lKXEh9Gwb@H91f`aUzi$?{&aVGB$!7?UmypOw+a!CjA@?4qdoART_ zZ4+yBj~eaQy6Q%(()zu&y7OWRj*Q}WegG0#P8u80-E_lN>WKZnT~rMiKiv8;+xzJO z9Z=Y`fJ4DPz*Hag-yq`55$%=K7W-9H`N#y@PqR5rwBO=sDdXjVF#lCXn@I81;i@ss0M5;18F!9p zsTT2a^=y`>FNGsr%lpf!X986H@*2|L(ZU*h{kk6UU+?Sk=**}lU5?4=LA_y%Lxh@J z#S>G4!3bOAtc?nBMqJQ&YLryT2}M1Nm@XmxfmotA6PH7p|#7(2kXLN3N}0c=0C?-7oo$A(P%zqJR~61(73RY*wbS@>E_xeZ}* zsb8x;dlYIVT1JGV-w!33r!W614%!>1b4`R-!69O|MfVMTuvAt4lznG`=7)=;QOT*B zhTZiSZsOrKHP6Iif)B8{-p0y7lSP z{ltlwV1g)Ge1-D7O#E^8cf$;fYL}D^6iQME=WqiI?#We?>or>!oF9IK>5KAgY9}|cjwT=ANRz{wlkDns#(~%tJY&pQM|%n!cDa8`Wr0#s|vJIu%3q!G6}7G zgI9vAy_OOjFCWsr>obpj*-phPsg+XK!PT&*!}UZ7K0>xwmjA9mj*P!iu_T$6$ivt+ zwoNl#d=rY(=D*ON)sld7-)9whFnq-Q5kL0S9rm+&VIS_4CcGe`vBFvISiGuS%zm02 zqUMqP%KZmD3Fx+uM@NV^bJ%5@^DP%f?IM2&scjFkuRyOL-KI2A%W`E|rOotc3X2uU zmVNrZV#M!)@RgbAEj^xpH2mC@Y|j21rJLN+Cm<@?)2dyDGZe$@?4wcNn9)oPH~&~} z7~1U8&*6;IR*yt^uPR>Y%r(G+X|@ATKSSDO#zvUAr&3KK)S}=%tdX9WEu84PO*U1c zez>>&kdd60ZyJ*OX&1rZ_H%)VJo&9mef%&P{o`K7ruP>)$>}1+S52#10~|2dECSW5 zkao#ii-?m!DtJeL&jBZ|k6VZA828~?8oy?u9~FztL+X3YV&9gDxy-(@(ky^erk>z9W-MPeLE_cz?LbADdgYd=o( z`Dni1d2suNZvhU?o~(@HA!|BGfYS*XI=Z^D}uFB>r}d3pPr zV0kw5umFk%Vvm||E0@49@5*~kw?0q#R77IGwp(-gC+-g@G>A{4?;me=g^z}m_CHYz zNMjnts}gS|&&(jLP3dkxRCt%Sc^)^rzW?O8XNv?wA=_r6jDL>G)Yel~{Cn>KLgeZ? zIc0rkW%RT|p;JSG-R9Ue7?aKyzK$>LM!v8>|Kc}~zVZ8zX#t@lOK&8<>$KQtiEBJe zRzalJx|9l2$SL(Irv982ocx8>ge!JK7mQF`dpB%*wal8)Eg{ZH)VsegPx;^Ox6Ay+G&l@Q() z*;{H~!+3V?SzeUKg)_@sEN{<)O%d;7VJ4K1eFldT*g1Kl{BeXL2mpZH3W215m7Z1; zS0_j*Q(R|o?aqG7&Q|4fyw)La2mASnz|u~e*&mx z3==A#TdzVaV}jwo>}U^;WHU?caRm%)LRg={inn5)RUrn*SC@}v3%VxIFl{S~L`$rA zGXQ}K&>Hm#&%{Nlr8K}ZUhpRdTZnBS-rNX_tj5*jhJG&wJqorVRUf%uP#>sxQZ#(& z>NEP8J978oS!h?Qo&Ykwj4N$LC&EQ}a>KrWmAe5*Af&D*K#(UGftNu__Wse(bLZt#~I; z3faYu4hnr1O-NQ(z?jUyYEhO8)=%$K`6;~`g_WK{YAiWaw5RhIsq3HrY~H|Q7Pey> z<8O`av#yUf!<1n1V=~MiKUiONLBqPtv`6)UsI`;|6G}<>fD`eL<_YxE`@Pgvhi}$K z#II2`kCRohTc2-Ukgoc_n)?IEJigrp6p zy2E3GXD4b|O<%v=rVLsSI;Jc}YHylnhJ^iF8LWlZQ~xZ3VB(KE0}MOx-=4Ccu*bjF z;D7Qd|5;U^{S4zAGyiU=ZT|uA@yOxh5kw7|V+0RvrHb&0#n@eJrT$h3|A29y`p1-D z%|xg#c!VN#d`Di2{E(7wJ5UI?pguBvO40cQrt|t4h98VZEm4M^)c$Hf2y1FJUJ*m3TS^zlHLzy^aJAa zMsGHOpP=AT^2r7{7|m0l-*$2u)wlX$Q3J*ndqWVHsxfDQ!C-oL_+%OmkF-igt)AjI z4muNP)GLMTOXsz{U1^;+l&~qC9{PE9L4Afv=NEtSeE97hMv0^Xvj?TJg{kxw&2>W; zIhTD!ij0ZeZIoz|v!V$pp-1(W*o*(oXa!Pbg?+3+V+6-z23rte(MdYy`=;R@gAplj zk+d1FAuZo6X=-jsi=L?E4n4~iLc<2h@l%S=1C9k!n+k@j39@k^Nr|)1yYj6!@ce`SZeR;`eOHsk)6+LlRjM!o273Y)v z*A^d(h@3L`Sy#hg`oAiGx_Y%pQdgTBrINj01mt}6-k?~>{Lv8JV+RZn&hxg+=uc1b zCI_Csc;TT#oy@~lw9;G^E|83_{6e|y7?`Ogoos)WA&d`bj1>g^QNX2p1FGN*WW`x6 zXXM_QZPh08!>g(jdXS&Cp6Mwum=};NPV3paw7sR^ta9$a-7yPOqI*7qyN22B@d5li z9uH15gc6ju-?GD)$shS07UDaI`C<1x-?#4Wc4A$NDO6k-nS{MQCAdfsNL=)WEm0Sep1ItklSY+$82k%=kjh%YCuH zca~M+`@=JeWZS5$&Av+HXmDmL-6(fhGo0OdaysS$%mvsA-3sj-WmB`Dq<-B7a?8(= zGf0GseP~3#h8Zp__|s2ORWcnQ+n#YsD)CmbgJ~KHYd~QxwM7saE7prdUM$r z;r#4p-EUfnk2ShEGP7mlSDsE?t_rnRO^DHK#hG&+SY}1&`!N}l*Ef<{1aC4aFbe28gKNxew=h%O8~7r#GCDmEZ1K4Ds5>d zTA_od)u?_;W3|y;_`yP%uJ9Wk>2b=Y=OW16huzjMA%8h6SS%F>8v0AeIDqVViOu8b z*N3HEmcNjZq(XIN7pwLeloV58rf%BBIRO))fi$U0&nwSIcy)&CTDG1rK9Fa5F{9wD zb;eqr@x{N<5p3nl6k$e_&6j1{GiS`(&?}l5KREGiDd9id-YsPn=+mU(R%s+NyvSeo zN(hf>^Qeh#eAG(Nf4_)@*gEtDacZ&TH;!xMt4#>Zl{1oh!pMxY(|?hsye`DRe~;C8 z>%DDg^%uwTtDF>Dr=H`HmjH-~Lkaz?Ez_(0cKECh{_gF4UWyAEDqi{l?*j$qtFpt9 zgG-T$PCpACHP&xizCU!49|{)~4u18g%EPU^RElN-KZurM9(k1Whp7qD(dTJ z{?ZQJ50MDgDs--BsKwntkCw;A#)bvq8e7s@{AL}ED=o2?HOlX>RqiHjUCNa?kJfxq zOW;oScFFwaOANSx;t1blc6wvYD8rkjTwHm1`;$j-RYqFB7_6nkA~f)FU5s8G?;=K3 zsytjvtTF21OWxGjL}#%JF>Viep8cCPjAp4>I7Ze~4b_b}joH>Pdm4 za00`%Sg~alo)mc&Eq!ya8D?W7D+d>7YKUk<_=ze%bqWC6AfYZDK)7 zY-?9@>vb!Q5@(7h_u^4C)z@a7YKyWgwVf}@gHEI+k9`>kVIB4WAuLZrLd@$EB$2!+ zywn51<#T1tbR?yLryXQo#Or4{M;5z;uD`>=2U+K8%nbQ_6gkHmT#42WyhWjUUL-5J zmai*TI!hdQ6M5V~<-@_N-Fnipq(PDk;TDi|-9D2n1 zA_l!%*E1OJ;;YgfP&3=#5cW#tD8VbaOsiM~_~GaNwOUG_pXmK97u^I@eFsxYsIW>` z2dM`r1Zuuj=lRPVX`185!Gx&izXF>bf7qC}0yXRbRr4fG1ugfXUwoG_3T(dKwA#t6 zZ)&3GMxW7iM`D46-@jCnK+x%H2+=~mNvx0{!x%_{1K!+mqFYPjM+nRN=Qu288=k1q zdJ;#n4+dFT&7{%T%e=Mm^2a|B4PDIWmGDnb{J^@g6{rnsM!VUDi z=}&=RB>QyC#6?d~KoY$|5Ul=Xz*6Z=@`oFLjJ(dujwV3;cbJr%)DdCvZ}^I-)-#Z# z)Z~<&Wevp8*P4DHEq7&MSyKMRTcx00u+0JIw^V*qK_AQbE-x?N zF)nVuXc3Y1V2?_+{>Jc$t{Y?b*Ky@LJA>Xgx##so?dbBJa!D;QW|04u%i)_p1L0 z5ls(7T0*ug$3|A@3p^I3tq;&!NsU6Od_K(cPK&qwTE(wAwt>nNxE(ieP?AbNYF(|j z0BEAs$)&qHA-0C&%ub~Im%)j#9#E(iyRix&%bYOffL%++(EtQ>O*w8XS*F|;jC$L-^4{UaT0JYNQ_T> zF_?)iEvqRgjZZ*|1)lySff{8>C?gej3$}v2O>P{>DZPJ87}OQKD5+7@Nltzc$vzK8 zjO1hU_dYdnOh@6f<4aUW)@6Ex-k4^FMJnc=h#Cm;^7ue)B!3}Z1rSg-P*LdT^^3A) z!WC{8Zca?T${8QYOa1kwr?uUkXC}`FW2Wq+FvKFQ53HHjrMb_XQ2dMm-heNvU{81K z;a5cb{;6742se~$zl=-J-Yq2+mD>QkGj5;yqDxi$qkPh|9c?4G6?^#&;?6W+_T&48gsEpM08fS14Z^IK~tq0jQw}30zogW4^OQe84 z4BVPg`OZ`R6)X&;o=OOOAf>@RN&FV%;nBDR`rP+HI{+c1ko3#>7FE2qD>1c^auJS% zciWjM_#sQ|sE9ewbkE>$-ww2~D8C44=z=@tuVF$VwD4Vx0K?%849t&>_LINj6lC+P z=@xQ|)Q!HFn|JZCLX)j8uNWk1PCRnY<|U@=;+-F5s$)JhN6|Dv?_7q>b`G1 zrZ0M~mg20rI_>Cbr&(nG=1TTWlA2MQk~rkr155xYbVNKgZbFe|n~52X)0@*iOrqVY z=y$Ba+&BDu)V7mP@8D`*q^hT$srU`Z8w<2T`5&$6<71>^#>HU6pLZX`f#)wcAY72G z`v*_#RlT$EUaa-d;DPbU9e!Suyi@+Mx{Kz{0u1S@UkKmFKM(hg+ClNQxsO~+4X3aI zDQ1n*QrU~4+X>`$FT8b55qII{E_xnQ78>nfNCJD`dq^|6tt!yWpFh<}{Be&E2d`+) zowW%>ewg+2ZpOi=_R>=M1yHB}T~CQraBt-TZ+d8pr;jGNA0_J{9Y!IQfCSP~7F zvyWAkbT6qua-4+5K3)oh*{4afib+A9$@Ni9B}CoDP^f({o(p(il_%Q3eQ$h-s15bs zq6*{3HACYoNSxhf0zI}W;GZgQSw)Pi!(8PpfOcD3M->Fg4Ir0D71eo#@BtbE$p+QT z{|xRWFuSkIC=9xrFW?LW?v+@gx$W$iVUCqDl!a?Nn+%27J0RfWPbdIb*Zqw2^o)MM z#?S}Ps8kjR*S2?z{6^z8eLuwEa4;)Cu)E7sK=?B_V^VjuUU*-KY9dJYp@4{1QFrPS zq&)Y1_Z2n{u%7J2pjpcJ>b3D*EF}AsdS&oMiMkB@73PI^6cC^Ly8eIyulHzpcsQ^N z9Ldb62O`mR9LVajHWUGH(ZpxvO@Gqz+(q30ARX%Tj5Kvz3p0VeN2%1;+t>v=_@$Kv zZOCmnjq?@tfDR;p7{XUvAhq+$-?j4Pe{?nB4{*=d@CG7{?3RrKFOf_=h?MvA+7RoA zzXcDq^5*xZZ2Q6k9%T-}u=lFjiz*K~pYaa{&S&j20ML5?uKJfg(Q#6d9-es0s{9l+ z)Dww^I6i)?YdCiyk41kSru-`uLLE`@>XdE>41LkG>H7*&uRAJL|qpSODgMMle38;f~ zqaCI0o@z@Uhu9{2rWzMS7d_D1t>&WI8NFF`5NtbXT;jRW(Z1LJ)=!s9FVI?%fP61&ic9m|R6GbQ|A@=GQ?ythX7r?bG*e&I)~^Yf+*M*Z zQtEi#AF+TI90kg19r6b1vT>=qp1>ZV9F$}T3ch=HFv%7XFC4>ZEFE9{os?~L)0DQO z65vRk%M3ZnA*Mz_u17EZz&>RxICw3?(^@uGwgY@l`xNRqPg>WBj8Bs79f3`m{}H@% z`cP8~(2+VGw5Nu!i0%uGG;h3a-ecfspqx$JYg#|t-@r{72%Pdwe$zO-4Ld}Xe$Gc6 zge|Dt-VCm3GoF(A1P60-+`;->wt7}0J8B$Ffnnnbf&3*4>^xq_M$L40C&@yk!ugrg z{&F6-^JLz0NMus@=&^t@=*Lxj|klRzl{<^`WOcc~lD4@OU!dnCq2 z@rATG@#abzTFBys0RMDDgt4{F%{;v9Zq2C!(*=h$Hjp*Wt95>4M!#+obz3Q$ zD-?B5(zRJw`%KbXaB;4J6lwT7^9u1!;W`a9%(|&9b3s8!O5iu;>N4?e?ru0(ZM5^4 zLNyK(G>jil@w`{En#xyX=WL5$|3YHn#R8bw_Up0;d^_kL0%;Vual>X#YZ-9scpCL}LY;>QjKeq)KzzFfFP~&SQ8)8+kj9=-NRCa&ki~r5D1rD;}A9q{XJbh)R7BrI8zjoZN`83WHb_h_f(%;Hlgt(|B}g z#4(SRu3*w&Yp|@^w13=s&F3ht8*qMaGKx}OjD+htx3PlVmV&pxY1x$mmj0j=<0f7-E7K=6y;aa(wy=5OU1^6(0w44im_N zWEG&;HW~20r#=egb=`*ORkt>#qCgvG4HD8j1J)a;gW1ZE+-gvSX{xKbqIa(Vh2w#F zgBH*!ygKwFY5V*;{+KzsUMETF2gnJB4DSZH_^vwSJ4ERPlf$lDDB@;2GuBg614e%1MCS$70Jn8}mM0kJob0(H0TdhKH^LQkgAI^)K;CmD zUYOOLESZ&Ut9g~Pb4d>xkFKguYRxFg3wQF^nI$b%`RJJ*9&+@2W%eWKpeL9$K`eAz z#qQ9ge9&1=oW6?yzjV(#0O2jKUQl6viT04b6TbQI1CNSN0zhWJU)HE8{1q_2pX%Y) zbT5#CZJYn5;9{c+f}-#;1C##zu^uNDx!A7@sE-8Sdoby5{Fg68VEI!RPrJf`j^TJ6xXNlG$C@_SaR0qZ zmtPg7p98~b%Phei2SSCuq8)!Wr(ceOfT*iLb^I*qVWY94&Y37xiv-owpjQ34S&q}3 zSmeZLOeRIvVhnq65Es8Q|H;10GM`TM)~jXzyFqgs=V7DVLS$c!+sw`pN^i zt>{=6`?1%(Eh4wp9n+oI1tDOrtJ9cV;ZWQ z(o!9TUeUOE+T=|+_9qzMY(#!j2TMCynhiE1?IZ#onp9<->rF|n{$d7w>vhz@+f+!U z0q4h1=z~8*Vt$3u(s3*ejWc-7reK?v_{&Skh~i60G8w+F58F5yS*8?QimDDHZMuI? zfm!FNVAk>8+4tB42tD;CosJZBqjhXQcJ~yh)O&N^V1DI(QUOeKH#dJ3wHDtynbLe6!S(|>Xiuko}fhsq-_B4u=Y-P zy@^><^0bn;cWQE@|IBJV9i(tO{!H>aNf)pIi+PBECGXgH+@GDptkMT}O7}SQ6~Q1J zLU{Jqjm=$3dR{BG_@)Duv*w3K0B}Nn54B|Fw+5E83BKWjHTs}ro<6WxrT- z1Kb7zc&o-zwmjZ3s`Lhtq6W&nE8qls#w7r8(q8&km$@^8OOiY$Lw#%cKiZ`3cK&G) z6+OI+eIrsweG4TsNH3_t3%LDst^v)*(AxQ!3cLtFoIrV$Ujfmzh~xvT6r{YMZ}SIR z`oE2ylKC#=|C0gAP4`qIM!!<+Q=jQXco+2o28eLKxUg<{9w&L;!%1m$|qY)Oy9H0+sZzj zs3pp`)=$}MS|bnm+01VhgG+tNyWuwh#`GxG6fh)gWvYEC7R5|31EkaO<5UsaQ{fU~5+6&T|Tk2@<__suHXmTvrAvuIk@xV0zhKTl3sMbuX zXVB^HP-c2s7ij?S8Fa?T;YA^c;vPJ@kV0PrBVo=z#RR6Yxvb6}EjHWj;~wXXY|EaV z&?o$cUjtfrqA`e8_s6NBsZS^cj%U^j*0W$7uSsw!X)24SDe#J(lx9AQa(fYgC;OGP)M8ELO1o7rWEH+k1y~%ntW#_?Dp@lA9a*LZ^9ir z7gB>a(}d`LT_ffynf{b^J(=AOM;cZF)3`tets8Vo zyO=uIIFJpv5dg519g5jT_xac`5IE~Np*4a<5?{1AR)lLY`9}{_i5A{n@w37EiMa#o z?cEMNFC4>GC>5w8?t}WUrx}2LIfVy}{o#t&q-~8FLOs-+HK<>tD2m{@^{9|~MTCOy ze^vq)?ZdkfW6kAq0wZp73TFIHbU%BZDtfy+825uuR%rl!j`mc z`;|SDDD!xgAK&Q#)==u~um4r*k?$kKfo*y^%0WAO8k*o|A{Tu$dIS0jYwW8K!JQsw z1r#PT6oEB5dL;5bYC|&@HYJGbFzCnJu0es8iqq0 zlZ}xo9DDi`HMXGZh|OXv`AfFVm@Nh?TB+}ggsml-c$`L1xJj|C0Zd`SCu)3b0?RIc z)YOO-sR>_Doy^H{j2$NQ1+S`qo}B-bxzYytcJBIFP{@q^F=YZipvG{Y9#u#YCg~Z! z2F=ewpaSh+D=>K6foEq+#0ew!q?L!xd{BM;cV7PRFJ3whZnu=IEKP>rd31x3va>wI z6NAhkX-_~IRGzjs_LX|-&0xWB2J3y9(eLW2qr?>dtYdV^NIxr$4KiRAL_56Nm>6$; z(E9*Y_l8sB9(BNB^SGAD|DxR(S6YX^-_AFzDAvTaSgUgjX1ouy;7?eWm-&TJoj&ea z$=y*p=k1ivKipA{?T%96w9g*Pw~&pHlX`XW-x;Tw0RUa_ z1uB;g6Pk9TdR%E~sq2vJvD=55KXC8g*_4a>E>nC%GIY5OS3nX%e*s#{E0s&EDtcO- zG4W%^1Lj2Ib|iEb%elNFaztSXz{d9fS1nun*Z)i{RJ3||VdYR{7DZ}Un&w}YYp1C0 zXuIPMdGiQ)&q%{9P5{dVHnW=5xmUqIG1Ks%j)#bBQLNO&CodPsNyVK2mVe-}ZFv%g z_&$)X@m-;v819*I!uDLHf12)Mdk2c-wSpFeg5AcSLJ z*W}Uhr;Ez8m6o)Z1vrcsxjraTxkaee%H9o|dANf8!2KfoxJc}_#m3DZu9wOV4Vcq; zOghCfl6qAL>N%-P0QRJ+;k+q${g@WsVc^vhu5M+$jO|641>Ub|O?cBSEt8A#WXie% zj-Jf^pi1mNlGQ+5yg<*!)Ff8k={7gO;ISVwMiy_DqZDt6oin45$Po-7;@8h3U^rC< zjW5CV8*=`I*;o~*-fZa{;nEF!X z-a>cAWR6VBpz5mfZ-hhB-v8$ z+Y)7w+ZzhK$D-zxtxI*WytJ>{igc~R4;K`cr!2OBpCG%b$+Zm@l+!L~z_R+Z)?ySM zcE5ucmYL6qEJFD=2vmJ5J6_JHeX$+e>{nrsKaf7J0y$`Y5lIc8Y|1bztgI{kJ?t1?g0bkm~XmJ z=MrHwLfxCW$`$yCN|im6+&{+us*54upwYxlYINst zRzOhB*!+t#;6tT$0umvZ9GLWp`k%2k=q+QD!Jt=D2|6=x&20VcIR`8gwQarPONY!p zuuj>1y$R^qMPLYjeRlpWW?%|tTi!dC5;(RG*liWiOf)2NOnBKX6lSa`3z~vYK%e(q zYh53TE7WBi1o813^(`2XsHBbSZV`Wh7;4!|2A9z6I#T= z{5h-u0*28L_>x;Kei}Y{yAY+2;-1!BJF%%o{FD>7r} z`C({@tfeU#!J3IQD0p^Jzby_+!iAwgWhzd9b=l;0$mz7aZaMURz{ zv8i{XBz&!?reYjCsbc8OW!~Znx&f}fEKE5QSiB>^JO-0nbRHAG^vkD{wn3kExA`7D zLavV?hH{?w*{WxYn%7!bo)WHJ(p`f9EqJ{ebG@*jLeY~Li{w7w-pur~-SB+o56`PJ zq)t$wXxp%E&m{8nhN5Oz5ytU7X#Z+ zO(Rp(6LoJ*@-K4Ks?dxDxFPHuo}>&n9>=DO2q*Q-1o8?EOj|pJqrnB@hk1O%;5_k1 zms#yCFhOm>r~Rxs0;K>+Al;>@yxIuipDw^cQuMadnPYF8>djHTr1PnS3r*kz?Av||#AR&{cP zFGOd?Nm zHv;nw5EMESD0E>h~6%C)z+gIEZlv-8!w&4TCuFWw4O}UUBrfcXSN>X{r^Ti&w?KC?kbt zM~;HfyLsW1j*>7CXV`C`T^zi;2YChQLb0&&<<)A_9Kw1+i!uY+1 zHQC4gr&?-)d6g2WpMAwX_74K(5Ib{{??lN8R9|f}6QFSnx2Gxc88*9xxsQt0SNdR` za`g&l&JKN6WG%YqB#DW|bj%F;;WD;ZzVq8Mf{Q=G9uOox*ei%_Y`6>cyzAL^d({Zc zUf{uGWF>_@H}App3`nKs;4*f{5eahZYQ4wj_>;rqGNX6Je}r>eD4gTEw+Qbf%S9T$Up~L|S4vQ+dVQqN&2HPni2I!J?rv z!@r}R#BfvOVS2gc|K^zlv{PU}pGTsK-+^U70v(&wy$S&w-w%2AF4%9h^Jhm&QJX$7 z4VX8lZQNMz#4FxlcDI#{&tb+lET5gT8~?JfdATGeHiq!y)^{EPnUkRN-&7ibl1c;A z+vLx6g-74`z1=9)d`J@ScN$70yX}9I>^@KJJ@BE^#`TIlrUw1Rw!GQ=kJ3YH#846b z_N-Fzrm%SjB*huG{co_VYf+goWQHe;QxZ7{gi*$c*A!GXaz1Xz@HC2qd}V-TPU1B1 ztUyo(KcfMQf-_p!lu#WeN6~m2Md=UKkNIh)aO1k~`k`VBXj1*>|L6($BLSwf9Ho3|PDI z)i4Gb;s=}yfXW@@kIi^{KcR?z8$NSRawJ+G%7TjJRkGxu9Qu_#c8k1oHAvv#H460F z&+Q44AI0JHnRbH6I>O*`z7J|atMFPj~l&^!^CJ;VR0_7K~k71 z)&g9N1JLaTl;Yws`-^7SFV0pN=e}A-_xy*37sO1`!F)nei2!9}=%8>U)E7fjlG68M%O;=}T{l z(~JHetAzkoP-jA`-S~ZOOa@)8J8On{O~A5SbLP8ru#ifNb>usi+98hdjt`seoWqS_ zBgOj`kR|K>F0TaZG%JDCf)067cl8%|`HWLw%XsM-jYI5u^_4{{jf{*^XR`y*`&jn7 z?A<1<#;WvwU8}Wysx%+tu*IXg;|0$)P<%7eeV_*E4RaKEUORTj_1*c=ro)lnE>I$v zk(j(mgeU!M=bO1N=jWGX33Nscr@50Av;&BkNHZS4aBCl-py7?tan`nC8TVP{8n?Ng>^&*B&Tf+$*L47jz`F)}<3h=W}%T(_J%O`p1U*iF5zSRk1vv3*| z1NWm;68eXC%daoEfgL_}%&xAiro5itsUCym7XD7Xl4Z-^`RL{3_e+d*6uAgTJG-P* ze#|?;@W|yI*msy0)X3hexB3os(54%bfivimaBZ8So2w?t-%kIf(D=U;11N!f(Y^#y zDSqdD^exI#dj?8uF1oqDlD05kQF0o6b@6h?-G~#%1B|4B&wDpCR06AJ&I%OhQLndE zyw80Y-hT|NRC=Mm=vHLfkr!|^4J2->=Y+cNgBN^(jGvXk_bZih>XYBCwevPCj@W;g zy9TSEQrnLAQke0e3!|ks8|A+hWS)Y3IHllVTVa7r&IeN)kME?(8K69P!L1sw?ec42 zW?wU6o4N*b%T>R+6$2zQ*86Bjs-V1hN?_(>*&8&J2Tpr0LHk!zIfteTEVWMABX zE<*ZJ+{(_j!L942&ef{!nepE65B0=~GqztF?aOL>3|Hk`EV4v;Z}}saZ|^hEdP738 zukxrXC;0o)JX8%=PzBL0*BvEc;MXdls@z08Vv9o%C^QL8MZ{ko{L_(9=9ipjHvPvA zFXftP#5fwfyCpvgGI%cHHSG@Cd^q*e&@a3GnZe)wZyDUPg||&Er-S6^THaQax@byg zZqcxrrI_k&?b~F0>t!AXQasPb`4cx0Rt&Ie+bZiMt5FZNq#{*fl4k#Z>YJFo9I9w9 zpZ1@Fg7N3F)-UU*JDw7o%4fFN7Ut(S-)<+ol%6gKx##dT+;Of-e~o)}1NL>=9>?An zu(DQFpQ}^>1{Qqn#+ky+*Ez?pwBF0i?RhixZ+RFMjElbo3?I9GuiNDYFWW^ZAIhqw zd{8I?evEFSAre1p)={AWkvBNRB~vQegD%?|g)s{USD0MM0;w2rcie-dQMb#EVMXcl z&MuA(5ShX*r{9Z)D!Nxs#+6?*5sapLAGluUgE_gH$k^9eNk}(gojf4*3e!c^%gCp_Jm``Obz>u=P3(-wLOUmmHju1e?b4%sKbc!Wp-YMwg{;~2tnm7D0P9H0tS8a8+4zHQ=bd> z>y^I;Y=?=T{}CjdG8@I9-bE^!7Wxw*r4a4&;10?L+bYV0Qvmz z?B)9*;!1?FfvcggtBEWl6Q2QgGX0r2`Cm`Xf!s?Ex1W78^S##42~EF^{*qmGBh3oO zPNRc1e8eh{Pw1MJr2{FSCRoGihsspt7W>f-AGp0iI(Cj)7N4!Cndv+ogejSB{dX4n6I)0r@L3d%-Ugc>Cd)~rVc-GT^5}_xo!Mvgx$!U~cM<@m zW^a%y#vu~otPFdoiu5qVP(Dj*&u3VPZfm()Q}E~u*d74&MssUj6y>MYT>kR(PGfzT zwf(AK|4G60UpjP#Q(bL%7p>_@2)|A%T)ezE^2|Om$;3ku_U4S_l$-hZ^B>^Sau{G1 zJNN61yAZ|ixec+p59sN+MK&ZOs4nGTeF)!BhTYjw9k_La^fuoK{6&jdaiAJYh-KwiX{^ z{x??C-riljKXdU*3ZDVi*}6bYX=J=*28q8`$FP;3ZO%4(d)9Ta-l2-EqX^~vaI~-~ zkTr2-aj+Yi7GBhr5qwl34Vg@I+d5>5c{c7#Ww6_gb{?PG>tCC1iEZ*zAUvF{c=nDf z-vfDNzRGIrLv6Tb9idWH^DgzU)0L)CO*o2a-H>6({S9|k96j7YK_r}E8+BS+%Cf&T zzp(GVQf=(%Fe;t!sC%_L)A-(2+7U^8h1G|gF%6HOYJsilWuAx5Vf-8yC!_zB*&3G z$}JqGTj(9Iyy5+36|mphjw`8P(dxk~26Tb1wJ)VL{pN%A*xG9}-=o+0_O%AafzjIX z7dR-9K*vATbT#$nwt!@QgdAbR*}R7Gh0xxBfZz%r_OyQIRXxlJ`d zFO^q+g?m00Q^OHCIwU@pA#QHhCQQHNcGu*q;vS8usCylCcGZLol*#P>bGn{yxVQh8 z4focxpk(>iwlR#_&(@toz8m01DV;ln+uF7vMHw+2Ibo+HAgybEwQ%$7N_DE&E=7mP zYN7QC6ds$aOfCM??>I;R{En|i{N3-kEZ-wP^_!p4@R=U{V1@G)3j9KISL+rZFQXB` z+P%zE+y-`d`ZWvc+hrJAA)Yj9@2J~>2n}}sRgh?kOa$9-Kw;ruXr?#T>ahW%)=&C; z(w-q>b=R`W%S$iw<{Pt&w}L2_q8|Ti;(z*r`tI z*+0W?m8Lh0DA0PW88CRD^?rP~er4>oXAajlO!l``9BEukxx;}C?ZR3^M#PDs*n@0FQ>;a18Dd7{NTIjD~G;c(PZVRpnkJ00H{Ix`*gCrE!RKL_kotL z11d*FB(Qt8#P?qeVLJFqrM>rM>H&IFpB8E8N}=JA-WsoDm*b}L#7Rmv9{|&fP*+G5g^#W zj&LS*4v8yoZzC&2Jl(l&+quvYXWIL{46WGP)_qEHW+sd){UsV57lit`C}sXf#h-gK z2{t<&jhZh@`pKn`CVKEjZ>KfKC{ zP`=*L$jD7SfU^ns(j9 zKq^N$&{K1K?^Nm>W`d|9CSRmZ2H!#x;!4U-SzG=8r2?20XqPLMd%pa2YTnfcCx<4?viT$yV;aegFa5z$)vdq!EgG53w=H@-C!TVW7vKO$VEd}g_1n0YM&D1VDEH_-lwNa@nh za@CExlQK1~CA-u|+|*}(#9X)5_;t|?X7Ykv$xijRYG8xCvEQz8W25aNgz&0Odzr7J z-lzR@$FQj-J=2HNf$P?DG)`1k{k!!}=DUAs%xAn+n@)WvX&!FpcKmG3VU>^{**}*) zwEz3khph%|(V?|DGv1qL zMFkW#0WDMkrNrVUw@4a$q@2qPqwbu?-8#F*sh@XM2ew%cJ;1nD2l|}vRwD~aH=p=w zoQAs^%{t+L-{e&lelBne?2O@sm*u_LcKB<&gb|04Y_ zI*w1-jZ$%nH?cO_BVabCM#LNnVBwGsrz$*_d`CN4U7ZqrTRy9;V4()PaeW^B)WwG% zeSmAO!oA0Z+R3sr!%Lw$pf`&9^7mivf;9^AA?Zsv-lyl@imn&^<~wl>d-jF9c5LL< zhg*J2(ssAcqO`unt}~0AnjYd5-&~b7Z_R+NzyuX|*c6GrF7n{|#wV~3vKUgR2yx2p zV95@JW29;BrFdn(`1E0cFB2K5P{F&%xzO*+BBcp6;eYSOESH5aXI$|^$ z24e=1BGhV8R%0vevE`$LFg}{B?@-CaFfYbOOw1rN#xTzPPVJsOr}J@L=kNZR>-}S% z=X&1fy`SfPp5J}nzaNUWFI-WjP_q<`EV;tWn&9X%_3R2VyCH$K2zw@F%;H5_S*Ud9 zEvni0sm%7`TO$M1_UUbI9Vw$3h^k|e=WMW-#V6ed5%saYv z%vGTNvc^90>OU)tl{Bp5s6AA|ENMd^MI_f;3R;>VEdZGf6w3#$C3H z?yC}vTX8qMWR}; z_C0ccXP4u707n{In_beH6Uq_J&ueATU6vKEaggx#d)cMEJ^bi?@R!9OU*or*E>vOk z`0Wa-5dO{raHi8bEp3$N+NG((Xqw$OA zbgMu8yEiP?;(U?e;X-^E!larl;Nsae#;E_ivx>8ZIGILBP9t1pL42Rdpa|!mr4bIX zpEfG(=SGegm$4!;&TsoE^m`eCtIg3%y2E@rru;` z`NoAIl1B3d_VRmBELh$9;r%2R_xE{RL5QuF8p|MFFVoHjpDQOg4`~V?TqG3P;;t`- z0|H1Epoa$d;J*J`gZN_<{SSZy98liZvHu_9#-BR=FOiB*bBs!8{44(T$=ooxoBJ3L zKIa`p?BJm6AX~q_KY$kc^fEr=D8AP!M>mBm7q~;5d>Xc$Q8ngIFd3%PtYFz2%M`S; zNPzGuGAPBMgZ`UpIvNrin$D-~{=>goaC}C!21g*z1N^E8dW{5~Y1A~)-;F4Q@}C}w z{WYIYfzVNUoEG)KT={D4csg4CCJ6-}d*1?xJl z9vv(aSiJ>8>5K_>3Bk;)J5Vli%atl^ad&3?A@4KM4Bk#+xle9Q6V<78?Gxn4X zWnD}N%1K{MmbE{%k`pC_L2+d$Y$kxmK;*VucEQ4&16S{h{kUPGAp<}A=JqJ%0l=<> zP){B-R>b-x6ZYMdJuTD3mb9R%V8??Y6B*5sr-_>qxcK5kKD0{izD z4W(X60uoDEof%%&BVvR?QS(y7SgAyx_pbZYO@4Wj&+D*n;NI7?ghs$#Cm3|!d^XI# z-DJCg)AN-%X>)1_Qj`SASPi-?%misq74L#tU&Ye^FJnY)tLaKk`2_)I^UlY3!uUM{ zF4|0;Gru};RHr$25`%HjE0w>PuXMv5H>1ec^m$soRySrz{P7rpSi%UU*0*(B8eSlZ z%~O(1%cfB+*W$-L1{CczZGO_cTL)Wlr;W>19(!0#(mDAOqL~EvC!;tvv5;VmQZJs@;(z zTUIq#kfu-%?f0z`SMYC*>olvv<=8lLw6ehHCLII=|3oFX zYPsU+;|j6u7G0FZwRW2rC2`Aop1hGn_&kwj#?Z7z*h89P4W20E;@}sl0?#;lyd1`R zwtfCD?w0w>;lluVL}*F(YzbGsN#!y}BMRPD71*+naS7?3 zlu{2(Nu;^QOAW%ws>Dh7(50~e_c!PchwU2ndlI4cn*l>yJU_gL*AB}JQ{di_=b-^| z!1nhOu$8af3Vjm6@{WK7tAParox>6)t}P%Y#G5e-bP{QTq?|`U-nn6lyje@m;}sNa z1%jHhK469)On+~RprsyHK>U57=)pn>1}uU53d@+fj;`sYTt%owBe-I(V3eJtH)n8i zwR+QL0Hn~RBXnKZ*(2Bd`3zY#M^H)P9WgO6`EI`nM%j7quMixqL)IDVl4xk@8>VeL z^aFXiny@PYd%_{6MBj5_X^|tOQ98j{2<_*{fmo4wwLMu?h2xs*hOSfxQ&o=F)D2d8 zT7`5vzXY1OTHRSRt@%+0P0ZUUCsb$(@D@=R69@j*1J_`k^3XC(_c)Qss z2ly*zjS!24E>>kn%e7c$Hf58jQ_h;@4ocgJKZbBZ7r7$z5N@KGd=us}N8d}_Xl>~n zU8YSZsluqM?-2Vu9+oZR8&#uu{J}o!`xj;1LwVKfHISLw$b0-&G?TSJx%!P*PQhut zVBVS^&B*BJ0F7=;mcC8~TR;Wohc1E9iFY0|5X~t&^YX4^Vm)D>#65z>F)Zm;F_1G3 zBSxttJgrzeSya8Db+yp|l%wfNOB*xU0~EdCJ-hIBwzN)k@8&9y*F}Xurld8?gY?QJ zv3}Sm0-Xd^YM*K}$YwhmIURR<9FjcmjU<<=L3LH)Tu>^lGHBRFgX0k?vSDZq!07WT zXGd)Z67nf%Qz4tM0_JbGVVhE^@7V>5vwuroNYuhfe}z0+eM2kf(x8mMVar_#l@67Z z1}IUg@jC`DF?7Q(?{v-l+%@i~WXehqwPFcOUfkNzi21!00xWqhVdE+ Date: Thu, 9 Feb 2023 17:49:25 +0000 Subject: [PATCH 0517/1271] Hound --- .../maya/plugins/publish/validate_arnold_scene_source.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py index ad00502d56..2d6c6e8e14 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py @@ -1,8 +1,4 @@ -import os -import types - import maya.cmds as cmds -from mtoa.core import createOptions import pyblish.api from openpype.pipeline.publish import ( From 0fdb957a4e9b43b638ee1e36f30b54cb81be0280 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 19:13:39 +0100 Subject: [PATCH 0518/1271] have information about project code on anatomy --- openpype/pipeline/anatomy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index a18b46d9ac..49d86d69d6 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -60,6 +60,7 @@ class BaseAnatomy(object): def __init__(self, project_doc, local_settings, site_name): project_name = project_doc["name"] self.project_name = project_name + self.project_code = project_doc["data"]["code"] if (site_name and site_name not in ["studio", "local", get_local_site_id()]): From 684c759f516d987e3e59a84584bd295869d02fcc Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 9 Feb 2023 18:32:52 +0000 Subject: [PATCH 0519/1271] Fix capturing from wrong camera. --- .../maya/plugins/publish/extract_playblast.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_playblast.py b/openpype/hosts/maya/plugins/publish/extract_playblast.py index e4e44e4770..0140850dc9 100644 --- a/openpype/hosts/maya/plugins/publish/extract_playblast.py +++ b/openpype/hosts/maya/plugins/publish/extract_playblast.py @@ -1,4 +1,5 @@ import os +import json import clique import capture @@ -44,10 +45,6 @@ class ExtractPlayblast(publish.Extractor): # get cameras camera = instance.data['review_camera'] - override_viewport_options = ( - self.capture_preset['Viewport Options'] - ['override_viewport_options'] - ) preset = lib.load_capture_preset(data=self.capture_preset) # Grab capture presets from the project settings capture_presets = self.capture_preset @@ -119,6 +116,9 @@ class ExtractPlayblast(publish.Extractor): pan_zoom = cmds.getAttr("{}.panZoomEnabled".format(preset["camera"])) cmds.setAttr("{}.panZoomEnabled".format(preset["camera"]), False) + override_viewport_options = ( + capture_presets['Viewport Options']['override_viewport_options'] + ) with lib.maintained_time(): filename = preset.get("filename", "%TEMP%") @@ -127,16 +127,21 @@ class ExtractPlayblast(publish.Extractor): # playblast and viewer preset['viewer'] = False - self.log.info('using viewport preset: {}'.format(preset)) - # Update preset with current panel setting # if override_viewport_options is turned off panel = cmds.getPanel(withFocus=True) or "" if not override_viewport_options and "modelPanel" in panel: panel_preset = capture.parse_active_view() + panel_preset.pop("camera") preset.update(panel_preset) cmds.setFocus(panel) + self.log.info( + "Using preset:\n{}".format( + json.dumps(preset, sort_keys=True, indent=4) + ) + ) + path = capture.capture(log=self.log, **preset) cmds.setAttr("{}.panZoomEnabled".format(preset["camera"]), pan_zoom) From b5b155828aeb340621d6be12ed4716e259b190ac Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 20:10:41 +0100 Subject: [PATCH 0520/1271] use 'backslashreplace' on error of output decoding --- openpype/lib/execute.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index f1f2a4fa0a..39532b7aa5 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -117,12 +117,12 @@ def run_subprocess(*args, **kwargs): full_output = "" _stdout, _stderr = proc.communicate() if _stdout: - _stdout = _stdout.decode("utf-8") + _stdout = _stdout.decode("utf-8", errors="backslashreplace") full_output += _stdout logger.debug(_stdout) if _stderr: - _stderr = _stderr.decode("utf-8") + _stderr = _stderr.decode("utf-8", errors="backslashreplace") # Add additional line break if output already contains stdout if full_output: full_output += "\n" From f6ee5db2278a4e7e3cbb71242aee418fafb72357 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 20:26:49 +0100 Subject: [PATCH 0521/1271] creasted spin boxes that allow mouse scroll changes only on active widgets --- openpype/tools/utils/__init__.py | 4 ++++ openpype/tools/utils/widgets.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/openpype/tools/utils/__init__.py b/openpype/tools/utils/__init__.py index d51ebb5744..4292e2d726 100644 --- a/openpype/tools/utils/__init__.py +++ b/openpype/tools/utils/__init__.py @@ -1,4 +1,6 @@ from .widgets import ( + FocusSpinBox, + FocusDoubleSpinBox, CustomTextComboBox, PlaceholderLineEdit, BaseClickableFrame, @@ -34,6 +36,8 @@ from .overlay_messages import ( __all__ = ( + "FocusSpinBox", + "FocusDoubleSpinBox", "CustomTextComboBox", "PlaceholderLineEdit", "BaseClickableFrame", diff --git a/openpype/tools/utils/widgets.py b/openpype/tools/utils/widgets.py index 41573687e1..b416c56797 100644 --- a/openpype/tools/utils/widgets.py +++ b/openpype/tools/utils/widgets.py @@ -13,6 +13,34 @@ from openpype.lib.attribute_definitions import AbstractAttrDef log = logging.getLogger(__name__) +class FocusSpinBox(QtWidgets.QSpinBox): + """QSpinBox which allow scroll wheel changes only in active state.""" + + def __init__(self, *args, **kwargs): + super(FocusSpinBox, self).__init__(*args, **kwargs) + self.setFocusPolicy(QtCore.Qt.StrongFocus) + + def wheelEvent(self, event): + if not self.hasFocus(): + event.ignore() + else: + super(FocusSpinBox, self).wheelEvent(event) + + +class FocusDoubleSpinBox(QtWidgets.QDoubleSpinBox): + """QDoubleSpinBox which allow scroll wheel changes only in active state.""" + + def __init__(self, *args, **kwargs): + super(FocusDoubleSpinBox, self).__init__(*args, **kwargs) + self.setFocusPolicy(QtCore.Qt.StrongFocus) + + def wheelEvent(self, event): + if not self.hasFocus(): + event.ignore() + else: + super(FocusDoubleSpinBox, self).wheelEvent(event) + + class CustomTextComboBox(QtWidgets.QComboBox): """Combobox which can have different text showed.""" From 9cc8f1818c7b37d44c6c52b4c90cce3febe9a2ea Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 20:27:15 +0100 Subject: [PATCH 0522/1271] use new widgets in attribute definitions --- openpype/tools/attribute_defs/widgets.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openpype/tools/attribute_defs/widgets.py b/openpype/tools/attribute_defs/widgets.py index 3cec1d2683..26aa794930 100644 --- a/openpype/tools/attribute_defs/widgets.py +++ b/openpype/tools/attribute_defs/widgets.py @@ -16,7 +16,11 @@ from openpype.lib.attribute_definitions import ( UISeparatorDef, UILabelDef ) -from openpype.tools.utils import CustomTextComboBox +from openpype.tools.utils import ( + CustomTextComboBox, + FocusSpinBox, + FocusDoubleSpinBox, +) from openpype.widgets.nice_checkbox import NiceCheckbox from .files_widget import FilesWidget @@ -243,10 +247,10 @@ class NumberAttrWidget(_BaseAttrDefWidget): def _ui_init(self): decimals = self.attr_def.decimals if decimals > 0: - input_widget = QtWidgets.QDoubleSpinBox(self) + input_widget = FocusDoubleSpinBox(self) input_widget.setDecimals(decimals) else: - input_widget = QtWidgets.QSpinBox(self) + input_widget = FocusSpinBox(self) if self.attr_def.tooltip: input_widget.setToolTip(self.attr_def.tooltip) From b7078d77e20bb2d6711f1fc842c99e915f3c9abd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Feb 2023 20:27:36 +0100 Subject: [PATCH 0523/1271] label of attribute definition also have tooltip --- openpype/tools/attribute_defs/widgets.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/tools/attribute_defs/widgets.py b/openpype/tools/attribute_defs/widgets.py index 26aa794930..18e2e13d06 100644 --- a/openpype/tools/attribute_defs/widgets.py +++ b/openpype/tools/attribute_defs/widgets.py @@ -146,6 +146,9 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget): if attr_def.label: label_widget = QtWidgets.QLabel(attr_def.label, self) + tooltip = attr_def.tooltip + if tooltip: + label_widget.setToolTip(tooltip) layout.addWidget( label_widget, row, 0, 1, expand_cols ) From 514ba7e79b55e1aedcfe32c1fe4b949c32af0c13 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 10 Feb 2023 07:25:38 +0000 Subject: [PATCH 0524/1271] Fix reset_frame_range --- openpype/hosts/maya/api/commands.py | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/openpype/hosts/maya/api/commands.py b/openpype/hosts/maya/api/commands.py index 4a36406632..19ad18d824 100644 --- a/openpype/hosts/maya/api/commands.py +++ b/openpype/hosts/maya/api/commands.py @@ -4,6 +4,7 @@ from maya import cmds from openpype.client import get_asset_by_name, get_project from openpype.pipeline import legacy_io +from . import lib class ToolWindows: @@ -59,25 +60,11 @@ def edit_shader_definitions(): def reset_frame_range(): """Set frame range to current asset""" - # Set FPS first - fps = {15: 'game', - 24: 'film', - 25: 'pal', - 30: 'ntsc', - 48: 'show', - 50: 'palf', - 60: 'ntscf', - 23.98: '23.976fps', - 23.976: '23.976fps', - 29.97: '29.97fps', - 47.952: '47.952fps', - 47.95: '47.952fps', - 59.94: '59.94fps', - 44100: '44100fps', - 48000: '48000fps' - }.get(float(legacy_io.Session.get("AVALON_FPS", 25)), "pal") - cmds.currentUnit(time=fps) + fps = lib.convert_to_maya_fps( + float(legacy_io.Session.get("AVALON_FPS", 25)) + ) + lib.set_scene_fps(fps) # Set frame start/end project_name = legacy_io.active_project() From 10c4305542b162d93907c3c1d48c6433cbde1742 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 10 Feb 2023 08:06:47 +0000 Subject: [PATCH 0525/1271] Create Arnold options on repair. --- .../maya/plugins/publish/validate_ass_relative_paths.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py b/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py index ac6ce4d22d..1c271a4a04 100644 --- a/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py +++ b/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py @@ -2,6 +2,7 @@ import os import types import maya.cmds as cmds +from mtoa.core import createOptions import pyblish.api from openpype.pipeline.publish import ( @@ -34,8 +35,7 @@ class ValidateAssRelativePaths(pyblish.api.InstancePlugin): "defaultArnoldRenderOptions.pspath" ) except ValueError: - assert False, ("Can not validate, render setting were not opened " - "yet so Arnold setting cannot be validate") + assert False, ("Default Arnold options has not been created yet.") scene_dir, scene_basename = os.path.split(cmds.file(q=True, loc=True)) scene_name, _ = os.path.splitext(scene_basename) @@ -66,6 +66,8 @@ class ValidateAssRelativePaths(pyblish.api.InstancePlugin): @classmethod def repair(cls, instance): + createOptions() + texture_path = cmds.getAttr("defaultArnoldRenderOptions.tspath") procedural_path = cmds.getAttr("defaultArnoldRenderOptions.pspath") From 561cebc51c61e02e01d2ae3145a688f2709beb85 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 10 Feb 2023 08:14:30 +0000 Subject: [PATCH 0526/1271] Fix assertion --- .../maya/plugins/publish/validate_ass_relative_paths.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py b/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py index 1c271a4a04..6975d583bb 100644 --- a/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py +++ b/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py @@ -8,6 +8,7 @@ import pyblish.api from openpype.pipeline.publish import ( RepairAction, ValidateContentsOrder, + PublishValidationError ) @@ -35,7 +36,9 @@ class ValidateAssRelativePaths(pyblish.api.InstancePlugin): "defaultArnoldRenderOptions.pspath" ) except ValueError: - assert False, ("Default Arnold options has not been created yet.") + raise PublishValidationError( + "Default Arnold options has not been created yet." + ) scene_dir, scene_basename = os.path.split(cmds.file(q=True, loc=True)) scene_name, _ = os.path.splitext(scene_basename) From ff63a91864af8d9dbfdfe940863468a03c0233af Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 10 Feb 2023 10:00:49 +0000 Subject: [PATCH 0527/1271] Support switching between proxy and non-proxy --- .../maya/plugins/load/load_arnold_standin.py | 25 ++++++++++++------- .../publish/extract_arnold_scene_source.py | 8 +++++- .../publish/validate_arnold_scene_source.py | 12 ++++++--- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index e2bb89ed77..bebe40f9a6 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -53,10 +53,8 @@ class ArnoldStandinLoader(load.LoaderPlugin): root = cmds.group(name=label, empty=True) # Set color. - project_name = context["project"]["name"] - settings = get_project_settings(project_name) - colors = settings['maya']['load']['colors'] - color = colors.get('ass') + settings = get_project_settings(context["project"]["name"]) + color = settings['maya']['load']['colors'].get('ass') if color is not None: cmds.setAttr(root + ".useOutlinerColor", True) cmds.setAttr( @@ -121,10 +119,6 @@ class ArnoldStandinLoader(load.LoaderPlugin): def _setup_proxy(self, shape, path, namespace): proxy_basename, proxy_path = self._get_proxy_path(path) - if not os.path.exists(proxy_path): - self.log.error("Proxy files do not exist. Skipping proxy setup.") - return path, None - options_node = "defaultArnoldRenderOptions" merge_operator = get_attribute_input(options_node + ".operator") if merge_operator is None: @@ -163,6 +157,12 @@ class ArnoldStandinLoader(load.LoaderPlugin): ) ) + # We setup the string operator no matter whether there is a proxy or + # not. This makes it easier to update since the string operator will + # always be created. Return original path to use for standin. + if not os.path.exists(proxy_path): + return path, string_replace_operator + return proxy_path, string_replace_operator def update(self, container, representation): @@ -180,6 +180,9 @@ class ArnoldStandinLoader(load.LoaderPlugin): path = get_representation_path(representation) proxy_basename, proxy_path = self._get_proxy_path(path) + + # Whether there is proxy or so, we still update the string operator. + # If no proxy exists, the string operator wont replace anything. cmds.setAttr( string_replace_operator + ".match", "resources/" + proxy_basename, @@ -190,7 +193,11 @@ class ArnoldStandinLoader(load.LoaderPlugin): os.path.basename(path), type="string" ) - cmds.setAttr(standin + ".dso", proxy_path, type="string") + + dso_path = path + if os.path.exists(proxy_path): + dso_path = proxy_path + cmds.setAttr(standin + ".dso", dso_path, type="string") sequence = is_sequence(os.listdir(os.path.dirname(path))) cmds.setAttr(standin + ".useFrameExtension", sequence) diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 10943dd810..153a1a513e 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -95,6 +95,9 @@ class ExtractArnoldSceneSource(publish.Extractor): ) # Extract proxy. + if not instance.data.get("proxy", []): + return + kwargs["filename"] = file_path.replace(".ass", "_proxy.ass") filenames = self._extract( instance.data["proxy"], attribute_data, kwargs @@ -132,7 +135,6 @@ class ExtractArnoldSceneSource(publish.Extractor): duplicate_nodes = [] for node in nodes: duplicate_transform = cmds.duplicate(node)[0] - delete_bin.append(duplicate_transform) # Discard the children. shapes = cmds.listRelatives(duplicate_transform, shapes=True) @@ -145,7 +147,11 @@ class ExtractArnoldSceneSource(publish.Extractor): duplicate_transform, world=True )[0] + cmds.rename(duplicate_transform, node.split("|")[-1]) + duplicate_transform = "|" + node.split("|")[-1] + duplicate_nodes.append(duplicate_transform) + delete_bin.append(duplicate_transform) with attribute_values(attribute_data): with maintained_selection(): diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py index 2d6c6e8e14..3b0ffd52d7 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_scene_source.py @@ -9,6 +9,9 @@ from openpype.pipeline.publish import ( class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): """Validate Arnold Scene Source. + We require at least 1 root node/parent for the meshes. This is to ensure we + can duplicate the nodes and preserve the names. + If using proxies we need the nodes to share the same names and not be parent to the world. This ends up needing at least two groups with content nodes and proxy nodes in another. @@ -39,9 +42,6 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): return ungrouped_nodes, nodes_by_name, parents def process(self, instance): - if not instance.data["proxy"]: - return - ungrouped_nodes = [] nodes, content_nodes_by_name, content_parents = self._get_nodes_data( @@ -50,7 +50,7 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): ungrouped_nodes.extend(nodes) nodes, proxy_nodes_by_name, proxy_parents = self._get_nodes_data( - instance.data["proxy"] + instance.data.get("proxy", []) ) ungrouped_nodes.extend(nodes) @@ -61,6 +61,10 @@ class ValidateArnoldSceneSource(pyblish.api.InstancePlugin): "All nodes need to be grouped.".format(ungrouped_nodes) ) + # Proxy validation. + if not instance.data.get("proxy", []): + return + # Validate for content and proxy nodes amount being the same. if len(instance.data["setMembers"]) != len(instance.data["proxy"]): raise PublishValidationError( From 165689463dde7be46804a18166434a5ef8f6ee8b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 11:20:20 +0100 Subject: [PATCH 0528/1271] typo --- 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 aaa2dd444a..c15eadb22f 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -650,7 +650,7 @@ def get_instance_staging_dir(instance): else: project_name = os.getenv("AVALON_PROJECT") - # get customized tempdir path from `OPENPYPE_TEMPDIR` env var + # get customized tempdir path from `OPENPYPE_TMPDIR` env var custom_temp_dir = temporarydir.create_custom_tempdir( project_name, anatomy=anatomy, formating_data=anatomy_data ) From 87f9cf09d77cc8ccec04c2c8dd31905f425ba212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Fri, 10 Feb 2023 11:23:53 +0100 Subject: [PATCH 0529/1271] Update openpype/pipeline/temporarydir.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/pipeline/temporarydir.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/pipeline/temporarydir.py b/openpype/pipeline/temporarydir.py index 31586d82c8..c5805b2dc1 100644 --- a/openpype/pipeline/temporarydir.py +++ b/openpype/pipeline/temporarydir.py @@ -38,10 +38,9 @@ def create_custom_tempdir(project_name, anatomy=None, formating_data=None): } if formating_data is None: # We still don't have `project_code` on Anatomy... - project_doc = anatomy.get_project_doc_from_cache(project_name) data["project"] = { "name": project_name, - "code": project_doc["data"]["code"], + "code": anatomy.project_code, } else: data["project"] = formating_data["project"] From bbd634bcd428b630324b7fbe57324c6eac8bf4eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Fri, 10 Feb 2023 11:24:06 +0100 Subject: [PATCH 0530/1271] Update openpype/pipeline/publish/lib.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- 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 c15eadb22f..423661880c 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -643,7 +643,7 @@ def get_instance_staging_dir(instance): return staging_dir anatomy_data = instance.data.get("anatomy_data") - anatomy = instance.data.get("anatomy") + anatomy = instance.context.data.get("anatomy") if anatomy_data: project_name = anatomy_data["project"]["name"] From af3c0cb951bcd4227ab07cfe174734cd43645b1d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 11:26:09 +0100 Subject: [PATCH 0531/1271] pr comments --- openpype/pipeline/publish/lib.py | 4 ++-- openpype/pipeline/{temporarydir.py => tempdir.py} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename openpype/pipeline/{temporarydir.py => tempdir.py} (100%) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 423661880c..d6e8097690 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -19,7 +19,7 @@ from openpype.settings import ( get_system_settings, ) from openpype.pipeline import ( - temporarydir + tempdir ) from .contants import ( @@ -651,7 +651,7 @@ def get_instance_staging_dir(instance): project_name = os.getenv("AVALON_PROJECT") # get customized tempdir path from `OPENPYPE_TMPDIR` env var - custom_temp_dir = temporarydir.create_custom_tempdir( + custom_temp_dir = tempdir.create_custom_tempdir( project_name, anatomy=anatomy, formating_data=anatomy_data ) diff --git a/openpype/pipeline/temporarydir.py b/openpype/pipeline/tempdir.py similarity index 100% rename from openpype/pipeline/temporarydir.py rename to openpype/pipeline/tempdir.py From 69937c62858a69d9d42beaeeaa6d23e5073a9446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Fri, 10 Feb 2023 11:27:30 +0100 Subject: [PATCH 0532/1271] Update openpype/pipeline/publish/lib.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- 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 d6e8097690..7d3c367c7a 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -648,7 +648,7 @@ def get_instance_staging_dir(instance): if anatomy_data: project_name = anatomy_data["project"]["name"] else: - project_name = os.getenv("AVALON_PROJECT") + project_name = instance.context.data["projectName"] # get customized tempdir path from `OPENPYPE_TMPDIR` env var custom_temp_dir = tempdir.create_custom_tempdir( From be0209e4135bea83ffbda230aa23f33651e9cbd0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 11:34:05 +0100 Subject: [PATCH 0533/1271] refactor in favour of code changes from #4445 https://github.com/ynput/OpenPype/pull/4445 --- openpype/pipeline/publish/lib.py | 10 +--------- openpype/pipeline/tempdir.py | 19 ++++++------------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 7d3c367c7a..2884dd495f 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -642,18 +642,10 @@ def get_instance_staging_dir(instance): if staging_dir: return staging_dir - anatomy_data = instance.data.get("anatomy_data") anatomy = instance.context.data.get("anatomy") - if anatomy_data: - project_name = anatomy_data["project"]["name"] - else: - project_name = instance.context.data["projectName"] - # get customized tempdir path from `OPENPYPE_TMPDIR` env var - custom_temp_dir = tempdir.create_custom_tempdir( - project_name, anatomy=anatomy, formating_data=anatomy_data - ) + custom_temp_dir = tempdir.create_custom_tempdir(anatomy) if custom_temp_dir: staging_dir = os.path.normpath( diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py index c5805b2dc1..ff5c58bbc5 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/tempdir.py @@ -7,7 +7,7 @@ from openpype.lib import StringTemplate from openpype.pipeline import Anatomy -def create_custom_tempdir(project_name, anatomy=None, formating_data=None): +def create_custom_tempdir(anatomy=None): """ Create custom tempdir Template path formatting is supporting: @@ -17,9 +17,7 @@ def create_custom_tempdir(project_name, anatomy=None, formating_data=None): - project[name | code] Args: - project_name (str): name of project anatomy (openpype.pipeline.Anatomy): Anatomy object - formating_data (dict): formating data used for filling template. Returns: bool | str: formated path or None @@ -31,20 +29,15 @@ def create_custom_tempdir(project_name, anatomy=None, formating_data=None): custom_tempdir = None if "{" in openpype_tempdir: if anatomy is None: - anatomy = Anatomy(project_name) + anatomy = Anatomy() # create base formate data data = { - "root": anatomy.roots - } - if formating_data is None: - # We still don't have `project_code` on Anatomy... - data["project"] = { - "name": project_name, + "root": anatomy.roots, + "project": { + "name": anatomy.project_name, "code": anatomy.project_code, } - else: - data["project"] = formating_data["project"] - + } # path is anatomy template custom_tempdir = StringTemplate.format_template( openpype_tempdir, data).normalized() From c5c91183c3930e8798a1d21340ab71b833342a05 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 11:35:01 +0100 Subject: [PATCH 0534/1271] fix other places where decoding of ffmpeg happens --- openpype/hosts/harmony/plugins/publish/extract_render.py | 4 ++-- .../plugins/OpenPypeTileAssembler/OpenPypeTileAssembler.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/harmony/plugins/publish/extract_render.py b/openpype/hosts/harmony/plugins/publish/extract_render.py index 2f8169248e..c29864bb28 100644 --- a/openpype/hosts/harmony/plugins/publish/extract_render.py +++ b/openpype/hosts/harmony/plugins/publish/extract_render.py @@ -108,9 +108,9 @@ class ExtractRender(pyblish.api.InstancePlugin): output = process.communicate()[0] if process.returncode != 0: - raise ValueError(output.decode("utf-8")) + raise ValueError(output.decode("utf-8", errors="backslashreplace")) - self.log.debug(output.decode("utf-8")) + self.log.debug(output.decode("utf-8", errors="backslashreplace")) # Generate representations. extension = collection.tail[1:] diff --git a/openpype/modules/deadline/repository/custom/plugins/OpenPypeTileAssembler/OpenPypeTileAssembler.py b/openpype/modules/deadline/repository/custom/plugins/OpenPypeTileAssembler/OpenPypeTileAssembler.py index 625a3f1a28..861f16518c 100644 --- a/openpype/modules/deadline/repository/custom/plugins/OpenPypeTileAssembler/OpenPypeTileAssembler.py +++ b/openpype/modules/deadline/repository/custom/plugins/OpenPypeTileAssembler/OpenPypeTileAssembler.py @@ -204,10 +204,10 @@ def info_about_input(oiiotool_path, filepath): _stdout, _stderr = popen.communicate() output = "" if _stdout: - output += _stdout.decode("utf-8") + output += _stdout.decode("utf-8", errors="backslashreplace") if _stderr: - output += _stderr.decode("utf-8") + output += _stderr.decode("utf-8", errors="backslashreplace") output = output.replace("\r\n", "\n") xml_started = False From 01293aaa2db33747ee45a9194d65fd1555e2d61a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 12:00:30 +0100 Subject: [PATCH 0535/1271] fix burnins script again --- openpype/scripts/otio_burnin.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openpype/scripts/otio_burnin.py b/openpype/scripts/otio_burnin.py index 7223e8d4de..3e40bf0c8b 100644 --- a/openpype/scripts/otio_burnin.py +++ b/openpype/scripts/otio_burnin.py @@ -340,13 +340,11 @@ class ModifiedBurnins(ffmpeg_burnins.Burnins): _stdout, _stderr = proc.communicate() if _stdout: - for line in _stdout.split(b"\r\n"): - print(line.decode("utf-8")) + print(_stdout.decode("utf-8", errors="backslashreplace")) # This will probably never happen as ffmpeg use stdout if _stderr: - for line in _stderr.split(b"\r\n"): - print(line.decode("utf-8")) + print(_stderr.decode("utf-8", errors="backslashreplace")) if proc.returncode != 0: raise RuntimeError( From 9f4153fbe64ee0f5918354a2723358a760fb571d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 10 Feb 2023 12:27:10 +0000 Subject: [PATCH 0536/1271] Code cosmetics --- .../hosts/maya/plugins/load/load_arnold_standin.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index bebe40f9a6..6e5fe16bcd 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -94,17 +94,13 @@ class ArnoldStandinLoader(load.LoaderPlugin): def get_next_free_multi_index(self, attr_name): """Find the next unconnected multi index at the input attribute.""" - - start_index = 0 - # Assume a max of 10 million connections - while start_index < 10000000: + for index in range(10000000): connection_info = cmds.connectionInfo( - "{}[{}]".format(attr_name, start_index), + "{}[{}]".format(attr_name, index), sourceFromDestination=True ) if len(connection_info or []) == 0: - return start_index - start_index += 1 + return index def _get_proxy_path(self, path): basename_split = os.path.basename(path).split(".") From 3927dc13af80888375dd1151ff9eef0929a71f9e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 13:42:46 +0100 Subject: [PATCH 0537/1271] adding back project name --- openpype/pipeline/publish/lib.py | 3 ++- openpype/pipeline/tempdir.py | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 2884dd495f..cc7f5678f5 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -645,7 +645,8 @@ def get_instance_staging_dir(instance): anatomy = instance.context.data.get("anatomy") # get customized tempdir path from `OPENPYPE_TMPDIR` env var - custom_temp_dir = tempdir.create_custom_tempdir(anatomy) + custom_temp_dir = tempdir.create_custom_tempdir( + anatomy.project_name, anatomy) if custom_temp_dir: staging_dir = os.path.normpath( diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py index ff5c58bbc5..ab3cc216ef 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/tempdir.py @@ -7,7 +7,7 @@ from openpype.lib import StringTemplate from openpype.pipeline import Anatomy -def create_custom_tempdir(anatomy=None): +def create_custom_tempdir(project_name, anatomy=None): """ Create custom tempdir Template path formatting is supporting: @@ -17,7 +17,8 @@ def create_custom_tempdir(anatomy=None): - project[name | code] Args: - anatomy (openpype.pipeline.Anatomy): Anatomy object + project_name (str): project name + anatomy (openpype.pipeline.Anatomy)[optional]: Anatomy object Returns: bool | str: formated path or None @@ -29,7 +30,7 @@ def create_custom_tempdir(anatomy=None): custom_tempdir = None if "{" in openpype_tempdir: if anatomy is None: - anatomy = Anatomy() + anatomy = Anatomy(project_name) # create base formate data data = { "root": anatomy.roots, From f458fbc9258e97e0e9f340d3e9e59c3eb5b2b820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Fri, 10 Feb 2023 13:44:01 +0100 Subject: [PATCH 0538/1271] Update openpype/pipeline/tempdir.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/pipeline/tempdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py index ab3cc216ef..6a346f3342 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/tempdir.py @@ -21,7 +21,7 @@ def create_custom_tempdir(project_name, anatomy=None): anatomy (openpype.pipeline.Anatomy)[optional]: Anatomy object Returns: - bool | str: formated path or None + str | None: formated path or None """ openpype_tempdir = os.getenv("OPENPYPE_TMPDIR") if not openpype_tempdir: From 88bda4e1f6bfbe628d80edc76789393dcc9a68a4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 14:24:28 +0100 Subject: [PATCH 0539/1271] TVPaint host inherit from IPublishHost --- openpype/hosts/tvpaint/api/pipeline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 249326791b..bd6b929f6b 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -8,7 +8,7 @@ import requests import pyblish.api from openpype.client import get_project, get_asset_by_name -from openpype.host import HostBase, IWorkfileHost, ILoadHost +from openpype.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost from openpype.hosts.tvpaint import TVPAINT_ROOT_DIR from openpype.settings import get_current_project_settings from openpype.lib import register_event_callback @@ -58,7 +58,7 @@ instances=2 """ -class TVPaintHost(HostBase, IWorkfileHost, ILoadHost): +class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): name = "tvpaint" def install(self): From 2c0f057a913b9a891f40ffed06959749b90eaae5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 14:25:59 +0100 Subject: [PATCH 0540/1271] implemented methods for context data --- openpype/hosts/tvpaint/api/pipeline.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index bd6b929f6b..7ab660137a 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -29,6 +29,7 @@ log = logging.getLogger(__name__) METADATA_SECTION = "avalon" SECTION_NAME_CONTEXT = "context" +SECTION_NAME_CREATE_CONTEXT = "create_context" SECTION_NAME_INSTANCES = "instances" SECTION_NAME_CONTAINERS = "containers" # Maximum length of metadata chunk string @@ -93,6 +94,14 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): register_event_callback("application.launched", self.initial_launch) register_event_callback("application.exit", self.application_exit) + + # --- Create --- + def get_context_data(self): + return get_workfile_metadata(SECTION_NAME_CREATE_CONTEXT, {}) + + def update_context_data(self, data, changes): + return write_workfile_metadata(SECTION_NAME_CREATE_CONTEXT, data) + def open_workfile(self, filepath): george_script = "tv_LoadProject '\"'\"{}\"'\"'".format( filepath.replace("\\", "/") From 85afe4f53ba31678e0d35bbd675c114d7e133b96 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 14:27:22 +0100 Subject: [PATCH 0541/1271] cleanup of methods --- openpype/hosts/tvpaint/api/pipeline.py | 60 ++++++++++++++------------ 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 7ab660137a..38d3922f3b 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -102,6 +102,35 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): def update_context_data(self, data, changes): return write_workfile_metadata(SECTION_NAME_CREATE_CONTEXT, data) + def list_instances(self): + """List all created instances from current workfile.""" + return list_instances() + + def write_instances(self, data): + return write_instances(data) + + # --- Legacy Create --- + def remove_instance(self, instance): + """Remove instance from current workfile metadata. + + Implementation for Subset manager tool. + """ + + 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) + + # --- Workfile --- def open_workfile(self, filepath): george_script = "tv_LoadProject '\"'\"{}\"'\"'".format( filepath.replace("\\", "/") @@ -134,6 +163,7 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): def get_workfile_extensions(self): return [".tvpp"] + # --- Load --- def get_containers(self): return get_containers() @@ -148,26 +178,6 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): log.info("Setting up project...") set_context_settings() - def remove_instance(self, instance): - """Remove instance from current workfile metadata. - - Implementation for Subset manager tool. - """ - - 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 application_exit(self): """Logic related to TimerManager. @@ -186,6 +196,7 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): rest_api_url = "{}/timers_manager/stop_timer".format(webserver_url) requests.post(rest_api_url) + # --- Legacy Publish --- def on_instance_toggle(self, instance, old_value, new_value): """Update instance data in workfile on publish toggle.""" # Review may not have real instance in wokrfile metadata @@ -196,7 +207,7 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): found_idx = None current_instances = list_instances() for idx, workfile_instance in enumerate(current_instances): - if workfile_instance["uuid"] == instance_id: + if workfile_instance.get("uuid") == instance_id: found_idx = idx break @@ -207,13 +218,6 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): current_instances[found_idx]["active"] = new_value self.write_instances(current_instances) - def list_instances(self): - """List all created instances from current workfile.""" - return list_instances() - - def write_instances(self, data): - return write_instances(data) - def containerise( name, namespace, members, context, loader, current_containers=None From a17f46486405efe859b626e1633bc4666ac2b709 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 14:28:13 +0100 Subject: [PATCH 0542/1271] implement custom context methods --- openpype/hosts/tvpaint/api/pipeline.py | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 38d3922f3b..85ade41b9b 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -18,6 +18,7 @@ from openpype.pipeline import ( register_creator_plugin_path, AVALON_CONTAINER_ID, ) +from openpype.pipeline.context_tools import get_global_context from .lib import ( execute_george, @@ -94,6 +95,40 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): register_event_callback("application.launched", self.initial_launch) register_event_callback("application.exit", self.application_exit) + def get_current_project_name(self): + """ + Returns: + Union[str, None]: Current project name. + """ + + return self.get_current_context().get("project_name") + + def get_current_asset_name(self): + """ + Returns: + Union[str, None]: Current asset name. + """ + + return self.get_current_context().get("asset_name") + + def get_current_task_name(self): + """ + Returns: + Union[str, None]: Current task name. + """ + + return self.get_current_context().get("task_name") + + def get_current_context(self): + context = get_current_workfile_context() + if not context: + return get_global_context() + + return { + "project_name": context["project"], + "asset_name": context.get("asset"), + "task_name": context.get("task") + } # --- Create --- def get_context_data(self): From 65e7717b6c9bc64f5a832125340bcba0aed50981 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 14:28:25 +0100 Subject: [PATCH 0543/1271] use 'get_global_context' on workfile save --- openpype/hosts/tvpaint/api/pipeline.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 85ade41b9b..6a729e39c3 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -175,11 +175,7 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): def save_workfile(self, filepath=None): if not filepath: filepath = self.get_current_workfile() - context = { - "project": legacy_io.Session["AVALON_PROJECT"], - "asset": legacy_io.Session["AVALON_ASSET"], - "task": legacy_io.Session["AVALON_TASK"] - } + context = get_global_context() save_current_workfile_context(context) # Execute george script to save workfile. From b9f22cf2bec8f93d673a3aba9a1cbfd264da69f0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 14:28:45 +0100 Subject: [PATCH 0544/1271] removed unused method --- openpype/hosts/tvpaint/api/plugin.py | 31 ---------------------------- 1 file changed, 31 deletions(-) diff --git a/openpype/hosts/tvpaint/api/plugin.py b/openpype/hosts/tvpaint/api/plugin.py index da456e7067..c7feccd125 100644 --- a/openpype/hosts/tvpaint/api/plugin.py +++ b/openpype/hosts/tvpaint/api/plugin.py @@ -32,37 +32,6 @@ class Creator(LegacyCreator): 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 skipped 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)) From 1cb7c37b9ef51066d2a2c3cc3b243d058303397f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 14:29:02 +0100 Subject: [PATCH 0545/1271] implemented base creators for tvpaint --- openpype/hosts/tvpaint/api/plugin.py | 125 +++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/openpype/hosts/tvpaint/api/plugin.py b/openpype/hosts/tvpaint/api/plugin.py index c7feccd125..e6fc087665 100644 --- a/openpype/hosts/tvpaint/api/plugin.py +++ b/openpype/hosts/tvpaint/api/plugin.py @@ -6,11 +6,136 @@ from openpype.pipeline import ( LoaderPlugin, registered_host, ) +from openpype.pipeline.create import ( + CreatedInstance, + get_subset_name, + AutoCreator, + Creator as NewCreator, +) +from openpype.pipeline.create.creator_plugins import cache_and_get_instances from .lib import get_layers_data from .pipeline import get_current_workfile_context +SHARED_DATA_KEY = "openpype.tvpaint.instances" + + +def _collect_instances(creator): + instances_by_identifier = cache_and_get_instances( + creator, SHARED_DATA_KEY, creator.host.list_instances + ) + for instance_data in instances_by_identifier[creator.identifier]: + instance = CreatedInstance.from_existing(instance_data, creator) + creator._add_instance_to_context(instance) + + +def _update_instances(creator, update_list): + if not update_list: + return + + cur_instances = creator.host.list_instances() + cur_instances_by_id = {} + for instance_data in cur_instances: + instance_id = instance_data.get("instance_id") + if instance_id: + cur_instances_by_id[instance_id] = instance_data + + for instance, changes in update_list: + instance_data = instance.data_to_store() + cur_instance_data = cur_instances_by_id.get(instance.id) + if cur_instance_data is None: + cur_instances.append(instance_data) + continue + for key in set(cur_instance_data) - set(instance_data): + instance_data.pop(key) + instance_data.update(cur_instance_data) + creator.host.write_instances(cur_instances) + + +class TVPaintCreator(NewCreator): + @property + def subset_template_family(self): + return self.family + + def collect_instances(self): + _collect_instances(self) + + def update_instances(self, update_list): + _update_instances(self, update_list) + + def remove_instances(self, instances): + ids_to_remove = { + instance.id + for instance in instances + } + cur_instances = self.host.list_instances() + changed = False + new_instances = [] + for instance_data in cur_instances: + if instance_data.get("instance_id") in ids_to_remove: + changed = True + else: + new_instances.append(instance_data) + + if changed: + self.host.write_instances(new_instances) + + for instance in instances: + self._remove_instance_from_context(instance) + + def get_dynamic_data(self, *args, **kwargs): + # Change asset and name by current workfile context + # TODO use context from 'create_context' + workfile_context = self.host.get_current_context() + asset_name = workfile_context.get("asset") + task_name = workfile_context.get("task") + output = {} + if asset_name: + output["asset"] = asset_name + if task_name: + output["task"] = task_name + return output + + def get_subset_name( + self, + variant, + task_name, + asset_doc, + project_name, + host_name=None, + instance=None + ): + dynamic_data = self.get_dynamic_data( + variant, task_name, asset_doc, project_name, host_name, instance + ) + + return get_subset_name( + self.subset_template_family, + variant, + task_name, + asset_doc, + project_name, + host_name, + dynamic_data=dynamic_data, + project_settings=self.project_settings + ) + + def _store_new_instance(self, new_instance): + instances_data = self.host.list_instances() + instances_data.append(new_instance.data_to_store()) + self.host.write_instances(instances_data) + self._add_instance_to_context(new_instance) + + +class TVPaintAutoCreator(AutoCreator): + def collect_instances(self): + _collect_instances(self) + + def update_instances(self, update_list): + _update_instances(self, update_list) + + class Creator(LegacyCreator): def __init__(self, *args, **kwargs): super(Creator, self).__init__(*args, **kwargs) From f984dd8fc5381dd901d69bca6fef5c234ffae1c5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 14:29:15 +0100 Subject: [PATCH 0546/1271] implemented workfile autocreator --- .../tvpaint/plugins/create/create_workfile.py | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 openpype/hosts/tvpaint/plugins/create/create_workfile.py diff --git a/openpype/hosts/tvpaint/plugins/create/create_workfile.py b/openpype/hosts/tvpaint/plugins/create/create_workfile.py new file mode 100644 index 0000000000..3e5cd86852 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/create/create_workfile.py @@ -0,0 +1,63 @@ +from openpype.client import get_asset_by_name +from openpype.pipeline import CreatedInstance +from openpype.hosts.tvpaint.api.plugin import TVPaintAutoCreator + + +class TVPaintWorkfileCreator(TVPaintAutoCreator): + family = "workfile" + identifier = "workfile" + + default_variant = "Main" + + def create(self): + existing_instance = None + for instance in self.create_context.instances: + if instance.creator_identifier == self.identifier: + existing_instance = instance + break + + context = self.host.get_current_context() + host_name = self.host.name + project_name = context["project_name"] + asset_name = context["asset_name"] + task_name = context["task_name"] + + if existing_instance is None: + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + self.default_variant, + task_name, + asset_doc, + project_name, + host_name + ) + data = { + "asset": asset_name, + "task": task_name, + "variant": self.default_variant + } + + new_instance = CreatedInstance( + self.family, subset_name, data, self + ) + instances_data = self.host.list_instances() + instances_data.append(new_instance.data_to_store()) + self.host.write_instances(instances_data) + self._add_instance_to_context(new_instance) + + elif ( + existing_instance["asset"] != asset_name + or existing_instance["task"] != task_name + ): + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + existing_instance["variant"], + task_name, + asset_doc, + project_name, + host_name, + existing_instance + ) + existing_instance["asset"] = asset_name + existing_instance["task"] = task_name + existing_instance["subset"] = subset_name From 2a137622fbe17d588e770e796ad3fdc48401918e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 14:29:37 +0100 Subject: [PATCH 0547/1271] base of render layer/pass creators --- .../tvpaint/plugins/create/create_render.py | 309 ++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 openpype/hosts/tvpaint/plugins/create/create_render.py diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py new file mode 100644 index 0000000000..67337b77a3 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -0,0 +1,309 @@ +from openpype.lib import ( + prepare_template_data, + EnumDef, + TextDef, +) +from openpype.pipeline.create import ( + CreatedInstance, + CreatorError, +) +from openpype.hosts.tvpaint.api.plugin import TVPaintCreator +from openpype.hosts.tvpaint.api.lib import ( + get_layers_data, + get_groups_data, + execute_george_through_file, +) + + +class CreateRenderlayer(TVPaintCreator): + """Mark layer group as one instance.""" + label = "Render Layer" + family = "render" + subset_template_family = "renderLayer" + identifier = "render.layer" + icon = "fa.cube" + + # George script to change color group + rename_script_template = ( + "tv_layercolor \"setcolor\"" + " {clip_id} {group_id} {r} {g} {b} \"{name}\"" + ) + order = 90 + + # Settings + render_pass = "beauty" + + def get_dynamic_data( + self, variant, task_name, asset_doc, project_name, host_name, instance + ): + dynamic_data = super().get_dynamic_data( + variant, task_name, asset_doc, project_name, host_name, instance + ) + dynamic_data["renderpass"] = self.render_pass + dynamic_data["renderlayer"] = variant + return dynamic_data + + def _get_selected_group_ids(self): + return { + layer["group_id"] + for layer in get_layers_data() + if layer["selected"] + } + + def create(self, subset_name, instance_data, pre_create_data): + self.log.debug("Query data from workfile.") + + group_id = pre_create_data.get("group_id") + # This creator should run only on one group + if group_id is None or group_id == -1: + selected_groups = self._get_selected_group_ids() + selected_groups.discard(0) + if len(selected_groups) > 1: + raise CreatorError("You have selected more than one group") + + if len(selected_groups) == 0: + raise CreatorError("You don't have selected any group") + group_id = tuple(selected_groups)[0] + + self.log.debug("Querying groups data from workfile.") + groups_data = get_groups_data() + group_item = None + for group_data in groups_data: + if group_data["group_id"] == group_id: + group_item = group_data + + for instance in self.create_context.instances: + if ( + instance.creator_identifier == self.identifier + and instance["creator_attributes"]["group_id"] == group_id + ): + raise CreatorError(( + f"Group \"{group_item.get('name')}\" is already used" + f" by another render layer \"{instance['subset']}\"" + )) + + self.log.debug(f"Selected group id is \"{group_id}\".") + if "creator_attributes" not in instance_data: + instance_data["creator_attributes"] = {} + instance_data["creator_attributes"]["group_id"] = group_id + + self.log.info(f"Subset name is {subset_name}") + new_instance = CreatedInstance( + self.family, + subset_name, + instance_data, + self + ) + self._store_new_instance(new_instance) + + new_group_name = pre_create_data.get("group_name") + if not new_group_name or not group_id: + return + + self.log.debug("Changing name of the group.") + + new_group_name = pre_create_data.get("group_name") + if not new_group_name or group_item["name"] == new_group_name: + return + # Rename TVPaint group (keep color same) + # - groups can't contain spaces + rename_script = self.rename_script_template.format( + clip_id=group_item["clip_id"], + group_id=group_item["group_id"], + r=group_item["red"], + g=group_item["green"], + b=group_item["blue"], + name=new_group_name + ) + execute_george_through_file(rename_script) + + self.log.info(( + f"Name of group with index {group_id}" + f" was changed to \"{new_group_name}\"." + )) + + def get_pre_create_attr_defs(self): + groups_enum = [ + { + "label": group["name"], + "value": group["group_id"] + } + for group in get_groups_data() + if group["name"] + ] + groups_enum.insert(0, {"label": "", "value": -1}) + + return [ + EnumDef( + "group_id", + label="Group", + items=groups_enum + ), + TextDef( + "group_name", + label="New group name", + placeholder="< Keep unchanged >" + ) + ] + + def get_instance_attr_defs(self): + groups_enum = [ + { + "label": group["name"], + "value": group["group_id"] + } + for group in get_groups_data() + if group["name"] + ] + return [ + EnumDef( + "group_id", + label="Group", + items=groups_enum + ) + ] + + +class CreateRenderPass(TVPaintCreator): + icon = "fa.cube" + family = "render" + subset_template_family = "renderPass" + identifier = "render.pass" + label = "Render Pass" + + order = CreateRenderlayer.order + 10 + + def get_dynamic_data( + self, variant, task_name, asset_doc, project_name, host_name, instance + ): + dynamic_data = super().get_dynamic_data( + variant, task_name, asset_doc, project_name, host_name, instance + ) + dynamic_data["renderpass"] = variant + dynamic_data["renderlayer"] = "{renderlayer}" + return dynamic_data + + def create(self, subset_name, instance_data, pre_create_data): + render_layer_instance_id = pre_create_data.get( + "render_layer_instance_id" + ) + if not render_layer_instance_id: + raise CreatorError("Missing RenderLayer instance") + + render_layer_instance = self.create_context.instances_by_id.get( + render_layer_instance_id + ) + if render_layer_instance is None: + raise CreatorError(( + "RenderLayer instance was not found" + f" by id \"{render_layer_instance_id}\"" + )) + + group_id = render_layer_instance["creator_attributes"]["group_id"] + self.log.debug("Query data from workfile.") + layers_data = get_layers_data() + + self.log.debug("Checking selection.") + # Get all selected layers and their group ids + selected_layers = [ + layer + for layer in layers_data + if layer["selected"] + ] + + # Raise if nothing is selected + if not selected_layers: + raise CreatorError("Nothing is selected. Please select layers.") + + selected_layer_names = {layer["name"] for layer in selected_layers} + instances_to_remove = [] + for instance in self.create_context.instances: + if instance.creator_identifier != self.identifier: + continue + layer_names = set(instance["layer_names"]) + if not layer_names.intersection(selected_layer_names): + continue + new_layer_names = layer_names - selected_layer_names + if new_layer_names: + instance["layer_names"] = list(new_layer_names) + else: + instances_to_remove.append(instance) + + render_layer = render_layer_instance["variant"] + subset_name_fill_data = {"renderlayer": render_layer} + + # Format dynamic keys in subset name + new_subset_name = subset_name.format( + **prepare_template_data(subset_name_fill_data) + ) + self.log.info(f"New subset name is \"{new_subset_name}\".") + instance_data["layer_names"] = list(selected_layer_names) + new_instance = CreatedInstance( + self.family, + subset_name, + instance_data, + self + ) + instances_data = self._remove_and_filter_instances( + instances_to_remove + ) + instances_data.append(new_instance.data_to_store()) + + self.host.write_instances(instances_data) + self._add_instance_to_context(new_instance) + self._change_layers_group(selected_layers, group_id) + + def _change_layers_group(self, layers, group_id): + filtered_layers = [ + layer + for layer in layers + if layer["group_id"] != group_id + ] + if filtered_layers: + self.log.info(( + "Changing group of " + f"{','.join([l['name'] for l in filtered_layers])}" + f" to {group_id}" + )) + george_lines = [ + f"tv_layercolor \"set\" {layer['layer_id']} {group_id}" + for layer in filtered_layers + ] + execute_george_through_file("\n".join(george_lines)) + + def _remove_and_filter_instances(self, instances_to_remove): + instances_data = self.host.list_instances() + if not instances_to_remove: + return instances_data + + removed_ids = set() + for instance in instances_to_remove: + removed_ids.add(instance.id) + self._remove_instance_from_context(instance) + + return [ + instance_data + for instance_data in instances_data + if instance_data.get("instance_id") not in removed_ids + ] + + def get_pre_create_attr_defs(self): + render_layers = [] + for instance in self.create_context.instances: + if instance.creator_identifier == "render.layer": + render_layers.append({ + "value": instance.id, + "label": instance.label + }) + + if not render_layers: + render_layers.append({"value": None, "label": "N/A"}) + + return [ + EnumDef( + "render_layer_instance_id", + label="Render Layer", + items=render_layers + ) + ] + From bd538e7e70373caafd3067e0acd26acdf8297d28 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 14:30:08 +0100 Subject: [PATCH 0548/1271] use publisher instead of pyblish pype --- openpype/hosts/tvpaint/api/communication_server.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/openpype/hosts/tvpaint/api/communication_server.py b/openpype/hosts/tvpaint/api/communication_server.py index 6ac3e6324c..6fd2d69373 100644 --- a/openpype/hosts/tvpaint/api/communication_server.py +++ b/openpype/hosts/tvpaint/api/communication_server.py @@ -344,7 +344,7 @@ class QtTVPaintRpc(BaseTVPaintRpc): async def publish_tool(self): log.info("Triggering Publish tool") - item = MainThreadItem(self.tools_helper.show_publish) + item = MainThreadItem(self.tools_helper.show_publisher_tool) self._execute_in_main_thread(item) return @@ -875,10 +875,6 @@ class QtCommunicator(BaseCommunicator): "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", From 8494a45d6c6ac20f3a9fb287c3835378b2e0c4d1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 16:11:10 +0100 Subject: [PATCH 0549/1271] validate representation files in separated method --- openpype/plugins/publish/integrate.py | 56 +++++++++++++++++++-------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 7b73943c37..7074410a84 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -506,6 +506,43 @@ class IntegrateAsset(pyblish.api.InstancePlugin): return version_doc + def _validate_repre_files(self, files, is_sequence_representation): + """Validate representation files before transfer preparation. + + Check if files contain only filenames instead of full paths and check + if sequence don't contain more than one sequence or has remainders. + + Args: + files (Union[str, List[str]]): Files from representation. + is_sequence_representation (bool): Files are for sequence. + + Raises: + KnownPublishError: If validations don't pass. + """ + + if not files: + return + + if not is_sequence_representation: + files = [files] + + if any(os.path.isabs(fname) for fname in files): + raise KnownPublishError("Given file names contain full paths") + + if not is_sequence_representation: + return + + src_collections, remainders = clique.assemble(files) + if len(files) < 2 or len(src_collections) != 1 or remainders: + raise KnownPublishError(( + "Files of representation does not contain proper" + " sequence files.\nCollected collections: {}" + "\nCollected remainders: {}" + ).format( + ", ".join([str(col) for col in src_collections]), + ", ".join([str(rem) for rem in remainders]) + )) + def prepare_representation(self, repre, template_name, existing_repres_by_name, @@ -606,21 +643,14 @@ class IntegrateAsset(pyblish.api.InstancePlugin): template_data["originalDirname"] = without_root is_sequence_representation = isinstance(files, (list, tuple)) + self._validate_repre_files(files, is_sequence_representation) + if is_sequence_representation: # Collection of files (sequence) if any(os.path.isabs(fname) for fname in files): raise KnownPublishError("Given file names contain full paths") src_collections, remainders = clique.assemble(files) - if len(files) < 2 or len(src_collections) != 1 or remainders: - raise KnownPublishError(( - "Files of representation does not contain proper" - " sequence files.\nCollected collections: {}" - "\nCollected remainders: {}" - ).format( - ", ".join([str(col) for col in src_collections]), - ", ".join([str(rem) for rem in remainders]) - )) src_collection = src_collections[0] template_data["originalBasename"] = src_collection.head[:-1] @@ -705,14 +735,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): else: # Single file - fname = files - if os.path.isabs(fname): - self.log.error( - "Filename in representation is filepath {}".format(fname) - ) - raise KnownPublishError( - "This is a bug. Representation file name is full path" - ) template_data["originalBasename"], _ = os.path.splitext(fname) # Manage anatomy template data template_data.pop("frame", None) From c1499f9b2309b8604228762e56f1b655ae7b1980 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 16:12:08 +0100 Subject: [PATCH 0550/1271] Add special handling of transfers preparation if 'originalBasename' is in template --- openpype/plugins/publish/integrate.py | 56 +++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 7074410a84..5c3766afad 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -645,11 +645,59 @@ class IntegrateAsset(pyblish.api.InstancePlugin): is_sequence_representation = isinstance(files, (list, tuple)) self._validate_repre_files(files, is_sequence_representation) - if is_sequence_representation: - # Collection of files (sequence) - if any(os.path.isabs(fname) for fname in files): - raise KnownPublishError("Given file names contain full paths") + # Output variables of conditions below: + # - transfers (List[Tuple[str, str]]): src -> dst filepaths to copy + # - repre_context (Dict[str, Any]): context data used to fill template + # - template_data (Dict[str, Any]): source data used to fill template + # - to add required data to 'repre_context' not used for + # formatting + # - anatomy_filled (Dict[str, Any]): filled anatomy of last file + # - to fill 'publishDir' on instance.data -> not ideal + # Treat template with 'orignalBasename' in special way + if "{originalBasename}" in template: + # Remove 'frame' from template data + template_data.pop("frame", None) + + # Find out first frame string value + first_index_padded = None + if not is_udim and is_sequence_representation: + col = clique.assemble(files)[0][0] + sorted_frames = tuple(sorted(col.indexes)) + # First frame used for end value + first_frame = sorted_frames[0] + # Get last frame for padding + last_frame = sorted_frames[-1] + # Use padding from collection of length of last frame as string + padding = max(col.padding, len(str(last_frame))) + first_index_padded = get_frame_padded( + frame=first_frame, + padding=padding + ) + + # Convert files to list for single file as remaining part is only + # transfers creation (iteration over files) + if not is_sequence_representation: + files = [files] + + repre_context = None + transfers = [] + for src_file_name in files: + template_data["originalBasename"], _ = os.path.splitext( + src_file_name) + + anatomy_filled = anatomy.format(template_data) + dst = anatomy_filled[template_name]["path"] + src = os.path.join(stagingdir, src_file_name) + transfers.append((src, dst)) + if repre_context is None: + repre_context = dst.used_values + + if not is_udim and first_index_padded is not None: + repre_context["frame"] = first_index_padded + + elif is_sequence_representation: + # Collection of files (sequence) src_collections, remainders = clique.assemble(files) src_collection = src_collections[0] From 9b2e97fb74649f4fbcb54424adf8490ac4e23fa5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 16:12:31 +0100 Subject: [PATCH 0551/1271] don't handle originalBasename in other conditions --- openpype/plugins/publish/integrate.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 5c3766afad..334262fb63 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -701,7 +701,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): src_collections, remainders = clique.assemble(files) src_collection = src_collections[0] - template_data["originalBasename"] = src_collection.head[:-1] destination_indexes = list(src_collection.indexes) # Use last frame for minimum padding # - that should cover both 'udim' and 'frame' minimum padding @@ -723,11 +722,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # In case source are published in place we need to # skip renumbering repre_frame_start = repre.get("frameStart") - if ( - "originalBasename" not in template - and repre_frame_start is not None - ): - index_frame_start = int(repre["frameStart"]) + if repre_frame_start is not None: + index_frame_start = int(repre_frame_start) # Shift destination sequence to the start frame destination_indexes = [ index_frame_start + idx @@ -783,7 +779,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): else: # Single file - template_data["originalBasename"], _ = os.path.splitext(fname) # Manage anatomy template data template_data.pop("frame", None) if is_udim: @@ -795,7 +790,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): dst = os.path.normpath(template_filled) # Single file transfer - src = os.path.join(stagingdir, fname) + src = os.path.join(stagingdir, files) transfers = [(src, dst)] # todo: Are we sure the assumption each representation From a09999174d7ae8daa98141efcd2d06e1fa527aeb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 16:12:47 +0100 Subject: [PATCH 0552/1271] more explicit check for originalDirname --- openpype/plugins/publish/integrate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 334262fb63..b117006871 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -624,7 +624,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): is_udim = bool(repre.get("udim")) # handle publish in place - if "originalDirname" in template: + if "{originalDirname}" in template: # store as originalDirname only original value without project root # if instance collected originalDirname is present, it should be # used for all represe From 25e4a4b5a3b23fde16f5908b07d81103be99fd03 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 Feb 2023 16:15:55 +0100 Subject: [PATCH 0553/1271] Added support for multiple install dirs in Deadline SearchDirectoryList returns FIRST existing so if you would have multiple OP install dirs, it won't search for appropriate version in later ones. --- .../custom/plugins/GlobalJobPreLoad.py | 28 ++++++++++--------- .../custom/plugins/OpenPype/OpenPype.py | 23 +++++++-------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 984590ddba..65a3782dfe 100644 --- a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -196,19 +196,21 @@ def get_openpype_versions(dir_list): print(">>> Getting OpenPype executable ...") openpype_versions = [] - install_dir = DirectoryUtils.SearchDirectoryList(dir_list) - if install_dir: - print("--- Looking for OpenPype at: {}".format(install_dir)) - sub_dirs = [ - f.path for f in os.scandir(install_dir) - if f.is_dir() - ] - for subdir in sub_dirs: - version = get_openpype_version_from_path(subdir) - if not version: - continue - print(" - found: {} - {}".format(version, subdir)) - openpype_versions.append((version, subdir)) + # special case of multiple install dirs + for dir_list in dir_list.split(","): + install_dir = DirectoryUtils.SearchDirectoryList(dir_list) + if install_dir: + print("--- Looking for OpenPype at: {}".format(install_dir)) + sub_dirs = [ + f.path for f in os.scandir(install_dir) + if f.is_dir() + ] + for subdir in sub_dirs: + version = get_openpype_version_from_path(subdir) + if not version: + continue + print(" - found: {} - {}".format(version, subdir)) + openpype_versions.append((version, subdir)) return openpype_versions diff --git a/openpype/modules/deadline/repository/custom/plugins/OpenPype/OpenPype.py b/openpype/modules/deadline/repository/custom/plugins/OpenPype/OpenPype.py index 6b0f69d98f..ae31f2e35f 100644 --- a/openpype/modules/deadline/repository/custom/plugins/OpenPype/OpenPype.py +++ b/openpype/modules/deadline/repository/custom/plugins/OpenPype/OpenPype.py @@ -107,17 +107,18 @@ class OpenPypeDeadlinePlugin(DeadlinePlugin): "Scanning for compatible requested " f"version {requested_version}")) dir_list = self.GetConfigEntry("OpenPypeInstallationDirs") - install_dir = DirectoryUtils.SearchDirectoryList(dir_list) - if dir: - sub_dirs = [ - f.path for f in os.scandir(install_dir) - if f.is_dir() - ] - for subdir in sub_dirs: - version = self.get_openpype_version_from_path(subdir) - if not version: - continue - openpype_versions.append((version, subdir)) + for dir_list in dir_list.split(","): + install_dir = DirectoryUtils.SearchDirectoryList(dir_list) + if install_dir: + sub_dirs = [ + f.path for f in os.scandir(install_dir) + if f.is_dir() + ] + for subdir in sub_dirs: + version = self.get_openpype_version_from_path(subdir) + if not version: + continue + openpype_versions.append((version, subdir)) exe_list = self.GetConfigEntry("OpenPypeExecutable") exe = FileUtils.SearchFileList(exe_list) From 75dfd6c3f6b2e37cf9013a692df5385f9af7bddc Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 10 Feb 2023 15:19:56 +0000 Subject: [PATCH 0554/1271] Publish proxy representation. --- .../maya/plugins/load/load_arnold_standin.py | 6 ++-- .../publish/extract_arnold_scene_source.py | 32 ++++++------------- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index 6e5fe16bcd..66e8b69639 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -107,9 +107,7 @@ class ArnoldStandinLoader(load.LoaderPlugin): proxy_basename = ( basename_split[0] + "_proxy." + ".".join(basename_split[1:]) ) - proxy_path = "/".join( - [os.path.dirname(path), "resources", proxy_basename] - ) + proxy_path = "/".join([os.path.dirname(path), proxy_basename]) return proxy_basename, proxy_path def _setup_proxy(self, shape, path, namespace): @@ -136,7 +134,7 @@ class ArnoldStandinLoader(load.LoaderPlugin): ) cmds.setAttr( string_replace_operator + ".match", - "resources/" + proxy_basename, + proxy_basename, type="string" ) cmds.setAttr( diff --git a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py index 153a1a513e..924ac58c40 100644 --- a/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py +++ b/openpype/hosts/maya/plugins/publish/extract_arnold_scene_source.py @@ -1,5 +1,4 @@ import os -import copy from maya import cmds import arnold @@ -8,7 +7,6 @@ from openpype.pipeline import publish from openpype.hosts.maya.api.lib import ( maintained_selection, attribute_values, delete_after ) -from openpype.lib import StringTemplate class ExtractArnoldSceneSource(publish.Extractor): @@ -103,28 +101,16 @@ class ExtractArnoldSceneSource(publish.Extractor): instance.data["proxy"], attribute_data, kwargs ) - template_data = copy.deepcopy(instance.data["anatomyData"]) - template_data.update({"ext": "ass"}) - templates = instance.context.data["anatomy"].templates["publish"] - published_filename_without_extension = StringTemplate( - templates["file"] - ).format(template_data).replace(".ass", "_proxy") - transfers = [] - for filename in filenames: - source = os.path.join(staging_dir, filename) - destination = os.path.join( - instance.data["resourcesDir"], - filename.replace( - filename.split(".")[0], - published_filename_without_extension - ) - ) - transfers.append((source, destination)) + representation = { + "name": "proxy", + "ext": "ass", + "files": filenames if len(filenames) > 1 else filenames[0], + "stagingDir": staging_dir, + "frameStart": kwargs["startFrame"], + "outputName": "proxy" + } - for source, destination in transfers: - self.log.debug("Transfer: {} > {}".format(source, destination)) - - instance.data["transfers"] = transfers + instance.data["representations"].append(representation) def _extract(self, nodes, attribute_data, kwargs): self.log.info("Writing: " + kwargs["filename"]) From e2565dc2d205e45e939d2ecef05933928520632a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 17:12:19 +0100 Subject: [PATCH 0555/1271] Nuke: adding solution for originalBasename frame temlate formating https://github.com/ynput/OpenPype/pull/4452#issuecomment-1426020567 --- openpype/hosts/nuke/plugins/load/load_clip.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index 565d777811..f9364172ea 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -220,8 +220,21 @@ class LoadClip(plugin.NukeLoader): dict: altered representation data """ representation = deepcopy(representation) - frame = representation["context"]["frame"] - representation["context"]["frame"] = "#" * len(str(frame)) + context = representation["context"] + template = representation["data"]["template"] + frame = context["frame"] + hashed_frame = "#" * len(str(frame)) + + if ( + "{originalBasename}" in template + and "frame" in context + ): + origin_basename = context["originalBasename"] + context["originalBasename"] = origin_basename.replace( + frame, hashed_frame + ) + + representation["context"]["frame"] = hashed_frame return representation def update(self, container, representation): From e7072008af1a316f47ed52ec34823d5a046710e0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 17:33:35 +0100 Subject: [PATCH 0556/1271] added 'family_filter' argument to 'get_subset_name' --- openpype/pipeline/create/subset_name.py | 39 ++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/openpype/pipeline/create/subset_name.py b/openpype/pipeline/create/subset_name.py index ed05dd6083..3f0692b46a 100644 --- a/openpype/pipeline/create/subset_name.py +++ b/openpype/pipeline/create/subset_name.py @@ -70,7 +70,8 @@ def get_subset_name( host_name=None, default_template=None, dynamic_data=None, - project_settings=None + project_settings=None, + family_filter=None, ): """Calculate subset name based on passed context and OpenPype settings. @@ -82,23 +83,35 @@ def get_subset_name( That's main reason why so many arguments are required to calculate subset name. + Option to pass family filter was added for special cases when creator or + automated publishing require special subset name template which would be + hard to maintain using its family value. + Why not just pass the right family? -> Family is also used as fill + value and for filtering of publish plugins. + + Todos: + Find better filtering options to avoid requirement of + argument 'family_filter'. + Args: family (str): Instance family. variant (str): In most of the cases it is user input during creation. task_name (str): Task name on which context is instance created. asset_doc (dict): Queried asset document with its tasks in data. Used to get task type. - project_name (str): Name of project on which is instance created. - Important for project settings that are loaded. - host_name (str): One of filtering criteria for template profile - filters. - default_template (str): Default template if any profile does not match - passed context. Constant 'DEFAULT_SUBSET_TEMPLATE' is used if - is not passed. - dynamic_data (dict): Dynamic data specific for a creator which creates - instance. - project_settings (Union[Dict[str, Any], None]): Prepared settings for - project. Settings are queried if not passed. + project_name (Optional[str]): Name of project on which is instance + created. Important for project settings that are loaded. + host_name (Optional[str]): One of filtering criteria for template + profile filters. + default_template (Optional[str]): Default template if any profile does + not match passed context. Constant 'DEFAULT_SUBSET_TEMPLATE' + is used if is not passed. + dynamic_data (Optional[Dict[str, Any]]): Dynamic data specific for + a creator which creates instance. + project_settings (Optional[Union[Dict[str, Any]]]): Prepared settings + for project. Settings are queried if not passed. + family_filter (Optional[str]): Use different family for subset template + filtering. Value of 'family' is used when not passed. """ if not family: @@ -119,7 +132,7 @@ def get_subset_name( template = get_subset_name_template( project_name, - family, + family_filter or family, task_name, task_type, host_name, From 0ab0f323f81aa403789fb4e3b0114c8791d3b8db Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 17:34:03 +0100 Subject: [PATCH 0557/1271] use new option of family filter in TVPaint creators --- openpype/hosts/tvpaint/api/plugin.py | 7 ++++--- openpype/hosts/tvpaint/plugins/create/create_render.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/tvpaint/api/plugin.py b/openpype/hosts/tvpaint/api/plugin.py index e6fc087665..c57baf7212 100644 --- a/openpype/hosts/tvpaint/api/plugin.py +++ b/openpype/hosts/tvpaint/api/plugin.py @@ -55,7 +55,7 @@ def _update_instances(creator, update_list): class TVPaintCreator(NewCreator): @property - def subset_template_family(self): + def subset_template_family_filter(self): return self.family def collect_instances(self): @@ -111,14 +111,15 @@ class TVPaintCreator(NewCreator): ) return get_subset_name( - self.subset_template_family, + self.family, variant, task_name, asset_doc, project_name, host_name, dynamic_data=dynamic_data, - project_settings=self.project_settings + project_settings=self.project_settings, + family_filter=self.subset_template_family_filter ) def _store_new_instance(self, new_instance): diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 67337b77a3..4050bddd52 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -19,7 +19,7 @@ class CreateRenderlayer(TVPaintCreator): """Mark layer group as one instance.""" label = "Render Layer" family = "render" - subset_template_family = "renderLayer" + subset_template_family_filter = "renderLayer" identifier = "render.layer" icon = "fa.cube" @@ -167,7 +167,7 @@ class CreateRenderlayer(TVPaintCreator): class CreateRenderPass(TVPaintCreator): icon = "fa.cube" family = "render" - subset_template_family = "renderPass" + subset_template_family_filter = "renderPass" identifier = "render.pass" label = "Render Pass" From 8233b1ad8229ebc8388d453f08c3cdeec3196a9f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 17:51:29 +0100 Subject: [PATCH 0558/1271] publishing files with fixed versionData to fit originalBasename tempate --- .../publish/collect_otio_subset_resources.py | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/collect_otio_subset_resources.py b/openpype/plugins/publish/collect_otio_subset_resources.py index e72c12d9a9..537aef683c 100644 --- a/openpype/plugins/publish/collect_otio_subset_resources.py +++ b/openpype/plugins/publish/collect_otio_subset_resources.py @@ -14,16 +14,41 @@ from openpype.pipeline.editorial import ( range_from_frames, make_sequence_collection ) - +from openpype.pipeline.publish import ( + get_publish_template_name +) class CollectOtioSubsetResources(pyblish.api.InstancePlugin): """Get Resources for a subset version""" label = "Collect OTIO Subset Resources" - order = pyblish.api.CollectorOrder - 0.077 + order = pyblish.api.CollectorOrder + 0.0021 families = ["clip"] hosts = ["resolve", "hiero", "flame"] + def get_template_name(self, instance): + """Return anatomy template name to use for integration""" + + # Anatomy data is pre-filled by Collectors + context = instance.context + project_name = context.data["projectName"] + + # Task can be optional in anatomy data + host_name = context.data["hostName"] + family = instance.data["family"] + anatomy_data = instance.context.data["anatomyData"] + task_info = anatomy_data.get("task") or {} + + return get_publish_template_name( + project_name, + host_name, + family, + task_name=task_info.get("name"), + task_type=task_info.get("type"), + project_settings=context.data["project_settings"], + logger=self.log + ) + def process(self, instance): if "audio" in instance.data["family"]: @@ -35,6 +60,13 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): if not instance.data.get("versionData"): instance.data["versionData"] = {} + template_name = self.get_template_name(instance) + anatomy = instance.context.data["anatomy"] + publish_template_category = anatomy.templates[template_name] + template = os.path.normpath(publish_template_category["path"]) + self.log.debug( + ">> template: {}".format(template)) + handle_start = instance.data["handleStart"] handle_end = instance.data["handleEnd"] @@ -84,6 +116,10 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): frame_start = instance.data["frameStart"] frame_end = frame_start + (media_out - media_in) + if "{originalDirname}" in template: + frame_start = media_in + frame_end = media_out + # add to version data start and end range data # for loader plugins to be correctly displayed and loaded instance.data["versionData"].update({ @@ -153,7 +189,6 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): repre = self._create_representation( frame_start, frame_end, collection=collection) - instance.data["originalBasename"] = collection.format("{head}") else: _trim = False dirname, filename = os.path.split(media_ref.target_url) @@ -168,8 +203,6 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): repre = self._create_representation( frame_start, frame_end, file=filename, trim=_trim) - instance.data["originalBasename"] = os.path.splitext(filename)[0] - instance.data["originalDirname"] = self.staging_dir if repre: From a1ab32b32961a7785e97fdd74024fd7fb7f261a6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 17:53:36 +0100 Subject: [PATCH 0559/1271] polishing fixes --- .../publish/collect_otio_subset_resources.py | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/openpype/plugins/publish/collect_otio_subset_resources.py b/openpype/plugins/publish/collect_otio_subset_resources.py index 537aef683c..5daa1b40fe 100644 --- a/openpype/plugins/publish/collect_otio_subset_resources.py +++ b/openpype/plugins/publish/collect_otio_subset_resources.py @@ -26,28 +26,6 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): families = ["clip"] hosts = ["resolve", "hiero", "flame"] - def get_template_name(self, instance): - """Return anatomy template name to use for integration""" - - # Anatomy data is pre-filled by Collectors - context = instance.context - project_name = context.data["projectName"] - - # Task can be optional in anatomy data - host_name = context.data["hostName"] - family = instance.data["family"] - anatomy_data = instance.context.data["anatomyData"] - task_info = anatomy_data.get("task") or {} - - return get_publish_template_name( - project_name, - host_name, - family, - task_name=task_info.get("name"), - task_type=task_info.get("type"), - project_settings=context.data["project_settings"], - logger=self.log - ) def process(self, instance): @@ -116,6 +94,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): frame_start = instance.data["frameStart"] frame_end = frame_start + (media_out - media_in) + # Fit start /end frame to media in /out if "{originalDirname}" in template: frame_start = media_in frame_end = media_out @@ -258,3 +237,26 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): if kwargs.get("trim") is True: representation_data["tags"] = ["trim"] return representation_data + + def get_template_name(self, instance): + """Return anatomy template name to use for integration""" + + # Anatomy data is pre-filled by Collectors + context = instance.context + project_name = context.data["projectName"] + + # Task can be optional in anatomy data + host_name = context.data["hostName"] + family = instance.data["family"] + anatomy_data = instance.context.data["anatomyData"] + task_info = anatomy_data.get("task") or {} + + return get_publish_template_name( + project_name, + host_name, + family, + task_name=task_info.get("name"), + task_type=task_info.get("type"), + project_settings=context.data["project_settings"], + logger=self.log + ) \ No newline at end of file From 6fbf7be560cc68704a2ed2933955cac10a80176e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 17:54:54 +0100 Subject: [PATCH 0560/1271] wrong template key name --- openpype/plugins/publish/collect_otio_subset_resources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_otio_subset_resources.py b/openpype/plugins/publish/collect_otio_subset_resources.py index 5daa1b40fe..dab52986da 100644 --- a/openpype/plugins/publish/collect_otio_subset_resources.py +++ b/openpype/plugins/publish/collect_otio_subset_resources.py @@ -95,7 +95,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): frame_end = frame_start + (media_out - media_in) # Fit start /end frame to media in /out - if "{originalDirname}" in template: + if "{originalBasename}" in template: frame_start = media_in frame_end = media_out From 3fb87723a0194ad2644b8233dbc8daa7d81c8f01 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 18:29:54 +0100 Subject: [PATCH 0561/1271] added basic docstring for render creators --- .../tvpaint/plugins/create/create_render.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 4050bddd52..92439f329f 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -1,3 +1,34 @@ +"""Render Layer and Passes creators. + +Render layer is main part which is represented by group in TVPaint. All TVPaint +layers marked with that group color are part of the render layer. To be more +specific about some parts of layer it is possible to create sub-sets of layer +which are named passes. Render pass consist of layers in same color group as +render layer but define more specific part. + +For example render layer could be 'Bob' which consist of 5 TVPaint layers. +- Bob has 'head' which consist of 2 TVPaint layers -> Render pass 'head' +- Bob has 'body' which consist of 1 TVPaint layer -> Render pass 'body' +- Bob has 'arm' which consist of 1 TVPaint layer -> Render pass 'arm' +- Last layer does not belong to render pass at all + +Bob will be rendered as 'beauty' of bob (all visible layers in group). +His head will be rendered too but without any other parts. The same for body +and arm. + +What is this good for? Compositing has more power how the renders are used. +Can do transforms on each render pass without need to modify a re-render them +using TVPaint. + +The workflow may hit issues when there are used other blending modes than +default 'color' blend more. In that case it is not recommended to use this +workflow at all as other blend modes may affect all layers in clip which can't +be done. + +Todos: + Add option to extract marked layers and passes as json output format for + AfterEffects. +""" from openpype.lib import ( prepare_template_data, EnumDef, From e426c2c4f0216a3d280f75f9f865de3d3d5ab8d8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 18:32:34 +0100 Subject: [PATCH 0562/1271] added option to mark render instance for review --- .../tvpaint/plugins/create/create_render.py | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 92439f329f..3a89608c7c 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -33,6 +33,7 @@ from openpype.lib import ( prepare_template_data, EnumDef, TextDef, + BoolDef, ) from openpype.pipeline.create import ( CreatedInstance, @@ -48,6 +49,7 @@ from openpype.hosts.tvpaint.api.lib import ( class CreateRenderlayer(TVPaintCreator): """Mark layer group as one instance.""" + label = "Render Layer" family = "render" subset_template_family_filter = "renderLayer" @@ -63,6 +65,7 @@ class CreateRenderlayer(TVPaintCreator): # Settings render_pass = "beauty" + mark_for_review = True def get_dynamic_data( self, variant, task_name, asset_doc, project_name, host_name, instance @@ -116,7 +119,12 @@ class CreateRenderlayer(TVPaintCreator): self.log.debug(f"Selected group id is \"{group_id}\".") if "creator_attributes" not in instance_data: instance_data["creator_attributes"] = {} - instance_data["creator_attributes"]["group_id"] = group_id + creator_attributes = instance_data["creator_attributes"] + mark_for_review = pre_create_data.get("mark_for_review") + if mark_for_review is None: + mark_for_review = self.mark_for_review + creator_attributes["group_id"] = group_id + creator_attributes["mark_for_review"] = mark_for_review self.log.info(f"Subset name is {subset_name}") new_instance = CreatedInstance( @@ -174,6 +182,11 @@ class CreateRenderlayer(TVPaintCreator): "group_name", label="New group name", placeholder="< Keep unchanged >" + ), + BoolDef( + "mark_for_review", + label="Review", + default=self.mark_for_review ) ] @@ -191,6 +204,11 @@ class CreateRenderlayer(TVPaintCreator): "group_id", label="Group", items=groups_enum + ), + BoolDef( + "mark_for_review", + label="Review", + default=self.mark_for_review ) ] @@ -204,6 +222,9 @@ class CreateRenderPass(TVPaintCreator): order = CreateRenderlayer.order + 10 + # Settings + mark_for_review = True + def get_dynamic_data( self, variant, task_name, asset_doc, project_name, host_name, instance ): @@ -269,6 +290,18 @@ class CreateRenderPass(TVPaintCreator): ) self.log.info(f"New subset name is \"{new_subset_name}\".") instance_data["layer_names"] = list(selected_layer_names) + if "creator_attributes" not in instance_data: + instance_data["creator_attribtues"] = {} + + creator_attributes = instance_data["creator_attribtues"] + mark_for_review = pre_create_data.get("mark_for_review") + if mark_for_review is None: + mark_for_review = self.mark_for_review + creator_attributes["mark_for_review"] = mark_for_review + creator_attributes["render_layer_instance_id"] = ( + render_layer_instance_id + ) + new_instance = CreatedInstance( self.family, subset_name, @@ -321,7 +354,7 @@ class CreateRenderPass(TVPaintCreator): def get_pre_create_attr_defs(self): render_layers = [] for instance in self.create_context.instances: - if instance.creator_identifier == "render.layer": + if instance.creator_identifier == CreateRenderlayer.identifier: render_layers.append({ "value": instance.id, "label": instance.label @@ -335,6 +368,13 @@ class CreateRenderPass(TVPaintCreator): "render_layer_instance_id", label="Render Layer", items=render_layers + ), + BoolDef( + "mark_for_review", + label="Review", + default=self.mark_for_review ) ] + def get_instance_attr_defs(self): + return self.get_pre_create_attr_defs() From 4a53dce92ffe996900d2914c58ceaae0caedcb30 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Feb 2023 18:32:59 +0100 Subject: [PATCH 0563/1271] render layer creator changes group ids of render pass layers on save --- .../tvpaint/plugins/create/create_render.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 3a89608c7c..e5d3fa1a59 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -29,6 +29,9 @@ Todos: Add option to extract marked layers and passes as json output format for AfterEffects. """ + +import collections + from openpype.lib import ( prepare_template_data, EnumDef, @@ -212,6 +215,51 @@ class CreateRenderlayer(TVPaintCreator): ) ] + def update_instances(self, update_list): + self._update_renderpass_groups() + + super().update_instances(update_list) + + def _update_renderpass_groups(self): + render_layer_instances = {} + render_pass_instances = collections.defaultdict(list) + + for instance in self.create_context.instances: + if instance.creator_identifier == CreateRenderPass.identifier: + render_layer_id = ( + instance["creator_attributes"]["render_layer_instance_id"] + ) + render_pass_instances[render_layer_id].append(instance) + elif instance.creator_identifier == self.identifier: + render_layer_instances[instance.id] = instance + + if not render_pass_instances or not render_layer_instances: + return + + layers_data = get_layers_data() + layers_by_name = collections.defaultdict(list) + for layer in layers_data: + layers_by_name[layer["name"]].append(layer) + + george_lines = [] + for render_layer_id, instances in render_pass_instances.items(): + render_layer_inst = render_layer_instances.get(render_layer_id) + if render_layer_inst is None: + continue + group_id = render_layer_inst["creator_attributes"]["group_id"] + layer_names = set() + for instance in instances: + layer_names |= set(instance["layer_names"]) + + for layer_name in layer_names: + george_lines.extend( + f"tv_layercolor \"set\" {layer['layer_id']} {group_id}" + for layer in layers_by_name[layer_name] + if layer["group_id"] != group_id + ) + if george_lines: + execute_george_through_file("\n".join(george_lines)) + class CreateRenderPass(TVPaintCreator): icon = "fa.cube" From 4c98fe735f7151dbd5ba9d5c59879d0cd47f886b Mon Sep 17 00:00:00 2001 From: Ynbot Date: Sat, 11 Feb 2023 03:27:14 +0000 Subject: [PATCH 0564/1271] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 61339fb3dd..8dfd638414 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.1-nightly.4" +__version__ = "3.15.1-nightly.5" From c537999ffb6de7e9edfd14f226b44c521c809512 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 13 Feb 2023 14:43:08 +0800 Subject: [PATCH 0565/1271] correct the renderer name for redshift and update arnold render product --- openpype/hosts/max/api/lib_renderproducts.py | 36 +++++++++++++++++++- openpype/hosts/max/api/lib_rendersettings.py | 22 ++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index e3cccff982..c6432412bf 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -47,7 +47,7 @@ class RenderProducts(object): if ( renderer == "ART_Renderer" or - renderer == "Redshift Renderer" or + renderer == "Redshift_Renderer" or renderer == "V_Ray_6_Hotfix_3" or renderer == "V_Ray_GPU_6_Hotfix_3" or renderer == "Default_Scanline_Renderer" or @@ -59,11 +59,20 @@ class RenderProducts(object): img_fmt) for render_elem in render_elem_list: full_render_list.append(render_elem) + return full_render_list if renderer == "Arnold": + aov_list = self.arnold_render_product(output_file, + startFrame, + endFrame, + img_fmt) + if aov_list: + for aov in aov_list: + full_render_list.append(aov) return full_render_list + def beauty_render_product(self, folder, startFrame, endFrame, fmt): # get the beauty beauty_frame_range = list() @@ -78,6 +87,31 @@ class RenderProducts(object): return beauty_frame_range # TODO: Get the arnold render product + def arnold_render_product(self, folder, startFrame, endFrame, fmt): + """Get all the Arnold AOVs""" + aovs = list() + + amw = rt.MaxtoAOps.AOVsManagerWindow() + aov_mgr = rt.renderers.current.AOVManager + # Check if there is any aov group set in AOV manager + aov_group_num = len(aov_mgr.drivers) + if aov_group_num < 1: + return + for i in range(aov_group_num): + # get the specific AOV group + for aov in aov_mgr.drivers[i].aov_list: + for f in range(startFrame, endFrame): + render_element = "{0}_{1}.{2}.{3}".format(folder, + str(aov.name), + str(f), + fmt) + render_element = render_element.replace("\\", "/") + aovs.append(render_element) + # close the AOVs manager window + amw.close() + + return aovs + def render_elements_product(self, folder, startFrame, endFrame, fmt): """Get all the render element output files. """ render_dirname = list() diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 2324d743eb..bc9b02bc77 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -87,7 +87,7 @@ class RenderSettings(object): if ( renderer == "ART_Renderer" or - renderer == "Redshift Renderer" or + renderer == "Redshift_Renderer" or renderer == "V_Ray_6_Hotfix_3" or renderer == "V_Ray_GPU_6_Hotfix_3" or renderer == "Default_Scanline_Renderer" or @@ -104,9 +104,25 @@ class RenderSettings(object): render_camera = rt.viewport.GetCamera() arv.setOption("Camera", str(render_camera)) - aovmgr = rt.renderers.current.AOVManager - aovmgr.drivers = "#()" + # TODO: add AOVs and extension + img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa + setup_cmd = ( + f""" + amw = MaxtoAOps.AOVsManagerWindow() + amw.close() + aovmgr = renderers.current.AOVManager + aovmgr.drivers = #() + img_fmt = "{img_fmt}" + if img_fmt == "png" then driver = ArnoldPNGDriver() + if img_fmt == "jpg" then driver = ArnoldJPEGDriver() + if img_fmt == "exr" then driver = ArnoldEXRDriver() + if img_fmt == "tif" then driver = ArnoldTIFFDriver() + if img_fmt == "tiff" then driver = ArnoldTIFFDriver() + append aovmgr.drivers driver + aovmgr.drivers[1].aov_list = #() + """) + rt.execute(setup_cmd) arv.close() def render_element_layer(self, dir, width, height, ext): From af8278f67ca073386fbcc8dd8f03efd0226366db Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 13 Feb 2023 14:44:24 +0800 Subject: [PATCH 0566/1271] hound fix --- openpype/hosts/max/api/lib_renderproducts.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index c6432412bf..b54e2513e1 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -72,7 +72,6 @@ class RenderProducts(object): full_render_list.append(aov) return full_render_list - def beauty_render_product(self, folder, startFrame, endFrame, fmt): # get the beauty beauty_frame_range = list() From 61024a476a36ec0dac03dccfa0bbf3a76edabf8d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 13 Feb 2023 15:02:51 +0800 Subject: [PATCH 0567/1271] chucksize in create_render --- openpype/hosts/max/plugins/create/create_render.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/max/plugins/create/create_render.py b/openpype/hosts/max/plugins/create/create_render.py index 76c10ca4a9..699fc200d3 100644 --- a/openpype/hosts/max/plugins/create/create_render.py +++ b/openpype/hosts/max/plugins/create/create_render.py @@ -20,6 +20,8 @@ class CreateRender(plugin.MaxCreator): pre_create_data) # type: CreatedInstance container_name = instance.data.get("instance_node") container = rt.getNodeByName(container_name) + # chuckSize for submitting render + instance_data["chunkSize"] = 10 # TODO: Disable "Add to Containers?" Panel # parent the selected cameras into the container for obj in sel_obj: From b284ad43c04cf05bb604603d211194526f80971c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 13 Feb 2023 17:44:17 +0800 Subject: [PATCH 0568/1271] not include py script from the unrelated host --- openpype/hosts/maya/plugins/publish/collect_render.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index c5fce219fa..0683848c49 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -184,7 +184,7 @@ class CollectMayaRender(pyblish.api.ContextPlugin): self.log.info("multipart: {}".format( multipart)) assert exp_files, "no file names were generated, this is bug" - + self.log.info(exp_files) # if we want to attach render to subset, check if we have AOV's # in expectedFiles. If so, raise error as we cannot attach AOV # (considered to be subset on its own) to another subset @@ -319,6 +319,7 @@ class CollectMayaRender(pyblish.api.ContextPlugin): "renderSetupIncludeLights" ) } + # Collect Deadline url if Deadline module is enabled deadline_settings = ( context.data["system_settings"]["modules"]["deadline"] From 6236922e81b40a7ca0a7ed4d325aa97a0de9361e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 13 Feb 2023 17:45:10 +0800 Subject: [PATCH 0569/1271] not include py script from the unrelated host --- openpype/hosts/maya/plugins/publish/collect_render.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index 0683848c49..b1ad3ca58e 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -185,6 +185,7 @@ class CollectMayaRender(pyblish.api.ContextPlugin): multipart)) assert exp_files, "no file names were generated, this is bug" self.log.info(exp_files) + # if we want to attach render to subset, check if we have AOV's # in expectedFiles. If so, raise error as we cannot attach AOV # (considered to be subset on its own) to another subset From f56e7bcbf879072c88fcf74944d41cf4366c4e79 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 13 Feb 2023 10:51:21 +0100 Subject: [PATCH 0570/1271] removed deprecated functions from openpype lib --- openpype/lib/__init__.py | 43 -- openpype/lib/anatomy.py | 38 -- openpype/lib/avalon_context.py | 431 +----------------- openpype/lib/plugin_tools.py | 119 ----- .../tests/test_lib_restructuralization.py | 6 - openpype/tests/test_pyblish_filter.py | 6 +- 6 files changed, 7 insertions(+), 636 deletions(-) delete mode 100644 openpype/lib/anatomy.py diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index b5fb955a84..9eb7724a60 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -82,9 +82,6 @@ from .mongo import ( validate_mongo_connection, OpenPypeMongoConnection ) -from .anatomy import ( - Anatomy -) from .dateutils import ( get_datetime_data, @@ -119,36 +116,19 @@ from .transcoding import ( ) from .avalon_context import ( CURRENT_DOC_SCHEMAS, - PROJECT_NAME_ALLOWED_SYMBOLS, - PROJECT_NAME_REGEX, create_project, - is_latest, - any_outdated, - get_asset, - get_linked_assets, - get_latest_version, - get_system_general_anatomy_data, get_workfile_template_key, get_workfile_template_key_from_context, - get_workdir_data, - get_workdir, - get_workdir_with_workdir_data, get_last_workfile_with_version, get_last_workfile, - create_workfile_doc, - save_workfile_data_to_doc, - get_workfile_doc, - BuildWorkfile, get_creator_by_name, get_custom_workfile_template, - change_timer_to_current_context, - get_custom_workfile_template_by_context, get_custom_workfile_template_by_string_context, get_custom_workfile_template @@ -186,8 +166,6 @@ from .plugin_tools import ( get_subset_name, get_subset_name_with_asset_doc, prepare_template_data, - filter_pyblish_plugins, - set_plugin_attributes_from_settings, source_hash, ) @@ -278,34 +256,17 @@ __all__ = [ "convert_ffprobe_fps_to_float", "CURRENT_DOC_SCHEMAS", - "PROJECT_NAME_ALLOWED_SYMBOLS", - "PROJECT_NAME_REGEX", "create_project", - "is_latest", - "any_outdated", - "get_asset", - "get_linked_assets", - "get_latest_version", - "get_system_general_anatomy_data", "get_workfile_template_key", "get_workfile_template_key_from_context", - "get_workdir_data", - "get_workdir", - "get_workdir_with_workdir_data", "get_last_workfile_with_version", "get_last_workfile", - "create_workfile_doc", - "save_workfile_data_to_doc", - "get_workfile_doc", - "BuildWorkfile", "get_creator_by_name", - "change_timer_to_current_context", - "get_custom_workfile_template_by_context", "get_custom_workfile_template_by_string_context", "get_custom_workfile_template", @@ -338,8 +299,6 @@ __all__ = [ "TaskNotSetError", "get_subset_name", "get_subset_name_with_asset_doc", - "filter_pyblish_plugins", - "set_plugin_attributes_from_settings", "source_hash", "format_file_size", @@ -358,8 +317,6 @@ __all__ = [ "terminal", - "Anatomy", - "get_datetime_data", "get_formatted_current_time", diff --git a/openpype/lib/anatomy.py b/openpype/lib/anatomy.py deleted file mode 100644 index 6d339f058f..0000000000 --- a/openpype/lib/anatomy.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Code related to project Anatomy was moved -to 'openpype.pipeline.anatomy' please change your imports as soon as -possible. File will be probably removed in OpenPype 3.14.* -""" - -import warnings -import functools - - -class AnatomyDeprecatedWarning(DeprecationWarning): - pass - - -def anatomy_deprecated(func): - """Mark functions as deprecated. - - It will result in a warning being emitted when the function is used. - """ - - @functools.wraps(func) - def new_func(*args, **kwargs): - warnings.simplefilter("always", AnatomyDeprecatedWarning) - warnings.warn( - ( - "Deprecated import of 'Anatomy'." - " Class was moved to 'openpype.pipeline.anatomy'." - " Please change your imports of Anatomy in codebase." - ), - category=AnatomyDeprecatedWarning - ) - return func(*args, **kwargs) - return new_func - - -@anatomy_deprecated -def Anatomy(*args, **kwargs): - from openpype.pipeline.anatomy import Anatomy - return Anatomy(*args, **kwargs) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 12f4a5198b..a9ae27cb79 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -1,6 +1,5 @@ """Should be used only inside of hosts.""" -import os -import copy + import platform import logging import functools @@ -10,17 +9,12 @@ import six from openpype.client import ( get_project, - get_assets, get_asset_by_name, - get_last_version_by_subset_name, - get_workfile_info, ) from openpype.client.operations import ( CURRENT_ASSET_DOC_SCHEMA, CURRENT_PROJECT_SCHEMA, CURRENT_PROJECT_CONFIG_SCHEMA, - PROJECT_NAME_ALLOWED_SYMBOLS, - PROJECT_NAME_REGEX, ) from .profiles_filtering import filter_profiles from .path_templates import StringTemplate @@ -128,70 +122,6 @@ def with_pipeline_io(func): return wrapped -@deprecated("openpype.pipeline.context_tools.is_representation_from_latest") -def is_latest(representation): - """Return whether the representation is from latest version - - Args: - representation (dict): The representation document from the database. - - Returns: - bool: Whether the representation is of latest version. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.context_tools import is_representation_from_latest - - return is_representation_from_latest(representation) - - -@deprecated("openpype.pipeline.load.any_outdated_containers") -def any_outdated(): - """Return whether the current scene has any outdated content. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.load import any_outdated_containers - - return any_outdated_containers() - - -@deprecated("openpype.pipeline.context_tools.get_current_project_asset") -def get_asset(asset_name=None): - """ Returning asset document from database by its name. - - Doesn't count with duplicities on asset names! - - Args: - asset_name (str) - - Returns: - (MongoDB document) - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.context_tools import get_current_project_asset - - return get_current_project_asset(asset_name=asset_name) - - -@deprecated("openpype.pipeline.template_data.get_general_template_data") -def get_system_general_anatomy_data(system_settings=None): - """ - Deprecated: - Function will be removed after release version 3.15.* - """ - from openpype.pipeline.template_data import get_general_template_data - - return get_general_template_data(system_settings) - - @deprecated("openpype.client.get_linked_asset_ids") def get_linked_asset_ids(asset_doc): """Return linked asset ids for `asset_doc` from DB @@ -214,66 +144,6 @@ def get_linked_asset_ids(asset_doc): return get_linked_asset_ids(project_name, asset_doc=asset_doc) -@deprecated("openpype.client.get_linked_assets") -def get_linked_assets(asset_doc): - """Return linked assets for `asset_doc` from DB - - Args: - asset_doc (dict): Asset document from DB - - Returns: - (list) Asset documents of input links for passed asset doc. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline import legacy_io - from openpype.client import get_linked_assets - - project_name = legacy_io.active_project() - - return get_linked_assets(project_name, asset_doc=asset_doc) - - -@deprecated("openpype.client.get_last_version_by_subset_name") -def get_latest_version(asset_name, subset_name, dbcon=None, project_name=None): - """Retrieve latest version from `asset_name`, and `subset_name`. - - Do not use if you want to query more than 5 latest versions as this method - query 3 times to mongo for each call. For those cases is better to use - more efficient way, e.g. with help of aggregations. - - Args: - asset_name (str): Name of asset. - subset_name (str): Name of subset. - dbcon (AvalonMongoDB, optional): Avalon Mongo connection with Session. - project_name (str, optional): Find latest version in specific project. - - Returns: - None: If asset, subset or version were not found. - dict: Last version document for entered. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - if not project_name: - if not dbcon: - from openpype.pipeline import legacy_io - - log.debug("Using `legacy_io` for query.") - dbcon = legacy_io - # Make sure is installed - dbcon.install() - - project_name = dbcon.active_project() - - return get_last_version_by_subset_name( - project_name, subset_name, asset_name=asset_name - ) - - @deprecated( "openpype.pipeline.workfile.get_workfile_template_key_from_context") def get_workfile_template_key_from_context( @@ -361,142 +231,6 @@ def get_workfile_template_key( ) -@deprecated("openpype.pipeline.template_data.get_template_data") -def get_workdir_data(project_doc, asset_doc, task_name, host_name): - """Prepare data for workdir template filling from entered information. - - Args: - project_doc (dict): Mongo document of project from MongoDB. - asset_doc (dict): Mongo document of asset from MongoDB. - task_name (str): Task name for which are workdir data preapred. - host_name (str): Host which is used to workdir. This is required - because workdir template may contain `{app}` key. - - Returns: - dict: Data prepared for filling workdir template. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.template_data import get_template_data - - return get_template_data( - project_doc, asset_doc, task_name, host_name - ) - - -@deprecated("openpype.pipeline.workfile.get_workdir_with_workdir_data") -def get_workdir_with_workdir_data( - workdir_data, anatomy=None, project_name=None, template_key=None -): - """Fill workdir path from entered data and project's anatomy. - - It is possible to pass only project's name instead of project's anatomy but - one of them **must** be entered. It is preferred to enter anatomy if is - available as initialization of a new Anatomy object may be time consuming. - - Args: - workdir_data (dict): Data to fill workdir template. - anatomy (Anatomy): Anatomy object for specific project. Optional if - `project_name` is entered. - project_name (str): Project's name. Optional if `anatomy` is entered - otherwise Anatomy object is created with using the project name. - template_key (str): Key of work templates in anatomy templates. If not - passed `get_workfile_template_key_from_context` is used to get it. - dbcon(AvalonMongoDB): Mongo connection. Required only if 'template_key' - and 'project_name' are not passed. - - Returns: - TemplateResult: Workdir path. - - Raises: - ValueError: When both `anatomy` and `project_name` are set to None. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - if not anatomy and not project_name: - raise ValueError(( - "Missing required arguments one of `project_name` or `anatomy`" - " must be entered." - )) - - if not project_name: - project_name = anatomy.project_name - - from openpype.pipeline.workfile import get_workdir_with_workdir_data - - return get_workdir_with_workdir_data( - workdir_data, project_name, anatomy, template_key - ) - - -@deprecated("openpype.pipeline.workfile.get_workdir_with_workdir_data") -def get_workdir( - project_doc, - asset_doc, - task_name, - host_name, - anatomy=None, - template_key=None -): - """Fill workdir path from entered data and project's anatomy. - - Args: - project_doc (dict): Mongo document of project from MongoDB. - asset_doc (dict): Mongo document of asset from MongoDB. - task_name (str): Task name for which are workdir data preapred. - host_name (str): Host which is used to workdir. This is required - because workdir template may contain `{app}` key. In `Session` - is stored under `AVALON_APP` key. - anatomy (Anatomy): Optional argument. Anatomy object is created using - project name from `project_doc`. It is preferred to pass this - argument as initialization of a new Anatomy object may be time - consuming. - template_key (str): Key of work templates in anatomy templates. Default - value is defined in `get_workdir_with_workdir_data`. - - Returns: - TemplateResult: Workdir path. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.workfile import get_workdir - # Output is TemplateResult object which contain useful data - return get_workdir( - project_doc, - asset_doc, - task_name, - host_name, - anatomy, - template_key - ) - - -@deprecated("openpype.pipeline.context_tools.get_template_data_from_session") -def template_data_from_session(session=None): - """ Return dictionary with template from session keys. - - Args: - session (dict, Optional): The Session to use. If not provided use the - currently active global Session. - - Returns: - dict: All available data from session. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.context_tools import get_template_data_from_session - - return get_template_data_from_session(session) - - @deprecated("openpype.pipeline.context_tools.compute_session_changes") def compute_session_changes( session, task=None, asset=None, app=None, template_key=None @@ -588,133 +322,6 @@ def update_current_task(task=None, asset=None, app=None, template_key=None): return change_current_context(asset, task, template_key) -@deprecated("openpype.client.get_workfile_info") -def get_workfile_doc(asset_id, task_name, filename, dbcon=None): - """Return workfile document for entered context. - - Do not use this method to get more than one document. In that cases use - custom query as this will return documents from database one by one. - - Args: - asset_id (ObjectId): Mongo ID of an asset under which workfile belongs. - task_name (str): Name of task under which the workfile belongs. - filename (str): Name of a workfile. - dbcon (AvalonMongoDB): Optionally enter avalon AvalonMongoDB object and - `legacy_io` is used if not entered. - - Returns: - dict: Workfile document or None. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - # Use legacy_io if dbcon is not entered - if not dbcon: - from openpype.pipeline import legacy_io - dbcon = legacy_io - - project_name = dbcon.active_project() - return get_workfile_info(project_name, asset_id, task_name, filename) - - -@deprecated -def create_workfile_doc(asset_doc, task_name, filename, workdir, dbcon=None): - """Creates or replace workfile document in mongo. - - Do not use this method to update data. This method will remove all - additional data from existing document. - - Args: - asset_doc (dict): Document of asset under which workfile belongs. - task_name (str): Name of task for which is workfile related to. - filename (str): Filename of workfile. - workdir (str): Path to directory where `filename` is located. - dbcon (AvalonMongoDB): Optionally enter avalon AvalonMongoDB object and - `legacy_io` is used if not entered. - """ - - from openpype.pipeline import Anatomy - from openpype.pipeline.template_data import get_template_data - - # Use legacy_io if dbcon is not entered - if not dbcon: - from openpype.pipeline import legacy_io - dbcon = legacy_io - - # Filter of workfile document - doc_filter = { - "type": "workfile", - "parent": asset_doc["_id"], - "task_name": task_name, - "filename": filename - } - # Document data are copy of filter - doc_data = copy.deepcopy(doc_filter) - - # Prepare project for workdir data - project_name = dbcon.active_project() - project_doc = get_project(project_name) - workdir_data = get_template_data( - project_doc, asset_doc, task_name, dbcon.Session["AVALON_APP"] - ) - # Prepare anatomy - anatomy = Anatomy(project_name) - # Get workdir path (result is anatomy.TemplateResult) - template_workdir = get_workdir_with_workdir_data( - workdir_data, anatomy - ) - template_workdir_path = str(template_workdir).replace("\\", "/") - - # Replace slashses in workdir path where workfile is located - mod_workdir = workdir.replace("\\", "/") - - # Replace workdir from templates with rootless workdir - rootles_workdir = mod_workdir.replace( - template_workdir_path, - template_workdir.rootless.replace("\\", "/") - ) - - doc_data["schema"] = "pype:workfile-1.0" - doc_data["files"] = ["/".join([rootles_workdir, filename])] - doc_data["data"] = {} - - dbcon.replace_one( - doc_filter, - doc_data, - upsert=True - ) - - -@deprecated -def save_workfile_data_to_doc(workfile_doc, data, dbcon=None): - if not workfile_doc: - # TODO add log message - return - - if not data: - return - - # Use legacy_io if dbcon is not entered - if not dbcon: - from openpype.pipeline import legacy_io - dbcon = legacy_io - - # Convert data to mongo modification keys/values - # - this is naive implementation which does not expect nested - # dictionaries - set_data = {} - for key, value in data.items(): - new_key = "data.{}".format(key) - set_data[new_key] = value - - # Update workfile document with data - dbcon.update_one( - {"_id": workfile_doc["_id"]}, - {"$set": set_data} - ) - - @deprecated("openpype.pipeline.workfile.BuildWorkfile") def BuildWorkfile(): """Build workfile class was moved to workfile pipeline. @@ -747,38 +354,6 @@ def get_creator_by_name(creator_name, case_sensitive=False): return get_legacy_creator_by_name(creator_name, case_sensitive) -@deprecated -def change_timer_to_current_context(): - """Called after context change to change timers. - - Deprecated: - This method is specific for TimersManager module so please use the - functionality from there. Function will be removed after release - version 3.15.* - """ - - from openpype.pipeline import legacy_io - - webserver_url = os.environ.get("OPENPYPE_WEBSERVER_URL") - if not webserver_url: - log.warning("Couldn't find webserver url") - return - - rest_api_url = "{}/timers_manager/start_timer".format(webserver_url) - try: - import requests - except Exception: - log.warning("Couldn't start timer") - return - data = { - "project_name": legacy_io.Session["AVALON_PROJECT"], - "asset_name": legacy_io.Session["AVALON_ASSET"], - "task_name": legacy_io.Session["AVALON_TASK"] - } - - requests.post(rest_api_url, json=data) - - def _get_task_context_data_for_anatomy( project_doc, asset_doc, task_name, anatomy=None ): @@ -800,6 +375,8 @@ def _get_task_context_data_for_anatomy( dict: With Anatomy context data. """ + from openpype.pipeline.template_data import get_general_template_data + if anatomy is None: from openpype.pipeline import Anatomy anatomy = Anatomy(project_doc["name"]) @@ -840,7 +417,7 @@ def _get_task_context_data_for_anatomy( } } - system_general_data = get_system_general_anatomy_data() + system_general_data = get_general_template_data() data.update(system_general_data) return data diff --git a/openpype/lib/plugin_tools.py b/openpype/lib/plugin_tools.py index 1e157dfbfd..10fd3940b8 100644 --- a/openpype/lib/plugin_tools.py +++ b/openpype/lib/plugin_tools.py @@ -8,7 +8,6 @@ import warnings import functools from openpype.client import get_asset_by_id -from openpype.settings import get_project_settings log = logging.getLogger(__name__) @@ -101,8 +100,6 @@ def get_subset_name_with_asset_doc( is not passed. dynamic_data (dict): Dynamic data specific for a creator which creates instance. - dbcon (AvalonMongoDB): Mongo connection to be able query asset document - if 'asset_doc' is not passed. """ from openpype.pipeline.create import get_subset_name @@ -202,122 +199,6 @@ def prepare_template_data(fill_pairs): return fill_data -@deprecated("openpype.pipeline.publish.lib.filter_pyblish_plugins") -def filter_pyblish_plugins(plugins): - """Filter pyblish plugins by presets. - - This servers as plugin filter / modifier for pyblish. It will load plugin - definitions from presets and filter those needed to be excluded. - - Args: - plugins (dict): Dictionary of plugins produced by :mod:`pyblish-base` - `discover()` method. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - from openpype.pipeline.publish.lib import filter_pyblish_plugins - - filter_pyblish_plugins(plugins) - - -@deprecated -def set_plugin_attributes_from_settings( - plugins, superclass, host_name=None, project_name=None -): - """Change attribute values on Avalon plugins by project settings. - - This function should be used only in host context. Modify - behavior of plugins. - - Args: - plugins (list): Plugins discovered by origin avalon discover method. - superclass (object): Superclass of plugin type (e.g. Cretor, Loader). - host_name (str): Name of host for which plugins are loaded and from. - Value from environment `AVALON_APP` is used if not entered. - project_name (str): Name of project for which settings will be loaded. - Value from environment `AVALON_PROJECT` is used if not entered. - - Deprecated: - Function will be removed after release version 3.15.* - """ - - # Function is not used anymore - from openpype.pipeline import LegacyCreator, LoaderPlugin - - # determine host application to use for finding presets - if host_name is None: - host_name = os.environ.get("AVALON_APP") - - if project_name is None: - project_name = os.environ.get("AVALON_PROJECT") - - # map plugin superclass to preset json. Currently supported is load and - # create (LoaderPlugin and LegacyCreator) - plugin_type = None - if superclass is LoaderPlugin or issubclass(superclass, LoaderPlugin): - plugin_type = "load" - elif superclass is LegacyCreator or issubclass(superclass, LegacyCreator): - plugin_type = "create" - - if not host_name or not project_name or plugin_type is None: - msg = "Skipped attributes override from settings." - if not host_name: - msg += " Host name is not defined." - - if not project_name: - msg += " Project name is not defined." - - if plugin_type is None: - msg += " Plugin type is unsupported for class {}.".format( - superclass.__name__ - ) - - print(msg) - return - - print(">>> Finding presets for {}:{} ...".format(host_name, plugin_type)) - - project_settings = get_project_settings(project_name) - plugin_type_settings = ( - project_settings - .get(host_name, {}) - .get(plugin_type, {}) - ) - global_type_settings = ( - project_settings - .get("global", {}) - .get(plugin_type, {}) - ) - if not global_type_settings and not plugin_type_settings: - return - - for plugin in plugins: - plugin_name = plugin.__name__ - - plugin_settings = None - # Look for plugin settings in host specific settings - if plugin_name in plugin_type_settings: - plugin_settings = plugin_type_settings[plugin_name] - - # Look for plugin settings in global settings - elif plugin_name in global_type_settings: - plugin_settings = global_type_settings[plugin_name] - - if not plugin_settings: - continue - - print(">>> We have preset for {}".format(plugin_name)) - for option, value in plugin_settings.items(): - if option == "enabled" and value is False: - setattr(plugin, "active", False) - print(" - is disabled by preset") - else: - setattr(plugin, option, value) - print(" - setting `{}`: `{}`".format(option, value)) - - def source_hash(filepath, *args): """Generate simple identifier for a source file. This is used to identify whether a source file has previously been diff --git a/openpype/tests/test_lib_restructuralization.py b/openpype/tests/test_lib_restructuralization.py index c8952e5a1c..669706d470 100644 --- a/openpype/tests/test_lib_restructuralization.py +++ b/openpype/tests/test_lib_restructuralization.py @@ -5,11 +5,9 @@ def test_backward_compatibility(printer): printer("Test if imports still work") try: - from openpype.lib import filter_pyblish_plugins from openpype.lib import execute_hook from openpype.lib import PypeHook - from openpype.lib import get_latest_version from openpype.lib import ApplicationLaunchFailed from openpype.lib import get_ffmpeg_tool_path @@ -18,10 +16,6 @@ def test_backward_compatibility(printer): from openpype.lib import get_version_from_path from openpype.lib import version_up - from openpype.lib import is_latest - from openpype.lib import any_outdated - from openpype.lib import get_asset - from openpype.lib import get_linked_assets from openpype.lib import get_ffprobe_streams from openpype.hosts.fusion.lib import switch_item diff --git a/openpype/tests/test_pyblish_filter.py b/openpype/tests/test_pyblish_filter.py index ea23da26e4..b74784145f 100644 --- a/openpype/tests/test_pyblish_filter.py +++ b/openpype/tests/test_pyblish_filter.py @@ -1,9 +1,9 @@ -from . import lib +import os import pyblish.api import pyblish.util import pyblish.plugin -from openpype.lib import filter_pyblish_plugins -import os +from openpype.pipeline.publish.lib import filter_pyblish_plugins +from . import lib def test_pyblish_plugin_filter_modifier(printer, monkeypatch): From 09dff1629d734e4172cebc4ec9d1134ff36d0f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 13 Feb 2023 12:42:36 +0100 Subject: [PATCH 0571/1271] Update openpype/pipeline/tempdir.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- openpype/pipeline/tempdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py index 6a346f3342..7e1778539c 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/tempdir.py @@ -11,7 +11,7 @@ def create_custom_tempdir(project_name, anatomy=None): """ Create custom tempdir Template path formatting is supporting: - - optional key formating + - optional key formatting - available keys: - root[work | ] - project[name | code] From 8daa8059ccede7693a01a869810acfe0c0fd0cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 13 Feb 2023 12:42:45 +0100 Subject: [PATCH 0572/1271] Update openpype/pipeline/tempdir.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- openpype/pipeline/tempdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py index 7e1778539c..f26f988557 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/tempdir.py @@ -21,7 +21,7 @@ def create_custom_tempdir(project_name, anatomy=None): anatomy (openpype.pipeline.Anatomy)[optional]: Anatomy object Returns: - str | None: formated path or None + str | None: formatted path or None """ openpype_tempdir = os.getenv("OPENPYPE_TMPDIR") if not openpype_tempdir: From 198050959a4835b436d3fc7e7529f341ed870560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 13 Feb 2023 12:42:54 +0100 Subject: [PATCH 0573/1271] Update openpype/pipeline/tempdir.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- openpype/pipeline/tempdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py index f26f988557..4bb62f0afa 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/tempdir.py @@ -47,7 +47,7 @@ def create_custom_tempdir(project_name, anatomy=None): # path is absolute custom_tempdir = openpype_tempdir - # create he dir path if it doesnt exists + # create the dir path if it doesn't exists if not os.path.exists(custom_tempdir): try: # create it if it doesnt exists From 053a903662b026837f43308e26df18d049589df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 13 Feb 2023 12:43:21 +0100 Subject: [PATCH 0574/1271] Update openpype/pipeline/tempdir.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- openpype/pipeline/tempdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py index 4bb62f0afa..88f8296dcf 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/tempdir.py @@ -53,6 +53,6 @@ def create_custom_tempdir(project_name, anatomy=None): # create it if it doesnt exists os.makedirs(custom_tempdir) except IOError as error: - raise IOError("Path couldn't be created: {}".format(error)) + raise IOError("Path couldn't be created: {}".format(error)) from error return custom_tempdir From 7ea78fee7b0a7b59fdfbe830ea83d66f399350b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 13 Feb 2023 12:43:30 +0100 Subject: [PATCH 0575/1271] Update openpype/pipeline/tempdir.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- openpype/pipeline/tempdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py index 88f8296dcf..3f9384a7fd 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/tempdir.py @@ -50,7 +50,7 @@ def create_custom_tempdir(project_name, anatomy=None): # create the dir path if it doesn't exists if not os.path.exists(custom_tempdir): try: - # create it if it doesnt exists + # create it if it doesn't exists os.makedirs(custom_tempdir) except IOError as error: raise IOError("Path couldn't be created: {}".format(error)) from error From 859863129a033bcde335f4b3af441aecd991716b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 13 Feb 2023 12:43:39 +0100 Subject: [PATCH 0576/1271] Update openpype/pipeline/publish/lib.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- 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 cc7f5678f5..2b0d111412 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -620,7 +620,7 @@ def get_instance_staging_dir(instance): It also supports `OPENPYPE_TMPDIR`, so studio can define own temp shared repository per project or even per more granular context. - Template formating is supported also with optional keys. Folder is + Template formatting is supported also with optional keys. Folder is created in case it doesnt exists. Available anatomy formatting keys: From 1735d6cc74d75ae732fcd1b0d7832ccd8d89fb53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 13 Feb 2023 12:45:00 +0100 Subject: [PATCH 0577/1271] Update openpype/pipeline/publish/lib.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- 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 2b0d111412..27ab523352 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -628,7 +628,7 @@ def get_instance_staging_dir(instance): - project[name | code] Note: - Staging dir does not have to be necessarily in tempdir so be carefull + Staging dir does not have to be necessarily in tempdir so be careful about it's usage. Args: From abe803234ea73ebad6fa12b22932689daa527a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 13 Feb 2023 12:45:12 +0100 Subject: [PATCH 0578/1271] Update website/docs/admin_environment.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- website/docs/admin_environment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/admin_environment.md b/website/docs/admin_environment.md index 2cc558b530..1eb755b90b 100644 --- a/website/docs/admin_environment.md +++ b/website/docs/admin_environment.md @@ -9,8 +9,8 @@ import TabItem from '@theme/TabItem'; ## OPENPYPE_TMPDIR: - Custom staging dir directory - - Supports anatomy keys formating. ex `{root[work]}/{project[name]}/temp` - - supported formating keys: + - Supports anatomy keys formatting. ex `{root[work]}/{project[name]}/temp` + - supported formatting keys: - root[work] - project[name | code] From 3885f3cd7c502d04d0ec801cf62e4c047e2a2d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 13 Feb 2023 12:45:29 +0100 Subject: [PATCH 0579/1271] Update website/docs/admin_settings_system.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- website/docs/admin_settings_system.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/admin_settings_system.md b/website/docs/admin_settings_system.md index c39cac61f5..d61713ccd5 100644 --- a/website/docs/admin_settings_system.md +++ b/website/docs/admin_settings_system.md @@ -176,4 +176,4 @@ In the image before you can see that we set most of the environment variables in In this example MTOA will automatically will the `MAYA_VERSION`(which is set by Maya Application environment) and `MTOA_VERSION` into the `MTOA` variable. We then use the `MTOA` to set all the other variables needed for it to function within Maya. ![tools](assets/settings/tools_01.png) -All of the tools defined in here can then be assigned to projects. You can also change the tools versions on any project level all the way down to individual asset or shot overrides. So if you just need to upgrade you render plugin for a single shot, while not risking the incompatibilities on the rest of the project, it is possible. +All the tools defined in here can then be assigned to projects. You can also change the tools versions on any project level all the way down to individual asset or shot overrides. So if you just need to upgrade you render plugin for a single shot, while not risking the incompatibilities on the rest of the project, it is possible. From 9591d42b84aed7c3321cff5b01b718053593f58d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 13 Feb 2023 12:51:51 +0100 Subject: [PATCH 0580/1271] spell errors --- openpype/pipeline/publish/lib.py | 6 +++--- openpype/pipeline/tempdir.py | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 27ab523352..d0a9396a42 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -601,7 +601,7 @@ def context_plugin_should_run(plugin, context): Args: plugin (pyblish.api.Plugin): Plugin with filters. - context (pyblish.api.Context): Pyblish context with insances. + context (pyblish.api.Context): Pyblish context with instances. Returns: bool: Context plugin should run based on valid instances. @@ -621,7 +621,7 @@ def get_instance_staging_dir(instance): It also supports `OPENPYPE_TMPDIR`, so studio can define own temp shared repository per project or even per more granular context. Template formatting is supported also with optional keys. Folder is - created in case it doesnt exists. + created in case it doesn't exists. Available anatomy formatting keys: - root[work | ] @@ -629,7 +629,7 @@ def get_instance_staging_dir(instance): Note: Staging dir does not have to be necessarily in tempdir so be careful - about it's usage. + about its usage. Args: instance (pyblish.lib.Instance): Instance for which we want to get diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py index 3f9384a7fd..3216c596da 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/tempdir.py @@ -53,6 +53,7 @@ def create_custom_tempdir(project_name, anatomy=None): # create it if it doesn't exists os.makedirs(custom_tempdir) except IOError as error: - raise IOError("Path couldn't be created: {}".format(error)) from error + raise IOError( + "Path couldn't be created: {}".format(error)) from error return custom_tempdir From 21b2cbe34830e27fb02d797443e1e5025fffea8c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 13 Feb 2023 15:59:22 +0100 Subject: [PATCH 0581/1271] better methods propagation --- openpype/hosts/tvpaint/api/plugin.py | 68 +++++++++++++++------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/openpype/hosts/tvpaint/api/plugin.py b/openpype/hosts/tvpaint/api/plugin.py index c57baf7212..d267d87acd 100644 --- a/openpype/hosts/tvpaint/api/plugin.py +++ b/openpype/hosts/tvpaint/api/plugin.py @@ -21,48 +21,52 @@ from .pipeline import get_current_workfile_context SHARED_DATA_KEY = "openpype.tvpaint.instances" -def _collect_instances(creator): - instances_by_identifier = cache_and_get_instances( - creator, SHARED_DATA_KEY, creator.host.list_instances - ) - for instance_data in instances_by_identifier[creator.identifier]: - instance = CreatedInstance.from_existing(instance_data, creator) - creator._add_instance_to_context(instance) +class TVPaintCreatorCommon: + def _cache_and_get_instances(self): + return cache_and_get_instances( + self, SHARED_DATA_KEY, self.host.list_instances + ) + + def _collect_create_instances(self): + instances_by_identifier = self._cache_and_get_instances() + for instance_data in instances_by_identifier[self.identifier]: + instance = CreatedInstance.from_existing(instance_data, self) + self._add_instance_to_context(instance) -def _update_instances(creator, update_list): - if not update_list: - return + def _update_create_instances(self, update_list): + if not update_list: + return - cur_instances = creator.host.list_instances() - cur_instances_by_id = {} - for instance_data in cur_instances: - instance_id = instance_data.get("instance_id") - if instance_id: - cur_instances_by_id[instance_id] = instance_data + cur_instances = self.host.list_instances() + cur_instances_by_id = {} + for instance_data in cur_instances: + instance_id = instance_data.get("instance_id") + if instance_id: + cur_instances_by_id[instance_id] = instance_data - for instance, changes in update_list: - instance_data = instance.data_to_store() - cur_instance_data = cur_instances_by_id.get(instance.id) - if cur_instance_data is None: - cur_instances.append(instance_data) - continue - for key in set(cur_instance_data) - set(instance_data): - instance_data.pop(key) - instance_data.update(cur_instance_data) - creator.host.write_instances(cur_instances) + for instance, changes in update_list: + instance_data = changes.new_value + cur_instance_data = cur_instances_by_id.get(instance.id) + if cur_instance_data is None: + cur_instances.append(instance_data) + continue + for key in set(cur_instance_data) - set(instance_data): + cur_instance_data.pop(key) + cur_instance_data.update(instance_data) + self.host.write_instances(cur_instances) -class TVPaintCreator(NewCreator): +class TVPaintCreator(NewCreator, TVPaintCreatorCommon): @property def subset_template_family_filter(self): return self.family def collect_instances(self): - _collect_instances(self) + self._collect_create_instances() def update_instances(self, update_list): - _update_instances(self, update_list) + self._update_create_instances(update_list) def remove_instances(self, instances): ids_to_remove = { @@ -129,12 +133,12 @@ class TVPaintCreator(NewCreator): self._add_instance_to_context(new_instance) -class TVPaintAutoCreator(AutoCreator): +class TVPaintAutoCreator(AutoCreator, TVPaintCreatorCommon): def collect_instances(self): - _collect_instances(self) + self._collect_create_instances() def update_instances(self, update_list): - _update_instances(self, update_list) + self._update_create_instances(update_list) class Creator(LegacyCreator): From db7748995617721d7d564d0097f08ab3fecc141f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 13 Feb 2023 17:24:56 +0100 Subject: [PATCH 0582/1271] added labels to render pass instances --- .../tvpaint/plugins/create/create_render.py | 70 ++++++++++++++++--- 1 file changed, 62 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index e5d3fa1a59..8f7ba121c1 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -273,6 +273,35 @@ class CreateRenderPass(TVPaintCreator): # Settings mark_for_review = True + def collect_instances(self): + instances_by_identifier = self._cache_and_get_instances() + render_layers = { + instance_data["instance_id"]: { + "variant": instance_data["variant"], + "template_data": prepare_template_data({ + "renderlayer": instance_data["variant"] + }) + } + for instance_data in ( + instances_by_identifier[CreateRenderlayer.identifier] + ) + } + + for instance_data in instances_by_identifier[self.identifier]: + render_layer_instance_id = ( + instance_data + .get("creator_attributes", {}) + .get("render_layer_instance_id") + ) + render_layer_info = render_layers.get(render_layer_instance_id) + self.update_instance_labels( + instance_data, + render_layer_info["variant"], + render_layer_info["template_data"] + ) + instance = CreatedInstance.from_existing(instance_data, self) + self._add_instance_to_context(instance) + def get_dynamic_data( self, variant, task_name, asset_doc, project_name, host_name, instance ): @@ -283,6 +312,29 @@ class CreateRenderPass(TVPaintCreator): dynamic_data["renderlayer"] = "{renderlayer}" return dynamic_data + def update_instance_labels( + self, instance, render_layer_variant, render_layer_data=None + ): + old_label = instance.get("label") + old_group = instance.get("group") + new_label = None + new_group = None + if render_layer_variant is not None: + if render_layer_data is None: + render_layer_data = prepare_template_data({ + "renderlayer": render_layer_variant + }) + try: + new_label = instance["subset"].format(**render_layer_data) + except (KeyError, ValueError): + pass + + new_group = f"{self.get_group_label()} ({render_layer_variant})" + + instance["label"] = new_label + instance["group"] = new_group + return old_group != new_group or old_label != new_label + def create(self, subset_name, instance_data, pre_create_data): render_layer_instance_id = pre_create_data.get( "render_layer_instance_id" @@ -337,6 +389,8 @@ class CreateRenderPass(TVPaintCreator): **prepare_template_data(subset_name_fill_data) ) self.log.info(f"New subset name is \"{new_subset_name}\".") + instance_data["label"] = new_subset_name + instance_data["group"] = f"{self.get_group_label()} ({render_layer})" instance_data["layer_names"] = list(selected_layer_names) if "creator_attributes" not in instance_data: instance_data["creator_attribtues"] = {} @@ -400,14 +454,14 @@ class CreateRenderPass(TVPaintCreator): ] def get_pre_create_attr_defs(self): - render_layers = [] - for instance in self.create_context.instances: - if instance.creator_identifier == CreateRenderlayer.identifier: - render_layers.append({ - "value": instance.id, - "label": instance.label - }) - + render_layers = [ + { + "value": instance.id, + "label": instance.label + } + for instance in self.create_context.instances + if instance.creator_identifier == CreateRenderlayer.identifier + ] if not render_layers: render_layers.append({"value": None, "label": "N/A"}) From d39dd9ce1de40544cbdf612c6f9e5ff00b126129 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 13 Feb 2023 17:59:40 +0000 Subject: [PATCH 0583/1271] Correct colorspace when using "View Transform" --- openpype/hosts/maya/api/lib_renderproducts.py | 7 +- openpype/pipeline/colorspace.py | 99 +++++++++---------- openpype/scripts/ocio_wrapper.py | 20 +++- 3 files changed, 70 insertions(+), 56 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 38415c2ae2..4930143fb1 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -46,6 +46,7 @@ import attr from . import lib from . import lib_rendersetup +from openpype.pipeline.colorspace import get_ocio_config_views from maya import cmds, mel @@ -646,7 +647,11 @@ class RenderProductsArnold(ARenderProducts): def _view_transform(): preferences = lib.get_color_management_preferences() - return preferences["view_transform"] + views_data = get_ocio_config_views(preferences["config"]) + view_data = views_data[ + "{}/{}".format(preferences["display"], preferences["view"]) + ] + return view_data["colorspace"] def _raw(): preferences = lib.get_color_management_preferences() diff --git a/openpype/pipeline/colorspace.py b/openpype/pipeline/colorspace.py index cb37b2c4ae..6f68bdc5bf 100644 --- a/openpype/pipeline/colorspace.py +++ b/openpype/pipeline/colorspace.py @@ -198,41 +198,19 @@ def validate_imageio_colorspace_in_config(config_path, colorspace_name): return True -def get_ocio_config_colorspaces(config_path): - """Get all colorspace data - - Wrapper function for aggregating all names and its families. - Families can be used for building menu and submenus in gui. - - Args: - config_path (str): path leading to config.ocio file - - Returns: - dict: colorspace and family in couple - """ - if sys.version_info[0] == 2: - return get_colorspace_data_subprocess(config_path) - - from ..scripts.ocio_wrapper import _get_colorspace_data - return _get_colorspace_data(config_path) - - -def get_colorspace_data_subprocess(config_path): - """Get colorspace data via subprocess +def get_data_subprocess(config_path, data_type): + """Get data via subprocess Wrapper for Python 2 hosts. Args: config_path (str): path leading to config.ocio file - - Returns: - dict: colorspace and family in couple """ with _make_temp_json_file() as tmp_json_path: # Prepare subprocess arguments args = [ "run", get_ocio_config_script_path(), - "config", "get_colorspace", + "config", data_type, "--in_path", config_path, "--out_path", tmp_json_path @@ -251,6 +229,47 @@ def get_colorspace_data_subprocess(config_path): return json.loads(return_json_data) +def compatible_python(): + """Only 3.9 or higher can directly use PyOpenColorIO in ocio_wrapper""" + compatible = False + if sys.version[0] == 3 and sys.version[1] >= 9: + compatible = True + return compatible + + +def get_ocio_config_colorspaces(config_path): + """Get all colorspace data + + Wrapper function for aggregating all names and its families. + Families can be used for building menu and submenus in gui. + + Args: + config_path (str): path leading to config.ocio file + + Returns: + dict: colorspace and family in couple + """ + if compatible_python(): + from ..scripts.ocio_wrapper import _get_colorspace_data + return _get_colorspace_data(config_path) + else: + return get_colorspace_data_subprocess(config_path) + + +def get_colorspace_data_subprocess(config_path): + """Get colorspace data via subprocess + + Wrapper for Python 2 hosts. + + Args: + config_path (str): path leading to config.ocio file + + Returns: + dict: colorspace and family in couple + """ + return get_data_subprocess(config_path, "get_colorspace") + + def get_ocio_config_views(config_path): """Get all viewer data @@ -263,12 +282,12 @@ def get_ocio_config_views(config_path): Returns: dict: `display/viewer` and viewer data """ - if sys.version_info[0] == 2: + if compatible_python(): + from ..scripts.ocio_wrapper import _get_views_data + return _get_views_data(config_path) + else: return get_views_data_subprocess(config_path) - from ..scripts.ocio_wrapper import _get_views_data - return _get_views_data(config_path) - def get_views_data_subprocess(config_path): """Get viewers data via subprocess @@ -281,27 +300,7 @@ def get_views_data_subprocess(config_path): Returns: dict: `display/viewer` and viewer data """ - with _make_temp_json_file() as tmp_json_path: - # Prepare subprocess arguments - args = [ - "run", get_ocio_config_script_path(), - "config", "get_views", - "--in_path", config_path, - "--out_path", tmp_json_path - - ] - log.info("Executing: {}".format(" ".join(args))) - - process_kwargs = { - "logger": log, - "env": {} - } - - run_openpype_process(*args, **process_kwargs) - - # return all colorspaces - return_json_data = open(tmp_json_path).read() - return json.loads(return_json_data) + return get_data_subprocess(config_path, "get_views") def get_imageio_config( diff --git a/openpype/scripts/ocio_wrapper.py b/openpype/scripts/ocio_wrapper.py index 0685b2e52a..aa9e11c841 100644 --- a/openpype/scripts/ocio_wrapper.py +++ b/openpype/scripts/ocio_wrapper.py @@ -157,11 +157,21 @@ def _get_views_data(config_path): config = ocio.Config().CreateFromFile(str(config_path)) - return { - f"{d}/{v}": {"display": d, "view": v} - for d in config.getDisplays() - for v in config.getViews(d) - } + data = {} + for display in config.getDisplays(): + for view in config.getViews(display): + colorspace = config.getDisplayViewColorSpaceName(display, view) + # Special token. See https://opencolorio.readthedocs.io/en/latest/guides/authoring/authoring.html#shared-views + if colorspace == "": + colorspace = display + + data[f"{display}/{view}"] = { + "display": display, + "view": view, + "colorspace": colorspace + } + + return data if __name__ == '__main__': From e21323c78841902bf66f8670b1890a7cde11773c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 13 Feb 2023 18:01:38 +0000 Subject: [PATCH 0584/1271] Hound --- openpype/scripts/ocio_wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/scripts/ocio_wrapper.py b/openpype/scripts/ocio_wrapper.py index aa9e11c841..16558642c6 100644 --- a/openpype/scripts/ocio_wrapper.py +++ b/openpype/scripts/ocio_wrapper.py @@ -161,7 +161,7 @@ def _get_views_data(config_path): for display in config.getDisplays(): for view in config.getViews(display): colorspace = config.getDisplayViewColorSpaceName(display, view) - # Special token. See https://opencolorio.readthedocs.io/en/latest/guides/authoring/authoring.html#shared-views + # Special token. See https://opencolorio.readthedocs.io/en/latest/guides/authoring/authoring.html#shared-views # noqa if colorspace == "": colorspace = display From 75637cc1a46eca825fee99346424828fe41a83fc Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 14 Feb 2023 07:00:21 +0000 Subject: [PATCH 0585/1271] Strict Error Checking Default Provide default of strict error checking for instances created prior to PR. --- openpype/hosts/maya/plugins/publish/collect_render.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index fc297ef612..5bc295a56f 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -320,7 +320,8 @@ class CollectMayaRender(pyblish.api.ContextPlugin): "renderSetupIncludeLights" ), "strict_error_checking": render_instance.data.get( - "strict_error_checking") + "strict_error_checking", False + ) } # Collect Deadline url if Deadline module is enabled From 6225083ffe36e197187506e5fd8a59a895e57fb2 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 14 Feb 2023 07:06:56 +0000 Subject: [PATCH 0586/1271] Default Ftrack Family on RenderLayer With default settings, renderlayers in Maya were not being tagged with the Ftrack family leading to confusion when doing reviews. --- openpype/settings/defaults/project_settings/ftrack.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_settings/ftrack.json b/openpype/settings/defaults/project_settings/ftrack.json index cdf861df4a..ed646e29fa 100644 --- a/openpype/settings/defaults/project_settings/ftrack.json +++ b/openpype/settings/defaults/project_settings/ftrack.json @@ -324,7 +324,8 @@ "animation", "look", "rig", - "camera" + "camera", + "renderlayer" ], "task_types": [], "tasks": [], @@ -494,4 +495,4 @@ "farm_status_profiles": [] } } -} \ No newline at end of file +} From 83c75b8b41be6fc865056b105a75cb9fe5f4dcaa Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 14 Feb 2023 09:08:09 +0000 Subject: [PATCH 0587/1271] Revert "Default Ftrack Family on RenderLayer" This reverts commit 6225083ffe36e197187506e5fd8a59a895e57fb2. --- openpype/settings/defaults/project_settings/ftrack.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/settings/defaults/project_settings/ftrack.json b/openpype/settings/defaults/project_settings/ftrack.json index ed646e29fa..cdf861df4a 100644 --- a/openpype/settings/defaults/project_settings/ftrack.json +++ b/openpype/settings/defaults/project_settings/ftrack.json @@ -324,8 +324,7 @@ "animation", "look", "rig", - "camera", - "renderlayer" + "camera" ], "task_types": [], "tasks": [], @@ -495,4 +494,4 @@ "farm_status_profiles": [] } } -} +} \ No newline at end of file From bcf020e4fa2d9bd1c653a96a084691a451c37b4f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 14 Feb 2023 09:19:17 +0000 Subject: [PATCH 0588/1271] Modify defaults through UI --- openpype/settings/defaults/project_settings/ftrack.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/ftrack.json b/openpype/settings/defaults/project_settings/ftrack.json index cdf861df4a..bd761ccd92 100644 --- a/openpype/settings/defaults/project_settings/ftrack.json +++ b/openpype/settings/defaults/project_settings/ftrack.json @@ -324,7 +324,8 @@ "animation", "look", "rig", - "camera" + "camera", + "renderlayer" ], "task_types": [], "tasks": [], From d3cc8b59c5add4269908947f5fdad108ec2ade30 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 14 Feb 2023 10:42:54 +0100 Subject: [PATCH 0589/1271] replaced call to mongo 'dbcon.parenthood' with 'get_representation_parents' function --- openpype/pipeline/load/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/load/utils.py b/openpype/pipeline/load/utils.py index e30923f922..fefdb8537b 100644 --- a/openpype/pipeline/load/utils.py +++ b/openpype/pipeline/load/utils.py @@ -28,7 +28,6 @@ from openpype.lib import ( TemplateUnsolved, ) from openpype.pipeline import ( - schema, legacy_io, Anatomy, ) @@ -643,7 +642,10 @@ def get_representation_path(representation, root=None, dbcon=None): def path_from_config(): try: - version_, subset, asset, project = dbcon.parenthood(representation) + project_name = dbcon.active_project() + version_, subset, asset, project = get_representation_parents( + project_name, representation + ) except ValueError: log.debug( "Representation %s wasn't found in database, " From 33a7ecd19eacfe237cc7ea68513ff17d692e6a77 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 14 Feb 2023 10:03:01 +0000 Subject: [PATCH 0590/1271] Code cosmetics --- openpype/hosts/maya/plugins/publish/collect_render.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index 5bc295a56f..aa35f687ca 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -42,7 +42,6 @@ Provides: import re import os import platform -import json from maya import cmds import maya.app.renderSetup.model.renderSetup as renderSetup From abe9a2b8951414327c2df4718085dec5ccd20485 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 14 Feb 2023 10:03:12 +0000 Subject: [PATCH 0591/1271] Default should be True --- openpype/hosts/maya/plugins/publish/collect_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index aa35f687ca..f2b5262187 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -319,7 +319,7 @@ class CollectMayaRender(pyblish.api.ContextPlugin): "renderSetupIncludeLights" ), "strict_error_checking": render_instance.data.get( - "strict_error_checking", False + "strict_error_checking", True ) } From 6653df6369afbe53905af5eccacd29a2faed72d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Tue, 14 Feb 2023 12:18:00 +0100 Subject: [PATCH 0592/1271] Update openpype/hosts/nuke/plugins/load/load_clip.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/nuke/plugins/load/load_clip.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index f9364172ea..8f9b463037 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -222,13 +222,12 @@ class LoadClip(plugin.NukeLoader): representation = deepcopy(representation) context = representation["context"] template = representation["data"]["template"] - frame = context["frame"] - hashed_frame = "#" * len(str(frame)) - if ( "{originalBasename}" in template and "frame" in context ): + frame = context["frame"] + hashed_frame = "#" * len(str(frame)) origin_basename = context["originalBasename"] context["originalBasename"] = origin_basename.replace( frame, hashed_frame From 1505478cbdd7def0ab2040d65d09be823b39d958 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 14 Feb 2023 21:05:54 +0800 Subject: [PATCH 0593/1271] add general 3dsmax settings and add max families into submit publish job --- .../hosts/max/plugins/create/create_render.py | 2 - .../max/plugins/publish/collect_render.py | 22 ++++++-- .../plugins/publish/submit_3dmax_deadline.py | 41 +++++++++++--- .../plugins/publish/submit_publish_job.py | 4 +- .../defaults/project_settings/deadline.json | 11 ++++ .../schema_project_deadline.json | 54 +++++++++++++++++++ 6 files changed, 120 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/max/plugins/create/create_render.py b/openpype/hosts/max/plugins/create/create_render.py index 699fc200d3..76c10ca4a9 100644 --- a/openpype/hosts/max/plugins/create/create_render.py +++ b/openpype/hosts/max/plugins/create/create_render.py @@ -20,8 +20,6 @@ class CreateRender(plugin.MaxCreator): pre_create_data) # type: CreatedInstance container_name = instance.data.get("instance_node") container = rt.getNodeByName(container_name) - # chuckSize for submitting render - instance_data["chunkSize"] = 10 # TODO: Disable "Add to Containers?" Panel # parent the selected cameras into the container for obj in sel_obj: diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index 857ac88ea6..5089f107d3 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -6,7 +6,7 @@ import pyblish.api from pymxs import runtime as rt from openpype.pipeline import legacy_io from openpype.hosts.max.api.lib_renderproducts import RenderProducts - +from openpype.client import get_last_version_by_subset_name class CollectRender(pyblish.api.InstancePlugin): """Collect Render for Deadline""" @@ -30,6 +30,21 @@ class CollectRender(pyblish.api.InstancePlugin): folder = folder.replace("\\", "/") imgFormat = RenderProducts().image_format() + project_name = context.data["projectName"] + asset_doc = context.data["assetEntity"] + asset_id = asset_doc["_id"] + version_doc = get_last_version_by_subset_name(project_name, + instance.name, + asset_id) + + self.log.debug("version_doc: {0}".format(version_doc)) + version_int = 1 + if version_doc: + version_int += int(version_doc["name"]) + + self.log.debug(f"Setting {version_int} to context.") + context.data["version"] = version_int + # setup the plugin as 3dsmax for the internal renderer data = { "subset": instance.name, @@ -39,10 +54,11 @@ class CollectRender(pyblish.api.InstancePlugin): "family": 'maxrender', "families": ['maxrender'], "source": filepath, - "files": render_layer_files, + "expectedFiles": render_layer_files, "plugin": "3dsmax", "frameStart": context.data['frameStart'], - "frameEnd": context.data['frameEnd'] + "frameEnd": context.data['frameEnd'], + "version": version_int } self.log.info("data: {0}".format(data)) instance.data.update(data) diff --git a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py index 88834e4a91..056e74cf3f 100644 --- a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py @@ -20,6 +20,12 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): hosts = ["max"] families = ["maxrender"] targets = ["local"] + use_published = True + priority = 50 + chunk_size = 1 + group = None + deadline_pool = None + deadline_pool_secondary = None def process(self, instance): context = instance.context @@ -34,6 +40,24 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): start=int(instance.data["frameStart"]), end=int(instance.data["frameEnd"]) ) + if self.use_published: + for item in context: + if "workfile" in item.data["families"]: + msg = "Workfile (scene) must be published along" + assert item.data["publish"] is True, msg + + template_data = item.data.get("anatomyData") + rep = item.data.get("representations")[0].get("name") + template_data["representation"] = rep + template_data["ext"] = rep + template_data["comment"] = None + anatomy_filled = context.data["anatomy"].format(template_data) + template_filled = anatomy_filled["publish"]["path"] + filepath = os.path.normpath(template_filled) + + self.log.info( + "Using published scene for render {}".format(filepath) + ) payload = { "JobInfo": { @@ -47,15 +71,17 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): "UserName": deadline_user, "Plugin": instance.data["plugin"], - "Pool": instance.data.get("primaryPool"), - "secondaryPool": instance.data.get("secondaryPool"), + "Group": self.group, + "Pool": self.deadline_pool, + "secondaryPool": self.deadline_pool_secondary, "Frames": frames, - "ChunkSize": instance.data.get("chunkSize", 10), + "ChunkSize": self.chunk_size, + "Priority": instance.data.get("priority", self.priority), "Comment": comment }, "PluginInfo": { # Input - "SceneFile": instance.data["source"], + "SceneFile": filepath, "Version": "2023", "SaveFile": True, # Mandatory for Deadline @@ -94,7 +120,7 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): # frames from Deadline Monitor output_data = {} # need to be fixed - for i, filepath in enumerate(instance.data["files"]): + for i, filepath in enumerate(instance.data["expectedFiles"]): dirname = os.path.dirname(filepath) fname = os.path.basename(filepath) output_data["OutputDirectory%d" % i] = dirname.replace("\\", "/") @@ -129,9 +155,10 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): response = requests.post(url, json=payload) if not response.ok: raise Exception(response.text) - # Store output dir for unified publisher (filesequence) - expected_files = instance.data["files"] + # Store output dir for unified publisher (expectedFilesequence) + expected_files = instance.data["expectedFiles"] self.log.info("exp:{}".format(expected_files)) output_dir = os.path.dirname(expected_files[0]) + instance.data["toBeRenderedOn"] = "deadline" instance.data["outputDir"] = output_dir instance.data["deadlineSubmissionJob"] = response.json() diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 7e39a644a2..71934aef93 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -117,10 +117,10 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): deadline_plugin = "OpenPype" targets = ["local"] - hosts = ["fusion", "maya", "nuke", "celaction", "aftereffects", "harmony"] + hosts = ["fusion", "max", "maya", "nuke", "celaction", "aftereffects", "harmony"] families = ["render.farm", "prerender.farm", - "renderlayer", "imagesequence", "vrayscene"] + "renderlayer", "imagesequence", "maxrender", "vrayscene"] aov_filter = {"maya": [r".*([Bb]eauty).*"], "aftereffects": [r".*"], # for everything from AE diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index ceb0b2e39a..0fab284c66 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -35,6 +35,17 @@ "pluginInfo": {}, "scene_patches": [] }, + "MaxSubmitRenderDeadline": { + "enabled": true, + "optional": false, + "active": true, + "use_published": true, + "priority": 50, + "chunk_size": 10, + "group": "none", + "deadline_pool": "", + "deadline_pool_secondary": "" + }, "NukeSubmitDeadline": { "enabled": true, "optional": false, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index 08a505bd47..afefd3266a 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -198,6 +198,60 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "MaxSubmitRenderDeadline", + "label": "3dsMax Submit to Deadline", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + }, + { + "type": "boolean", + "key": "use_published", + "label": "Use Published scene" + }, + { + "type": "number", + "key": "priority", + "label": "Priority" + }, + { + "type": "number", + "key": "chunk_size", + "label": "Chunk Size" + }, + { + "type": "text", + "key": "group", + "label": "Group Name" + }, + { + "type": "text", + "key": "deadline_pool", + "label": "Deadline pool" + }, + { + "type": "text", + "key": "deadline_pool_secondary", + "label": "Deadline pool (secondary)" + } + ] + }, { "type": "dict", "collapsible": true, From 0dbf111d05843996f64a789e1a938a84ebf585bd Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 14 Feb 2023 21:15:46 +0800 Subject: [PATCH 0594/1271] hound fix --- openpype/hosts/max/plugins/publish/collect_render.py | 1 + .../modules/deadline/plugins/publish/submit_3dmax_deadline.py | 3 ++- .../modules/deadline/plugins/publish/submit_publish_job.py | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index 5089f107d3..55391d40e8 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -8,6 +8,7 @@ from openpype.pipeline import legacy_io from openpype.hosts.max.api.lib_renderproducts import RenderProducts from openpype.client import get_last_version_by_subset_name + class CollectRender(pyblish.api.InstancePlugin): """Collect Render for Deadline""" diff --git a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py index 056e74cf3f..dec951da7a 100644 --- a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py @@ -51,7 +51,8 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): template_data["representation"] = rep template_data["ext"] = rep template_data["comment"] = None - anatomy_filled = context.data["anatomy"].format(template_data) + anatomy_data = context.data["anatomy"] + anatomy_filled = anatomy_data.format(template_data) template_filled = anatomy_filled["publish"]["path"] filepath = os.path.normpath(template_filled) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 71934aef93..b70301ab7e 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -117,7 +117,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): deadline_plugin = "OpenPype" targets = ["local"] - hosts = ["fusion", "max", "maya", "nuke", "celaction", "aftereffects", "harmony"] + hosts = ["fusion", "max", "maya", "nuke", + "celaction", "aftereffects", "harmony"] families = ["render.farm", "prerender.farm", "renderlayer", "imagesequence", "maxrender", "vrayscene"] From 1abe920b5f18e32e51dbdaa8ac60b6dff175e22f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 14 Feb 2023 14:27:42 +0100 Subject: [PATCH 0595/1271] anatomy data from instance rather then context --- .../plugins/publish/collect_otio_subset_resources.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/plugins/publish/collect_otio_subset_resources.py b/openpype/plugins/publish/collect_otio_subset_resources.py index dab52986da..f659791d95 100644 --- a/openpype/plugins/publish/collect_otio_subset_resources.py +++ b/openpype/plugins/publish/collect_otio_subset_resources.py @@ -22,7 +22,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): """Get Resources for a subset version""" label = "Collect OTIO Subset Resources" - order = pyblish.api.CollectorOrder + 0.0021 + order = pyblish.api.CollectorOrder + 0.491 families = ["clip"] hosts = ["resolve", "hiero", "flame"] @@ -50,9 +50,9 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): # get basic variables otio_clip = instance.data["otioClip"] - otio_avalable_range = otio_clip.available_range() - media_fps = otio_avalable_range.start_time.rate - available_duration = otio_avalable_range.duration.value + otio_available_range = otio_clip.available_range() + media_fps = otio_available_range.start_time.rate + available_duration = otio_available_range.duration.value # get available range trimmed with processed retimes retimed_attributes = get_media_range_with_retimes( @@ -248,7 +248,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): # Task can be optional in anatomy data host_name = context.data["hostName"] family = instance.data["family"] - anatomy_data = instance.context.data["anatomyData"] + anatomy_data = instance.data["anatomyData"] task_info = anatomy_data.get("task") or {} return get_publish_template_name( @@ -259,4 +259,4 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): task_type=task_info.get("type"), project_settings=context.data["project_settings"], logger=self.log - ) \ No newline at end of file + ) From 714454d7300cf2e067785c888dadddb415f88baf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 14:54:25 +0100 Subject: [PATCH 0596/1271] OP-4643 - fix logging Wrong variable used --- openpype/plugins/publish/extract_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index dcb43d7fa2..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -169,7 +169,7 @@ class ExtractReview(pyblish.api.InstancePlugin): "Skipped representation. All output definitions from" " selected profile does not match to representation's" " custom tags. \"{}\"" - ).format(str(tags))) + ).format(str(custom_tags))) continue outputs_per_representations.append((repre, outputs)) From 2b5e4bc14e7a4241ee97a3bbb0716b148ec13513 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 15:14:14 +0100 Subject: [PATCH 0597/1271] OP-4643 - allow new repre to stay One might want to delete outputs with 'delete' tag, but repre must stay there at least until extract_review. More universal new tag might be created for this. --- openpype/plugins/publish/extract_color_transcode.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 93ee1ec44d..4a03e623fd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,15 +161,17 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation + if not new_repre.get("tags"): + new_repre["tags"] = [] for tag in output_def["tags"]: - if not new_repre.get("tags"): - new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) if tag == "review": added_review = True + new_repre["tags"].append("newly_added") + instance.data["representations"].append(new_repre) added_representations = True @@ -179,6 +181,12 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] + # TODO implement better way, for now do not delete new repre + # new repre might have 'delete' tag to removed, but it first must + # be there for review to be created + if "newly_added" in tags: + tags.remove("newly_added") + continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) From 6bdbdd4337d7d268667cf08f3ef784a8e306184f Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 14 Feb 2023 15:32:54 +0000 Subject: [PATCH 0598/1271] Update openpype/hosts/maya/plugins/load/load_arnold_standin.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- .../hosts/maya/plugins/load/load_arnold_standin.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index 66e8b69639..ab69d62ef5 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -65,20 +65,20 @@ class ArnoldStandinLoader(load.LoaderPlugin): # Create transform with shape transform_name = label + "_standin" - standinShape = mtoa.ui.arnoldmenu.createStandIn() - standin = cmds.listRelatives(standinShape, parent=True)[0] + standin_shape = mtoa.ui.arnoldmenu.createStandIn() + standin = cmds.listRelatives(standin_shape, parent=True)[0] standin = cmds.rename(standin, transform_name) - standinShape = cmds.listRelatives(standin, shapes=True)[0] + standin_shape = cmds.listRelatives(standin, shapes=True)[0] cmds.parent(standin, root) # Set the standin filepath path, operator = self._setup_proxy( - standinShape, self.fname, namespace + standin_shape, self.fname, namespace ) - cmds.setAttr(standinShape + ".dso", path, type="string") + cmds.setAttr(standin_shape + ".dso", path, type="string") sequence = is_sequence(os.listdir(os.path.dirname(self.fname))) - cmds.setAttr(standinShape + ".useFrameExtension", sequence) + cmds.setAttr(standin_shape + ".useFrameExtension", sequence) nodes = [root, standin] if operator is not None: From 5903dbce9e176d26d45681a86efe92f607a512e2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 14 Feb 2023 17:03:05 +0100 Subject: [PATCH 0599/1271] autofill precreate attributes if are not passed --- openpype/pipeline/create/context.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index ba566f93d4..1567acdb79 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -17,6 +17,7 @@ from openpype.lib.attribute_definitions import ( UnknownDef, serialize_attr_defs, deserialize_attr_defs, + get_default_values, ) from openpype.host import IPublishHost from openpype.pipeline import legacy_io @@ -1866,6 +1867,13 @@ class CreateContext: if pre_create_data is None: pre_create_data = {} + precreate_attr_defs = creator.get_pre_create_attr_defs() or [] + # Create default values of precreate data + _pre_create_data = get_default_values(precreate_attr_defs) + # Update passed precreate data to default values + # TODO validate types + _pre_create_data.update(pre_create_data) + subset_name = creator.get_subset_name( variant, task_name, @@ -1881,7 +1889,7 @@ class CreateContext: return creator.create( subset_name, instance_data, - pre_create_data + _pre_create_data ) def _create_with_unified_error( From 2621016015b0b2c3e36f36d4c3853851d5154256 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 14 Feb 2023 16:27:43 +0000 Subject: [PATCH 0600/1271] Template for config. --- .../modules/deadline/plugins/publish/submit_publish_job.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 02aa1043d1..89a4e5d377 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -552,7 +552,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "colorspace": colorspace, "config": { "path": additional_data["colorspaceConfig"], - "template": "" + "template": additional_data["colorspaceTemplate"] }, "display": additional_data["display"], "view": additional_data["view"] @@ -920,7 +920,10 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "renderProducts": instance.data["renderProducts"], "colorspaceConfig": instance.data["colorspaceConfig"], "display": instance.data["colorspaceDisplay"], - "view": instance.data["colorspaceView"] + "view": instance.data["colorspaceView"], + "colorspaceTemplate": instance.data["colorspaceConfig"].replace( + context.data["anatomy"].roots["work"], "{root[work]}" + ) } if isinstance(data.get("expectedFiles")[0], dict): From 8ddcc9c151aea67ae893ee1518b70731fa776deb Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 15 Feb 2023 03:29:31 +0000 Subject: [PATCH 0601/1271] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 8dfd638414..6d060656cb 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.1-nightly.5" +__version__ = "3.15.1-nightly.6" From ac4078259200edbdf88d58954b1e96e09468e5b9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 10:14:19 +0100 Subject: [PATCH 0602/1271] fix used constant 'ActiveWindow' -> 'WindowActive' --- openpype/tools/publisher/window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 097e289f32..a82f60d5a5 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -366,7 +366,7 @@ class PublisherWindow(QtWidgets.QDialog): def make_sure_is_visible(self): if self._window_is_visible: - self.setWindowState(QtCore.Qt.ActiveWindow) + self.setWindowState(QtCore.Qt.WindowActive) else: self.show() From bd8f68c6e9574431886a2dd31b8c1620b38a941f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 10:37:10 +0100 Subject: [PATCH 0603/1271] changed collect workfile to instance plugin --- .../plugins/publish/collect_workfile.py | 57 ++++--------------- 1 file changed, 10 insertions(+), 47 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_workfile.py b/openpype/hosts/tvpaint/plugins/publish/collect_workfile.py index 8c7c8c3899..a3449663f8 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_workfile.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_workfile.py @@ -2,17 +2,15 @@ import os import json import pyblish.api -from openpype.client import get_asset_by_name -from openpype.pipeline import legacy_io -from openpype.pipeline.create import get_subset_name - -class CollectWorkfile(pyblish.api.ContextPlugin): +class CollectWorkfile(pyblish.api.InstancePlugin): label = "Collect Workfile" order = pyblish.api.CollectorOrder - 0.4 hosts = ["tvpaint"] + families = ["workfile"] - def process(self, context): + def process(self, instance): + context = instance.context current_file = context.data["currentFile"] self.log.info( @@ -21,49 +19,14 @@ class CollectWorkfile(pyblish.api.ContextPlugin): dirpath, filename = os.path.split(current_file) basename, ext = os.path.splitext(filename) - instance = context.create_instance(name=basename) - # Project name from workfile context - project_name = context.data["workfile_context"]["project"] - - # Get subset name of workfile instance - # Collect asset doc to get asset id - # - not sure if it's good idea to require asset id in - # get_subset_name? - family = "workfile" - asset_name = context.data["workfile_context"]["asset"] - asset_doc = get_asset_by_name(project_name, asset_name) - - # Host name from environment variable - host_name = os.environ["AVALON_APP"] - # Use empty variant value - variant = "" - task_name = legacy_io.Session["AVALON_TASK"] - subset_name = get_subset_name( - family, - variant, - task_name, - asset_doc, - project_name, - host_name, - project_settings=context.data["project_settings"] - ) - - # Create Workfile instance - instance.data.update({ - "subset": subset_name, - "asset": context.data["asset"], - "label": subset_name, - "publish": True, - "family": "workfile", - "families": ["workfile"], - "representations": [{ - "name": ext.lstrip("."), - "ext": ext.lstrip("."), - "files": filename, - "stagingDir": dirpath - }] + instance.data["representations"].append({ + "name": ext.lstrip("."), + "ext": ext.lstrip("."), + "files": filename, + "stagingDir": dirpath }) + self.log.info("Collected workfile instance: {}".format( json.dumps(instance.data, indent=4) )) From d26d9083ce3931ac521ee66a9dd526cf693f5adb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 10:43:20 +0100 Subject: [PATCH 0604/1271] fix how context information is returned --- openpype/hosts/tvpaint/api/pipeline.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 6a729e39c3..3794bf2e24 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -124,8 +124,11 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): if not context: return get_global_context() + if "project_name" in context: + return context + # This is legacy way how context was stored return { - "project_name": context["project"], + "project_name": context.get("project"), "asset_name": context.get("asset"), "task_name": context.get("task") } From 319c9c60eaae1748cb6662dd9e1c9922d01f17bd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 10:43:39 +0100 Subject: [PATCH 0605/1271] added autocreator for scene review --- .../tvpaint/plugins/create/create_review.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 openpype/hosts/tvpaint/plugins/create/create_review.py diff --git a/openpype/hosts/tvpaint/plugins/create/create_review.py b/openpype/hosts/tvpaint/plugins/create/create_review.py new file mode 100644 index 0000000000..84127eb9b7 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/create/create_review.py @@ -0,0 +1,64 @@ +from openpype.client import get_asset_by_name +from openpype.pipeline import CreatedInstance +from openpype.hosts.tvpaint.api.plugin import TVPaintAutoCreator + + +class TVPaintReviewCreator(TVPaintAutoCreator): + family = "review" + identifier = "scene.review" + label = "Review" + + default_variant = "Main" + + def create(self): + existing_instance = None + for instance in self.create_context.instances: + if instance.creator_identifier == self.identifier: + existing_instance = instance + break + + context = self.host.get_current_context() + host_name = self.host.name + project_name = context["project_name"] + asset_name = context["asset_name"] + task_name = context["task_name"] + + if existing_instance is None: + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + self.default_variant, + task_name, + asset_doc, + project_name, + host_name + ) + data = { + "asset": asset_name, + "task": task_name, + "variant": self.default_variant + } + + new_instance = CreatedInstance( + self.family, subset_name, data, self + ) + instances_data = self.host.list_instances() + instances_data.append(new_instance.data_to_store()) + self.host.write_instances(instances_data) + self._add_instance_to_context(new_instance) + + elif ( + existing_instance["asset"] != asset_name + or existing_instance["task"] != task_name + ): + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + existing_instance["variant"], + task_name, + asset_doc, + project_name, + host_name, + existing_instance + ) + existing_instance["asset"] = asset_name + existing_instance["task"] = task_name + existing_instance["subset"] = subset_name From 8bad64e59f2fb2b10a912d57e451531da8099f7b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 10:43:48 +0100 Subject: [PATCH 0606/1271] change lael of workfile creator --- openpype/hosts/tvpaint/plugins/create/create_workfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/tvpaint/plugins/create/create_workfile.py b/openpype/hosts/tvpaint/plugins/create/create_workfile.py index 3e5cd86852..e421fbc3f8 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_workfile.py +++ b/openpype/hosts/tvpaint/plugins/create/create_workfile.py @@ -6,6 +6,7 @@ from openpype.hosts.tvpaint.api.plugin import TVPaintAutoCreator class TVPaintWorkfileCreator(TVPaintAutoCreator): family = "workfile" identifier = "workfile" + label = "Workfile" default_variant = "Main" From dae4094d8a3c3ec00f60c13e344a4e211b105ce1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 10:44:50 +0100 Subject: [PATCH 0607/1271] change how instance frames are calculated --- .../publish/collect_instance_frames.py | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_instance_frames.py b/openpype/hosts/tvpaint/plugins/publish/collect_instance_frames.py index d5b79758ad..5eb702a1da 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_instance_frames.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_instance_frames.py @@ -1,37 +1,34 @@ import pyblish.api -class CollectOutputFrameRange(pyblish.api.ContextPlugin): +class CollectOutputFrameRange(pyblish.api.InstancePlugin): """Collect frame start/end from context. When instances are collected context does not contain `frameStart` and `frameEnd` keys yet. They are collected in global plugin `CollectContextEntities`. """ + label = "Collect output frame range" - order = pyblish.api.CollectorOrder + order = pyblish.api.CollectorOrder + 0.4999 hosts = ["tvpaint"] + families = ["review", "render"] - def process(self, context): - for instance in context: - frame_start = instance.data.get("frameStart") - frame_end = instance.data.get("frameEnd") - if frame_start is not None and frame_end is not None: - self.log.debug( - "Instance {} already has set frames {}-{}".format( - str(instance), frame_start, frame_end - ) - ) - return + def process(self, instance): + asset_doc = instance.data.get("assetEntity") + if not asset_doc: + return - frame_start = context.data.get("frameStart") - frame_end = context.data.get("frameEnd") + context = instance.context - instance.data["frameStart"] = frame_start - instance.data["frameEnd"] = frame_end - - self.log.info( - "Set frames {}-{} on instance {} ".format( - frame_start, frame_end, str(instance) - ) + frame_start = asset_doc["data"]["frameStart"] + frame_end = frame_start + ( + context.data["sceneMarkOut"] - context.data["sceneMarkIn"] + ) + instance.data["frameStart"] = frame_start + instance.data["frameEnd"] = frame_end + self.log.info( + "Set frames {}-{} on instance {} ".format( + frame_start, frame_end, instance.data["subset"] ) + ) From 71d9ceb91e5eddc9daa82da27cb21d55d04e3ffb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 10:45:57 +0100 Subject: [PATCH 0608/1271] fix collect workfile data --- .../plugins/publish/collect_workfile_data.py | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py b/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py index 8fe71a4a46..95a5cd77bd 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py @@ -65,9 +65,9 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): # Collect and store current context to have reference current_context = { - "project": legacy_io.Session["AVALON_PROJECT"], - "asset": legacy_io.Session["AVALON_ASSET"], - "task": legacy_io.Session["AVALON_TASK"] + "project_name": context.data["projectName"], + "asset_name": context.data["asset"], + "task_name": context.data["task"] } context.data["previous_context"] = current_context self.log.debug("Current context is: {}".format(current_context)) @@ -76,25 +76,31 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): self.log.info("Collecting workfile context") workfile_context = get_current_workfile_context() + if "project" in workfile_context: + workfile_context = { + "project_name": workfile_context.get("project"), + "asset_name": workfile_context.get("asset"), + "task_name": workfile_context.get("task"), + } # Store workfile context to pyblish context context.data["workfile_context"] = workfile_context if workfile_context: # Change current context with context from workfile key_map = ( - ("AVALON_ASSET", "asset"), - ("AVALON_TASK", "task") + ("AVALON_ASSET", "asset_name"), + ("AVALON_TASK", "task_name") ) for env_key, key in key_map: legacy_io.Session[env_key] = workfile_context[key] os.environ[env_key] = workfile_context[key] self.log.info("Context changed to: {}".format(workfile_context)) - asset_name = workfile_context["asset"] - task_name = workfile_context["task"] + asset_name = workfile_context["asset_name"] + task_name = workfile_context["task_name"] else: - asset_name = current_context["asset"] - task_name = current_context["task"] + asset_name = current_context["asset_name"] + task_name = current_context["task_name"] # Handle older workfiles or workfiles without metadata self.log.warning(( "Workfile does not contain information about context." @@ -103,6 +109,7 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): # Store context asset name context.data["asset"] = asset_name + context.data["task"] = task_name self.log.info( "Context is set to Asset: \"{}\" and Task: \"{}\"".format( asset_name, task_name From b3e34fce324603cf955f3fcf8dca795995796d98 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 10:46:17 +0100 Subject: [PATCH 0609/1271] fix few validators --- .../publish/validate_render_pass_group.py | 1 - .../publish/validate_workfile_metadata.py | 17 +++++++++++++---- .../publish/validate_workfile_project_name.py | 7 +++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py b/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py index 0fbfca6c56..2a3173c698 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py @@ -85,6 +85,5 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin): ), "expected_group": correct_group["name"], "layer_names": ", ".join(invalid_layer_names) - } ) diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py index d66ae50c60..b38231e208 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py @@ -1,5 +1,9 @@ import pyblish.api -from openpype.pipeline import PublishXmlValidationError, registered_host +from openpype.pipeline import ( + PublishXmlValidationError, + PublishValidationError, + registered_host, +) class ValidateWorkfileMetadataRepair(pyblish.api.Action): @@ -27,13 +31,18 @@ class ValidateWorkfileMetadata(pyblish.api.ContextPlugin): actions = [ValidateWorkfileMetadataRepair] - required_keys = {"project", "asset", "task"} + required_keys = {"project_name", "asset_name", "task_name"} def process(self, context): workfile_context = context.data["workfile_context"] if not workfile_context: - raise AssertionError( - "Current workfile is missing whole metadata about context." + raise PublishValidationError( + "Current workfile is missing whole metadata about context.", + "Missing context", + ( + "Current workfile is missing metadata about task." + " To fix this issue save the file using Workfiles tool." + ) ) missing_keys = [] diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py index 0f25f2f7be..2ed5afa11c 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py @@ -1,4 +1,3 @@ -import os import pyblish.api from openpype.pipeline import PublishXmlValidationError @@ -16,15 +15,15 @@ class ValidateWorkfileProjectName(pyblish.api.ContextPlugin): def process(self, context): workfile_context = context.data.get("workfile_context") # If workfile context is missing than project is matching to - # `AVALON_PROJECT` value for 100% + # global project if not workfile_context: self.log.info( "Workfile context (\"workfile_context\") is not filled." ) return - workfile_project_name = workfile_context["project"] - env_project_name = os.environ["AVALON_PROJECT"] + workfile_project_name = workfile_context["project_name"] + env_project_name = context.data["projectName"] if workfile_project_name == env_project_name: self.log.info(( "Both workfile project and environment project are same. {}" From 8551b1f1b3969ef1fb2db815883169175dd1b0ce Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 10:59:06 +0100 Subject: [PATCH 0610/1271] change how extract sequence works --- .../tvpaint/plugins/publish/extract_sequence.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py b/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py index 78074f720c..f2856c72a9 100644 --- a/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py +++ b/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py @@ -6,6 +6,7 @@ from PIL import Image import pyblish.api +from openpype.pipeline.publish import KnownPublishError from openpype.hosts.tvpaint.api.lib import ( execute_george, execute_george_through_file, @@ -24,8 +25,7 @@ from openpype.hosts.tvpaint.lib import ( class ExtractSequence(pyblish.api.Extractor): label = "Extract Sequence" hosts = ["tvpaint"] - families = ["review", "renderPass", "renderLayer", "renderScene"] - families_to_review = ["review"] + families = ["review", "render"] # Modifiable with settings review_bg = [255, 255, 255, 255] @@ -136,7 +136,7 @@ class ExtractSequence(pyblish.api.Extractor): # Fill tags and new families from project settings tags = [] - if family_lowered in self.families_to_review: + if family_lowered == "review": tags.append("review") # Sequence of one frame @@ -162,10 +162,6 @@ class ExtractSequence(pyblish.api.Extractor): instance.data["representations"].append(new_repre) - if family_lowered in ("renderpass", "renderlayer", "renderscene"): - # Change family to render - instance.data["family"] = "render" - if not thumbnail_fullpath: return @@ -259,7 +255,7 @@ class ExtractSequence(pyblish.api.Extractor): output_filepaths_by_frame_idx[frame_idx] = filepath if not os.path.exists(filepath): - raise AssertionError( + raise KnownPublishError( "Output was not rendered. File was not found {}".format( filepath ) From ff7b3004ad953253afbc8e3a9640aa99a24f1c47 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 11:00:10 +0100 Subject: [PATCH 0611/1271] added validator for duplicated usage of render layer groups --- .../help/validate_render_layer_group.xml | 18 +++++ .../publish/validate_render_layer_group.py | 74 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_render_layer_group.xml create mode 100644 openpype/hosts/tvpaint/plugins/publish/validate_render_layer_group.py diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_render_layer_group.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_render_layer_group.xml new file mode 100644 index 0000000000..a95387356f --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_render_layer_group.xml @@ -0,0 +1,18 @@ + + + +Overused Color group +## One Color group is used by multiple Render Layers + +Single color group used by multiple Render Layers would cause clashes of rendered TVPaint layers. The same layers would be used for output files of both groups. + +### Missing layer names + +{groups_information} + +### How to repair? + +Refresh, go to 'Publish' tab and go through Render Layers and change their groups to not clash each other. If you reach limit of TVPaint color groups there is nothing you can do about it to fix the issue. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_render_layer_group.py b/openpype/hosts/tvpaint/plugins/publish/validate_render_layer_group.py new file mode 100644 index 0000000000..bb0a9a4ffe --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/validate_render_layer_group.py @@ -0,0 +1,74 @@ +import collections +import pyblish.api +from openpype.pipeline import PublishXmlValidationError + + +class ValidateRenderLayerGroups(pyblish.api.ContextPlugin): + """Validate group ids of renderLayer subsets. + + Validate that there are not 2 render layers using the same group. + """ + + label = "Validate Render Layers Group" + order = pyblish.api.ValidatorOrder + 0.1 + + def process(self, context): + # Prepare layers + render_layers_by_group_id = collections.defaultdict(list) + for instance in context: + families = instance.data.get("families") + if not families or "renderLayer" not in families: + continue + + group_id = instance.data["creator_attributes"]["group_id"] + render_layers_by_group_id[group_id].append(instance) + + duplicated_instances = [] + for group_id, instances in render_layers_by_group_id.items(): + if len(instances) > 1: + duplicated_instances.append((group_id, instances)) + + if not duplicated_instances: + return + + # Exception message preparations + groups_data = context.data["groupsData"] + groups_by_id = { + group["group_id"]: group + for group in groups_data + } + + per_group_msgs = [] + groups_information_lines = [] + for group_id, instances in duplicated_instances: + group = groups_by_id[group_id] + group_label = "Group \"{}\" ({})".format( + group["name"], + group["group_id"], + ) + line_join_subset_names = "\n".join([ + f" - {instance['subset']}" + for instance in instances + ]) + joined_subset_names = ", ".join([ + f"\"{instance['subset']}\"" + for instance in instances + ]) + per_group_msgs.append( + "{} < {} >".format(group_label, joined_subset_names) + ) + groups_information_lines.append( + "{}\n{}".format(group_label, line_join_subset_names) + ) + + # Raise an error + raise PublishXmlValidationError( + self, + ( + "More than one Render Layer is using the same TVPaint" + " group color. {}" + ).format(" | ".join(per_group_msgs)), + formatting_data={ + "groups_information": "\n".join(groups_information_lines) + } + ) From b405cac45cc082cfd30e735a0b8e8ee0859aae74 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 11:07:59 +0100 Subject: [PATCH 0612/1271] implemented new collection of render instances --- .../plugins/publish/collect_instances.py | 280 ------------------ .../publish/collect_render_instances.py | 87 ++++++ 2 files changed, 87 insertions(+), 280 deletions(-) delete mode 100644 openpype/hosts/tvpaint/plugins/publish/collect_instances.py create mode 100644 openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_instances.py b/openpype/hosts/tvpaint/plugins/publish/collect_instances.py deleted file mode 100644 index ae1326a5bd..0000000000 --- a/openpype/hosts/tvpaint/plugins/publish/collect_instances.py +++ /dev/null @@ -1,280 +0,0 @@ -import json -import copy -import pyblish.api - -from openpype.client import get_asset_by_name -from openpype.pipeline import legacy_io -from openpype.pipeline.create import get_subset_name - - -class CollectInstances(pyblish.api.ContextPlugin): - label = "Collect Instances" - order = pyblish.api.CollectorOrder - 0.4 - hosts = ["tvpaint"] - - def process(self, context): - workfile_instances = context.data["workfileInstances"] - - self.log.debug("Collected ({}) instances:\n{}".format( - len(workfile_instances), - json.dumps(workfile_instances, indent=4) - )) - - filtered_instance_data = [] - # Backwards compatibility for workfiles that already have review - # instance in metadata. - review_instance_exist = False - for instance_data in workfile_instances: - family = instance_data["family"] - if family == "review": - review_instance_exist = True - - elif family not in ("renderPass", "renderLayer"): - self.log.info("Unknown family \"{}\". Skipping {}".format( - family, json.dumps(instance_data, indent=4) - )) - continue - - filtered_instance_data.append(instance_data) - - # Fake review instance if review was not found in metadata families - if not review_instance_exist: - filtered_instance_data.append( - self._create_review_instance_data(context) - ) - - for instance_data in filtered_instance_data: - instance_data["fps"] = context.data["sceneFps"] - - # Conversion from older instances - # - change 'render_layer' to 'renderlayer' - render_layer = instance_data.get("instance_data") - if not render_layer: - # Render Layer has only variant - if instance_data["family"] == "renderLayer": - render_layer = instance_data.get("variant") - - # Backwards compatibility for renderPasses - elif "render_layer" in instance_data: - render_layer = instance_data["render_layer"] - - if render_layer: - instance_data["renderlayer"] = render_layer - - # Store workfile instance data to instance data - instance_data["originData"] = copy.deepcopy(instance_data) - # Global instance data modifications - # Fill families - family = instance_data["family"] - families = [family] - if family != "review": - families.append("review") - # Add `review` family for thumbnail integration - instance_data["families"] = families - - # Instance name - subset_name = instance_data["subset"] - name = instance_data.get("name", subset_name) - instance_data["name"] = name - instance_data["label"] = "{} [{}-{}]".format( - name, - context.data["sceneMarkIn"] + 1, - context.data["sceneMarkOut"] + 1 - ) - - active = instance_data.get("active", True) - instance_data["active"] = active - instance_data["publish"] = active - # Add representations key - instance_data["representations"] = [] - - # Different instance creation based on family - instance = None - if family == "review": - # Change subset name of review instance - - # Project name from workfile context - project_name = context.data["workfile_context"]["project"] - - # Collect asset doc to get asset id - # - not sure if it's good idea to require asset id in - # get_subset_name? - asset_name = context.data["workfile_context"]["asset"] - asset_doc = get_asset_by_name(project_name, asset_name) - - # Host name from environment variable - host_name = context.data["hostName"] - # Use empty variant value - variant = "" - task_name = legacy_io.Session["AVALON_TASK"] - new_subset_name = get_subset_name( - family, - variant, - task_name, - asset_doc, - project_name, - host_name, - project_settings=context.data["project_settings"] - ) - instance_data["subset"] = new_subset_name - - instance = context.create_instance(**instance_data) - - instance.data["layers"] = copy.deepcopy( - context.data["layersData"] - ) - - elif family == "renderLayer": - instance = self.create_render_layer_instance( - context, instance_data - ) - elif family == "renderPass": - instance = self.create_render_pass_instance( - context, instance_data - ) - - if instance is None: - continue - - any_visible = False - for layer in instance.data["layers"]: - if layer["visible"]: - any_visible = True - break - - instance.data["publish"] = any_visible - - self.log.debug("Created instance: {}\n{}".format( - instance, json.dumps(instance.data, indent=4) - )) - - def _create_review_instance_data(self, context): - """Fake review instance data.""" - - return { - "family": "review", - "asset": context.data["asset"], - # Dummy subset name - "subset": "reviewMain" - } - - def create_render_layer_instance(self, context, instance_data): - name = instance_data["name"] - # Change label - subset_name = instance_data["subset"] - - # Backwards compatibility - # - subset names were not stored as final subset names during creation - if "variant" not in instance_data: - instance_data["label"] = "{}_Beauty".format(name) - - # Change subset name - # Final family of an instance will be `render` - new_family = "render" - task_name = legacy_io.Session["AVALON_TASK"] - new_subset_name = "{}{}_{}_Beauty".format( - new_family, task_name.capitalize(), name - ) - instance_data["subset"] = new_subset_name - self.log.debug("Changed subset name \"{}\"->\"{}\"".format( - subset_name, new_subset_name - )) - - # Get all layers for the layer - layers_data = context.data["layersData"] - group_id = instance_data["group_id"] - group_layers = [] - for layer in layers_data: - if layer["group_id"] == group_id: - group_layers.append(layer) - - if not group_layers: - # Should be handled here? - self.log.warning(( - f"Group with id {group_id} does not contain any layers." - f" Instance \"{name}\" not created." - )) - return None - - instance_data["layers"] = group_layers - - return context.create_instance(**instance_data) - - def create_render_pass_instance(self, context, instance_data): - pass_name = instance_data["pass"] - self.log.info( - "Creating render pass instance. \"{}\"".format(pass_name) - ) - # Change label - render_layer = instance_data["renderlayer"] - - # Backwards compatibility - # - subset names were not stored as final subset names during creation - if "variant" not in instance_data: - instance_data["label"] = "{}_{}".format(render_layer, pass_name) - # Change subset name - # Final family of an instance will be `render` - new_family = "render" - old_subset_name = instance_data["subset"] - task_name = legacy_io.Session["AVALON_TASK"] - new_subset_name = "{}{}_{}_{}".format( - new_family, task_name.capitalize(), render_layer, pass_name - ) - instance_data["subset"] = new_subset_name - self.log.debug("Changed subset name \"{}\"->\"{}\"".format( - old_subset_name, new_subset_name - )) - - layers_data = context.data["layersData"] - layers_by_name = { - layer["name"]: layer - for layer in layers_data - } - - if "layer_names" in instance_data: - layer_names = instance_data["layer_names"] - else: - # Backwards compatibility - # - 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["layer_id"]: layer - for layer in layers_data - } - layer_ids = instance_data["layer_ids"] - layer_names = [] - for layer_id in layer_ids: - layer = layers_by_id.get(layer_id) - if layer: - layer_names.append(layer["name"]) - - if not layer_names: - raise ValueError(( - "Metadata contain old way of storing layers information." - " It is not possible to identify layers to publish with" - " these data. Please remove Render Pass instances with" - " Subset manager and use Creator tool to recreate them." - )) - - render_pass_layers = [] - for layer_name in layer_names: - layer = layers_by_name.get(layer_name) - # NOTE This is kind of validation before validators? - if not layer: - self.log.warning( - f"Layer with name {layer_name} was not found." - ) - continue - - render_pass_layers.append(layer) - - if not render_pass_layers: - name = instance_data["name"] - self.log.warning( - f"None of the layers from the RenderPass \"{name}\"" - " exist anymore. Instance not created." - ) - return None - - instance_data["layers"] = render_pass_layers - return context.create_instance(**instance_data) diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py b/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py new file mode 100644 index 0000000000..34bb5aba24 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py @@ -0,0 +1,87 @@ +import copy +import pyblish.api +from openpype.lib import prepare_template_data + + +class CollectRenderInstances(pyblish.api.InstancePlugin): + label = "Collect Render Instances" + order = pyblish.api.CollectorOrder - 0.4 + hosts = ["tvpaint"] + families = ["render", "review"] + + def process(self, instance): + context = instance.context + creator_identifier = instance.data["creator_identifier"] + if creator_identifier == "render.layer": + self._collect_data_for_render_layer(instance) + + elif creator_identifier == "render.pass": + self._collect_data_for_render_pass(instance) + + else: + if creator_identifier == "scene.review": + self._collect_data_for_review(instance) + return + + subset_name = instance.data["subset"] + instance.data["name"] = subset_name + instance.data["label"] = "{} [{}-{}]".format( + subset_name, + context.data["sceneMarkIn"] + 1, + context.data["sceneMarkOut"] + 1 + ) + + def _collect_data_for_render_layer(self, instance): + instance.data["families"].append("renderLayer") + creator_attributes = instance.data["creator_attributes"] + group_id = creator_attributes["group_id"] + if creator_attributes["mark_for_review"]: + instance.data["families"].append("review") + + layers_data = instance.context.data["layersData"] + instance.data["layers"] = [ + copy.deepcopy(layer) + for layer in layers_data + if layer["group_id"] == group_id + ] + + def _collect_data_for_render_pass(self, instance): + instance.data["families"].append("renderPass") + + layer_names = set(instance.data["layer_names"]) + layers_data = instance.context.data["layersData"] + + creator_attributes = instance.data["creator_attributes"] + if creator_attributes["mark_for_review"]: + instance.data["families"].append("review") + + instance.data["layers"] = [ + copy.deepcopy(layer) + for layer in layers_data + if layer["name"] in layer_names + ] + + render_layer_data = None + render_layer_id = creator_attributes["render_layer_instance_id"] + for in_data in instance.context.data["workfileInstances"]: + if ( + in_data["creator_identifier"] == "render.layer" + and in_data["instance_id"] == render_layer_id + ): + render_layer_data = in_data + break + + instance.data["renderLayerData"] = copy.deepcopy(render_layer_data) + # Invalid state + if render_layer_data is None: + return + render_layer_name = render_layer_data["variant"] + subset_name = instance.data["subset"] + instance.data["subset"] = subset_name.format( + **prepare_template_data({"renderlayer": render_layer_name}) + ) + + def _collect_data_for_review(self, instance): + instance.data["layers"] = copy.deepcopy( + instance.context.data["layersData"] + ) From db75420899f3419e937dbf7b88d6f89e8dc42ae1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 11:09:49 +0100 Subject: [PATCH 0613/1271] removed legacy creators --- .../plugins/create/create_render_layer.py | 231 ------------------ .../plugins/create/create_render_pass.py | 167 ------------- 2 files changed, 398 deletions(-) delete mode 100644 openpype/hosts/tvpaint/plugins/create/create_render_layer.py delete mode 100644 openpype/hosts/tvpaint/plugins/create/create_render_pass.py diff --git a/openpype/hosts/tvpaint/plugins/create/create_render_layer.py b/openpype/hosts/tvpaint/plugins/create/create_render_layer.py deleted file mode 100644 index 009b69c4f1..0000000000 --- a/openpype/hosts/tvpaint/plugins/create/create_render_layer.py +++ /dev/null @@ -1,231 +0,0 @@ -from openpype.lib import prepare_template_data -from openpype.pipeline import CreatorError -from openpype.hosts.tvpaint.api import ( - plugin, - CommunicationWrapper -) -from openpype.hosts.tvpaint.api.lib import ( - get_layers_data, - get_groups_data, - execute_george_through_file, -) -from openpype.hosts.tvpaint.api.pipeline import list_instances - - -class CreateRenderlayer(plugin.Creator): - """Mark layer group as one instance.""" - name = "render_layer" - label = "RenderLayer" - family = "renderLayer" - icon = "cube" - defaults = ["Main"] - - rename_group = True - render_pass = "beauty" - - rename_script_template = ( - "tv_layercolor \"setcolor\"" - " {clip_id} {group_id} {r} {g} {b} \"{name}\"" - ) - - dynamic_subset_keys = [ - "renderpass", "renderlayer", "render_pass", "render_layer", "group" - ] - - @classmethod - def get_dynamic_data( - cls, variant, task_name, asset_id, project_name, host_name - ): - dynamic_data = super(CreateRenderlayer, cls).get_dynamic_data( - variant, task_name, asset_id, project_name, host_name - ) - # Use render pass name from creator's plugin - dynamic_data["renderpass"] = cls.render_pass - # Add variant to render layer - dynamic_data["renderlayer"] = variant - # Change family for subset name fill - dynamic_data["family"] = "render" - - # TODO remove - Backwards compatibility for old subset name templates - # - added 2022/04/28 - dynamic_data["render_pass"] = dynamic_data["renderpass"] - dynamic_data["render_layer"] = dynamic_data["renderlayer"] - - return dynamic_data - - @classmethod - def get_default_variant(cls): - """Default value for variant in Creator tool. - - Method checks if TVPaint implementation is running and tries to find - selected layers from TVPaint. If only one is selected it's name is - returned. - - Returns: - str: Default variant name for Creator tool. - """ - # Validate that communication is initialized - if CommunicationWrapper.communicator: - # Get currently selected layers - layers_data = get_layers_data() - - selected_layers = [ - layer - for layer in layers_data - if layer["selected"] - ] - # Return layer name if only one is selected - if len(selected_layers) == 1: - return selected_layers[0]["name"] - - # Use defaults - if cls.defaults: - return cls.defaults[0] - return None - - def process(self): - self.log.debug("Query data from workfile.") - instances = list_instances() - layers_data = get_layers_data() - - self.log.debug("Checking for selection groups.") - # Collect group ids from selection - group_ids = set() - for layer in layers_data: - if layer["selected"]: - group_ids.add(layer["group_id"]) - - # Raise if there is no selection - if not group_ids: - raise CreatorError("Nothing is selected.") - - # This creator should run only on one group - if len(group_ids) > 1: - raise CreatorError("More than one group is in selection.") - - group_id = tuple(group_ids)[0] - # If group id is `0` it is `default` group which is invalid - if group_id == 0: - raise CreatorError( - "Selection is not in group. Can't mark selection as Beauty." - ) - - self.log.debug(f"Selected group id is \"{group_id}\".") - self.data["group_id"] = group_id - - group_data = get_groups_data() - group_name = None - for group in group_data: - if group["group_id"] == group_id: - group_name = group["name"] - break - - if group_name is None: - raise AssertionError( - "Couldn't find group by id \"{}\"".format(group_id) - ) - - subset_name_fill_data = { - "group": group_name - } - - family = self.family = self.data["family"] - - # Fill dynamic key 'group' - subset_name = self.data["subset"].format( - **prepare_template_data(subset_name_fill_data) - ) - self.data["subset"] = subset_name - - # Check for instances of same group - existing_instance = None - existing_instance_idx = None - # Check if subset name is not already taken - same_subset_instance = None - same_subset_instance_idx = None - for idx, instance in enumerate(instances): - if instance["family"] == family: - if instance["group_id"] == group_id: - existing_instance = instance - existing_instance_idx = idx - elif instance["subset"] == subset_name: - same_subset_instance = instance - same_subset_instance_idx = idx - - if ( - same_subset_instance_idx is not None - and existing_instance_idx is not None - ): - break - - if same_subset_instance_idx is not None: - if self._ask_user_subset_override(same_subset_instance): - instances.pop(same_subset_instance_idx) - else: - return - - if existing_instance is not None: - self.log.info( - f"Beauty instance for group id {group_id} already exists" - ", overriding" - ) - instances[existing_instance_idx] = self.data - else: - instances.append(self.data) - - self.write_instances(instances) - - if not self.rename_group: - self.log.info("Group rename function is turned off. Skipping") - return - - self.log.debug("Querying groups data from workfile.") - groups_data = get_groups_data() - - self.log.debug("Changing name of the group.") - selected_group = None - for group_data in groups_data: - if group_data["group_id"] == group_id: - selected_group = group_data - - # Rename TVPaint group (keep color same) - # - groups can't contain spaces - new_group_name = self.data["variant"].replace(" ", "_") - rename_script = self.rename_script_template.format( - clip_id=selected_group["clip_id"], - group_id=selected_group["group_id"], - r=selected_group["red"], - g=selected_group["green"], - b=selected_group["blue"], - name=new_group_name - ) - execute_george_through_file(rename_script) - - self.log.info( - f"Name of group with index {group_id}" - f" was changed to \"{new_group_name}\"." - ) - - def _ask_user_subset_override(self, instance): - from qtpy import QtCore - from qtpy.QtWidgets import QMessageBox - - title = "Subset \"{}\" already exist".format(instance["subset"]) - text = ( - "Instance with subset name \"{}\" already exists." - "\n\nDo you want to override existing?" - ).format(instance["subset"]) - - dialog = QMessageBox() - dialog.setWindowFlags( - dialog.windowFlags() - | QtCore.Qt.WindowStaysOnTopHint - ) - dialog.setWindowTitle(title) - dialog.setText(text) - dialog.setStandardButtons(QMessageBox.Yes | QMessageBox.No) - dialog.setDefaultButton(QMessageBox.Yes) - dialog.exec_() - if dialog.result() == QMessageBox.Yes: - return True - return False diff --git a/openpype/hosts/tvpaint/plugins/create/create_render_pass.py b/openpype/hosts/tvpaint/plugins/create/create_render_pass.py deleted file mode 100644 index a44cb29f20..0000000000 --- a/openpype/hosts/tvpaint/plugins/create/create_render_pass.py +++ /dev/null @@ -1,167 +0,0 @@ -from openpype.pipeline import CreatorError -from openpype.lib import prepare_template_data -from openpype.hosts.tvpaint.api import ( - plugin, - CommunicationWrapper -) -from openpype.hosts.tvpaint.api.lib import get_layers_data -from openpype.hosts.tvpaint.api.pipeline import list_instances - - -class CreateRenderPass(plugin.Creator): - """Render pass is combination of one or more layers from same group. - - Requirement to create Render Pass is to have already created beauty - instance. Beauty instance is used as base for subset name. - """ - name = "render_pass" - label = "RenderPass" - family = "renderPass" - icon = "cube" - defaults = ["Main"] - - dynamic_subset_keys = [ - "renderpass", "renderlayer", "render_pass", "render_layer" - ] - - @classmethod - def get_dynamic_data( - cls, variant, task_name, asset_id, project_name, host_name - ): - dynamic_data = super(CreateRenderPass, cls).get_dynamic_data( - variant, task_name, asset_id, project_name, host_name - ) - dynamic_data["renderpass"] = variant - dynamic_data["family"] = "render" - - # TODO remove - Backwards compatibility for old subset name templates - # - added 2022/04/28 - dynamic_data["render_pass"] = dynamic_data["renderpass"] - - return dynamic_data - - @classmethod - def get_default_variant(cls): - """Default value for variant in Creator tool. - - Method checks if TVPaint implementation is running and tries to find - selected layers from TVPaint. If only one is selected it's name is - returned. - - Returns: - str: Default variant name for Creator tool. - """ - # Validate that communication is initialized - if CommunicationWrapper.communicator: - # Get currently selected layers - layers_data = get_layers_data() - - selected_layers = [ - layer - for layer in layers_data - if layer["selected"] - ] - # Return layer name if only one is selected - if len(selected_layers) == 1: - return selected_layers[0]["name"] - - # Use defaults - if cls.defaults: - return cls.defaults[0] - return None - - def process(self): - self.log.debug("Query data from workfile.") - instances = list_instances() - layers_data = get_layers_data() - - self.log.debug("Checking selection.") - # Get all selected layers and their group ids - group_ids = set() - selected_layers = [] - for layer in layers_data: - if layer["selected"]: - selected_layers.append(layer) - group_ids.add(layer["group_id"]) - - # Raise if nothing is selected - if not selected_layers: - raise CreatorError("Nothing is selected.") - - # Raise if layers from multiple groups are selected - if len(group_ids) != 1: - raise CreatorError("More than one group is in selection.") - - group_id = tuple(group_ids)[0] - self.log.debug(f"Selected group id is \"{group_id}\".") - - # Find beauty instance for selected layers - beauty_instance = None - for instance in instances: - if ( - instance["family"] == "renderLayer" - and instance["group_id"] == group_id - ): - beauty_instance = instance - break - - # Beauty is required for this creator so raise if was not found - if beauty_instance is None: - raise CreatorError("Beauty pass does not exist yet.") - - subset_name = self.data["subset"] - - subset_name_fill_data = {} - - # Backwards compatibility - # - beauty may be created with older creator where variant was not - # stored - if "variant" not in beauty_instance: - render_layer = beauty_instance["name"] - else: - render_layer = beauty_instance["variant"] - - subset_name_fill_data["renderlayer"] = render_layer - subset_name_fill_data["render_layer"] = render_layer - - # Format dynamic keys in subset name - new_subset_name = subset_name.format( - **prepare_template_data(subset_name_fill_data) - ) - self.data["subset"] = new_subset_name - self.log.info(f"New subset name is \"{new_subset_name}\".") - - family = self.data["family"] - variant = self.data["variant"] - - self.data["group_id"] = group_id - self.data["pass"] = variant - self.data["renderlayer"] = render_layer - - # Collect selected layer ids to be stored into instance - layer_names = [layer["name"] for layer in selected_layers] - self.data["layer_names"] = layer_names - - # Check if same instance already exists - existing_instance = None - existing_instance_idx = None - for idx, instance in enumerate(instances): - if ( - instance["family"] == family - and instance["group_id"] == group_id - and instance["pass"] == variant - ): - existing_instance = instance - existing_instance_idx = idx - break - - if existing_instance is not None: - self.log.info( - f"Render pass instance for group id {group_id}" - f" and name \"{variant}\" already exists, overriding." - ) - instances[existing_instance_idx] = self.data - else: - instances.append(self.data) - - self.write_instances(instances) From 3e6a120eaa808bf69e0bdbe893b0dd8c21c6939a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 11:46:20 +0100 Subject: [PATCH 0614/1271] fix default settings of nuke --- openpype/settings/defaults/project_settings/nuke.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index cd8ea02272..2ec2028219 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -1,7 +1,6 @@ { "general": { "menu": { - "create": "ctrl+alt+c", "publish": "ctrl+alt+p", "load": "ctrl+alt+l", "manage": "ctrl+alt+m", @@ -246,6 +245,7 @@ "sourcetype": "python", "title": "Gizmo Note", "command": "nuke.nodes.StickyNote(label='You can create your own toolbar menu in the Nuke GizmoMenu of OpenPype')", + "icon": "", "shortcut": "" } ] From df532268a2e05b6b48074336453ab3e18b86e08f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 12:00:15 +0100 Subject: [PATCH 0615/1271] add family to instance data --- openpype/pipeline/create/context.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 1567acdb79..79c9805604 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1884,6 +1884,7 @@ class CreateContext: instance_data = { "asset": asset_doc["name"], "task": task_name, + "family": self.family, "variant": variant } return creator.create( From fb93780640ed5de588cb11499ccd68d1f6a91d75 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 12:08:45 +0100 Subject: [PATCH 0616/1271] use family form creator --- openpype/pipeline/create/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 79c9805604..89eec52676 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1884,7 +1884,7 @@ class CreateContext: instance_data = { "asset": asset_doc["name"], "task": task_name, - "family": self.family, + "family": creator.family, "variant": variant } return creator.create( From 6097a9627607a70bb91d977274da8bd9f39742b6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 12:14:53 +0100 Subject: [PATCH 0617/1271] added option to use 'subset_template_family_filter' in all tvpaint creators --- openpype/hosts/tvpaint/api/plugin.py | 62 +++++++++++++++------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/openpype/hosts/tvpaint/api/plugin.py b/openpype/hosts/tvpaint/api/plugin.py index d267d87acd..397e4295f5 100644 --- a/openpype/hosts/tvpaint/api/plugin.py +++ b/openpype/hosts/tvpaint/api/plugin.py @@ -22,6 +22,10 @@ SHARED_DATA_KEY = "openpype.tvpaint.instances" class TVPaintCreatorCommon: + @property + def subset_template_family_filter(self): + return self.family + def _cache_and_get_instances(self): return cache_and_get_instances( self, SHARED_DATA_KEY, self.host.list_instances @@ -56,12 +60,33 @@ class TVPaintCreatorCommon: cur_instance_data.update(instance_data) self.host.write_instances(cur_instances) + def _custom_get_subset_name( + self, + variant, + task_name, + asset_doc, + project_name, + host_name=None, + instance=None + ): + dynamic_data = self.get_dynamic_data( + variant, task_name, asset_doc, project_name, host_name, instance + ) + + return get_subset_name( + self.family, + variant, + task_name, + asset_doc, + project_name, + host_name, + dynamic_data=dynamic_data, + project_settings=self.project_settings, + family_filter=self.subset_template_family_filter + ) + class TVPaintCreator(NewCreator, TVPaintCreatorCommon): - @property - def subset_template_family_filter(self): - return self.family - def collect_instances(self): self._collect_create_instances() @@ -101,30 +126,8 @@ class TVPaintCreator(NewCreator, TVPaintCreatorCommon): output["task"] = task_name return output - def get_subset_name( - self, - variant, - task_name, - asset_doc, - project_name, - host_name=None, - instance=None - ): - dynamic_data = self.get_dynamic_data( - variant, task_name, asset_doc, project_name, host_name, instance - ) - - return get_subset_name( - self.family, - variant, - task_name, - asset_doc, - project_name, - host_name, - dynamic_data=dynamic_data, - project_settings=self.project_settings, - family_filter=self.subset_template_family_filter - ) + def get_subset_name(self, *args, **kwargs): + return self._custom_get_subset_name(*args, **kwargs) def _store_new_instance(self, new_instance): instances_data = self.host.list_instances() @@ -140,6 +143,9 @@ class TVPaintAutoCreator(AutoCreator, TVPaintCreatorCommon): def update_instances(self, update_list): self._update_create_instances(update_list) + def get_subset_name(self, *args, **kwargs): + return self._custom_get_subset_name(*args, **kwargs) + class Creator(LegacyCreator): def __init__(self, *args, **kwargs): From 222b39f024e5fcf8890145f08eba8298282ddb39 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 15 Feb 2023 12:22:13 +0100 Subject: [PATCH 0618/1271] nuke: adding back Create shortcut it was removed accidentally --- openpype/settings/defaults/project_settings/nuke.json | 3 ++- .../schemas/projects_schema/schema_project_nuke.json | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 2ec2028219..d475c337d9 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -1,6 +1,7 @@ { "general": { "menu": { + "create": "ctrl+alt+c", "publish": "ctrl+alt+p", "load": "ctrl+alt+l", "manage": "ctrl+alt+m", @@ -532,4 +533,4 @@ "profiles": [] }, "filters": {} -} \ No newline at end of file +} diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json b/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json index b1a8cc1812..26c64e6219 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json @@ -17,6 +17,11 @@ "key": "menu", "label": "OpenPype Menu shortcuts", "children": [ + { + "type": "text", + "key": "create", + "label": "Create..." + }, { "type": "text", "key": "publish", @@ -288,4 +293,4 @@ "name": "schema_publish_gui_filter" } ] -} \ No newline at end of file +} From d727ec1f5f58b1fcd18401cd9ad03b9a690a2cff Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 12:23:24 +0100 Subject: [PATCH 0619/1271] added screne render auto creator --- .../tvpaint/plugins/create/create_render.py | 125 +++++++++++++++++- .../publish/collect_render_instances.py | 22 +++ .../plugins/publish/collect_scene_render.py | 114 ---------------- 3 files changed, 146 insertions(+), 115 deletions(-) delete mode 100644 openpype/hosts/tvpaint/plugins/publish/collect_scene_render.py diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 8f7ba121c1..2b693d4bc0 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -32,6 +32,7 @@ Todos: import collections +from openpype.client import get_asset_by_name from openpype.lib import ( prepare_template_data, EnumDef, @@ -42,7 +43,10 @@ from openpype.pipeline.create import ( CreatedInstance, CreatorError, ) -from openpype.hosts.tvpaint.api.plugin import TVPaintCreator +from openpype.hosts.tvpaint.api.plugin import ( + TVPaintCreator, + TVPaintAutoCreator, +) from openpype.hosts.tvpaint.api.lib import ( get_layers_data, get_groups_data, @@ -480,3 +484,122 @@ class CreateRenderPass(TVPaintCreator): def get_instance_attr_defs(self): return self.get_pre_create_attr_defs() + + +class TVPaintSceneRenderCreator(TVPaintAutoCreator): + family = "render" + subset_template_family_filter = "renderScene" + identifier = "render.scene" + label = "Scene Render" + + # Settings + default_variant = "Main" + default_pass_name = "beauty" + mark_for_review = True + + def get_dynamic_data(self, variant, *args, **kwargs): + dynamic_data = super().get_dynamic_data(variant, *args, **kwargs) + dynamic_data["renderpass"] = "{renderpass}" + dynamic_data["renderlayer"] = variant + return dynamic_data + + def _create_new_instance(self): + context = self.host.get_current_context() + host_name = self.host.name + project_name = context["project_name"] + asset_name = context["asset_name"] + task_name = context["task_name"] + + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + self.default_variant, + task_name, + asset_doc, + project_name, + host_name + ) + data = { + "asset": asset_name, + "task": task_name, + "variant": self.default_variant, + "creator_attributes": { + "render_pass_name": self.default_pass_name, + "mark_for_review": True + }, + "label": self._get_label( + subset_name, + self.default_pass_name + ) + } + + new_instance = CreatedInstance( + self.family, subset_name, data, self + ) + instances_data = self.host.list_instances() + instances_data.append(new_instance.data_to_store()) + self.host.write_instances(instances_data) + self._add_instance_to_context(new_instance) + return new_instance + + def create(self): + existing_instance = None + for instance in self.create_context.instances: + if instance.creator_identifier == self.identifier: + existing_instance = instance + break + + if existing_instance is None: + return self._create_new_instance() + + context = self.host.get_current_context() + host_name = self.host.name + project_name = context["project_name"] + asset_name = context["asset_name"] + task_name = context["task_name"] + + if ( + existing_instance["asset"] != asset_name + or existing_instance["task"] != task_name + ): + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + existing_instance["variant"], + task_name, + asset_doc, + project_name, + host_name, + existing_instance + ) + existing_instance["asset"] = asset_name + existing_instance["task"] = task_name + existing_instance["subset"] = subset_name + + existing_instance["label"] = self._get_label( + existing_instance["subset"], + existing_instance["creator_attributes"]["render_pass_name"] + ) + + + + def _get_label(self, subset_name, render_layer_name): + return subset_name.format(**prepare_template_data({ + "renderlayer": render_layer_name + })) + + def get_instance_attr_defs(self): + return [ + TextDef( + "render_pass_name", + label="Pass Name", + default=self.default_pass_name, + tooltip=( + "Value is calculated during publishing and UI will update" + " label after refresh." + ) + ), + BoolDef( + "mark_for_review", + label="Review", + default=self.mark_for_review + ) + ] \ No newline at end of file diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py b/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py index 34bb5aba24..ba89deac5d 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py @@ -18,6 +18,9 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): elif creator_identifier == "render.pass": self._collect_data_for_render_pass(instance) + elif creator_identifier == "render.scene": + self._collect_data_for_render_scene(instance) + else: if creator_identifier == "scene.review": self._collect_data_for_review(instance) @@ -81,6 +84,25 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): **prepare_template_data({"renderlayer": render_layer_name}) ) + def _collect_data_for_render_scene(self, instance): + instance.data["families"].append("renderScene") + + creator_attributes = instance.data["creator_attributes"] + if creator_attributes["mark_for_review"]: + instance.data["families"].append("review") + + instance.data["layers"] = copy.deepcopy( + instance.context.data["layersData"] + ) + + render_pass_name = ( + instance.data["creator_attributes"]["render_pass_name"] + ) + subset_name = instance.data["subset"] + instance.data["subset"] = subset_name.format( + **prepare_template_data({"renderpass": render_pass_name}) + ) + def _collect_data_for_review(self, instance): instance.data["layers"] = copy.deepcopy( instance.context.data["layersData"] diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_scene_render.py b/openpype/hosts/tvpaint/plugins/publish/collect_scene_render.py deleted file mode 100644 index 92a2815ba0..0000000000 --- a/openpype/hosts/tvpaint/plugins/publish/collect_scene_render.py +++ /dev/null @@ -1,114 +0,0 @@ -import json -import copy -import pyblish.api - -from openpype.client import get_asset_by_name -from openpype.pipeline.create import get_subset_name - - -class CollectRenderScene(pyblish.api.ContextPlugin): - """Collect instance which renders whole scene in PNG. - - Creates instance with family 'renderScene' which will have all layers - to render which will be composite into one result. The instance is not - collected from scene. - - Scene will be rendered with all visible layers similar way like review is. - - Instance is disabled if there are any created instances of 'renderLayer' - or 'renderPass'. That is because it is expected that this instance is - used as lazy publish of TVPaint file. - - Subset name is created similar way like 'renderLayer' family. It can use - `renderPass` and `renderLayer` keys which can be set using settings and - `variant` is filled using `renderPass` value. - """ - label = "Collect Render Scene" - order = pyblish.api.CollectorOrder - 0.39 - hosts = ["tvpaint"] - - # Value of 'render_pass' in subset name template - render_pass = "beauty" - - # Settings attributes - enabled = False - # Value of 'render_layer' and 'variant' in subset name template - render_layer = "Main" - - def process(self, context): - # Check if there are created instances of renderPass and renderLayer - # - that will define if renderScene instance is enabled after - # collection - any_created_instance = False - for instance in context: - family = instance.data["family"] - if family in ("renderPass", "renderLayer"): - any_created_instance = True - break - - # Global instance data modifications - # Fill families - family = "renderScene" - # Add `review` family for thumbnail integration - families = [family, "review"] - - # Collect asset doc to get asset id - # - not sure if it's good idea to require asset id in - # get_subset_name? - workfile_context = context.data["workfile_context"] - # Project name from workfile context - project_name = context.data["workfile_context"]["project"] - asset_name = workfile_context["asset"] - asset_doc = get_asset_by_name(project_name, asset_name) - - # Host name from environment variable - host_name = context.data["hostName"] - # Variant is using render pass name - variant = self.render_layer - dynamic_data = { - "renderlayer": self.render_layer, - "renderpass": self.render_pass, - } - # TODO remove - Backwards compatibility for old subset name templates - # - added 2022/04/28 - dynamic_data["render_layer"] = dynamic_data["renderlayer"] - dynamic_data["render_pass"] = dynamic_data["renderpass"] - - task_name = workfile_context["task"] - subset_name = get_subset_name( - "render", - variant, - task_name, - asset_doc, - project_name, - host_name, - dynamic_data=dynamic_data, - project_settings=context.data["project_settings"] - ) - - instance_data = { - "family": family, - "families": families, - "fps": context.data["sceneFps"], - "subset": subset_name, - "name": subset_name, - "label": "{} [{}-{}]".format( - subset_name, - context.data["sceneMarkIn"] + 1, - context.data["sceneMarkOut"] + 1 - ), - "active": not any_created_instance, - "publish": not any_created_instance, - "representations": [], - "layers": copy.deepcopy(context.data["layersData"]), - "asset": asset_name, - "task": task_name, - # Add render layer to instance data - "renderlayer": self.render_layer - } - - instance = context.create_instance(**instance_data) - - self.log.debug("Created instance: {}\n{}".format( - instance, json.dumps(instance.data, indent=4) - )) From b21d55c10d9885c1d184e609253592bf4f44c6e7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 12:24:09 +0100 Subject: [PATCH 0620/1271] change families filter for validate layers visibility --- openpype/hosts/tvpaint/plugins/create/create_render.py | 2 +- .../hosts/tvpaint/plugins/publish/validate_layers_visibility.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 2b693d4bc0..2d44282879 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -602,4 +602,4 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): label="Review", default=self.mark_for_review ) - ] \ No newline at end of file + ] diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py b/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py index d3a04cc69f..47632453fc 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py @@ -8,7 +8,7 @@ class ValidateLayersVisiblity(pyblish.api.InstancePlugin): label = "Validate Layers Visibility" order = pyblish.api.ValidatorOrder - families = ["review", "renderPass", "renderLayer", "renderScene"] + families = ["review", "render"] def process(self, instance): layer_names = set() From 8598d5c1f3f5d10c9c72ad4c0f413cb5119d70fb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 12:25:18 +0100 Subject: [PATCH 0621/1271] remove empty lines --- openpype/hosts/tvpaint/plugins/create/create_render.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 2d44282879..7acd9b2260 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -579,8 +579,6 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): existing_instance["creator_attributes"]["render_pass_name"] ) - - def _get_label(self, subset_name, render_layer_name): return subset_name.format(**prepare_template_data({ "renderlayer": render_layer_name From e93c5d0d4055db7e2ff8cd7067fda24ccc243250 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 12:26:27 +0100 Subject: [PATCH 0622/1271] OP-4928 - fix wrong usage of legacy_io Import was removed, but usage stayed. Now it should be replaced from context --- openpype/hosts/photoshop/plugins/create/create_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index cdea82cb05..3d82d6b6f0 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -193,7 +193,7 @@ class ImageCreator(Creator): instance_data.pop("uuid") if not instance_data.get("task"): - instance_data["task"] = legacy_io.Session.get("AVALON_TASK") + instance_data["task"] = self.create_context.get_current_task_name() if not instance_data.get("variant"): instance_data["variant"] = '' From 423f2bcbdadc723d6499f8f92a9ed391533a75e8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 15 Feb 2023 12:26:50 +0100 Subject: [PATCH 0623/1271] removing python3 only code --- openpype/pipeline/tempdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/tempdir.py b/openpype/pipeline/tempdir.py index 3216c596da..55a1346b08 100644 --- a/openpype/pipeline/tempdir.py +++ b/openpype/pipeline/tempdir.py @@ -54,6 +54,6 @@ def create_custom_tempdir(project_name, anatomy=None): os.makedirs(custom_tempdir) except IOError as error: raise IOError( - "Path couldn't be created: {}".format(error)) from error + "Path couldn't be created: {}".format(error)) return custom_tempdir From 410ea87e18a582628fbd456549207e2dac2ef164 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 12:27:44 +0100 Subject: [PATCH 0624/1271] OP-4928 - fix wrong usage of legacy_io Import should be removed. Now it should be replaced from context. --- openpype/hosts/aftereffects/plugins/create/create_render.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 10ded8b912..02f045b0ec 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -6,8 +6,7 @@ from openpype.hosts.aftereffects import api from openpype.pipeline import ( Creator, CreatedInstance, - CreatorError, - legacy_io, + CreatorError ) from openpype.hosts.aftereffects.api.pipeline import cache_and_get_instances from openpype.lib import prepare_template_data @@ -195,7 +194,7 @@ class RenderCreator(Creator): instance_data.pop("uuid") if not instance_data.get("task"): - instance_data["task"] = legacy_io.Session.get("AVALON_TASK") + instance_data["task"] = self.create_context.get_current_task_name() if not instance_data.get("creator_attributes"): is_old_farm = instance_data["family"] != "renderLocal" From eb5d1e3816b07760c6ffdc8c71999fe1167dfdf9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 12:29:52 +0100 Subject: [PATCH 0625/1271] resave to remove empty line --- openpype/settings/defaults/project_settings/nuke.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index d475c337d9..2999d1427d 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -533,4 +533,4 @@ "profiles": [] }, "filters": {} -} +} \ No newline at end of file From 66c42dde73174c8a3b288419a616f8c23b98064a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 12:32:41 +0100 Subject: [PATCH 0626/1271] OP-4928 - removed legacy_io in workfile creator in PS Legacy_io should be eradicated, replaced by abstracted methods --- .../photoshop/plugins/create/workfile_creator.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/photoshop/plugins/create/workfile_creator.py b/openpype/hosts/photoshop/plugins/create/workfile_creator.py index 8ee9a0d832..f5d56adcbc 100644 --- a/openpype/hosts/photoshop/plugins/create/workfile_creator.py +++ b/openpype/hosts/photoshop/plugins/create/workfile_creator.py @@ -2,8 +2,7 @@ import openpype.hosts.photoshop.api as api from openpype.client import get_asset_by_name from openpype.pipeline import ( AutoCreator, - CreatedInstance, - legacy_io + CreatedInstance ) from openpype.hosts.photoshop.api.pipeline import cache_and_get_instances @@ -38,10 +37,11 @@ class PSWorkfileCreator(AutoCreator): existing_instance = instance break - project_name = legacy_io.Session["AVALON_PROJECT"] - asset_name = legacy_io.Session["AVALON_ASSET"] - task_name = legacy_io.Session["AVALON_TASK"] - host_name = legacy_io.Session["AVALON_APP"] + context = self.create_context + project_name = context.get_current_project_name() + asset_name = context.get_current_asset_name() + task_name = context.get_current_task_name() + host_name = context.host_name if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) subset_name = self.get_subset_name( From 03013095023cdce494142740a70efdbce60cb03c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 12:33:35 +0100 Subject: [PATCH 0627/1271] OP-4928 - removed legacy_io in workfile creator in AE Legacy_io should be eradicated, replaced by abstracted methods --- .../aftereffects/plugins/create/workfile_creator.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/workfile_creator.py b/openpype/hosts/aftereffects/plugins/create/workfile_creator.py index c698af896b..2e7b9d4a7e 100644 --- a/openpype/hosts/aftereffects/plugins/create/workfile_creator.py +++ b/openpype/hosts/aftereffects/plugins/create/workfile_creator.py @@ -2,8 +2,7 @@ import openpype.hosts.aftereffects.api as api from openpype.client import get_asset_by_name from openpype.pipeline import ( AutoCreator, - CreatedInstance, - legacy_io, + CreatedInstance ) from openpype.hosts.aftereffects.api.pipeline import cache_and_get_instances @@ -38,10 +37,11 @@ class AEWorkfileCreator(AutoCreator): existing_instance = instance break - project_name = legacy_io.Session["AVALON_PROJECT"] - asset_name = legacy_io.Session["AVALON_ASSET"] - task_name = legacy_io.Session["AVALON_TASK"] - host_name = legacy_io.Session["AVALON_APP"] + context = self.create_context + project_name = context.get_current_project_name() + asset_name = context.get_current_asset_name() + task_name = context.get_current_task_name() + host_name = context.host_name if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) From 122a72506257bf15354b0dedf20fdd73495c8c47 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:17:25 +0100 Subject: [PATCH 0628/1271] implemented basic of conver plugin --- .../tvpaint/plugins/create/convert_legacy.py | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 openpype/hosts/tvpaint/plugins/create/convert_legacy.py diff --git a/openpype/hosts/tvpaint/plugins/create/convert_legacy.py b/openpype/hosts/tvpaint/plugins/create/convert_legacy.py new file mode 100644 index 0000000000..79244b4fc4 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/create/convert_legacy.py @@ -0,0 +1,135 @@ +import collections + +from openpype.pipeline.create.creator_plugins import ( + SubsetConvertorPlugin, + cache_and_get_instances, +) +from openpype.hosts.tvpaint.api.plugin import SHARED_DATA_KEY +from openpype.hosts.tvpaint.api.lib import get_groups_data + + +class TVPaintLegacyConverted(SubsetConvertorPlugin): + identifier = "tvpaint.legacy.converter" + + def find_instances(self): + instances = cache_and_get_instances( + self, SHARED_DATA_KEY, self.host.list_instances + ) + + for instance in instances: + if instance.get("creator_identifier") is None: + self.add_convertor_item("Convert legacy instances") + return + + def convert(self): + current_instances = self.host.list_instances() + to_convert = collections.defaultdict(list) + converted = False + for instance in current_instances: + if instance.get("creator_identifier") is not None: + continue + converted = True + + family = instance.get("family") + if family in ( + "renderLayer", + "renderPass", + "renderScene", + "review", + "workfile", + ): + to_convert[family].append(instance) + else: + instance["keep"] = False + + # Skip if nothing was changed + if not converted: + self.remove_convertor_item() + return + + self._convert_render_layers( + to_convert["renderLayer"], current_instances) + self._convert_render_passes( + to_convert["renderpass"], current_instances) + self._convert_render_scenes( + to_convert["renderScene"], current_instances) + self._convert_workfiles( + to_convert["workfile"], current_instances) + self._convert_reviews( + to_convert["review"], current_instances) + + new_instances = [ + instance + for instance in current_instances + if instance.get("keep") is not False + ] + self.host.write_instances(new_instances) + # remove legacy item if all is fine + self.remove_convertor_item() + + def _convert_render_layers(self, render_layers, current_instances): + if not render_layers: + return + + render_layers_by_group_id = {} + for instance in current_instances: + if instance.get("creator_identifier") == "render.layer": + group_id = instance["creator_identifier"]["group_id"] + render_layers_by_group_id[group_id] = instance + + groups_by_id = { + group["group_id"]: group + for group in get_groups_data() + } + for render_layer in render_layers: + group_id = render_layer.pop("group_id") + if group_id in render_layers_by_group_id: + render_layer["keep"] = False + continue + render_layer["creator_identifier"] = "render.layer" + render_layer["instance_id"] = render_layer.pop("uuid") + render_layer["creator_attributes"] = { + "group_id": group_id + } + render_layer["family"] = "render" + group = groups_by_id[group_id] + group["variant"] = group["name"] + + def _convert_render_passes(self, render_passes, current_instances): + if not render_passes: + return + + render_layers_by_group_id = {} + for instance in current_instances: + if instance.get("creator_identifier") == "render.layer": + group_id = instance["creator_identifier"]["group_id"] + render_layers_by_group_id[group_id] = instance + + for render_pass in render_passes: + group_id = render_pass.pop("group_id") + render_layer = render_layers_by_group_id.get(group_id) + if not render_layer: + render_pass["keep"] = False + continue + + render_pass["creator_identifier"] = "render.pass" + render_pass["instance_id"] = render_pass.pop("uuid") + render_pass["family"] = "render" + + render_pass["creator_attributes"] = { + "render_layer_instance_id": render_layer["instance_id"] + } + render_pass["variant"] = render_pass.pop("pass") + render_pass.pop("renderlayer") + + def _convert_render_scenes(self, render_scenes, current_instances): + for render_scene in render_scenes: + render_scene["keep"] = False + + def _convert_workfiles(self, workfiles, current_instances): + for render_scene in workfiles: + render_scene["keep"] = False + + def _convert_reviews(self, reviews, current_instances): + for render_scene in reviews: + render_scene["keep"] = False From 06e11a45c20740307a45273ee236d44f3272d55c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:17:39 +0100 Subject: [PATCH 0629/1271] removed legacy creator --- openpype/hosts/tvpaint/api/plugin.py | 47 ++-------------------------- 1 file changed, 3 insertions(+), 44 deletions(-) diff --git a/openpype/hosts/tvpaint/api/plugin.py b/openpype/hosts/tvpaint/api/plugin.py index 397e4295f5..64784bfb83 100644 --- a/openpype/hosts/tvpaint/api/plugin.py +++ b/openpype/hosts/tvpaint/api/plugin.py @@ -1,21 +1,15 @@ import re -import uuid -from openpype.pipeline import ( - LegacyCreator, - LoaderPlugin, - registered_host, -) +from openpype.pipeline import LoaderPlugin from openpype.pipeline.create import ( CreatedInstance, get_subset_name, AutoCreator, - Creator as NewCreator, + Creator, ) from openpype.pipeline.create.creator_plugins import cache_and_get_instances from .lib import get_layers_data -from .pipeline import get_current_workfile_context SHARED_DATA_KEY = "openpype.tvpaint.instances" @@ -86,7 +80,7 @@ class TVPaintCreatorCommon: ) -class TVPaintCreator(NewCreator, TVPaintCreatorCommon): +class TVPaintCreator(Creator, TVPaintCreatorCommon): def collect_instances(self): self._collect_create_instances() @@ -147,41 +141,6 @@ class TVPaintAutoCreator(AutoCreator, TVPaintCreatorCommon): return self._custom_get_subset_name(*args, **kwargs) -class Creator(LegacyCreator): - def __init__(self, *args, **kwargs): - super(Creator, self).__init__(*args, **kwargs) - # Add unified identifier created with `uuid` module - self.data["uuid"] = str(uuid.uuid4()) - - @classmethod - def get_dynamic_data(cls, *args, **kwargs): - dynamic_data = super(Creator, cls).get_dynamic_data(*args, **kwargs) - - # Change asset and name by current workfile context - workfile_context = get_current_workfile_context() - asset_name = workfile_context.get("asset") - task_name = workfile_context.get("task") - if "asset" not in dynamic_data and asset_name: - dynamic_data["asset"] = asset_name - - if "task" not in dynamic_data and task_name: - dynamic_data["task"] = task_name - return dynamic_data - - def write_instances(self, data): - self.log.debug( - "Storing instance data to workfile. {}".format(str(data)) - ) - host = registered_host() - return host.write_instances(data) - - def process(self): - host = registered_host() - data = host.list_instances() - data.append(self.data) - self.write_instances(data) - - class Loader(LoaderPlugin): hosts = ["tvpaint"] From 3b87087a574fc35aa8cb7c7d190f1a5f8291f5c5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:39:51 +0100 Subject: [PATCH 0630/1271] don't use project document during context settings --- openpype/hosts/tvpaint/api/pipeline.py | 55 +++++--------------------- 1 file changed, 10 insertions(+), 45 deletions(-) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 3794bf2e24..88ad3b4b4d 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -147,27 +147,6 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): def write_instances(self, data): return write_instances(data) - # --- Legacy Create --- - def remove_instance(self, instance): - """Remove instance from current workfile metadata. - - Implementation for Subset manager tool. - """ - - 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) - # --- Workfile --- def open_workfile(self, filepath): george_script = "tv_LoadProject '\"'\"{}\"'\"'".format( @@ -515,17 +494,19 @@ def set_context_settings(asset_doc=None): Change fps, resolution and frame start/end. """ - project_name = legacy_io.active_project() - if asset_doc is None: - asset_name = legacy_io.Session["AVALON_ASSET"] - # Use current session asset if not passed - asset_doc = get_asset_by_name(project_name, asset_name) + width_key = "resolutionWidth" + height_key = "resolutionHeight" - project_doc = get_project(project_name) + width = asset_doc["data"].get(width_key) + height = asset_doc["data"].get(height_key) + if width is None or height is None: + print("Resolution was not found!") + else: + execute_george( + "tv_resizepage {} {} 0".format(width, height) + ) framerate = asset_doc["data"].get("fps") - if framerate is None: - framerate = project_doc["data"].get("fps") if framerate is not None: execute_george( @@ -534,22 +515,6 @@ def set_context_settings(asset_doc=None): 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: - execute_george( - "tv_resizepage {} {} 0".format(width, height) - ) - frame_start = asset_doc["data"].get("frameStart") frame_end = asset_doc["data"].get("frameEnd") From 7438aebc58217d85327360ee228e469dd138b168 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:40:16 +0100 Subject: [PATCH 0631/1271] remove logic related to pyblish instance toggle --- openpype/hosts/tvpaint/api/pipeline.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 88ad3b4b4d..4a737f8f72 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -87,10 +87,6 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): registered_callbacks = ( pyblish.api.registered_callbacks().get("instanceToggled") or [] ) - if self.on_instance_toggle not in registered_callbacks: - pyblish.api.register_callback( - "instanceToggled", self.on_instance_toggle - ) register_event_callback("application.launched", self.initial_launch) register_event_callback("application.exit", self.application_exit) @@ -209,28 +205,6 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): rest_api_url = "{}/timers_manager/stop_timer".format(webserver_url) requests.post(rest_api_url) - # --- Legacy Publish --- - def on_instance_toggle(self, 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.get("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 - self.write_instances(current_instances) - def containerise( name, namespace, members, context, loader, current_containers=None From 7721520dd8cbb58b96a39cc2bb46ce0034767db2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:40:36 +0100 Subject: [PATCH 0632/1271] set context settings is called with explicit arguments --- openpype/hosts/tvpaint/api/pipeline.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 4a737f8f72..575e6aa755 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -185,7 +185,15 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): return log.info("Setting up project...") - set_context_settings() + global_context = get_global_context() + project_name = global_context.get("project_name") + asset_name = global_context.get("aset_name") + if not project_name or not asset_name: + return + + asset_doc = get_asset_by_name(project_name, asset_name) + + set_context_settings(project_name, asset_doc) def application_exit(self): """Logic related to TimerManager. @@ -462,7 +470,7 @@ def get_containers(): return output -def set_context_settings(asset_doc=None): +def set_context_settings(project_name, asset_doc): """Set workfile settings by asset document data. Change fps, resolution and frame start/end. From 118cf08d91e9fd3a4a540a5bf5d118198965fad6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:40:50 +0100 Subject: [PATCH 0633/1271] removed not needed tools --- .../hosts/tvpaint/api/communication_server.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/openpype/hosts/tvpaint/api/communication_server.py b/openpype/hosts/tvpaint/api/communication_server.py index 6fd2d69373..e94e64e04a 100644 --- a/openpype/hosts/tvpaint/api/communication_server.py +++ b/openpype/hosts/tvpaint/api/communication_server.py @@ -309,8 +309,6 @@ class QtTVPaintRpc(BaseTVPaintRpc): 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), @@ -330,18 +328,6 @@ class QtTVPaintRpc(BaseTVPaintRpc): 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_publisher_tool) @@ -859,10 +845,6 @@ class QtCommunicator(BaseCommunicator): "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", From 38ff3adc4ea9e614f44f7fbbb84655bb91b87250 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:42:17 +0100 Subject: [PATCH 0634/1271] use context from create context --- openpype/hosts/tvpaint/api/plugin.py | 8 +++----- .../tvpaint/plugins/create/create_render.py | 20 +++++++++---------- .../tvpaint/plugins/create/create_review.py | 10 +++++----- .../tvpaint/plugins/create/create_workfile.py | 10 +++++----- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/openpype/hosts/tvpaint/api/plugin.py b/openpype/hosts/tvpaint/api/plugin.py index 64784bfb83..96b99199f2 100644 --- a/openpype/hosts/tvpaint/api/plugin.py +++ b/openpype/hosts/tvpaint/api/plugin.py @@ -31,7 +31,6 @@ class TVPaintCreatorCommon: instance = CreatedInstance.from_existing(instance_data, self) self._add_instance_to_context(instance) - def _update_create_instances(self, update_list): if not update_list: return @@ -109,10 +108,9 @@ class TVPaintCreator(Creator, TVPaintCreatorCommon): def get_dynamic_data(self, *args, **kwargs): # Change asset and name by current workfile context - # TODO use context from 'create_context' - workfile_context = self.host.get_current_context() - asset_name = workfile_context.get("asset") - task_name = workfile_context.get("task") + create_context = self.create_context + asset_name = create_context.get_current_asset_name() + task_name = create_context.get_current_task_name() output = {} if asset_name: output["asset"] = asset_name diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 7acd9b2260..0df066edc4 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -504,11 +504,11 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): return dynamic_data def _create_new_instance(self): - context = self.host.get_current_context() - host_name = self.host.name - project_name = context["project_name"] - asset_name = context["asset_name"] - task_name = context["task_name"] + create_context = self.create_context + host_name = create_context.host_name + project_name = create_context.get_current_project_name() + asset_name = create_context.get_current_asset_name() + task_name = create_context.get_current_task_name() asset_doc = get_asset_by_name(project_name, asset_name) subset_name = self.get_subset_name( @@ -551,11 +551,11 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): if existing_instance is None: return self._create_new_instance() - context = self.host.get_current_context() - host_name = self.host.name - project_name = context["project_name"] - asset_name = context["asset_name"] - task_name = context["task_name"] + create_context = self.create_context + host_name = create_context.host_name + project_name = create_context.get_current_project_name() + asset_name = create_context.get_current_asset_name() + task_name = create_context.get_current_task_name() if ( existing_instance["asset"] != asset_name diff --git a/openpype/hosts/tvpaint/plugins/create/create_review.py b/openpype/hosts/tvpaint/plugins/create/create_review.py index 84127eb9b7..1c220831bf 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_review.py +++ b/openpype/hosts/tvpaint/plugins/create/create_review.py @@ -17,11 +17,11 @@ class TVPaintReviewCreator(TVPaintAutoCreator): existing_instance = instance break - context = self.host.get_current_context() - host_name = self.host.name - project_name = context["project_name"] - asset_name = context["asset_name"] - task_name = context["task_name"] + create_context = self.create_context + host_name = create_context.host_name + project_name = create_context.get_current_project_name() + asset_name = create_context.get_current_asset_name() + task_name = create_context.get_current_task_name() if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) diff --git a/openpype/hosts/tvpaint/plugins/create/create_workfile.py b/openpype/hosts/tvpaint/plugins/create/create_workfile.py index e421fbc3f8..d968e4f77d 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_workfile.py +++ b/openpype/hosts/tvpaint/plugins/create/create_workfile.py @@ -17,11 +17,11 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): existing_instance = instance break - context = self.host.get_current_context() - host_name = self.host.name - project_name = context["project_name"] - asset_name = context["asset_name"] - task_name = context["task_name"] + create_context = self.create_context + host_name = create_context.host_name + project_name = create_context.get_current_project_name() + asset_name = create_context.get_current_asset_name() + task_name = create_context.get_current_task_name() if existing_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) From 6ab581df7da24302158d32c9a68a9baca33b1cb3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:45:22 +0100 Subject: [PATCH 0635/1271] on first reset always go to create tab --- openpype/tools/publisher/window.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 097e289f32..5ef25c9f8c 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -647,10 +647,7 @@ class PublisherWindow(QtWidgets.QDialog): # otherwise 'create' is used # - this happens only on first show if first_reset: - if self._overview_widget.has_items(): - self._go_to_publish_tab() - else: - self._go_to_create_tab() + self._go_to_create_tab() elif ( not self._is_on_create_tab() From 1cc9a7a90fd6deef343b45f0944bcefed6497521 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:45:45 +0100 Subject: [PATCH 0636/1271] change tab on reset only if is on report tab (Details for user) --- openpype/tools/publisher/window.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 5ef25c9f8c..ef9c99d998 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -649,11 +649,8 @@ class PublisherWindow(QtWidgets.QDialog): if first_reset: self._go_to_create_tab() - elif ( - not self._is_on_create_tab() - and not self._is_on_publish_tab() - ): - # If current tab is not 'Create' or 'Publish' go to 'Publish' + elif self._is_on_report_tab(): + # Go to 'Publish' tab if is on 'Details' tab # - this can happen when publishing started and was reset # at that moment it doesn't make sense to stay at publish # specific tabs. From 37a7841db8024341cbc4fa0c7881c6925ab7a188 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 15:46:02 +0100 Subject: [PATCH 0637/1271] reordered methods to match order of tabs in UI --- openpype/tools/publisher/window.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index ef9c99d998..86eed31afd 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -566,24 +566,24 @@ class PublisherWindow(QtWidgets.QDialog): def _go_to_publish_tab(self): self._set_current_tab("publish") - def _go_to_details_tab(self): - self._set_current_tab("details") - def _go_to_report_tab(self): self._set_current_tab("report") + def _go_to_details_tab(self): + self._set_current_tab("details") + def _is_on_create_tab(self): return self._is_current_tab("create") def _is_on_publish_tab(self): return self._is_current_tab("publish") - def _is_on_details_tab(self): - return self._is_current_tab("details") - def _is_on_report_tab(self): return self._is_current_tab("report") + def _is_on_details_tab(self): + return self._is_current_tab("details") + def _set_publish_overlay_visibility(self, visible): if visible: widget = self._publish_overlay From 0abcbc152390c00aa596b80ccc40c6a64c4861c4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:04:59 +0100 Subject: [PATCH 0638/1271] OP-4642 - added additional command arguments to Settings --- .../schemas/schema_global_publish.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3956f403f4..5333d514b5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -286,6 +286,20 @@ "label": "View", "type": "text" }, + { + "key": "oiiotool_args", + "label": "OIIOtool arguments", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "additional_command_args", + "label": "Additional command line arguments", + "type": "list", + "object_type": "text" + } + ] + }, { "type": "schema", "name": "schema_representation_tags" From 5ec5cda2bcd1e68de459496c20a1cc9699ba7144 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:08:06 +0100 Subject: [PATCH 0639/1271] OP-4642 - added additional command arguments for oiiotool Some extension requires special command line arguments (.dpx and binary depth). --- openpype/lib/transcoding.py | 6 ++++++ openpype/plugins/publish/extract_color_transcode.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 18273dd432..95042fb74c 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1047,6 +1047,7 @@ def convert_colorspace( target_colorspace, view=None, display=None, + additional_command_args=None, logger=None ): """Convert source file from one color space to another. @@ -1066,6 +1067,8 @@ def convert_colorspace( view (str): name for viewer space (ocio valid) both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) + additional_command_args (list): arguments for oiiotool (like binary + depth for .dpx) logger (logging.Logger): Logger used for logging. Raises: ValueError: if misconfigured @@ -1088,6 +1091,9 @@ def convert_colorspace( if not target_colorspace and not all([view, display]): raise ValueError("Both screen and display must be set.") + if additional_command_args: + oiio_cmd.extend(additional_command_args) + if target_colorspace: oiio_cmd.extend(["--colorconvert", source_colorspace, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4a03e623fd..3de404125d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -128,6 +128,9 @@ class ExtractOIIOTranscode(publish.Extractor): if display: new_repre["colorspaceData"]["display"] = display + additional_command_args = (output_def["oiiotool_args"] + ["additional_command_args"]) + files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: @@ -144,6 +147,7 @@ class ExtractOIIOTranscode(publish.Extractor): target_colorspace, view, display, + additional_command_args, self.log ) From b30979b8c1a4d4da0d998862516f95a89e522d9d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:21:25 +0100 Subject: [PATCH 0640/1271] OP-4642 - refactored newly added representations --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3de404125d..8c4ef59de9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -82,6 +82,7 @@ class ExtractOIIOTranscode(publish.Extractor): if not profile: return + new_representations = [] repres = instance.data.get("representations") or [] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) @@ -174,9 +175,7 @@ class ExtractOIIOTranscode(publish.Extractor): if tag == "review": added_review = True - new_repre["tags"].append("newly_added") - - instance.data["representations"].append(new_repre) + new_representations.append(new_repre) added_representations = True if added_representations: @@ -185,15 +184,11 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] - # TODO implement better way, for now do not delete new repre - # new repre might have 'delete' tag to removed, but it first must - # be there for review to be created - if "newly_added" in tags: - tags.remove("newly_added") - continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) + instance.data["representations"].extend(new_representations) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From 73cca8299506e95b2ec515e6ac085b85a9b2049d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:24:53 +0100 Subject: [PATCH 0641/1271] OP-4642 - refactored query of representations line 73 returns if no representations. --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 8c4ef59de9..de36ea7d5f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -83,7 +83,7 @@ class ExtractOIIOTranscode(publish.Extractor): return new_representations = [] - repres = instance.data.get("representations") or [] + repres = instance.data["representations"] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) if not self._repre_is_valid(repre): From 0182f73e32f42f130bf71249ce137d1b95788953 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 16:51:23 +0100 Subject: [PATCH 0642/1271] fix double spaces --- openpype/hosts/tvpaint/plugins/create/create_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 0df066edc4..2069a657b9 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -228,7 +228,7 @@ class CreateRenderlayer(TVPaintCreator): render_layer_instances = {} render_pass_instances = collections.defaultdict(list) - for instance in self.create_context.instances: + for instance in self.create_context.instances: if instance.creator_identifier == CreateRenderPass.identifier: render_layer_id = ( instance["creator_attributes"]["render_layer_instance_id"] From 7397bdcac23619177998c8460a9297af5d135319 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 17:52:42 +0100 Subject: [PATCH 0643/1271] added host property to legacy convertor --- openpype/pipeline/create/creator_plugins.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 53acb618ed..14c5d70462 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -79,6 +79,10 @@ class SubsetConvertorPlugin(object): self._log = Logger.get_logger(self.__class__.__name__) return self._log + @property + def host(self): + return self._create_context.host + @abstractproperty def identifier(self): """Converted identifier. From bb112ad1560294c92285b8794dedf28dad5c72cb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 17:53:01 +0100 Subject: [PATCH 0644/1271] fix legacy convertor --- openpype/hosts/tvpaint/plugins/create/convert_legacy.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/convert_legacy.py b/openpype/hosts/tvpaint/plugins/create/convert_legacy.py index 79244b4fc4..215c87f3e5 100644 --- a/openpype/hosts/tvpaint/plugins/create/convert_legacy.py +++ b/openpype/hosts/tvpaint/plugins/create/convert_legacy.py @@ -12,14 +12,11 @@ class TVPaintLegacyConverted(SubsetConvertorPlugin): identifier = "tvpaint.legacy.converter" def find_instances(self): - instances = cache_and_get_instances( + instances_by_identifier = cache_and_get_instances( self, SHARED_DATA_KEY, self.host.list_instances ) - - for instance in instances: - if instance.get("creator_identifier") is None: - self.add_convertor_item("Convert legacy instances") - return + if instances_by_identifier[None]: + self.add_convertor_item("Convert legacy instances") def convert(self): current_instances = self.host.list_instances() From c0f9811808c6a60f221c1b80a3c3e6bd76d4a6ac Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 17:53:14 +0100 Subject: [PATCH 0645/1271] fix render scene subset name creation --- openpype/hosts/tvpaint/plugins/create/create_render.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 2069a657b9..d2eb693ab9 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -579,9 +579,9 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): existing_instance["creator_attributes"]["render_pass_name"] ) - def _get_label(self, subset_name, render_layer_name): + def _get_label(self, subset_name, render_pass_name): return subset_name.format(**prepare_template_data({ - "renderlayer": render_layer_name + "renderpass": render_pass_name })) def get_instance_attr_defs(self): From 54be749de62cbc5ee2847a71d6ddb4a02c7ff04c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 17:55:25 +0100 Subject: [PATCH 0646/1271] safe string formatting --- .../tvpaint/plugins/create/create_render.py | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index d2eb693ab9..ebce695801 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -389,11 +389,16 @@ class CreateRenderPass(TVPaintCreator): subset_name_fill_data = {"renderlayer": render_layer} # Format dynamic keys in subset name - new_subset_name = subset_name.format( - **prepare_template_data(subset_name_fill_data) - ) - self.log.info(f"New subset name is \"{new_subset_name}\".") - instance_data["label"] = new_subset_name + label = subset_name + try: + label = label.format( + **prepare_template_data(subset_name_fill_data) + ) + except (KeyError, ValueError): + pass + + self.log.info(f"New subset name is \"{label}\".") + instance_data["label"] = label instance_data["group"] = f"{self.get_group_label()} ({render_layer})" instance_data["layer_names"] = list(selected_layer_names) if "creator_attributes" not in instance_data: @@ -580,9 +585,14 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): ) def _get_label(self, subset_name, render_pass_name): - return subset_name.format(**prepare_template_data({ - "renderpass": render_pass_name - })) + try: + subset_name = subset_name.format(**prepare_template_data({ + "renderpass": render_pass_name + })) + except (KeyError, ValueError): + pass + + return subset_name def get_instance_attr_defs(self): return [ From 8441a5c54b64b14f1d3cd1911c89be36c7f46f3a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 18:14:47 +0100 Subject: [PATCH 0647/1271] added at least some icons to creators --- openpype/hosts/tvpaint/plugins/create/create_render.py | 5 +++-- openpype/hosts/tvpaint/plugins/create/create_review.py | 1 + openpype/hosts/tvpaint/plugins/create/create_workfile.py | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index ebce695801..e8d6d2bb88 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -61,7 +61,7 @@ class CreateRenderlayer(TVPaintCreator): family = "render" subset_template_family_filter = "renderLayer" identifier = "render.layer" - icon = "fa.cube" + icon = "fa5.images" # George script to change color group rename_script_template = ( @@ -266,11 +266,11 @@ class CreateRenderlayer(TVPaintCreator): class CreateRenderPass(TVPaintCreator): - icon = "fa.cube" family = "render" subset_template_family_filter = "renderPass" identifier = "render.pass" label = "Render Pass" + icon = "fa5.image" order = CreateRenderlayer.order + 10 @@ -496,6 +496,7 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): subset_template_family_filter = "renderScene" identifier = "render.scene" label = "Scene Render" + icon = "fa.file-image-o" # Settings default_variant = "Main" diff --git a/openpype/hosts/tvpaint/plugins/create/create_review.py b/openpype/hosts/tvpaint/plugins/create/create_review.py index 1c220831bf..1172b53032 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_review.py +++ b/openpype/hosts/tvpaint/plugins/create/create_review.py @@ -7,6 +7,7 @@ class TVPaintReviewCreator(TVPaintAutoCreator): family = "review" identifier = "scene.review" label = "Review" + icon = "ei.video" default_variant = "Main" diff --git a/openpype/hosts/tvpaint/plugins/create/create_workfile.py b/openpype/hosts/tvpaint/plugins/create/create_workfile.py index d968e4f77d..7e8978e73a 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_workfile.py +++ b/openpype/hosts/tvpaint/plugins/create/create_workfile.py @@ -7,6 +7,7 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): family = "workfile" identifier = "workfile" label = "Workfile" + icon = "fa.file-o" default_variant = "Main" From 7d7c8a8d74ef201e4e063a6454ca8b15ac6483dc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 18:26:31 +0100 Subject: [PATCH 0648/1271] added some dosctrings, comments and descriptions --- .../tvpaint/plugins/create/convert_legacy.py | 18 ++++++++ .../tvpaint/plugins/create/create_render.py | 46 ++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/create/convert_legacy.py b/openpype/hosts/tvpaint/plugins/create/convert_legacy.py index 215c87f3e5..538c6e4c5e 100644 --- a/openpype/hosts/tvpaint/plugins/create/convert_legacy.py +++ b/openpype/hosts/tvpaint/plugins/create/convert_legacy.py @@ -9,6 +9,14 @@ from openpype.hosts.tvpaint.api.lib import get_groups_data class TVPaintLegacyConverted(SubsetConvertorPlugin): + """Conversion of legacy instances in scene to new creators. + + This convertor handles only instances created by core creators. + + All instances that would be created using auto-creators are removed as at + the moment of finding them would there already be existing instances. + """ + identifier = "tvpaint.legacy.converter" def find_instances(self): @@ -68,6 +76,7 @@ class TVPaintLegacyConverted(SubsetConvertorPlugin): if not render_layers: return + # Look for possible existing render layers in scene render_layers_by_group_id = {} for instance in current_instances: if instance.get("creator_identifier") == "render.layer": @@ -80,22 +89,30 @@ class TVPaintLegacyConverted(SubsetConvertorPlugin): } for render_layer in render_layers: group_id = render_layer.pop("group_id") + # Just remove legacy instance if group is already occupied if group_id in render_layers_by_group_id: render_layer["keep"] = False continue + # Add identifier render_layer["creator_identifier"] = "render.layer" + # Change 'uuid' to 'instance_id' render_layer["instance_id"] = render_layer.pop("uuid") + # Fill creator attributes render_layer["creator_attributes"] = { "group_id": group_id } render_layer["family"] = "render" group = groups_by_id[group_id] + # Use group name for variant group["variant"] = group["name"] def _convert_render_passes(self, render_passes, current_instances): if not render_passes: return + # Render passes must have available render layers so we look for render + # layers first + # - '_convert_render_layers' must be called before this method render_layers_by_group_id = {} for instance in current_instances: if instance.get("creator_identifier") == "render.layer": @@ -119,6 +136,7 @@ class TVPaintLegacyConverted(SubsetConvertorPlugin): render_pass["variant"] = render_pass.pop("pass") render_pass.pop("renderlayer") + # Rest of instances are just marked for deletion def _convert_render_scenes(self, render_scenes, current_instances): for render_scene in render_scenes: render_scene["keep"] = False diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index e8d6d2bb88..87d9014922 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -25,6 +25,10 @@ default 'color' blend more. In that case it is not recommended to use this workflow at all as other blend modes may affect all layers in clip which can't be done. +There is special case for simple publishing of scene which is called +'render.scene'. That will use all visible layers and render them as one big +sequence. + Todos: Add option to extract marked layers and passes as json output format for AfterEffects. @@ -53,9 +57,42 @@ from openpype.hosts.tvpaint.api.lib import ( execute_george_through_file, ) +RENDER_LAYER_DETAILED_DESCRIPTIONS = ( +"""Render Layer is "a group of TVPaint layers" + +Be aware Render Layer is not TVPaint layer. + +All TVPaint layers in the scene with the color group id are rendered in the +beauty pass. To create sub passes use Render Layer creator which is +dependent on existence of render layer instance. + +The group can represent an asset (tree) or different part of scene that consist +of one or more TVPaint layers that can be used as single item during +compositing (for example). + +In some cases may be needed to have sub parts of the layer. For example 'Bob' +could be Render Layer which has 'Arm', 'Head' and 'Body' as Render Passes. +""" +) + + +RENDER_PASS_DETAILED_DESCRIPTIONS = ( +"""Render Pass is sub part of Rende Layer. + +Render Pass can consist of one or more TVPaint layers. Render Layers must +belong to a Render Layer. Marker TVPaint layers will change it's group color +to match group color of Render Layer. +""" +) + class CreateRenderlayer(TVPaintCreator): - """Mark layer group as one instance.""" + """Mark layer group as Render layer instance. + + All TVPaint layers in the scene with the color group id are rendered in the + beauty pass. To create sub passes use Render Layer creator which is + dependent on existence of render layer instance. + """ label = "Render Layer" family = "render" @@ -68,10 +105,15 @@ class CreateRenderlayer(TVPaintCreator): "tv_layercolor \"setcolor\"" " {clip_id} {group_id} {r} {g} {b} \"{name}\"" ) + # Order to be executed before Render Pass creator order = 90 + description = "Mark TVPaint color group as one Render Layer." + detailed_description = RENDER_LAYER_DETAILED_DESCRIPTIONS # Settings + # - Default render pass name for beauty render_pass = "beauty" + # - Mark by default instance for review mark_for_review = True def get_dynamic_data( @@ -271,6 +313,8 @@ class CreateRenderPass(TVPaintCreator): identifier = "render.pass" label = "Render Pass" icon = "fa5.image" + description = "Mark selected TVPaint layers as pass of Render Layer." + detailed_description = RENDER_PASS_DETAILED_DESCRIPTIONS order = CreateRenderlayer.order + 10 From d440956b4ecee14b9d1ca8962f1c523112c6c9d7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 18:26:43 +0100 Subject: [PATCH 0649/1271] fix of empty chars --- openpype/hosts/tvpaint/plugins/create/create_render.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 87d9014922..21f6f86eb6 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -71,7 +71,7 @@ of one or more TVPaint layers that can be used as single item during compositing (for example). In some cases may be needed to have sub parts of the layer. For example 'Bob' -could be Render Layer which has 'Arm', 'Head' and 'Body' as Render Passes. +could be Render Layer which has 'Arm', 'Head' and 'Body' as Render Passes. """ ) @@ -81,7 +81,7 @@ RENDER_PASS_DETAILED_DESCRIPTIONS = ( Render Pass can consist of one or more TVPaint layers. Render Layers must belong to a Render Layer. Marker TVPaint layers will change it's group color -to match group color of Render Layer. +to match group color of Render Layer. """ ) From b70c6e4bfd433c1470efa1fde319834ec7068264 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 18:32:56 +0100 Subject: [PATCH 0650/1271] OP-4938 - fix obsolete access to instance change --- openpype/hosts/aftereffects/plugins/create/create_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 02f045b0ec..c20b0ec51b 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -126,7 +126,7 @@ class RenderCreator(Creator): subset_change = _changes.get("subset") if subset_change: api.get_stub().rename_item(created_inst.data["members"][0], - subset_change[1]) + subset_change.new_value) def remove_instances(self, instances): for instance in instances: From f73b84d6c36bdf31b48acd01292404400eee0196 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 18:40:33 +0100 Subject: [PATCH 0651/1271] added some basic options for settings --- openpype/hosts/tvpaint/plugins/create/create_render.py | 8 +++++--- openpype/hosts/tvpaint/plugins/create/create_workfile.py | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 21f6f86eb6..255f2605aa 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -112,7 +112,7 @@ class CreateRenderlayer(TVPaintCreator): # Settings # - Default render pass name for beauty - render_pass = "beauty" + default_pass_name = "beauty" # - Mark by default instance for review mark_for_review = True @@ -122,7 +122,7 @@ class CreateRenderlayer(TVPaintCreator): dynamic_data = super().get_dynamic_data( variant, task_name, asset_doc, project_name, host_name, instance ) - dynamic_data["renderpass"] = self.render_pass + dynamic_data["renderpass"] = self.default_pass_name dynamic_data["renderlayer"] = variant return dynamic_data @@ -543,9 +543,9 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): icon = "fa.file-image-o" # Settings - default_variant = "Main" default_pass_name = "beauty" mark_for_review = True + active_on_create = False def get_dynamic_data(self, variant, *args, **kwargs): dynamic_data = super().get_dynamic_data(variant, *args, **kwargs) @@ -581,6 +581,8 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): self.default_pass_name ) } + if not self.active_on_create: + data["active"] = False new_instance = CreatedInstance( self.family, subset_name, data, self diff --git a/openpype/hosts/tvpaint/plugins/create/create_workfile.py b/openpype/hosts/tvpaint/plugins/create/create_workfile.py index 7e8978e73a..152d29cf6f 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_workfile.py +++ b/openpype/hosts/tvpaint/plugins/create/create_workfile.py @@ -9,6 +9,8 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): label = "Workfile" icon = "fa.file-o" + # Settings + active_on_create = True default_variant = "Main" def create(self): @@ -38,6 +40,8 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): "task": task_name, "variant": self.default_variant } + if not self.active_on_create: + data["active"] = False new_instance = CreatedInstance( self.family, subset_name, data, self From 9bb6c606985aa936279633f4b5bef2f7af1cbb71 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 18:40:56 +0100 Subject: [PATCH 0652/1271] added settings for tvpaint creators --- .../defaults/project_settings/tvpaint.json | 32 ++++ .../schema_project_tvpaint.json | 171 ++++++++++++++++++ 2 files changed, 203 insertions(+) diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index 5a3e1dc2df..0441b2da00 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -10,6 +10,38 @@ } }, "stop_timer_on_application_exit": false, + "create": { + "create_workfile": { + "enabled": true, + "default_variant": "Main", + "default_variants": [] + }, + "create_review": { + "enabled": true, + "active_on_create": true, + "default_variant": "Main", + "default_variants": [] + }, + "create_render_scene": { + "enabled": true, + "active_on_create": false, + "mark_for_review": true, + "default_pass_name": "beauty", + "default_variant": "Main", + "default_variants": [] + }, + "create_render_layer": { + "mark_for_review": true, + "render_pass_name": "beauty", + "default_variant": "Main", + "default_variants": [] + }, + "create_render_pass": { + "mark_for_review": true, + "default_variant": "Main", + "default_variants": [] + } + }, "publish": { "CollectRenderScene": { "enabled": false, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json index db38c938dc..10f4b538f7 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json @@ -27,6 +27,177 @@ "key": "stop_timer_on_application_exit", "label": "Stop timer on application exit" }, + { + "type": "dict", + "collapsible": true, + "key": "create", + "label": "Create plugins", + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "create_workfile", + "label": "Create Workfile", + "is_group": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "default_variant", + "label": "Default variant" + }, + { + "type": "list", + "key": "default_variants", + "label": "Default variants", + "object_type": { + "type": "text" + } + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "create_review", + "label": "Create Review", + "is_group": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "active_on_create", + "label": "Active by default" + }, + { + "type": "text", + "key": "default_variant", + "label": "Default variant" + }, + { + "type": "list", + "key": "default_variants", + "label": "Default variants", + "object_type": { + "type": "text" + } + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "create_render_scene", + "label": "Create Render Scene", + "is_group": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "active_on_create", + "label": "Active by default" + }, + { + "type": "boolean", + "key": "mark_for_review", + "label": "Review by default" + }, + { + "type": "text", + "key": "default_pass_name", + "label": "Default beauty pass" + }, + { + "type": "text", + "key": "default_variant", + "label": "Default variant" + }, + { + "type": "list", + "key": "default_variants", + "label": "Default variants", + "object_type": { + "type": "text" + } + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "create_render_layer", + "label": "Create Render Layer", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "mark_for_review", + "label": "Review by default" + }, + { + "type": "text", + "key": "render_pass_name", + "label": "Default beauty pass" + }, + { + "type": "text", + "key": "default_variant", + "label": "Default variant" + }, + { + "type": "list", + "key": "default_variants", + "label": "Default variants", + "object_type": { + "type": "text" + } + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "create_render_pass", + "label": "Create Render Pass", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "mark_for_review", + "label": "Review by default" + }, + { + "type": "text", + "key": "default_variant", + "label": "Default variant" + }, + { + "type": "list", + "key": "default_variants", + "label": "Default variants", + "object_type": { + "type": "text" + } + } + ] + } + ] + }, { "type": "dict", "collapsible": true, From 51196290b2419d37b3c71ead5501d918a47c9211 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 15 Feb 2023 18:46:48 +0100 Subject: [PATCH 0653/1271] apply settings in creators --- .../tvpaint/plugins/create/create_render.py | 27 +++++++++++++++++++ .../tvpaint/plugins/create/create_review.py | 5 +++- .../tvpaint/plugins/create/create_workfile.py | 7 ++++- .../defaults/project_settings/tvpaint.json | 2 +- .../schema_project_tvpaint.json | 2 +- 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 255f2605aa..fa724fabe2 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -116,6 +116,15 @@ class CreateRenderlayer(TVPaintCreator): # - Mark by default instance for review mark_for_review = True + def apply_settings(self, project_settings, system_settings): + plugin_settings = ( + project_settings["tvpain"]["create"]["create_render_layer"] + ) + self.default_variant = plugin_settings["default_variant"] + self.default_variants = plugin_settings["default_variants"] + self.default_pass_name = plugin_settings["default_pass_name"] + self.mark_for_review = plugin_settings["mark_for_review"] + def get_dynamic_data( self, variant, task_name, asset_doc, project_name, host_name, instance ): @@ -321,6 +330,14 @@ class CreateRenderPass(TVPaintCreator): # Settings mark_for_review = True + def apply_settings(self, project_settings, system_settings): + plugin_settings = ( + project_settings["tvpain"]["create"]["create_render_pass"] + ) + self.default_variant = plugin_settings["default_variant"] + self.default_variants = plugin_settings["default_variants"] + self.mark_for_review = plugin_settings["mark_for_review"] + def collect_instances(self): instances_by_identifier = self._cache_and_get_instances() render_layers = { @@ -547,6 +564,16 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): mark_for_review = True active_on_create = False + def apply_settings(self, project_settings, system_settings): + plugin_settings = ( + project_settings["tvpain"]["create"]["create_render_scene"] + ) + self.default_variant = plugin_settings["default_variant"] + self.default_variants = plugin_settings["default_variants"] + self.mark_for_review = plugin_settings["mark_for_review"] + self.active_on_create = plugin_settings["active_on_create"] + self.default_pass_name = plugin_settings["default_pass_name"] + def get_dynamic_data(self, variant, *args, **kwargs): dynamic_data = super().get_dynamic_data(variant, *args, **kwargs) dynamic_data["renderpass"] = "{renderpass}" diff --git a/openpype/hosts/tvpaint/plugins/create/create_review.py b/openpype/hosts/tvpaint/plugins/create/create_review.py index 1172b53032..a0af10f3be 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_review.py +++ b/openpype/hosts/tvpaint/plugins/create/create_review.py @@ -9,7 +9,10 @@ class TVPaintReviewCreator(TVPaintAutoCreator): label = "Review" icon = "ei.video" - default_variant = "Main" + def apply_settings(self, project_settings, system_settings): + plugin_settings = project_settings["tvpain"]["create"]["create_review"] + self.default_variant = plugin_settings["default_variant"] + self.default_variants = plugin_settings["default_variants"] def create(self): existing_instance = None diff --git a/openpype/hosts/tvpaint/plugins/create/create_workfile.py b/openpype/hosts/tvpaint/plugins/create/create_workfile.py index 152d29cf6f..e247072e3b 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_workfile.py +++ b/openpype/hosts/tvpaint/plugins/create/create_workfile.py @@ -11,7 +11,12 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): # Settings active_on_create = True - default_variant = "Main" + + def apply_settings(self, project_settings, system_settings): + plugin_settings = project_settings["tvpain"]["create"]["create_workfile"] + self.default_variant = plugin_settings["default_variant"] + self.default_variants = plugin_settings["default_variants"] + self.active_on_create = plugin_settings["active_on_create"] def create(self): existing_instance = None diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index 0441b2da00..74a5af403c 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -32,7 +32,7 @@ }, "create_render_layer": { "mark_for_review": true, - "render_pass_name": "beauty", + "default_pass_name": "beauty", "default_variant": "Main", "default_variants": [] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json index 10f4b538f7..d09c666d50 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json @@ -151,7 +151,7 @@ }, { "type": "text", - "key": "render_pass_name", + "key": "default_pass_name", "label": "Default beauty pass" }, { From a651553fe6847af29c5ee4becc02304cf2ee08e3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:41:44 +0100 Subject: [PATCH 0654/1271] fix settings access --- openpype/hosts/tvpaint/plugins/create/create_render.py | 6 +++--- openpype/hosts/tvpaint/plugins/create/create_review.py | 4 +++- openpype/hosts/tvpaint/plugins/create/create_workfile.py | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index fa724fabe2..cadd045fbf 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -118,7 +118,7 @@ class CreateRenderlayer(TVPaintCreator): def apply_settings(self, project_settings, system_settings): plugin_settings = ( - project_settings["tvpain"]["create"]["create_render_layer"] + project_settings["tvpaint"]["create"]["create_render_layer"] ) self.default_variant = plugin_settings["default_variant"] self.default_variants = plugin_settings["default_variants"] @@ -332,7 +332,7 @@ class CreateRenderPass(TVPaintCreator): def apply_settings(self, project_settings, system_settings): plugin_settings = ( - project_settings["tvpain"]["create"]["create_render_pass"] + project_settings["tvpaint"]["create"]["create_render_pass"] ) self.default_variant = plugin_settings["default_variant"] self.default_variants = plugin_settings["default_variants"] @@ -566,7 +566,7 @@ class TVPaintSceneRenderCreator(TVPaintAutoCreator): def apply_settings(self, project_settings, system_settings): plugin_settings = ( - project_settings["tvpain"]["create"]["create_render_scene"] + project_settings["tvpaint"]["create"]["create_render_scene"] ) self.default_variant = plugin_settings["default_variant"] self.default_variants = plugin_settings["default_variants"] diff --git a/openpype/hosts/tvpaint/plugins/create/create_review.py b/openpype/hosts/tvpaint/plugins/create/create_review.py index a0af10f3be..423c3ab30f 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_review.py +++ b/openpype/hosts/tvpaint/plugins/create/create_review.py @@ -10,7 +10,9 @@ class TVPaintReviewCreator(TVPaintAutoCreator): icon = "ei.video" def apply_settings(self, project_settings, system_settings): - plugin_settings = project_settings["tvpain"]["create"]["create_review"] + plugin_settings = ( + project_settings["tvpaint"]["create"]["create_review"] + ) self.default_variant = plugin_settings["default_variant"] self.default_variants = plugin_settings["default_variants"] diff --git a/openpype/hosts/tvpaint/plugins/create/create_workfile.py b/openpype/hosts/tvpaint/plugins/create/create_workfile.py index e247072e3b..cc64936bdd 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_workfile.py +++ b/openpype/hosts/tvpaint/plugins/create/create_workfile.py @@ -13,7 +13,9 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): active_on_create = True def apply_settings(self, project_settings, system_settings): - plugin_settings = project_settings["tvpain"]["create"]["create_workfile"] + plugin_settings = ( + project_settings["tvpaint"]["create"]["create_workfile"] + ) self.default_variant = plugin_settings["default_variant"] self.default_variants = plugin_settings["default_variants"] self.active_on_create = plugin_settings["active_on_create"] From 69cc794ed1492e2c22c84ee8ad2499a23ecbf023 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:41:55 +0100 Subject: [PATCH 0655/1271] fix indentation --- openpype/hosts/tvpaint/plugins/create/create_render.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index cadd045fbf..41288e5968 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -58,7 +58,7 @@ from openpype.hosts.tvpaint.api.lib import ( ) RENDER_LAYER_DETAILED_DESCRIPTIONS = ( -"""Render Layer is "a group of TVPaint layers" + """Render Layer is "a group of TVPaint layers" Be aware Render Layer is not TVPaint layer. @@ -77,7 +77,7 @@ could be Render Layer which has 'Arm', 'Head' and 'Body' as Render Passes. RENDER_PASS_DETAILED_DESCRIPTIONS = ( -"""Render Pass is sub part of Rende Layer. + """Render Pass is sub part of Render Layer. Render Pass can consist of one or more TVPaint layers. Render Layers must belong to a Render Layer. Marker TVPaint layers will change it's group color From eef8990101eef7e79ad34b7b8429c164b93e14c0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:52:39 +0100 Subject: [PATCH 0656/1271] public 'discover' function can expect all possible arguments --- openpype/pipeline/plugin_discover.py | 36 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/openpype/pipeline/plugin_discover.py b/openpype/pipeline/plugin_discover.py index 7edd9ac290..e5257b801a 100644 --- a/openpype/pipeline/plugin_discover.py +++ b/openpype/pipeline/plugin_discover.py @@ -135,11 +135,12 @@ class PluginDiscoverContext(object): allow_duplicates (bool): Validate class name duplications. ignore_classes (list): List of classes that will be ignored and not added to result. + return_report (bool): Output will be full report if set to 'True'. Returns: - DiscoverResult: Object holding succesfully discovered plugins, - ignored plugins, plugins with missing abstract implementation - and duplicated plugin. + Union[DiscoverResult, list[Any]]: Object holding successfully + discovered plugins, ignored plugins, plugins with missing + abstract implementation and duplicated plugin. """ if not ignore_classes: @@ -268,9 +269,34 @@ class _GlobalDiscover: return cls._context -def discover(superclass, allow_duplicates=True): +def discover( + superclass, + allow_duplicates=True, + ignore_classes=None, + return_report=False +): + """Find and return subclasses of `superclass` + + Args: + superclass (type): Class which determines discovered subclasses. + allow_duplicates (bool): Validate class name duplications. + ignore_classes (list): List of classes that will be ignored + and not added to result. + return_report (bool): Output will be full report if set to 'True'. + + Returns: + Union[DiscoverResult, list[Any]]: Object holding successfully + discovered plugins, ignored plugins, plugins with missing + abstract implementation and duplicated plugin. + """ + context = _GlobalDiscover.get_context() - return context.discover(superclass, allow_duplicates) + return context.discover( + superclass, + allow_duplicates, + ignore_classes, + return_report + ) def get_last_discovered_plugins(superclass): From 542405775a36c08075dc118dc0801be312d0e5e1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:53:15 +0100 Subject: [PATCH 0657/1271] discover creators and convertors can pass other arguments to 'discover' function --- openpype/pipeline/create/creator_plugins.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 53acb618ed..74e6cb289a 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -605,12 +605,12 @@ class AutoCreator(BaseCreator): pass -def discover_creator_plugins(): - return discover(BaseCreator) +def discover_creator_plugins(*args, **kwargs): + return discover(BaseCreator, *args, **kwargs) -def discover_convertor_plugins(): - return discover(SubsetConvertorPlugin) +def discover_convertor_plugins(*args, **kwargs): + return discover(SubsetConvertorPlugin, *args, **kwargs) def discover_legacy_creator_plugins(): From 86a9c77c1e970a78d08888af5326c19c0fd51aa3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:53:47 +0100 Subject: [PATCH 0658/1271] reuse 'DiscoverResult' from plugin discover --- openpype/pipeline/create/context.py | 4 ++-- openpype/pipeline/publish/__init__.py | 2 -- openpype/pipeline/publish/lib.py | 23 +---------------------- 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 89eec52676..8b5da74bc7 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -21,6 +21,7 @@ from openpype.lib.attribute_definitions import ( ) from openpype.host import IPublishHost from openpype.pipeline import legacy_io +from openpype.pipeline.plugin_discover import DiscoverResult from .creator_plugins import ( Creator, @@ -1620,8 +1621,7 @@ class CreateContext: from openpype.pipeline import OpenPypePyblishPluginMixin from openpype.pipeline.publish import ( - publish_plugins_discover, - DiscoverResult + publish_plugins_discover ) # Reset publish plugins diff --git a/openpype/pipeline/publish/__init__.py b/openpype/pipeline/publish/__init__.py index dc6fc0f97a..86f3bde0dc 100644 --- a/openpype/pipeline/publish/__init__.py +++ b/openpype/pipeline/publish/__init__.py @@ -25,7 +25,6 @@ from .publish_plugins import ( from .lib import ( get_publish_template_name, - DiscoverResult, publish_plugins_discover, load_help_content_from_plugin, load_help_content_from_filepath, @@ -68,7 +67,6 @@ __all__ = ( "get_publish_template_name", - "DiscoverResult", "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 d0a9396a42..50623e5110 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -21,6 +21,7 @@ from openpype.settings import ( from openpype.pipeline import ( tempdir ) +from openpype.pipeline.plugin_discover import DiscoverResult from .contants import ( DEFAULT_PUBLISH_TEMPLATE, @@ -202,28 +203,6 @@ def get_publish_template_name( return template or default_template -class DiscoverResult: - """Hold result of publish plugins discovery. - - Stores discovered plugins duplicated plugins and file paths which - crashed on execution of file. - """ - def __init__(self): - self.plugins = [] - self.crashed_file_paths = {} - self.duplicated_plugins = [] - - def __iter__(self): - for plugin in self.plugins: - yield plugin - - def __getitem__(self, item): - return self.plugins[item] - - def __setitem__(self, item, value): - self.plugins[item] = value - - class HelpContent: def __init__(self, title, description, detail=None): self.title = title From b3a86bdbf540c8e8f0df6c52cb0873f2b84b513b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:54:18 +0100 Subject: [PATCH 0659/1271] store reports of discovered plugins --- openpype/pipeline/create/context.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 8b5da74bc7..5f1371befa 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1379,6 +1379,8 @@ class CreateContext: # Instances by their ID self._instances_by_id = {} + self.creator_discover_result = None + self.convertor_discover_result = None # Discovered creators self.creators = {} # Prepare categories of creators @@ -1666,7 +1668,9 @@ class CreateContext: creators = {} autocreators = {} manual_creators = {} - for creator_class in discover_creator_plugins(): + report = discover_creator_plugins(return_report=True) + self.creator_discover_result = report + for creator_class in report.plugins: if inspect.isabstract(creator_class): self.log.info( "Skipping abstract Creator {}".format(str(creator_class)) @@ -1711,7 +1715,9 @@ class CreateContext: def _reset_convertor_plugins(self): convertors_plugins = {} - for convertor_class in discover_convertor_plugins(): + report = discover_convertor_plugins(return_report=True) + self.convertor_discover_result = report + for convertor_class in report.plugins: if inspect.isabstract(convertor_class): self.log.info( "Skipping abstract Creator {}".format(str(convertor_class)) From 0dc73617a65dfc947c3c2715a6948928403c9f54 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:54:44 +0100 Subject: [PATCH 0660/1271] use reports to store crashed files to publish report --- openpype/tools/publisher/control.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 9ab37f2a3e..023a20ca5e 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -169,6 +169,8 @@ class PublishReport: def __init__(self, controller): self.controller = controller + self._create_discover_result = None + self._convert_discover_result = None self._publish_discover_result = None self._plugin_data = [] self._plugin_data_with_plugin = [] @@ -181,6 +183,10 @@ class PublishReport: def reset(self, context, create_context): """Reset report and clear all data.""" + self._create_discover_result = create_context.creator_discover_result + self._convert_discover_result = ( + create_context.convertor_discover_result + ) self._publish_discover_result = create_context.publish_discover_result self._plugin_data = [] self._plugin_data_with_plugin = [] @@ -293,9 +299,19 @@ class PublishReport: if plugin not in self._stored_plugins: plugins_data.append(self._create_plugin_data_item(plugin)) - crashed_file_paths = {} + reports = [] + if self._create_discover_result is not None: + reports.append(self._create_discover_result) + + if self._convert_discover_result is not None: + reports.append(self._convert_discover_result) + if self._publish_discover_result is not None: - items = self._publish_discover_result.crashed_file_paths.items() + reports.append(self._publish_discover_result) + + crashed_file_paths = {} + for report in reports: + items = report.crashed_file_paths.items() for filepath, exc_info in items: crashed_file_paths[filepath] = "".join( traceback.format_exception(*exc_info) From 56470c47e23bcb337b48731252439c22e0300130 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:55:32 +0100 Subject: [PATCH 0661/1271] added Args to documentation --- openpype/pipeline/create/creator_plugins.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 74e6cb289a..628245faf2 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -153,6 +153,12 @@ class BaseCreator: Single object should be used for multiple instances instead of single instance per one creator object. Do not store temp data or mid-process data to `self` if it's not Plugin specific. + + Args: + project_settings (Dict[str, Any]): Project settings. + system_settings (Dict[str, Any]): System settings. + create_context (CreateContext): Context which initialized creator. + headless (bool): Running in headless mode. """ # Label shown in UI From 71adfb8b5044e38ffc0c5edcb39f0eeb54e2ed18 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 09:56:07 +0100 Subject: [PATCH 0662/1271] update instance label on refresh --- openpype/tools/publisher/widgets/card_view_widgets.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/tools/publisher/widgets/card_view_widgets.py b/openpype/tools/publisher/widgets/card_view_widgets.py index 47f8ebb914..3fd5243ce9 100644 --- a/openpype/tools/publisher/widgets/card_view_widgets.py +++ b/openpype/tools/publisher/widgets/card_view_widgets.py @@ -385,6 +385,7 @@ class InstanceCardWidget(CardWidget): self._last_subset_name = None self._last_variant = None + self._last_label = None icon_widget = IconValuePixmapLabel(group_icon, self) icon_widget.setObjectName("FamilyIconLabel") @@ -462,14 +463,17 @@ class InstanceCardWidget(CardWidget): def _update_subset_name(self): variant = self.instance["variant"] subset_name = self.instance["subset"] + label = self.instance.label if ( variant == self._last_variant and subset_name == self._last_subset_name + and label == self._last_label ): return self._last_variant = variant self._last_subset_name = subset_name + self._last_label = label # Make `variant` bold label = html_escape(self.instance.label) found_parts = set(re.findall(variant, label, re.IGNORECASE)) From 7739c3a54f0b12e106d05f8742dfa63266bbde7f Mon Sep 17 00:00:00 2001 From: mre7a <68907585+mre7a@users.noreply.github.com> Date: Thu, 16 Feb 2023 10:43:13 +0100 Subject: [PATCH 0663/1271] Update openpype/hosts/maya/api/workfile_template_builder.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/maya/api/workfile_template_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index 56a53c070c..1e6094e996 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -2,7 +2,7 @@ import json from maya import cmds -from openpype.pipeline import registered_host, legacy_io +from openpype.pipeline import registered_host, get_current_asset_name from openpype.pipeline.workfile.workfile_template_builder import ( TemplateAlreadyImported, AbstractTemplateBuilder, From 035c888d9be86d546c49569b370dbe1264844fa7 Mon Sep 17 00:00:00 2001 From: mre7a <68907585+mre7a@users.noreply.github.com> Date: Thu, 16 Feb 2023 10:43:55 +0100 Subject: [PATCH 0664/1271] Update openpype/hosts/maya/api/workfile_template_builder.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/maya/api/workfile_template_builder.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index 1e6094e996..094f45221c 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -50,6 +50,7 @@ class MayaTemplateBuilder(AbstractTemplateBuilder): return True # update imported sets information + asset_name = get_current_asset_name() for node in imported_sets: if not cmds.attributeQuery("id", node=node, exists=True): continue @@ -57,9 +58,9 @@ class MayaTemplateBuilder(AbstractTemplateBuilder): continue if not cmds.attributeQuery("asset", node=node, exists=True): continue - asset = legacy_io.Session["AVALON_ASSET"] - cmds.setAttr("{}.asset".format(node), asset, type="string") + cmds.setAttr( + "{}.asset".format(node), asset_name, type="string") return True From c2b2dbb3f32aec8041b6cf6b1da08b3968d330ca Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 10:47:24 +0100 Subject: [PATCH 0665/1271] fix compatibility of QAction in Publisher --- openpype/tools/publisher/widgets/widgets.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 587bcb059d..8da3886419 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -250,21 +250,25 @@ class PublishReportBtn(PublishIconBtn): self._actions = [] def add_action(self, label, identifier): - action = QtWidgets.QAction(label) - action.setData(identifier) - action.triggered.connect( - functools.partial(self._on_action_trigger, action) + self._actions.append( + (label, identifier) ) - self._actions.append(action) - def _on_action_trigger(self, action): - identifier = action.data() + def _on_action_trigger(self, identifier): self.triggered.emit(identifier) def mouseReleaseEvent(self, event): super(PublishReportBtn, self).mouseReleaseEvent(event) menu = QtWidgets.QMenu(self) - menu.addActions(self._actions) + actions = [] + for item in self._actions: + label, identifier = item + action = QtWidgets.QAction(label, menu) + action.triggered.connect( + functools.partial(self._on_action_trigger, identifier) + ) + actions.append(action) + menu.addActions(actions) menu.exec_(event.globalPos()) From 8eef66c3df8583a8503caefd69e6bd7255fd1498 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 12:29:43 +0100 Subject: [PATCH 0666/1271] Fix creation of DiscoverResult for pyblish plugins --- openpype/pipeline/create/context.py | 7 ++++--- openpype/pipeline/publish/lib.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 5f1371befa..7672c49eb3 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -8,6 +8,9 @@ import inspect from uuid import uuid4 from contextlib import contextmanager +import pyblish.logic +import pyblish.api + from openpype.client import get_assets, get_asset_by_name from openpype.settings import ( get_system_settings, @@ -1619,8 +1622,6 @@ class CreateContext: self._reset_convertor_plugins() def _reset_publish_plugins(self, discover_publish_plugins): - import pyblish.logic - from openpype.pipeline import OpenPypePyblishPluginMixin from openpype.pipeline.publish import ( publish_plugins_discover @@ -1629,7 +1630,7 @@ class CreateContext: # Reset publish plugins self._attr_plugins_by_family = {} - discover_result = DiscoverResult() + discover_result = DiscoverResult(pyblish.api.Plugin) plugins_with_defs = [] plugins_by_targets = [] plugins_mismatch_targets = [] diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 50623e5110..5f95c6695e 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -270,7 +270,7 @@ def publish_plugins_discover(paths=None): """ # The only difference with `pyblish.api.discover` - result = DiscoverResult() + result = DiscoverResult(pyblish.api.Plugin) plugins = dict() plugin_names = [] From 70ab3cd2ab75d29e6ceb60e793a9ffc85ecb3f5c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 14:14:45 +0100 Subject: [PATCH 0667/1271] fix newly added creators on refresh --- openpype/tools/publisher/widgets/create_widget.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index dbf075c216..ef9c5b98fe 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -457,13 +457,14 @@ class CreateWidget(QtWidgets.QWidget): # TODO add details about creator new_creators.add(identifier) if identifier in existing_items: + is_new = False item = existing_items[identifier] else: + is_new = True item = QtGui.QStandardItem() item.setFlags( QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable ) - self._creators_model.appendRow(item) item.setData(creator_item.label, QtCore.Qt.DisplayRole) item.setData(creator_item.show_order, CREATOR_SORT_ROLE) @@ -473,6 +474,8 @@ class CreateWidget(QtWidgets.QWidget): CREATOR_THUMBNAIL_ENABLED_ROLE ) item.setData(creator_item.family, FAMILY_ROLE) + if is_new: + self._creators_model.appendRow(item) # Remove families that are no more available for identifier in (old_creators - new_creators): From 2fce95a9df1cf89fb801e1228698be03ad93aaa0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 16 Feb 2023 16:10:03 +0100 Subject: [PATCH 0668/1271] OP-3026 - first implementation of saving settings changes to DB This logs all changes in Settings to separate collection ('setting_log') to help debug/trace changes. This functionality is pretty basic as it will be replaced by Ayon implementation in the future. --- openpype/settings/handlers.py | 34 ++++++++++++++++++++++++++++++++++ openpype/settings/lib.py | 4 +++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/openpype/settings/handlers.py b/openpype/settings/handlers.py index 373029d9df..3706433692 100644 --- a/openpype/settings/handlers.py +++ b/openpype/settings/handlers.py @@ -235,6 +235,18 @@ class SettingsHandler(object): """ pass + @abstractmethod + def save_change_log(self, project_name, changes, settings_type): + """Stores changes to settings to separate logging collection. + + Args: + project_name(str, null): Project name for which overrides are + or None for global settings. + changes(dict): Data of project overrides with override metadata. + settings_type (str): system|project|anatomy + """ + pass + @abstractmethod def get_studio_system_settings_overrides(self, return_version): """Studio overrides of system settings.""" @@ -913,6 +925,28 @@ class MongoSettingsHandler(SettingsHandler): return data + def save_change_log(self, project_name, changes, settings_type): + """Log all settings changes to separate collection""" + if not changes: + return + + from openpype.lib import get_local_site_id + + if settings_type == "project" and not project_name: + project_name = "default" + + document = { + "user": get_local_site_id(), + "date_created": datetime.datetime.now(), + "project": project_name, + "settings_type": settings_type, + "changes": changes + } + collection_name = "settings_log" + collection = (self.settings_collection[self.database_name] + [collection_name]) + collection.insert_one(document) + def _save_project_anatomy_data(self, project_name, data_cache): # Create copy of data as they will be modified during save new_data = data_cache.data_copy() diff --git a/openpype/settings/lib.py b/openpype/settings/lib.py index 796eaeda01..73554df236 100644 --- a/openpype/settings/lib.py +++ b/openpype/settings/lib.py @@ -159,6 +159,7 @@ def save_studio_settings(data): except SaveWarningExc as exc: warnings.extend(exc.warnings) + _SETTINGS_HANDLER.save_change_log(None, changes, "system") _SETTINGS_HANDLER.save_studio_settings(data) if warnings: raise SaveWarningExc(warnings) @@ -218,7 +219,7 @@ def save_project_settings(project_name, overrides): ) except SaveWarningExc as exc: warnings.extend(exc.warnings) - + _SETTINGS_HANDLER.save_change_log(project_name, changes, "project") _SETTINGS_HANDLER.save_project_settings(project_name, overrides) if warnings: @@ -280,6 +281,7 @@ def save_project_anatomy(project_name, anatomy_data): except SaveWarningExc as exc: warnings.extend(exc.warnings) + _SETTINGS_HANDLER.save_change_log(project_name, changes, "anatomy") _SETTINGS_HANDLER.save_project_anatomy(project_name, anatomy_data) if warnings: From 43f88ca797b286f6dfaf0bcc8f1a45e099f4886c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 16 Feb 2023 16:31:27 +0100 Subject: [PATCH 0669/1271] OP-3026 - Hound --- openpype/settings/handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/handlers.py b/openpype/settings/handlers.py index 3706433692..3fa5737b6f 100644 --- a/openpype/settings/handlers.py +++ b/openpype/settings/handlers.py @@ -238,7 +238,7 @@ class SettingsHandler(object): @abstractmethod def save_change_log(self, project_name, changes, settings_type): """Stores changes to settings to separate logging collection. - + Args: project_name(str, null): Project name for which overrides are or None for global settings. From a305de03f0116e06e490266d8f34c967f12d32f6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 15:31:33 +0000 Subject: [PATCH 0670/1271] Lower tolerance for framerate difference. --- openpype/hosts/maya/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 4c8b11ecd3..b920428b20 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3422,7 +3422,7 @@ def convert_to_maya_fps(fps): min_difference = min(differences) min_index = differences.index(min_difference) supported_framerate = float_framerates[min_index] - if round(min_difference) != 0: + if min_difference > 0.1: raise ValueError( "Framerate \"{}\" strays too far from any supported framerate" " in Maya. Closest supported framerate is \"{}\"".format( From 8a5831c6fe6e4bf434d45af457da013df4def88a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 17:11:00 +0100 Subject: [PATCH 0671/1271] check for 'pyside6' when filling kwargs for file dialog --- openpype/tools/workfiles/files_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/workfiles/files_widget.py b/openpype/tools/workfiles/files_widget.py index 765d32b3d5..18be746d49 100644 --- a/openpype/tools/workfiles/files_widget.py +++ b/openpype/tools/workfiles/files_widget.py @@ -621,7 +621,7 @@ class FilesWidget(QtWidgets.QWidget): "caption": "Work Files", "filter": ext_filter } - if qtpy.API in ("pyside", "pyside2"): + if qtpy.API in ("pyside", "pyside2", "pyside6"): kwargs["dir"] = self._workfiles_root else: kwargs["directory"] = self._workfiles_root From 7109c6ea1a483ed4b95a77e3dec2e379e569a2c2 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 17 Feb 2023 16:20:26 +0800 Subject: [PATCH 0672/1271] update the naming convention for the render outputs --- openpype/hosts/max/api/lib_renderproducts.py | 8 ++- openpype/hosts/max/api/lib_rendersettings.py | 25 ++++++- .../plugins/publish/submit_3dmax_deadline.py | 66 +++++++++++++++++-- .../plugins/publish/submit_publish_job.py | 5 +- .../defaults/project_settings/deadline.json | 3 +- .../defaults/project_settings/max.json | 2 +- .../schema_project_deadline.json | 5 ++ 7 files changed, 104 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index b54e2513e1..e09934e5de 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -25,11 +25,17 @@ class RenderProducts(object): def render_product(self, container): folder = rt.maxFilePath + file = rt.maxFileName folder = folder.replace("\\", "/") setting = self._project_settings render_folder = get_default_render_folder(setting) + filename, ext = os.path.splitext(file) + + output_file = os.path.join(folder, + render_folder, + filename, + container) - output_file = os.path.join(folder, render_folder, container) context = get_current_project_asset() startFrame = context["data"].get("frameStart") endFrame = context["data"].get("frameEnd") + 1 diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index bc9b02bc77..d4784d9dfb 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -44,11 +44,15 @@ class RenderSettings(object): def set_renderoutput(self, container): folder = rt.maxFilePath # hard-coded, should be customized in the setting + file = rt.maxFileName folder = folder.replace("\\", "/") # hard-coded, set the renderoutput path setting = self._project_settings render_folder = get_default_render_folder(setting) - output_dir = os.path.join(folder, render_folder) + filename, ext = os.path.splitext(file) + output_dir = os.path.join(folder, + render_folder, + filename) if not os.path.exists(output_dir): os.makedirs(output_dir) # hard-coded, should be customized in the setting @@ -139,3 +143,22 @@ class RenderSettings(object): target, renderpass = str(renderlayer_name).split(":") aov_name = "{0}_{1}..{2}".format(dir, renderpass, ext) render_elem.SetRenderElementFileName(i, aov_name) + + def get_renderoutput(self, container, output_dir): + output = os.path.join(output_dir, container) + img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa + outputFilename = "{0}..{1}".format(output, img_fmt) + return outputFilename + + def get_render_element(self): + orig_render_elem = list() + render_elem = rt.maxOps.GetCurRenderElementMgr() + render_elem_num = render_elem.NumRenderElements() + if render_elem_num < 0: + return + + for i in range(render_elem_num): + render_element = render_elem.GetRenderElementFilename(i) + orig_render_elem.append(render_element) + + return orig_render_elem diff --git a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py index dec951da7a..9316e34898 100644 --- a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py @@ -5,8 +5,10 @@ import getpass import requests import pyblish.api - +from pymxs import runtme as rt from openpype.pipeline import legacy_io +from openpype.hosts.max.api.lib import get_current_renderer +from openpype.hosts.max.api.lib_rendersettings import RenderSettings class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): @@ -26,6 +28,7 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): group = None deadline_pool = None deadline_pool_secondary = None + framePerTask = 1 def process(self, instance): context = instance.context @@ -55,10 +58,30 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): anatomy_filled = anatomy_data.format(template_data) template_filled = anatomy_filled["publish"]["path"] filepath = os.path.normpath(template_filled) - + filepath = filepath.replace("\\", "/") self.log.info( "Using published scene for render {}".format(filepath) ) + if not os.path.exists(filepath): + self.log.error("published scene does not exist!") + + new_scene = self._clean_name(filepath) + # use the anatomy data for setting up the path of the files + orig_scene = self._clean_name(instance.context.data["currentFile"]) + expected_files = instance.data.get("expectedFiles") + + new_exp = [] + for file in expected_files: + new_file = str(file).replace(orig_scene, new_scene) + new_exp.append(new_file) + + instance.data["expectedFiles"] = new_exp + + metadata_folder = instance.data.get("publishRenderMetadataFolder") + if metadata_folder: + metadata_folder = metadata_folder.replace(orig_scene, + new_scene) + instance.data["publishRenderMetadataFolder"] = metadata_folder payload = { "JobInfo": { @@ -78,7 +101,8 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): "Frames": frames, "ChunkSize": self.chunk_size, "Priority": instance.data.get("priority", self.priority), - "Comment": comment + "Comment": comment, + "FramesPerTask": self.framePerTask }, "PluginInfo": { # Input @@ -131,8 +155,37 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): self.log.info("Ensuring output directory exists: %s" % dirname) os.makedirs(dirname) + plugin_data = {} + if self.use_published: + old_output_dir = os.path.dirname(expected_files[0]) + output_beauty = RenderSettings().get_renderoutput(instance.name, + old_output_dir) + output_beauty = output_beauty.replace(orig_scene, new_scene) + output_beauty = output_beauty.replace("\\", "/") + plugin_data["RenderOutput"] = output_beauty + + renderer_class = get_current_renderer() + renderer = str(renderer_class).split(":")[0] + if ( + renderer == "ART_Renderer" or + renderer == "Redshift_Renderer" or + renderer == "V_Ray_6_Hotfix_3" or + renderer == "V_Ray_GPU_6_Hotfix_3" or + renderer == "Default_Scanline_Renderer" or + renderer == "Quicksilver_Hardware_Renderer" + ): + render_elem_list = RenderSettings().get_render_element() + for i, render_element in enumerate(render_elem_list): + render_element = render_element.replace(orig_scene, new_scene) + plugin_data["RenderElementOutputFilename%d" % i] = render_element + + self.log.debug("plugin data:{}".format(plugin_data)) + self.log.info("Scene name was switched {} -> {}".format( + orig_scene, new_scene + )) payload["JobInfo"].update(output_data) + payload["PluginInfo"].update(plugin_data) self.submit(instance, payload) @@ -158,8 +211,13 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): raise Exception(response.text) # Store output dir for unified publisher (expectedFilesequence) expected_files = instance.data["expectedFiles"] - self.log.info("exp:{}".format(expected_files)) output_dir = os.path.dirname(expected_files[0]) instance.data["toBeRenderedOn"] = "deadline" instance.data["outputDir"] = output_dir instance.data["deadlineSubmissionJob"] = response.json() + + def rename_render_element(self): + pass + + def _clean_name(self, path): + return os.path.splitext(os.path.basename(path))[0] \ No newline at end of file diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index b70301ab7e..34fa8a8c03 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -293,8 +293,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "Group": self.deadline_group, "Pool": instance.data.get("primaryPool"), "SecondaryPool": instance.data.get("secondaryPool"), - - "OutputDirectory0": output_dir + # ensure the outputdirectory with correct slashes + "OutputDirectory0": output_dir.replace("\\", "/") }, "PluginInfo": { "Version": self.plugin_pype_version, @@ -1000,6 +1000,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "FTRACK_SERVER": os.environ.get("FTRACK_SERVER"), } + submission_type = instance.data["toBeRenderedOn"] if submission_type == "deadline": # get default deadline webservice url from deadline module self.deadline_url = instance.context.data["defaultDeadline"] diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index 0fab284c66..25d2988982 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -44,7 +44,8 @@ "chunk_size": 10, "group": "none", "deadline_pool": "", - "deadline_pool_secondary": "" + "deadline_pool_secondary": "", + "framePerTask": 1 }, "NukeSubmitDeadline": { "enabled": true, diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json index 651a074a08..617e298310 100644 --- a/openpype/settings/defaults/project_settings/max.json +++ b/openpype/settings/defaults/project_settings/max.json @@ -1,6 +1,6 @@ { "RenderSettings": { - "default_render_image_folder": "renders/max", + "default_render_image_folder": "renders/3dsmax", "aov_separator": "underscore", "image_format": "exr" } diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index afefd3266a..f71a253105 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -249,6 +249,11 @@ "type": "text", "key": "deadline_pool_secondary", "label": "Deadline pool (secondary)" + }, + { + "type": "number", + "key": "framePerTask", + "label": "Frame Per Task" } ] }, From f1843b1120575e5a40c87c09ccc222e785c23d91 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 17 Feb 2023 16:24:19 +0800 Subject: [PATCH 0673/1271] hound fix --- openpype/hosts/max/api/lib_rendersettings.py | 4 ++-- .../deadline/plugins/publish/submit_3dmax_deadline.py | 9 ++++----- .../deadline/plugins/publish/submit_publish_job.py | 3 +-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index d4784d9dfb..92f716ba54 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -158,7 +158,7 @@ class RenderSettings(object): return for i in range(render_elem_num): - render_element = render_elem.GetRenderElementFilename(i) - orig_render_elem.append(render_element) + render_element = render_elem.GetRenderElementFilename(i) + orig_render_elem.append(render_element) return orig_render_elem diff --git a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py index 9316e34898..656f550e9e 100644 --- a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py @@ -5,7 +5,6 @@ import getpass import requests import pyblish.api -from pymxs import runtme as rt from openpype.pipeline import legacy_io from openpype.hosts.max.api.lib import get_current_renderer from openpype.hosts.max.api.lib_rendersettings import RenderSettings @@ -175,9 +174,9 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): renderer == "Quicksilver_Hardware_Renderer" ): render_elem_list = RenderSettings().get_render_element() - for i, render_element in enumerate(render_elem_list): - render_element = render_element.replace(orig_scene, new_scene) - plugin_data["RenderElementOutputFilename%d" % i] = render_element + for i, element in enumerate(render_elem_list): + element = element.replace(orig_scene, new_scene) + plugin_data["RenderElementOutputFilename%d" % i] = element # noqa self.log.debug("plugin data:{}".format(plugin_data)) self.log.info("Scene name was switched {} -> {}".format( @@ -220,4 +219,4 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): pass def _clean_name(self, path): - return os.path.splitext(os.path.basename(path))[0] \ No newline at end of file + return os.path.splitext(os.path.basename(path))[0] diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 34fa8a8c03..c347f50c59 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -339,7 +339,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self.log.info("Submitting Deadline job ...") url = "{}/api/jobs".format(self.deadline_url) - response = requests.post(url, json=payload, timeout=10) + response = requests.post(url, json=payload, timeout=10, verify=False) if not response.ok: raise Exception(response.text) @@ -1000,7 +1000,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "FTRACK_SERVER": os.environ.get("FTRACK_SERVER"), } - submission_type = instance.data["toBeRenderedOn"] if submission_type == "deadline": # get default deadline webservice url from deadline module self.deadline_url = instance.context.data["defaultDeadline"] From bf3bf31d999c179e2a88e850e4f7db8e6c7082d3 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 17 Feb 2023 16:40:43 +0800 Subject: [PATCH 0674/1271] fix the bug of reference before assignment --- .../modules/deadline/plugins/publish/submit_publish_job.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index c347f50c59..22a5069ac2 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -339,7 +339,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self.log.info("Submitting Deadline job ...") url = "{}/api/jobs".format(self.deadline_url) - response = requests.post(url, json=payload, timeout=10, verify=False) + response = requests.post(url, json=payload, timeout=10) if not response.ok: raise Exception(response.text) @@ -961,6 +961,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): ''' render_job = None + submission_type = "" if instance.data.get("toBeRenderedOn") == "deadline": render_job = data.pop("deadlineSubmissionJob", None) submission_type = "deadline" From e6bf6add2f0b25d93dfd0d822b3d1d5a4393f1a2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 17 Feb 2023 10:48:15 +0100 Subject: [PATCH 0675/1271] modify integrate ftrack instances to be able upload origin filename --- .../ftrack/plugins/publish/integrate_ftrack_instances.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py index d6cb3daf0d..75f43cb22f 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py @@ -56,6 +56,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): "reference": "reference" } keep_first_subset_name_for_review = True + upload_reviewable_with_origin_name = False asset_versions_status_profiles = [] additional_metadata_keys = [] @@ -294,6 +295,13 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): ) # Add item to component list component_list.append(review_item) + if self.upload_reviewable_with_origin_name: + origin_name_component = copy.deepcopy(review_item) + filename = os.path.basename(repre_path) + origin_name_component["component_data"]["name"] = ( + os.path.splitext(filename)[0] + ) + component_list.append(origin_name_component) # Duplicate thumbnail component for all not first reviews if first_thumbnail_component is not None: From f42831ecb04d163e647f68db8b008530926de81e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 17 Feb 2023 10:48:48 +0100 Subject: [PATCH 0676/1271] added settings for original basename upload --- .../defaults/project_settings/ftrack.json | 3 ++- .../projects_schema/schema_project_ftrack.json | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/ftrack.json b/openpype/settings/defaults/project_settings/ftrack.json index cdf861df4a..f3f2345a0f 100644 --- a/openpype/settings/defaults/project_settings/ftrack.json +++ b/openpype/settings/defaults/project_settings/ftrack.json @@ -488,7 +488,8 @@ }, "keep_first_subset_name_for_review": true, "asset_versions_status_profiles": [], - "additional_metadata_keys": [] + "additional_metadata_keys": [], + "upload_reviewable_with_origin_name": false }, "IntegrateFtrackFarmStatus": { "farm_status_profiles": [] diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json b/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json index da414cc961..7050721742 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json @@ -1037,6 +1037,21 @@ {"fps": "FPS"}, {"code": "Codec"} ] + }, + { + "type": "separator" + }, + { + "type": "boolean", + "key": "upload_reviewable_with_origin_name", + "label": "Upload reviewable with origin name" + }, + { + "type": "label", + "label": "Note: Reviewable will be uploaded twice into ftrack when enabled. One with original name and second with required 'ftrackreview-mp4'. That may cause dramatic increase of ftrack storage usage." + }, + { + "type": "separator" } ] }, From 40ce393b808a60d37454712cbaa44609afe379d0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 17 Feb 2023 12:28:32 +0100 Subject: [PATCH 0677/1271] swap workfile and review settings --- openpype/hosts/tvpaint/plugins/create/create_review.py | 4 ++++ openpype/hosts/tvpaint/plugins/create/create_workfile.py | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_review.py b/openpype/hosts/tvpaint/plugins/create/create_review.py index 423c3ab30f..0164d58262 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_review.py +++ b/openpype/hosts/tvpaint/plugins/create/create_review.py @@ -9,12 +9,16 @@ class TVPaintReviewCreator(TVPaintAutoCreator): label = "Review" icon = "ei.video" + # Settings + active_on_create = True + def apply_settings(self, project_settings, system_settings): plugin_settings = ( project_settings["tvpaint"]["create"]["create_review"] ) self.default_variant = plugin_settings["default_variant"] self.default_variants = plugin_settings["default_variants"] + self.active_on_create = plugin_settings["active_on_create"] def create(self): existing_instance = None diff --git a/openpype/hosts/tvpaint/plugins/create/create_workfile.py b/openpype/hosts/tvpaint/plugins/create/create_workfile.py index cc64936bdd..d56a6c59e7 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_workfile.py +++ b/openpype/hosts/tvpaint/plugins/create/create_workfile.py @@ -9,16 +9,12 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): label = "Workfile" icon = "fa.file-o" - # Settings - active_on_create = True - def apply_settings(self, project_settings, system_settings): plugin_settings = ( project_settings["tvpaint"]["create"]["create_workfile"] ) self.default_variant = plugin_settings["default_variant"] self.default_variants = plugin_settings["default_variants"] - self.active_on_create = plugin_settings["active_on_create"] def create(self): existing_instance = None From 173b404d0e2af1712f9e7cf4d9f60ac5ea030570 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 17 Feb 2023 13:06:52 +0100 Subject: [PATCH 0678/1271] swap also usage of settings in review and workfile creators --- openpype/hosts/tvpaint/plugins/create/create_review.py | 2 ++ openpype/hosts/tvpaint/plugins/create/create_workfile.py | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_review.py b/openpype/hosts/tvpaint/plugins/create/create_review.py index 0164d58262..886dae7c39 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_review.py +++ b/openpype/hosts/tvpaint/plugins/create/create_review.py @@ -47,6 +47,8 @@ class TVPaintReviewCreator(TVPaintAutoCreator): "task": task_name, "variant": self.default_variant } + if not self.active_on_create: + data["active"] = False new_instance = CreatedInstance( self.family, subset_name, data, self diff --git a/openpype/hosts/tvpaint/plugins/create/create_workfile.py b/openpype/hosts/tvpaint/plugins/create/create_workfile.py index d56a6c59e7..41347576d5 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_workfile.py +++ b/openpype/hosts/tvpaint/plugins/create/create_workfile.py @@ -43,8 +43,6 @@ class TVPaintWorkfileCreator(TVPaintAutoCreator): "task": task_name, "variant": self.default_variant } - if not self.active_on_create: - data["active"] = False new_instance = CreatedInstance( self.family, subset_name, data, self From 9d600e45880c8e8ab107b87ff14495003d1a76a9 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 17 Feb 2023 13:20:29 +0100 Subject: [PATCH 0679/1271] OP-4974 - fix - missing set of frame range when opening scene Added check for not up-to-date loaded containers. --- openpype/hosts/harmony/api/pipeline.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/harmony/api/pipeline.py b/openpype/hosts/harmony/api/pipeline.py index 4b9849c190..686770b64e 100644 --- a/openpype/hosts/harmony/api/pipeline.py +++ b/openpype/hosts/harmony/api/pipeline.py @@ -126,10 +126,6 @@ def check_inventory(): def application_launch(event): """Event that is executed after Harmony is launched.""" - # FIXME: This is breaking server <-> client communication. - # It is now moved so it it manually called. - # ensure_scene_settings() - # check_inventory() # fills OPENPYPE_HARMONY_JS pype_harmony_path = Path(__file__).parent.parent / "js" / "PypeHarmony.js" pype_harmony_js = pype_harmony_path.read_text() @@ -146,6 +142,9 @@ def application_launch(event): harmony.send({"script": script}) inject_avalon_js() + ensure_scene_settings() + check_inventory() + def export_template(backdrops, nodes, filepath): """Export Template to file. From a28cc008302328fe7b615551a567cdd8496baa94 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 17 Feb 2023 21:13:14 +0800 Subject: [PATCH 0680/1271] add multipass as setting and regex filter in publish job setting --- openpype/hosts/max/api/lib.py | 5 +++++ .../plugins/publish/submit_3dmax_deadline.py | 17 ++++++++++++++++- .../plugins/publish/submit_publish_job.py | 3 ++- .../defaults/project_settings/deadline.json | 3 +++ .../settings/defaults/project_settings/max.json | 3 ++- .../projects_schema/schema_project_max.json | 5 +++++ 6 files changed, 33 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index 0477b43182..a28ec4b6af 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -151,3 +151,8 @@ def set_framerange(startFrame, endFrame): if startFrame is not None and endFrame is not None: frameRange = "{0}-{1}".format(startFrame, endFrame) rt.rendPickupFrames = frameRange + +def get_multipass_setting(project_setting=None): + return (project_setting["max"] + ["RenderSettings"] + ["multipass"]) diff --git a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py index 656f550e9e..d7716527b1 100644 --- a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py @@ -6,7 +6,11 @@ import requests import pyblish.api from openpype.pipeline import legacy_io -from openpype.hosts.max.api.lib import get_current_renderer +from openpype.settings import get_project_settings +from openpype.hosts.max.api.lib import ( + get_current_renderer, + get_multipass_setting +) from openpype.hosts.max.api.lib_rendersettings import RenderSettings @@ -154,7 +158,18 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): self.log.info("Ensuring output directory exists: %s" % dirname) os.makedirs(dirname) + plugin_data = {} + project_setting = get_project_settings( + legacy_io.Session["AVALON_PROJECT"] + ) + + multipass = get_multipass_setting(project_setting) + if multipass: + plugin_data["DisableMultipass"] = 0 + else: + plugin_data["DisableMultipass"] = 1 + if self.use_published: old_output_dir = os.path.dirname(expected_files[0]) output_beauty = RenderSettings().get_renderoutput(instance.name, diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 22a5069ac2..505b940356 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -126,7 +126,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): aov_filter = {"maya": [r".*([Bb]eauty).*"], "aftereffects": [r".*"], # for everything from AE "harmony": [r".*"], # for everything from AE - "celaction": [r".*"]} + "celaction": [r".*"], + "max": [r".*"]} environ_job_filter = [ "OPENPYPE_METADATA_FILE" diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index 24d1f7405b..7a5903d8e0 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -115,6 +115,9 @@ ], "harmony": [ ".*" + ], + "max": [ + ".*" ] } } diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json index 617e298310..84e0c7dba7 100644 --- a/openpype/settings/defaults/project_settings/max.json +++ b/openpype/settings/defaults/project_settings/max.json @@ -2,6 +2,7 @@ "RenderSettings": { "default_render_image_folder": "renders/3dsmax", "aov_separator": "underscore", - "image_format": "exr" + "image_format": "exr", + "multipass": true } } \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_max.json b/openpype/settings/entities/schemas/projects_schema/schema_project_max.json index fbd9358c74..8a283c1acc 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_max.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_max.json @@ -44,6 +44,11 @@ {"tga": "tga"}, {"dds": "dds"} ] + }, + { + "type": "boolean", + "key": "multipass", + "label": "multipass" } ] } From 281d53bb01d1da9504be0a19ae0a004804ac8cc9 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 17 Feb 2023 21:14:43 +0800 Subject: [PATCH 0681/1271] hound fix --- openpype/hosts/max/api/lib.py | 1 + .../modules/deadline/plugins/publish/submit_3dmax_deadline.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index a28ec4b6af..ecea8b5541 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -152,6 +152,7 @@ def set_framerange(startFrame, endFrame): frameRange = "{0}-{1}".format(startFrame, endFrame) rt.rendPickupFrames = frameRange + def get_multipass_setting(project_setting=None): return (project_setting["max"] ["RenderSettings"] diff --git a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py index d7716527b1..ed448abe1f 100644 --- a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py @@ -161,8 +161,8 @@ class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): plugin_data = {} project_setting = get_project_settings( - legacy_io.Session["AVALON_PROJECT"] - ) + legacy_io.Session["AVALON_PROJECT"] + ) multipass = get_multipass_setting(project_setting) if multipass: From e246afe55b2ecee3f726b54c27b85a5a45f3dcfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Fri, 17 Feb 2023 15:07:24 +0100 Subject: [PATCH 0682/1271] removing typo --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 485ae7f4ee..514ffb62c0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ OpenPype [![documentation](https://github.com/pypeclub/pype/actions/workflows/documentation.yml/badge.svg)](https://github.com/pypeclub/pype/actions/workflows/documentation.yml) ![GitHub VFX Platform](https://img.shields.io/badge/vfx%20platform-2022-lightgrey?labelColor=303846) -this Introduction ------------ From 8afd618a0d2b2abbe6441d6d9c3d5c57170424ac Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 17 Feb 2023 15:38:43 +0100 Subject: [PATCH 0683/1271] updating workflows --- .../workflows/miletone_release_trigger.yml | 47 ++++++++++++ .github/workflows/nightly_merge.yml | 29 ------- .github/workflows/prerelease.yml | 67 ---------------- .github/workflows/release.yml | 76 ------------------- 4 files changed, 47 insertions(+), 172 deletions(-) create mode 100644 .github/workflows/miletone_release_trigger.yml delete mode 100644 .github/workflows/nightly_merge.yml delete mode 100644 .github/workflows/prerelease.yml delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/miletone_release_trigger.yml b/.github/workflows/miletone_release_trigger.yml new file mode 100644 index 0000000000..b5b8aab1dc --- /dev/null +++ b/.github/workflows/miletone_release_trigger.yml @@ -0,0 +1,47 @@ +name: Milestone Release [trigger] + +on: + workflow_dispatch: + inputs: + milestone: + required: true + release-type: + type: choice + description: What release should be created + options: + - release + - pre-release + milestone: + types: closed + + +jobs: + milestone-title: + runs-on: ubuntu-latest + outputs: + milestone: ${{ steps.milestoneTitle.outputs.value }} + steps: + - name: Switch input milestone + uses: haya14busa/action-cond@v1 + id: milestoneTitle + with: + cond: ${{ inputs.milestone == '' }} + if_true: ${{ github.event.milestone.title }} + if_false: ${{ inputs.milestone }} + - name: Print resulted milestone + run: | + echo "${{ steps.milestoneTitle.outputs.value }}" + + call-ci-tools-milestone-release: + needs: milestone-title + uses: ynput/ci-tools/.github/workflows/milestone_release_ref.yml@main + with: + milestone: ${{ needs.milestone-title.outputs.milestone }} + repo-owner: ${{ github.event.repository.owner.login }} + repo-name: ${{ github.event.repository.name }} + version-py-path: "./openpype/version.py" + pyproject-path: "./pyproject.toml" + secrets: + token: ${{ secrets.YNPUT_BOT_TOKEN }} + user_email: ${{ secrets.CI_EMAIL }} + user_name: ${{ secrets.CI_USER }} diff --git a/.github/workflows/nightly_merge.yml b/.github/workflows/nightly_merge.yml deleted file mode 100644 index 1776d7a464..0000000000 --- a/.github/workflows/nightly_merge.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Dev -> Main - -on: - schedule: - - cron: '21 3 * * 3,6' - workflow_dispatch: - -jobs: - develop-to-main: - - runs-on: ubuntu-latest - - steps: - - name: 🚛 Checkout Code - uses: actions/checkout@v2 - - - name: 🔨 Merge develop to main - uses: everlytic/branch-merge@1.1.0 - with: - github_token: ${{ secrets.YNPUT_BOT_TOKEN }} - source_ref: 'develop' - target_branch: 'main' - commit_message_template: '[Automated] Merged {source_ref} into {target_branch}' - - - name: Invoke pre-release workflow - uses: benc-uk/workflow-dispatch@v1 - with: - workflow: Nightly Prerelease - token: ${{ secrets.YNPUT_BOT_TOKEN }} diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml deleted file mode 100644 index 571b0339e1..0000000000 --- a/.github/workflows/prerelease.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Nightly Prerelease - -on: - workflow_dispatch: - - -jobs: - create_nightly: - runs-on: ubuntu-latest - - steps: - - name: 🚛 Checkout Code - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.9 - - - name: Install Python requirements - run: pip install gitpython semver PyGithub - - - name: 🔎 Determine next version type - id: version_type - run: | - TYPE=$(python ./tools/ci_tools.py --bump --github_token ${{ secrets.YNPUT_BOT_TOKEN }}) - echo "type=${TYPE}" >> $GITHUB_OUTPUT - - - name: 💉 Inject new version into files - id: version - if: steps.version_type.outputs.type != 'skip' - run: | - NEW_VERSION_TAG=$(python ./tools/ci_tools.py --nightly --github_token ${{ secrets.YNPUT_BOT_TOKEN }}) - echo "next_tag=${NEW_VERSION_TAG}" >> $GITHUB_OUTPUT - - - name: 💾 Commit and Tag - id: git_commit - if: steps.version_type.outputs.type != 'skip' - run: | - git config user.email ${{ secrets.CI_EMAIL }} - git config user.name ${{ secrets.CI_USER }} - git checkout main - git pull - git add . - git commit -m "[Automated] Bump version" - tag_name="CI/${{ steps.version.outputs.next_tag }}" - echo $tag_name - git tag -a $tag_name -m "nightly build" - - - name: Push to protected main branch - uses: CasperWA/push-protected@v2.10.0 - with: - token: ${{ secrets.YNPUT_BOT_TOKEN }} - branch: main - tags: true - unprotect_reviews: true - - - name: 🔨 Merge main back to develop - uses: everlytic/branch-merge@1.1.0 - if: steps.version_type.outputs.type != 'skip' - with: - github_token: ${{ secrets.YNPUT_BOT_TOKEN }} - source_ref: 'main' - target_branch: 'develop' - commit_message_template: '[Automated] Merged {source_ref} into {target_branch}' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 0b4c8af2c7..0000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Stable Release - -on: - release: - types: - - prereleased - -jobs: - create_release: - runs-on: ubuntu-latest - if: github.actor != 'pypebot' - - steps: - - name: 🚛 Checkout Code - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.9 - - name: Install Python requirements - run: pip install gitpython semver PyGithub - - - name: 💉 Inject new version into files - id: version - run: | - NEW_VERSION=$(python ./tools/ci_tools.py --finalize ${GITHUB_REF#refs/*/}) - LAST_VERSION=$(python ./tools/ci_tools.py --lastversion release) - - echo "current_version=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT - echo "last_release=${LAST_VERSION}" >> $GITHUB_OUTPUT - echo "release_tag=${NEW_VERSION}" >> $GITHUB_OUTPUT - - - name: 💾 Commit and Tag - id: git_commit - if: steps.version.outputs.release_tag != 'skip' - run: | - git config user.email ${{ secrets.CI_EMAIL }} - git config user.name ${{ secrets.CI_USER }} - git add . - git commit -m "[Automated] Release" - tag_name="${{ steps.version.outputs.release_tag }}" - git tag -a $tag_name -m "stable release" - - - name: 🔏 Push to protected main branch - if: steps.version.outputs.release_tag != 'skip' - uses: CasperWA/push-protected@v2.10.0 - with: - token: ${{ secrets.YNPUT_BOT_TOKEN }} - branch: main - tags: true - unprotect_reviews: true - - - name: 🚀 Github Release - if: steps.version.outputs.release_tag != 'skip' - uses: ncipollo/release-action@v1 - with: - tag: ${{ steps.version.outputs.release_tag }} - token: ${{ secrets.YNPUT_BOT_TOKEN }} - - - name: ☠ Delete Pre-release - if: steps.version.outputs.release_tag != 'skip' - uses: cb80/delrel@latest - with: - tag: "${{ steps.version.outputs.current_version }}" - - - name: 🔁 Merge main back to develop - if: steps.version.outputs.release_tag != 'skip' - uses: everlytic/branch-merge@1.1.0 - with: - github_token: ${{ secrets.YNPUT_BOT_TOKEN }} - source_ref: 'main' - target_branch: 'develop' - commit_message_template: '[Automated] Merged release {source_ref} into {target_branch}' From 3af80e97e81204ee461acc58ac73cb402f505a6f Mon Sep 17 00:00:00 2001 From: Ynbot Date: Fri, 17 Feb 2023 14:54:45 +0000 Subject: [PATCH 0684/1271] [Automated] Release --- CHANGELOG.md | 76 +++++++++++++++++++++++++++++++++++++++++++++ openpype/version.py | 2 +- pyproject.toml | 8 ++--- 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0da167763b..8a37886deb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,81 @@ # Changelog + +## [3.15.1](https://github.com/ynput/OpenPype/tree/3.15.1) + +[Full Changelog](https://github.com/ynput/OpenPype/compare/3.15.1...3.15.0) + +### **🆕 New features** + + +

+Maya: Xgen (3d / maya ) - #4256 + + +___ + + +## Brief description +Initial Xgen implementation. + +## Description +Client request of Xgen pipeline. + + + + +___ + + +
+ +### **🚀 Enhancements** + + +
+Adding path validator for non-maya nodes (3d / maya ) - #4271 + + +___ + + +## Brief description +Adding a path validator for filepaths from non-maya nodes, which are created by plugins such as Renderman, Yeti and abcImport. + +## Description +As File Path Editor cannot catch the wrong filenpaths from non-maya nodes such as AlembicNodes, It is neccessary to have a new validator to ensure the existence of the filepaths from the nodes. + + + + +___ + + +
+ +### **🐛 Bug fixes** + + +
+Fix features for gizmo menu (2d / nuke ) - #4280 + + +___ + + +## Brief description +Fix features for the Gizmo Menu project settings (shortcut for python type of usage and file type of usage functionality) + + + + +___ + + +
+ + + ## [3.15.0](https://github.com/ynput/OpenPype/tree/HEAD) [Full Changelog](https://github.com/ynput/OpenPype/compare/3.14.10...HEAD) diff --git a/openpype/version.py b/openpype/version.py index 6d060656cb..72d6b64c60 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.1-nightly.6" +__version__ = "3.15.1" diff --git a/pyproject.toml b/pyproject.toml index a872ed3609..d1d5c8e2d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.0" # OpenPype +version = "3.15.1" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" @@ -114,15 +114,15 @@ build-backend = "poetry.core.masonry.api" # https://pip.pypa.io/en/stable/cli/pip_install/#requirement-specifiers [openpype.qtbinding.windows] package = "PySide2" -version = "5.15.2" +version = "3.15.1" [openpype.qtbinding.darwin] package = "PySide6" -version = "6.4.1" +version = "3.15.1" [openpype.qtbinding.linux] package = "PySide2" -version = "5.15.2" +version = "3.15.1" # TODO: we will need to handle different linux flavours here and # also different macos versions too. From 15c9f71633c1cb598618e7629ef496b6d1abeec7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 17 Feb 2023 16:12:04 +0100 Subject: [PATCH 0685/1271] updating changelog --- CHANGELOG.md | 383 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 378 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a37886deb..752a7dc1d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,82 +3,455 @@ ## [3.15.1](https://github.com/ynput/OpenPype/tree/3.15.1) -[Full Changelog](https://github.com/ynput/OpenPype/compare/3.15.1...3.15.0) +[Full Changelog](https://github.com/ynput/OpenPype/compare/3.15.0...3.15.1) ### **🆕 New features** + +
Maya: Xgen (3d / maya ) - #4256 + ___ + ## Brief description + Initial Xgen implementation. + + ## Description + Client request of Xgen pipeline. + + + ___ +
+ ### **🚀 Enhancements** + +
Adding path validator for non-maya nodes (3d / maya ) - #4271 + ___ + ## Brief description + Adding a path validator for filepaths from non-maya nodes, which are created by plugins such as Renderman, Yeti and abcImport. + + ## Description + As File Path Editor cannot catch the wrong filenpaths from non-maya nodes such as AlembicNodes, It is neccessary to have a new validator to ensure the existence of the filepaths from the nodes. + + + ___ +
+ + +
+Deadline: Allow disabling strict error check in Maya submissions (3d / maya / deadline ) - #4420 + + + +___ + + + +## Brief description + +DL by default has Strict error checking, but some errors are not fatal. + + + +## Description + +This allows to set profile based on Task and Subset values to temporarily disable Strict Error Checks.Subset and task names should support regular expressions. (not wildcard notation though). + + + + + + + +___ + + + +
+ + + +
+Houdini: New publisher code tweak (3d / houdini ) - #4374 + + + +___ + + + +## Brief description + +This is cosmetics only - the previous code to me felt quite unreadable due to the lengthy strings being used. + + + +## Description + +Code should do roughly the same, but just be reformatted. + + + + + + + +___ + + + +
+ + + +
+Houdini: Do not visualize the hidden OpenPypeContext node (3d / houdini ) - #4382 + + + +___ + + + +## Brief description + +Using the new publisher UI would generate a visible 'null' locator at the origin. It's confusing to the user since it's supposed to be 'hidden'. + + + +## Description + +Before this PR the user would see a locator/null at the origin which was the 'hidden' `/obj/OpenPypeContext` node. This null would suddenly appear if the user would've ever opened the Publisher UI once.After this PR it will not show:Nice and tidy. + + + + + + + +___ + + + +
+ + + +
+Global: supporting `OPENPYPE_TMPDIR` in staging dir maker (editorial / hiero ) - #4398 + + + +___ + + + +## Brief description + +Productions can use OPENPYPE_TMPDIR for staging temp publishing directory + + + +## Description + +Studios were demanding to be able to configure their own shared storages as temporary staging directories. Template formatting is also supported with optional keys formatting and following anatomy keys: - root[work | ] - project[name | code] + + + + + + + +___ + + + +
+ + ### **🐛 Bug fixes** + + +
+Maya: Fix Validate Attributes plugin (3d / maya ) - #4401 + + + +___ + + + +## Brief description + +Code was broken. So either plug-in was unused or it had gone unnoticed. + + + +## Description + +Looking at the commit history of the plug-in itself it seems this might have been broken somewhere between two to three years. I think it's broken since two years since this commit.Should this plug-in be removed completely?@tokejepsen Is there still a use case where we should have this plug-in? (You created the original one) + + + + + + + +___ + + + +
+ + + +
+Maya: Ignore workfile lock in Untitled scene (3d / maya ) - #4414 + + + +___ + + + +## Brief description + +Skip workfile lock check if current scene is 'Untitled'. + + + + + + + +___ + + + +
+ + + +
+Maya: fps rounding - OP-2549 (3d / maya ) - #4424 + + + +___ + + + +## Brief description + +When FPS is registered in for example Ftrack and round either down or up (floor/ceil), comparing to Maya FPS can fail. Example:23.97 (Ftrack/Mongo) != 23.976023976023978 (Maya) + + + +## Description + +Since Maya only has a select number of supported framerates, I've taken the approach of converting any fps to supported framerates in Maya. We validate the input fps to make sure they are supported in Maya in two ways:Whole Numbers - are validated straight against the supported framerates in Maya.Demical Numbers - we find the closest supported framerate in Maya. If the difference to the closest supported framerate, is more than 0.5 we'll throw an error.If Maya ever supports arbitrary framerates, then we might have a problem but I'm not holding my breath... + + + + + + + +___ + + + +
+ + + +
+Strict Error Checking Default (3d / maya ) - #4457 + + + +___ + + + +## Brief description + +Provide default of strict error checking for instances created prior to PR. + + + + + + + +___ + + + +
+ + + +
+Create: Enhance instance & context changes (3d / houdini,after effects,3dsmax ) - #4375 + + + +___ + + + +## Brief description + +Changes of instances and context have complex, hard to get structure. The structure did not change but instead of complex dictionaries are used objected data. + + + +## Description + +This is poposal of changes data improvement for creators. Implemented `TrackChangesItem` which handles the changes for us. The item is creating changes based on old and new value and can provide information about changed keys or access to full old or new value. Can give the values on any "sub-dictionary".Used this new approach to fix change in houdini and 3ds max and also modified one aftereffects plugin using changes. + + + + + + + +___ + + + +
+ + + +
+Houdini: hotfix condition (3d / houdini ) - #4391 + + + +___ + + + +## Hotfix + + + +This is fixing bug introduced int #4374 + + + +___ + + + +
+ + + +
+Houdini: Houdini shelf tools fixes (3d / houdini ) - #4428 + + + +___ + + + +## Brief description + +Fix Houdini shelf tools. + + + +## Description + +Use `label` as mandatory key instead of `name`. Changed how shelves are created. If the script is empty it is gracefully skipping it instead of crashing. + + + + + + + +___ + + + +
+ + +
Fix features for gizmo menu (2d / nuke ) - #4280 + ___ + ## Brief description + Fix features for the Gizmo Menu project settings (shortcut for python type of usage and file type of usage functionality) - - ___ +
-## [3.15.0](https://github.com/ynput/OpenPype/tree/HEAD) +## [3.15.0](https://github.com/ynput/OpenPype/tree/3.15.0) -[Full Changelog](https://github.com/ynput/OpenPype/compare/3.14.10...HEAD) +[Full Changelog](https://github.com/ynput/OpenPype/compare/3.14.10...3.15.0) **Deprecated:** From aaba8c1f7be4dca894ab51c6a84092b9759e88e8 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 20 Feb 2023 07:37:50 +0000 Subject: [PATCH 0686/1271] Fix attributes --- openpype/settings/defaults/project_anatomy/attributes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_anatomy/attributes.json b/openpype/settings/defaults/project_anatomy/attributes.json index 0cc414fb69..bf8bbef8de 100644 --- a/openpype/settings/defaults/project_anatomy/attributes.json +++ b/openpype/settings/defaults/project_anatomy/attributes.json @@ -23,4 +23,4 @@ ], "tools_env": [], "active": true -} +} \ No newline at end of file From 9875185d7813e6cdf0212537b6a9eef0bb328ab8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 Feb 2023 10:39:12 +0100 Subject: [PATCH 0687/1271] fix CI release [automation bugs] --- CHANGELOG.md | 1008 +++++++++++++++++++++++++++++++++++++++++------- pyproject.toml | 6 +- 2 files changed, 872 insertions(+), 142 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 752a7dc1d6..c7ecbc83bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,32 +13,47 @@
Maya: Xgen (3d / maya ) - #4256 - - ___ - - -## Brief description +#### Brief description Initial Xgen implementation. -## Description +#### Description Client request of Xgen pipeline. +___ + +
+ + + +
+Data exchange cameras for 3d Studio Max (3d / 3dsmax ) - #4376 + +___ + +#### Brief description + +Add Camera Family into the 3d Studio Max + + + +#### Description + +Adding Camera Extractors(extract abc camera and extract fbx camera) and validators(for camera contents) into 3dMaxAlso add the extractor for exporting 3d max raw scene (which is also related to 3dMax Scene Family) for camera family + ___ - -
@@ -50,32 +65,23 @@ ___
Adding path validator for non-maya nodes (3d / maya ) - #4271 - - ___ - - -## Brief description +#### Brief description Adding a path validator for filepaths from non-maya nodes, which are created by plugins such as Renderman, Yeti and abcImport. -## Description +#### Description As File Path Editor cannot catch the wrong filenpaths from non-maya nodes such as AlembicNodes, It is neccessary to have a new validator to ensure the existence of the filepaths from the nodes. - - - ___ - -
@@ -83,32 +89,23 @@ ___
Deadline: Allow disabling strict error check in Maya submissions (3d / maya / deadline ) - #4420 - - ___ - - -## Brief description +#### Brief description DL by default has Strict error checking, but some errors are not fatal. -## Description +#### Description This allows to set profile based on Task and Subset values to temporarily disable Strict Error Checks.Subset and task names should support regular expressions. (not wildcard notation though). - - - ___ - -
@@ -116,65 +113,43 @@ ___
Houdini: New publisher code tweak (3d / houdini ) - #4374 - - ___ - - -## Brief description +#### Brief description This is cosmetics only - the previous code to me felt quite unreadable due to the lengthy strings being used. -## Description +#### Description Code should do roughly the same, but just be reformatted. - - - ___ - -
-Houdini: Do not visualize the hidden OpenPypeContext node (3d / houdini ) - #4382 - - +3dsmax: enhance alembic loader update function (3d / 3dsmax ) - #4387 ___ - - -## Brief description - -Using the new publisher UI would generate a visible 'null' locator at the origin. It's confusing to the user since it's supposed to be 'hidden'. - - - -## Description - -Before this PR the user would see a locator/null at the origin which was the 'hidden' `/obj/OpenPypeContext` node. This null would suddenly appear if the user would've ever opened the Publisher UI once.After this PR it will not show:Nice and tidy. - +## Enhancement +This PR is adding update/switch ability to pointcache/alembic loader in 3dsmax and fixing wrong tool shown when clicking on "Manage" item on OpenPype menu, that is now correctly Scene Inventory (but was Subset Manager). +Alembic update has still one caveat - it doesn't cope with changed number of object inside alembic, since loading alembic in max involves creating all those objects as first class nodes. So it will keep the objects in scene, just update path to alembic file on them. ___ - -
@@ -182,32 +157,143 @@ ___
Global: supporting `OPENPYPE_TMPDIR` in staging dir maker (editorial / hiero ) - #4398 - - ___ - - -## Brief description +#### Brief description Productions can use OPENPYPE_TMPDIR for staging temp publishing directory -## Description +#### Description Studios were demanding to be able to configure their own shared storages as temporary staging directories. Template formatting is also supported with optional keys formatting and following anatomy keys: - root[work | ] - project[name | code] +___ + +
+ + + +
+General: Functions for current context (other ) - #4324 + +___ + +#### Brief description + +Defined more functions to receive current context information and added the methods to host integration so host can affect the result. + + + +#### Description + +This is one of steps to reduce usage of `legacy_io.Session`. This change define how to receive current context information -> call functions instead of accessing `legacy_io.Session` or `os.environ` directly. Plus, direct access on session or environments is unfortunatelly not enough for some DCCs where multiple workfiles can be opened at one time which can heavily affect the context but host integration sometimes can't affect that at all.`HostBase` already had implemented `get_current_context`, that was enhanced by adding more specific methods `get_current_project_name`, `get_current_asset_name` and `get_current_task_name`. The same functions were added to `~/openpype/pipeline/cotext_tools.py`. The functions in context tools are calling host integration methods (if are available) otherwise are using environent variables as default implementation does. Also was added `get_current_host_name` to receive host name from registered host if is available or from environment variable. + ___ +
+ +
+Houdini: Do not visualize the hidden OpenPypeContext node (other / houdini ) - #4382 + +___ + +#### Brief description + +Using the new publisher UI would generate a visible 'null' locator at the origin. It's confusing to the user since it's supposed to be 'hidden'. + + + +#### Description + +Before this PR the user would see a locator/null at the origin which was the 'hidden' `/obj/OpenPypeContext` node. This null would suddenly appear if the user would've ever opened the Publisher UI once.After this PR it will not show:Nice and tidy. + + + + +___ + +
+ + + +
+Maya + Blender: Pyblish plugins removed unused `version` and `category` attributes (other ) - #4402 + +___ + +#### Brief description + +Once upon a time in a land far far away there lived a few plug-ins who felt like they didn't belong in generic boxes and felt they needed to be versioned well above others. They tried, but with no success. + + + +#### Description + +Even though they now lived in a universe with elaborate `version` and `category` attributes embedded into their tiny little plug-in DNA this particular deviation has been greatly unused. There is nothing special about the version, nothing special about the category.It does nothing. + + + + +___ + +
+ + + +
+General: Fix original basename frame issues (other ) - #4452 + +___ + +#### Brief description + +Treat `{originalBasename}` in different way then standard files processing. In case template should use `{originalBasename}` the transfers will use them as they are without any changes or handling of frames. + + + +#### Description + +Frames handling is problematic with original basename because their padding can't be defined to match padding in source filenames. Also it limits the usage of functionality to "must have frame at end of fiename". This is proposal how that could be solved by simply ignoring frame handling and using filenames as are on representation. First frame is still stored to representation context but is not used in formatting part. This way we don't have to care about padding of frames at all. + + + + +___ + +
+ + + +
+Publisher: Report also crashed creators and convertors (other ) - #4473 + +___ + +#### Brief description + +Added crashes of creators and convertos discovery (lazy solution). + + + +#### Description + +Report in Publisher also contains information about crashed files caused during creator plugin discovery and convertor plugin discovery. They're not separated into categroies and there is no other information in the report about them, but this helps a lot during development. This change does not need to change format/schema of the report nor UI logic. + + + + +___ +
@@ -219,32 +305,23 @@ ___
Maya: Fix Validate Attributes plugin (3d / maya ) - #4401 - - ___ - - -## Brief description +#### Brief description Code was broken. So either plug-in was unused or it had gone unnoticed. -## Description +#### Description Looking at the commit history of the plug-in itself it seems this might have been broken somewhere between two to three years. I think it's broken since two years since this commit.Should this plug-in be removed completely?@tokejepsen Is there still a use case where we should have this plug-in? (You created the original one) - - - ___ - -
@@ -252,26 +329,17 @@ ___
Maya: Ignore workfile lock in Untitled scene (3d / maya ) - #4414 - - ___ - - -## Brief description +#### Brief description Skip workfile lock check if current scene is 'Untitled'. - - - ___ - -
@@ -279,32 +347,23 @@ ___
Maya: fps rounding - OP-2549 (3d / maya ) - #4424 - - ___ - - -## Brief description +#### Brief description When FPS is registered in for example Ftrack and round either down or up (floor/ceil), comparing to Maya FPS can fail. Example:23.97 (Ftrack/Mongo) != 23.976023976023978 (Maya) -## Description +#### Description Since Maya only has a select number of supported framerates, I've taken the approach of converting any fps to supported framerates in Maya. We validate the input fps to make sure they are supported in Maya in two ways:Whole Numbers - are validated straight against the supported framerates in Maya.Demical Numbers - we find the closest supported framerate in Maya. If the difference to the closest supported framerate, is more than 0.5 we'll throw an error.If Maya ever supports arbitrary framerates, then we might have a problem but I'm not holding my breath... - - - ___ - -
@@ -312,26 +371,17 @@ ___
Strict Error Checking Default (3d / maya ) - #4457 - - ___ - - -## Brief description +#### Brief description Provide default of strict error checking for instances created prior to PR. - - - ___ - -
@@ -339,32 +389,23 @@ ___
Create: Enhance instance & context changes (3d / houdini,after effects,3dsmax ) - #4375 - - ___ - - -## Brief description +#### Brief description Changes of instances and context have complex, hard to get structure. The structure did not change but instead of complex dictionaries are used objected data. -## Description +#### Description This is poposal of changes data improvement for creators. Implemented `TrackChangesItem` which handles the changes for us. The item is creating changes based on old and new value and can provide information about changed keys or access to full old or new value. Can give the values on any "sub-dictionary".Used this new approach to fix change in houdini and 3ds max and also modified one aftereffects plugin using changes. - - - ___ - -
@@ -372,24 +413,15 @@ ___
Houdini: hotfix condition (3d / houdini ) - #4391 - - ___ - - ## Hotfix This is fixing bug introduced int #4374 - - - ___ - -
@@ -397,32 +429,47 @@ ___
Houdini: Houdini shelf tools fixes (3d / houdini ) - #4428 - - ___ - - -## Brief description +#### Brief description Fix Houdini shelf tools. -## Description +#### Description Use `label` as mandatory key instead of `name`. Changed how shelves are created. If the script is empty it is gracefully skipping it instead of crashing. +___ + +
+ + + +
+3dsmax: startup fixes (3d / 3dsmax ) - #4412 + +___ + +#### Brief description + +This is fixing various issues that can occur on some of the 3dsmax versions. + + + +#### Description + +On displays with +4K resolution UI was broken, some 3dsmax versions couldn't process `PYTHONPATH` correctly. This PR is forcing `sys.path` and disabling `QT_AUTO_SCREEN_SCALE_FACTOR` + ___ - -
@@ -430,25 +477,708 @@ ___
Fix features for gizmo menu (2d / nuke ) - #4280 - - ___ - - -## Brief description +#### Brief description Fix features for the Gizmo Menu project settings (shortcut for python type of usage and file type of usage functionality) + + ___ +
+ +
+Photoshop: fix missing legacy io for legacy instances (2d / photoshop,after effects ) - #4467 + +___ + +#### Brief description + +`legacy_io` import was removed, but usage stayed. + + + +#### Description + +Usage of `legacy_io` should be eradicated, in creators it should be replaced by `self.create_context.get_current_project_name/asset_name/task_name`. + + + + +___ +
+
+Fix - addSite loader handles hero version (other / sitesync ) - #4359 + +___ + +#### Brief description + +If adding site to representation presence of hero version is checked, if found hero version is marked to be donwloaded too.Replacing https://github.com/ynput/OpenPype/pull/4191 + + + + +___ + +
+ + + +
+Remove OIIO build for macos (other ) - #4381 + +___ + +## Fix + + + +Since we are not able to provide OpenImageIO tools binaries for macos, we should remove the item from th `pyproject.toml`. This PR is taking care of it. + + + +It is also changing the way `fetch_thirdparty_libs` script works in that it doesn't crash when lib cannot be processed, it only issue warning. + + + + + +Resolves #3858 +___ + +
+ + + +
+General: Attribute definitions fixes (other ) - #4392 + +___ + +#### Brief description + +Fix possible issues with attribute definitions in publisher if there is unknown attribute on an instance. + + + +#### Description + +Source of the issue is that attribute definitions from creator plugin could be "expanded" during `CreatedInstance` initialization. Which would affect all other instances using the same list of attributes -> literally object of list. If the same list object is used in "BaseClass" for other creators it would affect all instances (because of 1 instance). There had to be implemented other changes to fix the issue and keep behavior the same.Object of `CreatedInstance` can be created without reference to creator object. `CreatedInstance` is responsible to give UI attribute definitions (technically is prepared for cases when each instance may have different attribute definitions -> not yet).Attribute definition has added more conditions for `__eq__` method and have implemented `__ne__` method (which is required for Py 2 compatibility). Renamed `AbtractAttrDef` to `AbstractAttrDef` (fix typo). + + + + +___ + +
+ + + +
+Ftrack: Don't force ftrackapp endpoint (other / ftrack ) - #4411 + +___ + +#### Brief description + +Auto-fill of ftrack url don't break custom urls. Custom urls couldn't be used as `ftrackapp.com` is added if is not in the url. + + + +#### Description + +The code was changed in a way that auto-fill is still supported but before `ftrackapp` is added it will try to use url as is. If the connection works as is it is used. + + + + +___ + +
+ + + +
+Fix: DL on MacOS (other ) - #4418 + +___ + +#### Brief description + +This works if DL Openpype plugin Installation Directories is set to level of app bundle (eg. '/Applications/OpenPype 3.15.0.app') + + + + +___ + +
+ + + +
+Photoshop: make usage of layer name in subset name more controllable (other ) - #4432 + +___ + +#### Brief description + +Layer name was previously used in subset name only if multiple instances were being created in single step. This adds explicit toggle. + + + +#### Description + +Toggling this button allows to use layer name in created subset name even if single instance is being created.This follows more closely implementation if AE. + + + + +___ + +
+ + + +
+SiteSync: fix dirmap (other ) - #4436 + +___ + +#### Brief description + +Fixed issue in dirmap in Maya and Nuke + + + +#### Description + +Loads of error were thrown in Nuke console about dictionary value.`AttributeError: 'dict' object has no attribute 'lower'` + + + + +___ + +
+ + + +
+General: Ignore decode error of stdout/stderr in run_subprocess (other ) - #4446 + +___ + +#### Brief description + +Ignore decode errors and replace invalid character (byte) with escaped byte character. + + + +#### Description + +Calling of `run_subprocess` may cause crashes if output contains some unicode character which (for example Polish name of encoder handler). + + + + +___ + +
+ + + +
+Publisher: Fix reopen bug (other ) - #4463 + +___ + +#### Brief description + +Use right name of constant 'ActiveWindow' -> 'WindowActive'. + + + + +___ + +
+ + + +
+Publisher: Fix compatibility of QAction in Publisher (other ) - #4474 + +___ + +#### Brief description + +Fix `QAction` for older version of Qt bindings where QAction requires a parent on initialization. + + + +#### Description + +This bug was discovered in Nuke 11. Fixed by creating QAction when QMenu is already available and can be used as parent. + + + + +___ + +
+ + +### **🔀 Refactored code** + + + + +
+General: Remove 'openpype.api' (other ) - #4413 + +___ + +#### Brief description + +PR is removing `openpype/api.py` file which is causing a lot of troubles and cross-imports. + + + +#### Description + +I wanted to remove the file slowly function by function but it always reappear somewhere in codebase even if most of the functionality imported from there is triggering deprecation warnings. This is small change which may have huge impact.There shouldn't be anything in openpype codebase which is using `openpype.api` anymore so only possible issues are in customized repositories or custom addons. + + + + +___ + +
+ + +### **📃 Documentation** + + + + +
+docs-user-Getting Started adjustments (other ) - #4365 + +___ + +#### Brief description + +Small typo fixes here and there, additional info on install/ running OP. + + + + +___ + +
+ + +### **Merged pull requests** + + + + +
+Renderman support for sample and display filters (3d / maya ) - #4003 + +___ + +#### Brief description + +User can set up both sample and display filters in Openpype settings if they are using Renderman as renderer. + + + +#### Description + +You can preset which sample and display filters for renderman , including the cryptomatte renderpass, in Openpype settings. Once you select which filters to be included in openpype settings and then create render instance for your camera in maya, it would automatically tell the system to generate your selected filters in render settings.The place you can find for setting up the filters: _Maya > Render Settings > Renderman Renderer > Display Filters/ Sample Filters_ + + + + +___ + +
+ + + +
+Maya: Create Arnold options on repair. (3d / maya ) - #4448 + +___ + +#### Brief description + +When validating/repairing we previously required users to open render settings to create the Arnold options. This is done through code now. + + + + +___ + +
+ + + +
+Update Asset field of creator Instances in Maya Template Builder (3d / maya ) - #4470 + +___ + +#### Brief description + +When we build a template with Maya Template Builder, it will update the asset field of the sets (creator instances) that are imported from the template. + + + +#### Description + +When building a template, we also want to define the publishable content in advance: create an instance of a model, or look, etc., to speed up the workflow and reduce the number of questions we are asked. After building a work file from a saved template that contains pre-created instances, the template builder should update the asset field to the current asset. + + + + +___ + +
+ + + +
+Blender: fix import workfile all families (3d / blender ) - #4405 + +___ + +#### Brief description + +Having this feature related to workfile available for any family is absurd. + + + + +___ + +
+ + + +
+Nuke: update rendered frames in latest version (2d / nuke ) - #4362 + +___ + +#### Brief description + +Introduced new field to insert frame(s) to rerender only. + + + +#### Description + +Rendering is expensive, sometimes it is helpful only to re-render changed frames and reuse existing.Artists can in Publisher fill which frame(s) should be re-rendered.If there is already published version of currently publishing subset, all representation files are collected (currently for `render` family only) and then when Nuke is rendering (locally only for now), old published files are copied into into temporary render folder where will be rewritten only by frames explicitly set in new field.That way review/burnin process could also reuse old files and recreate reviews/burnins.New version is produced during this process! + + + + +___ + +
+ + + +
+Feature: Keep synced hero representations up-to-date. (other ) - #4343 + +___ + +#### Brief description + +Keep previously synchronized sites up-to-date by comparing old and new sites and adding old sites if missing in new ones.Fix #4331 + + + + +___ + +
+ + + +
+Maya: Fix template builder bug where assets are not put in the right hierarchy (other ) - #4367 + +___ + +#### Brief description + +When buiding scene from template, the assets loaded from the placeholders are not put in the hierarchy. Plus, the assets are loaded in double. + + + + +___ + +
+ + + +
+Bump ua-parser-js from 0.7.31 to 0.7.33 in /website (other ) - #4371 + +___ + +Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.31 to 0.7.33. +
+Changelog +

Sourced from ua-parser-js's changelog.

+
+

Version 0.7.31 / 1.0.2

+
    +
  • Fix OPPO Reno A5 incorrect detection
  • +
  • Fix TypeError Bug
  • +
  • Use AST to extract regexes and verify them with safe-regex
  • +
+

Version 0.7.32 / 1.0.32

+
    +
  • Add new browser : DuckDuckGo, Huawei Browser, LinkedIn
  • +
  • Add new OS : HarmonyOS
  • +
  • Add some Huawei models
  • +
  • Add Sharp Aquos TV
  • +
  • Improve detection Xiaomi Mi CC9
  • +
  • Fix Sony Xperia 1 III misidentified as Acer tablet
  • +
  • Fix Detect Sony BRAVIA as SmartTV
  • +
  • Fix Detect Xiaomi Mi TV as SmartTV
  • +
  • Fix Detect Galaxy Tab S8 as tablet
  • +
  • Fix WeGame mistakenly identified as WeChat
  • +
  • Fix included commas in Safari / Mobile Safari version
  • +
  • Increase UA_MAX_LENGTH to 350
  • +
+

Version 0.7.33 / 1.0.33

+
    +
  • Add new browser : Cobalt
  • +
  • Identify Macintosh as an Apple device
  • +
  • Fix ReDoS vulnerability
  • +
+

Version 0.8

+

Version 0.8 was created by accident. This version is now deprecated and no longer maintained, please update to version 0.7 / 1.0.

+
+
+
+Commits +
    +
  • f2d0db0 Bump version 0.7.33
  • +
  • a6140a1 Remove unsafe regex in trim() function
  • +
  • a886604 Fix #605 - Identify Macintosh as Apple device
  • +
  • b814bcd Merge pull request #606 from rileyjshaw/patch-1
  • +
  • 7f71024 Fix documentation
  • +
  • c239ac5 Merge pull request #604 from obecerra3/master
  • +
  • 8d3c2d3 Add new browser: Cobalt
  • +
  • d11fc47 Bump version 0.7.32
  • +
  • b490110 Merge branch 'develop' of github.com:faisalman/ua-parser-js
  • +
  • cb5da5e Merge pull request #600 from moekm/develop
  • +
  • Additional commits viewable in compare view
  • +
+
+
+ + +[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ua-parser-js&package-manager=npm_and_yarn&previous-version=0.7.31&new-version=0.7.33)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) + +Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. + +[//]: # (dependabot-automerge-start) +[//]: # (dependabot-automerge-end) + +--- + +
+Dependabot commands and options +
+ +You can trigger Dependabot actions by commenting on this PR: +- `@dependabot rebase` will rebase this PR +- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it +- `@dependabot merge` will merge this PR after your CI passes on it +- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it +- `@dependabot cancel merge` will cancel a previously requested merge and block automerging +- `@dependabot reopen` will reopen this PR if it is closed +- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually +- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) +- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) +- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) +- `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language +- `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language +- `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language +- `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language + +You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/ynput/OpenPype/network/alerts). + +
+___ + +
+ + + +
+Docs: Question about renaming in Kitsu (other ) - #4384 + +___ + +#### Brief description + +To keep memory of this discussion: https://discord.com/channels/517362899170230292/563751989075378201/1068112668491255818 + + + + +___ + +
+ + + +
+New Publisher: Fix Creator error typo (other ) - #4396 + +___ + +#### Brief description + +Fixes typo in error message. + + + + +___ + +
+ + + +
+Chore: pyproject.toml version because of Poetry (other ) - #4408 + +___ + +#### Brief description + +Automatization injects wrong format + + + + +___ + +
+ + + +
+Fix - remove minor part in toml (other ) - #4437 + +___ + +#### Brief description + +Causes issue in create_env and new Poetry + + + + +___ + +
+ + + +
+General: Add project code to anatomy (other ) - #4445 + +___ + +#### Brief description + +Added attribute `project_code` to `Anatomy` object. + + + +#### Description + +Anatomy already have access to almost all attributes from project anatomy except project code. This PR changing it. Technically `Anatomy` is everything what would be needed to get fill data of project. + +``` + +{ + + "project": { + + "name": anatomy.project_name, + + "code": anatomy.project_code + + } + +} + +``` + + +___ + +
+ + + +
+Maya: Arnold Scene Source overhaul - OP-4865 (other / maya ) - #4449 + +___ + +#### Brief description + +General overhaul of the Arnold Scene Source (ASS) workflow. + + + +#### Description + +This originally was to support static files (non-sequencial) ASS publishing, but digging deeper whole workflow needed an update to get ready for further issues. During this overhaul the following changes were made: + +- Generalized Arnold Standin workflow to a single loader. + +- Support multiple nodes as proxies. + +- Support proxies for `pointcache` family. + +- Generalized approach to proxies as resources, so they can be the same file format as the original.This workflow should allow further expansion to utilize operators and eventually USD. + + + + +___ + +
+ + + + ## [3.15.0](https://github.com/ynput/OpenPype/tree/3.15.0) [Full Changelog](https://github.com/ynput/OpenPype/compare/3.14.10...3.15.0) diff --git a/pyproject.toml b/pyproject.toml index d1d5c8e2d3..2fc4f6fe39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,15 +114,15 @@ build-backend = "poetry.core.masonry.api" # https://pip.pypa.io/en/stable/cli/pip_install/#requirement-specifiers [openpype.qtbinding.windows] package = "PySide2" -version = "3.15.1" +version = "5.15.2" [openpype.qtbinding.darwin] package = "PySide6" -version = "3.15.1" +version = "6.4.1" [openpype.qtbinding.linux] package = "PySide2" -version = "3.15.1" +version = "5.15.2" # TODO: we will need to handle different linux flavours here and # also different macos versions too. From 7a2c94f9d511c2d15ca554709beded5fe3055515 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 11:06:37 +0100 Subject: [PATCH 0688/1271] limit groups query to 26 max --- openpype/hosts/tvpaint/api/lib.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/api/lib.py b/openpype/hosts/tvpaint/api/lib.py index 5e64773b8e..312a211d49 100644 --- a/openpype/hosts/tvpaint/api/lib.py +++ b/openpype/hosts/tvpaint/api/lib.py @@ -202,8 +202,9 @@ def get_groups_data(communicator=None): # Variable containing full path to output file "output_path = \"{}\"".format(output_filepath), "empty = 0", - # Loop over 100 groups - "FOR idx = 1 TO 100", + # Loop over 26 groups which is ATM maximum possible (in 11.7) + # - ref: https://www.tvpaint.com/forum/viewtopic.php?t=13880 + "FOR idx = 1 TO 26", # Receive information about groups "tv_layercolor \"getcolor\" 0 idx", "PARSE result clip_id group_index c_red c_green c_blue group_name", From c3c850f0348de62126689e887cc99b20cdbd538c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 11:06:53 +0100 Subject: [PATCH 0689/1271] change how groups are filtered for render passes --- .../tvpaint/plugins/create/create_render.py | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 41288e5968..d9355c42fd 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -219,15 +219,30 @@ class CreateRenderlayer(TVPaintCreator): f" was changed to \"{new_group_name}\"." )) - def get_pre_create_attr_defs(self): - groups_enum = [ - { - "label": group["name"], + def _get_groups_enum(self): + groups_enum = [] + empty_groups = [] + for group in get_groups_data(): + group_name = group["name"] + item = { + "label": group_name, "value": group["group_id"] } - for group in get_groups_data() - if group["name"] - ] + # TVPaint have defined how many color groups is available, but + # the count is not consistent across versions. It is not possible + # to know how many groups there is. + # + if group_name and group_name != "0": + if empty_groups: + groups_enum.extend(empty_groups) + empty_groups = [] + groups_enum.append(item) + else: + empty_groups.append(item) + return groups_enum + + def get_pre_create_attr_defs(self): + groups_enum = self._get_groups_enum() groups_enum.insert(0, {"label": "", "value": -1}) return [ @@ -249,14 +264,7 @@ class CreateRenderlayer(TVPaintCreator): ] def get_instance_attr_defs(self): - groups_enum = [ - { - "label": group["name"], - "value": group["group_id"] - } - for group in get_groups_data() - if group["name"] - ] + groups_enum = self._get_groups_enum() return [ EnumDef( "group_id", From 1b69c5e3409d3c67e384eef8c4c1c12054bed2cd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 11:09:24 +0100 Subject: [PATCH 0690/1271] fix used key --- .../hosts/tvpaint/plugins/publish/validate_scene_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py b/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py index d235215ac9..4473e4b1b7 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py @@ -42,7 +42,7 @@ class ValidateProjectSettings(pyblish.api.ContextPlugin): "expected_width": expected_data["resolutionWidth"], "expected_height": expected_data["resolutionHeight"], "current_width": scene_data["resolutionWidth"], - "current_height": scene_data["resolutionWidth"], + "current_height": scene_data["resolutionHeight"], "expected_pixel_ratio": expected_data["pixelAspect"], "current_pixel_ratio": scene_data["pixelAspect"] } From a189a8df8a1aed35e7b393c4ec8027911d38d47f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 20 Feb 2023 13:23:25 +0100 Subject: [PATCH 0691/1271] OP-3026 - enhanced log with host info --- openpype/settings/handlers.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/openpype/settings/handlers.py b/openpype/settings/handlers.py index 3fa5737b6f..a1f3331ccc 100644 --- a/openpype/settings/handlers.py +++ b/openpype/settings/handlers.py @@ -9,6 +9,7 @@ import six import openpype.version from openpype.client.mongo import OpenPypeMongoConnection from openpype.client.entities import get_project_connection, get_project +from openpype.lib.pype_info import get_workstation_info from .constants import ( GLOBAL_SETTINGS_KEY, @@ -930,13 +931,17 @@ class MongoSettingsHandler(SettingsHandler): if not changes: return - from openpype.lib import get_local_site_id - if settings_type == "project" and not project_name: project_name = "default" + host_info = get_workstation_info() + document = { - "user": get_local_site_id(), + "local_id": host_info["local_id"], + "username": host_info["username"], + "hostname": host_info["hostname"], + "hostip": host_info["hostip"], + "system_name": host_info["system_name"], "date_created": datetime.datetime.now(), "project": project_name, "settings_type": settings_type, From 407068610afc447df075d555aa7b5e9068734f8a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 13:58:20 +0100 Subject: [PATCH 0692/1271] return created instance in creators --- openpype/hosts/tvpaint/plugins/create/create_render.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index d9355c42fd..71224927d1 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -195,13 +195,13 @@ class CreateRenderlayer(TVPaintCreator): new_group_name = pre_create_data.get("group_name") if not new_group_name or not group_id: - return + return new_instance self.log.debug("Changing name of the group.") new_group_name = pre_create_data.get("group_name") if not new_group_name or group_item["name"] == new_group_name: - return + return new_instance # Rename TVPaint group (keep color same) # - groups can't contain spaces rename_script = self.rename_script_template.format( @@ -218,6 +218,7 @@ class CreateRenderlayer(TVPaintCreator): f"Name of group with index {group_id}" f" was changed to \"{new_group_name}\"." )) + return new_instance def _get_groups_enum(self): groups_enum = [] @@ -497,6 +498,8 @@ class CreateRenderPass(TVPaintCreator): self._add_instance_to_context(new_instance) self._change_layers_group(selected_layers, group_id) + return new_instance + def _change_layers_group(self, layers, group_id): filtered_layers = [ layer From 7486591fab5e9ec0413262c5fdd80be774c6abab Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 13:58:46 +0100 Subject: [PATCH 0693/1271] it is possible to pass layer names to create pass --- .../tvpaint/plugins/create/create_render.py | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 71224927d1..b324beb8f6 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -431,25 +431,40 @@ class CreateRenderPass(TVPaintCreator): self.log.debug("Checking selection.") # Get all selected layers and their group ids - selected_layers = [ - layer - for layer in layers_data - if layer["selected"] - ] + marked_layer_names = pre_create_data.get("layer_names") + if marked_layer_names is not None: + layers_by_name = {layer["name"]: layer for layer in layers_data} + marked_layers = [] + for layer_name in marked_layer_names: + layer = layers_by_name.get(layer_name) + if layer is None: + raise CreatorError( + f"Layer with name \"{layer_name}\" was not found") + marked_layers.append(layer) - # Raise if nothing is selected - if not selected_layers: - raise CreatorError("Nothing is selected. Please select layers.") + else: + marked_layers = [ + layer + for layer in layers_data + if layer["selected"] + ] + + # Raise if nothing is selected + if not marked_layers: + raise CreatorError("Nothing is selected. Please select layers.") + + marked_layer_names = {layer["name"] for layer in marked_layers} + + marked_layer_names = set(marked_layer_names) - selected_layer_names = {layer["name"] for layer in selected_layers} instances_to_remove = [] for instance in self.create_context.instances: if instance.creator_identifier != self.identifier: continue - layer_names = set(instance["layer_names"]) - if not layer_names.intersection(selected_layer_names): + cur_layer_names = set(instance["layer_names"]) + if not cur_layer_names.intersection(marked_layer_names): continue - new_layer_names = layer_names - selected_layer_names + new_layer_names = cur_layer_names - marked_layer_names if new_layer_names: instance["layer_names"] = list(new_layer_names) else: @@ -470,7 +485,7 @@ class CreateRenderPass(TVPaintCreator): self.log.info(f"New subset name is \"{label}\".") instance_data["label"] = label instance_data["group"] = f"{self.get_group_label()} ({render_layer})" - instance_data["layer_names"] = list(selected_layer_names) + instance_data["layer_names"] = list(marked_layer_names) if "creator_attributes" not in instance_data: instance_data["creator_attribtues"] = {} @@ -496,7 +511,7 @@ class CreateRenderPass(TVPaintCreator): self.host.write_instances(instances_data) self._add_instance_to_context(new_instance) - self._change_layers_group(selected_layers, group_id) + self._change_layers_group(marked_layers, group_id) return new_instance From 80a9c3e65ee6bd781f9906e115e58fa3fe46ff7f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 14:05:49 +0100 Subject: [PATCH 0694/1271] fix formatting --- openpype/hosts/tvpaint/plugins/create/create_render.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index b324beb8f6..cae894035a 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -451,7 +451,8 @@ class CreateRenderPass(TVPaintCreator): # Raise if nothing is selected if not marked_layers: - raise CreatorError("Nothing is selected. Please select layers.") + raise CreatorError( + "Nothing is selected. Please select layers.") marked_layer_names = {layer["name"] for layer in marked_layers} From 8002dc4f4fb937edb07336232285e48bfec59989 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 20 Feb 2023 21:29:11 +0800 Subject: [PATCH 0695/1271] style fix --- openpype/hosts/max/api/lib.py | 38 ++- openpype/hosts/max/api/lib_renderproducts.py | 70 ++---- openpype/hosts/max/api/lib_rendersettings.py | 5 +- .../hosts/max/plugins/create/create_render.py | 2 +- .../max/plugins/publish/collect_render.py | 6 +- .../plugins/publish/submit_3dmax_deadline.py | 237 ------------------ .../plugins/publish/submit_max_deadline.py | 217 ++++++++++++++++ .../defaults/project_settings/deadline.json | 4 +- .../schema_project_deadline.json | 2 +- 9 files changed, 275 insertions(+), 306 deletions(-) delete mode 100644 openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py create mode 100644 openpype/modules/deadline/plugins/publish/submit_max_deadline.py diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index ecea8b5541..14aa4d750a 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -134,19 +134,21 @@ def get_default_render_folder(project_setting=None): def set_framerange(startFrame, endFrame): - """Get/set the type of time range to be rendered. - - Possible values are: - - 1 -Single frame. - - 2 -Active time segment ( animationRange ). - - 3 -User specified Range. - - 4 -User specified Frame pickup string (for example "1,3,5-12"). """ - # hard-code, there should be a custom setting for this + Args: + start_frame (int): Start frame number. + end_frame (int): End frame number. + Note: + Frame range can be specified in different types. Possible values are: + + * `1` - Single frame. + * `2` - Active time segment ( animationRange ). + * `3` - User specified Range. + * `4` - User specified Frame pickup string (for example `1,3,5-12`). + + Todo: + Current type is hard-coded, there should be a custom setting for this. + """ rt.rendTimeType = 4 if startFrame is not None and endFrame is not None: frameRange = "{0}-{1}".format(startFrame, endFrame) @@ -157,3 +159,15 @@ def get_multipass_setting(project_setting=None): return (project_setting["max"] ["RenderSettings"] ["multipass"]) + +def get_max_version(): + """ + Args: + get max version date for deadline + + Returns: + #(25000, 62, 0, 25, 0, 0, 997, 2023, "") + max_info[7] = max version date + """ + max_info = rt.maxversion() + return max_info[7] diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index e09934e5de..00e0978bc8 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -15,7 +15,6 @@ from openpype.pipeline import legacy_io class RenderProducts(object): - @classmethod def __init__(self, project_settings=None): self._project_settings = project_settings if not self._project_settings: @@ -36,15 +35,11 @@ class RenderProducts(object): filename, container) - context = get_current_project_asset() - startFrame = context["data"].get("frameStart") - endFrame = context["data"].get("frameEnd") + 1 - img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa - full_render_list = self.beauty_render_product(output_file, - startFrame, - endFrame, - img_fmt) + full_render_list = [] + beauty = self.beauty_render_product(output_file, img_fmt) + full_render_list.append(beauty) + renderer_class = get_current_renderer() renderer = str(renderer_class).split(":")[0] @@ -60,41 +55,29 @@ class RenderProducts(object): renderer == "Quicksilver_Hardware_Renderer" ): render_elem_list = self.render_elements_product(output_file, - startFrame, - endFrame, img_fmt) - for render_elem in render_elem_list: - full_render_list.append(render_elem) + if render_elem_list: + for render_elem in render_elem_list: + full_render_list.append(render_elem) return full_render_list if renderer == "Arnold": aov_list = self.arnold_render_product(output_file, - startFrame, - endFrame, img_fmt) if aov_list: for aov in aov_list: full_render_list.append(aov) return full_render_list - def beauty_render_product(self, folder, startFrame, endFrame, fmt): - # get the beauty - beauty_frame_range = list() - - for f in range(startFrame, endFrame): - beauty = "{0}.{1}.{2}".format(folder, - str(f), - fmt) - beauty = beauty.replace("\\", "/") - beauty_frame_range.append(beauty) - - return beauty_frame_range - + def beauty_render_product(self, folder, fmt): + beauty_output = f"{folder}.####.{fmt}" + beauty_output = beauty_output.replace("\\", "/") + return beauty_output # TODO: Get the arnold render product - def arnold_render_product(self, folder, startFrame, endFrame, fmt): + def arnold_render_product(self, folder, fmt): """Get all the Arnold AOVs""" - aovs = list() + aovs = [] amw = rt.MaxtoAOps.AOVsManagerWindow() aov_mgr = rt.renderers.current.AOVManager @@ -105,21 +88,17 @@ class RenderProducts(object): for i in range(aov_group_num): # get the specific AOV group for aov in aov_mgr.drivers[i].aov_list: - for f in range(startFrame, endFrame): - render_element = "{0}_{1}.{2}.{3}".format(folder, - str(aov.name), - str(f), - fmt) - render_element = render_element.replace("\\", "/") - aovs.append(render_element) + render_element = f"{folder}_{aov.name}.####.{fmt}" + render_element = render_element.replace("\\", "/") + aovs.append(render_element) # close the AOVs manager window amw.close() return aovs - def render_elements_product(self, folder, startFrame, endFrame, fmt): + def render_elements_product(self, folder, fmt): """Get all the render element output files. """ - render_dirname = list() + render_dirname = [] render_elem = rt.maxOps.GetCurRenderElementMgr() render_elem_num = render_elem.NumRenderElements() @@ -128,16 +107,11 @@ class RenderProducts(object): renderlayer_name = render_elem.GetRenderElement(i) target, renderpass = str(renderlayer_name).split(":") if renderlayer_name.enabled: - for f in range(startFrame, endFrame): - render_element = "{0}_{1}.{2}.{3}".format(folder, - renderpass, - str(f), - fmt) - render_element = render_element.replace("\\", "/") - render_dirname.append(render_element) + render_element = f"{folder}_{renderpass}.####.{fmt}" + render_element = render_element.replace("\\", "/") + render_dirname.append(render_element) return render_dirname def image_format(self): - img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa - return img_fmt + return self._project_settings["max"]["RenderSettings"]["image_format"] # noqa diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 92f716ba54..212c08846b 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -22,7 +22,6 @@ class RenderSettings(object): "underscore": "_" } - @classmethod def __init__(self, project_settings=None): self._project_settings = project_settings if not self._project_settings: @@ -41,7 +40,7 @@ class RenderSettings(object): if not found: raise RuntimeError("Camera not found") - def set_renderoutput(self, container): + def render_output(self, container): folder = rt.maxFilePath # hard-coded, should be customized in the setting file = rt.maxFileName @@ -144,7 +143,7 @@ class RenderSettings(object): aov_name = "{0}_{1}..{2}".format(dir, renderpass, ext) render_elem.SetRenderElementFileName(i, aov_name) - def get_renderoutput(self, container, output_dir): + def get_render_output(self, container, output_dir): output = os.path.join(output_dir, container) img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa outputFilename = "{0}..{1}".format(output, img_fmt) diff --git a/openpype/hosts/max/plugins/create/create_render.py b/openpype/hosts/max/plugins/create/create_render.py index 76c10ca4a9..269fff2e32 100644 --- a/openpype/hosts/max/plugins/create/create_render.py +++ b/openpype/hosts/max/plugins/create/create_render.py @@ -30,4 +30,4 @@ class CreateRender(plugin.MaxCreator): # set viewport camera for rendering(mandatory for deadline) RenderSettings().set_render_camera(sel_obj) # set output paths for rendering(mandatory for deadline) - RenderSettings().set_renderoutput(container_name) + RenderSettings().render_output(container_name) diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index 55391d40e8..16f8821986 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -4,7 +4,8 @@ import os import pyblish.api from pymxs import runtime as rt -from openpype.pipeline import legacy_io +from openpype.pipeline import get_current_asset_name +from openpype.hosts.max.api.lib import get_max_version from openpype.hosts.max.api.lib_renderproducts import RenderProducts from openpype.client import get_last_version_by_subset_name @@ -25,7 +26,7 @@ class CollectRender(pyblish.api.InstancePlugin): filepath = current_file.replace("\\", "/") context.data['currentFile'] = current_file - asset = legacy_io.Session["AVALON_ASSET"] + asset = get_current_asset_name() render_layer_files = RenderProducts().render_product(instance.name) folder = folder.replace("\\", "/") @@ -51,6 +52,7 @@ class CollectRender(pyblish.api.InstancePlugin): "subset": instance.name, "asset": asset, "publish": True, + "maxversion": str(get_max_version()), "imageFormat": imgFormat, "family": 'maxrender', "families": ['maxrender'], diff --git a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py b/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py deleted file mode 100644 index ed448abe1f..0000000000 --- a/openpype/modules/deadline/plugins/publish/submit_3dmax_deadline.py +++ /dev/null @@ -1,237 +0,0 @@ -import os -import json -import getpass - -import requests -import pyblish.api - -from openpype.pipeline import legacy_io -from openpype.settings import get_project_settings -from openpype.hosts.max.api.lib import ( - get_current_renderer, - get_multipass_setting -) -from openpype.hosts.max.api.lib_rendersettings import RenderSettings - - -class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin): - """ - 3DMax File Submit Render Deadline - - """ - - label = "Submit 3DsMax Render to Deadline" - order = pyblish.api.IntegratorOrder - hosts = ["max"] - families = ["maxrender"] - targets = ["local"] - use_published = True - priority = 50 - chunk_size = 1 - group = None - deadline_pool = None - deadline_pool_secondary = None - framePerTask = 1 - - def process(self, instance): - context = instance.context - filepath = context.data["currentFile"] - filename = os.path.basename(filepath) - comment = context.data.get("comment", "") - deadline_user = context.data.get("deadlineUser", getpass.getuser()) - jobname = "{0} - {1}".format(filename, instance.name) - - # StartFrame to EndFrame - frames = "{start}-{end}".format( - start=int(instance.data["frameStart"]), - end=int(instance.data["frameEnd"]) - ) - if self.use_published: - for item in context: - if "workfile" in item.data["families"]: - msg = "Workfile (scene) must be published along" - assert item.data["publish"] is True, msg - - template_data = item.data.get("anatomyData") - rep = item.data.get("representations")[0].get("name") - template_data["representation"] = rep - template_data["ext"] = rep - template_data["comment"] = None - anatomy_data = context.data["anatomy"] - anatomy_filled = anatomy_data.format(template_data) - template_filled = anatomy_filled["publish"]["path"] - filepath = os.path.normpath(template_filled) - filepath = filepath.replace("\\", "/") - self.log.info( - "Using published scene for render {}".format(filepath) - ) - if not os.path.exists(filepath): - self.log.error("published scene does not exist!") - - new_scene = self._clean_name(filepath) - # use the anatomy data for setting up the path of the files - orig_scene = self._clean_name(instance.context.data["currentFile"]) - expected_files = instance.data.get("expectedFiles") - - new_exp = [] - for file in expected_files: - new_file = str(file).replace(orig_scene, new_scene) - new_exp.append(new_file) - - instance.data["expectedFiles"] = new_exp - - metadata_folder = instance.data.get("publishRenderMetadataFolder") - if metadata_folder: - metadata_folder = metadata_folder.replace(orig_scene, - new_scene) - instance.data["publishRenderMetadataFolder"] = metadata_folder - - payload = { - "JobInfo": { - # Top-level group name - "BatchName": filename, - - # Job name, as seen in Monitor - "Name": jobname, - - # Arbitrary username, for visualisation in Monitor - "UserName": deadline_user, - - "Plugin": instance.data["plugin"], - "Group": self.group, - "Pool": self.deadline_pool, - "secondaryPool": self.deadline_pool_secondary, - "Frames": frames, - "ChunkSize": self.chunk_size, - "Priority": instance.data.get("priority", self.priority), - "Comment": comment, - "FramesPerTask": self.framePerTask - }, - "PluginInfo": { - # Input - "SceneFile": filepath, - "Version": "2023", - "SaveFile": True, - # Mandatory for Deadline - # Houdini version without patch number - - "IgnoreInputs": True - }, - - # Mandatory for Deadline, may be empty - "AuxFiles": [] - } - # Include critical environment variables with submission + api.Session - keys = [ - # Submit along the current Avalon tool setup that we launched - # this application with so the Render Slave can build its own - # similar environment using it, e.g. "maya2018;vray4.x;yeti3.1.9" - "AVALON_TOOLS", - "OPENPYPE_VERSION" - ] - # Add mongo url if it's enabled - if context.data.get("deadlinePassMongoUrl"): - keys.append("OPENPYPE_MONGO") - - environment = dict({key: os.environ[key] for key in keys - if key in os.environ}, **legacy_io.Session) - - payload["JobInfo"].update({ - "EnvironmentKeyValue%d" % index: "{key}={value}".format( - key=key, - value=environment[key] - ) for index, key in enumerate(environment) - }) - - # Include OutputFilename entries - # The first entry also enables double-click to preview rendered - # frames from Deadline Monitor - output_data = {} - # need to be fixed - for i, filepath in enumerate(instance.data["expectedFiles"]): - dirname = os.path.dirname(filepath) - fname = os.path.basename(filepath) - output_data["OutputDirectory%d" % i] = dirname.replace("\\", "/") - output_data["OutputFilename%d" % i] = fname - - if not os.path.exists(dirname): - self.log.info("Ensuring output directory exists: %s" % - dirname) - os.makedirs(dirname) - - plugin_data = {} - project_setting = get_project_settings( - legacy_io.Session["AVALON_PROJECT"] - ) - - multipass = get_multipass_setting(project_setting) - if multipass: - plugin_data["DisableMultipass"] = 0 - else: - plugin_data["DisableMultipass"] = 1 - - if self.use_published: - old_output_dir = os.path.dirname(expected_files[0]) - output_beauty = RenderSettings().get_renderoutput(instance.name, - old_output_dir) - output_beauty = output_beauty.replace(orig_scene, new_scene) - output_beauty = output_beauty.replace("\\", "/") - plugin_data["RenderOutput"] = output_beauty - - renderer_class = get_current_renderer() - renderer = str(renderer_class).split(":")[0] - if ( - renderer == "ART_Renderer" or - renderer == "Redshift_Renderer" or - renderer == "V_Ray_6_Hotfix_3" or - renderer == "V_Ray_GPU_6_Hotfix_3" or - renderer == "Default_Scanline_Renderer" or - renderer == "Quicksilver_Hardware_Renderer" - ): - render_elem_list = RenderSettings().get_render_element() - for i, element in enumerate(render_elem_list): - element = element.replace(orig_scene, new_scene) - plugin_data["RenderElementOutputFilename%d" % i] = element # noqa - - self.log.debug("plugin data:{}".format(plugin_data)) - self.log.info("Scene name was switched {} -> {}".format( - orig_scene, new_scene - )) - - payload["JobInfo"].update(output_data) - payload["PluginInfo"].update(plugin_data) - - self.submit(instance, payload) - - def submit(self, instance, payload): - - context = instance.context - deadline_url = context.data.get("defaultDeadline") - deadline_url = instance.data.get( - "deadlineUrl", deadline_url) - - assert deadline_url, "Requires Deadline Webservice URL" - - plugin = payload["JobInfo"]["Plugin"] - self.log.info("Using Render Plugin : {}".format(plugin)) - - self.log.info("Submitting..") - self.log.debug(json.dumps(payload, indent=4, sort_keys=True)) - - # E.g. http://192.168.0.1:8082/api/jobs - url = "{}/api/jobs".format(deadline_url) - response = requests.post(url, json=payload) - if not response.ok: - raise Exception(response.text) - # Store output dir for unified publisher (expectedFilesequence) - expected_files = instance.data["expectedFiles"] - output_dir = os.path.dirname(expected_files[0]) - instance.data["toBeRenderedOn"] = "deadline" - instance.data["outputDir"] = output_dir - instance.data["deadlineSubmissionJob"] = response.json() - - def rename_render_element(self): - pass - - def _clean_name(self, path): - return os.path.splitext(os.path.basename(path))[0] diff --git a/openpype/modules/deadline/plugins/publish/submit_max_deadline.py b/openpype/modules/deadline/plugins/publish/submit_max_deadline.py new file mode 100644 index 0000000000..3e00f8fd15 --- /dev/null +++ b/openpype/modules/deadline/plugins/publish/submit_max_deadline.py @@ -0,0 +1,217 @@ +import os +import getpass +import copy + +import attr +from openpype.pipeline import legacy_io +from openpype.settings import get_project_settings +from openpype.hosts.max.api.lib import ( + get_current_renderer, + get_multipass_setting +) +from openpype.hosts.max.api.lib_rendersettings import RenderSettings +from openpype_modules.deadline import abstract_submit_deadline +from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo + + +@attr.s +class MaxPluginInfo(object): + SceneFile = attr.ib(default=None) # Input + Version = attr.ib(default=None) # Mandatory for Deadline + SaveFile = attr.ib(default=True) + IgnoreInputs = attr.ib(default=True) + + +class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): + + label = "Submit Render to Deadline" + hosts = ["max"] + families = ["maxrender"] + targets = ["local"] + + use_published = True + priority = 50 + tile_priority = 50 + chunk_size = 1 + jobInfo = {} + pluginInfo = {} + group = None + deadline_pool = None + deadline_pool_secondary = None + framePerTask = 1 + + def get_job_info(self): + job_info = DeadlineJobInfo(Plugin="3dsmax") + + # todo: test whether this works for existing production cases + # where custom jobInfo was stored in the project settings + job_info.update(self.jobInfo) + + instance = self._instance + context = instance.context + + # Always use the original work file name for the Job name even when + # rendering is done from the published Work File. The original work + # file name is clearer because it can also have subversion strings, + # etc. which are stripped for the published file. + src_filepath = context.data["currentFile"] + src_filename = os.path.basename(src_filepath) + + job_info.Name = "%s - %s" % (src_filename, instance.name) + job_info.BatchName = src_filename + job_info.Plugin = instance.data["plugin"] + job_info.UserName = context.data.get("deadlineUser", getpass.getuser()) + + # Deadline requires integers in frame range + frames = "{start}-{end}".format( + start=int(instance.data["frameStart"]), + end=int(instance.data["frameEnd"]) + ) + job_info.Frames = frames + + job_info.Pool = instance.data.get("primaryPool") + job_info.SecondaryPool = instance.data.get("secondaryPool") + job_info.ChunkSize = instance.data.get("chunkSize", 1) + job_info.Comment = context.data.get("comment") + job_info.Priority = instance.data.get("priority", self.priority) + job_info.FramesPerTask = instance.data.get("framesPerTask", 1) + + if self.group: + job_info.Group = self.group + + # Add options from RenderGlobals + render_globals = instance.data.get("renderGlobals", {}) + job_info.update(render_globals) + + keys = [ + "FTRACK_API_KEY", + "FTRACK_API_USER", + "FTRACK_SERVER", + "OPENPYPE_SG_USER", + "AVALON_PROJECT", + "AVALON_ASSET", + "AVALON_TASK", + "AVALON_APP_NAME", + "OPENPYPE_DEV", + "OPENPYPE_VERSION", + "IS_TEST" + ] + # Add mongo url if it's enabled + if self._instance.context.data.get("deadlinePassMongoUrl"): + keys.append("OPENPYPE_MONGO") + + environment = dict({key: os.environ[key] for key in keys + if key in os.environ}, **legacy_io.Session) + + for key in keys: + value = environment.get(key) + if not value: + continue + job_info.EnvironmentKeyValue[key] = value + + # to recognize job from PYPE for turning Event On/Off + job_info.EnvironmentKeyValue["OPENPYPE_RENDER_JOB"] = "1" + job_info.EnvironmentKeyValue["OPENPYPE_LOG_NO_COLORS"] = "1" + + # Add list of expected files to job + # --------------------------------- + exp = instance.data.get("expectedFiles") + for filepath in exp: + job_info.OutputDirectory += os.path.dirname(filepath) + job_info.OutputFilename += os.path.basename(filepath) + + return job_info + + def get_plugin_info(self): + instance = self._instance + + plugin_info = MaxPluginInfo( + SceneFile=self.scene_path, + Version=instance.data["maxversion"], + SaveFile = True, + IgnoreInputs = True + ) + + plugin_payload = attr.asdict(plugin_info) + + # Patching with pluginInfo from settings + for key, value in self.pluginInfo.items(): + plugin_payload[key] = value + + return plugin_payload + + def process_submission(self): + + instance = self._instance + context = instance.context + filepath = self.scene_path + + expected_files = instance.data["expectedFiles"] + if not expected_files: + raise RuntimeError("No Render Elements found!") + output_dir = os.path.dirname(expected_files[0]) + instance.data["outputDir"] = output_dir + instance.data["toBeRenderedOn"] = "deadline" + + filename = os.path.basename(filepath) + + payload_data = { + "filename": filename, + "dirname": output_dir + } + + self.log.debug("Submitting 3dsMax render..") + payload = self._use_puhlished_name(payload_data) + job_info, plugin_info = payload + self.submit(self.assemble_payload(job_info, plugin_info)) + + def _use_puhlished_name(self, data): + instance = self._instance + job_info = copy.deepcopy(self.job_info) + plugin_info = copy.deepcopy(self.plugin_info) + plugin_data = {} + project_setting = get_project_settings( + legacy_io.Session["AVALON_PROJECT"] + ) + + multipass = get_multipass_setting(project_setting) + if multipass: + plugin_data["DisableMultipass"] = 0 + else: + plugin_data["DisableMultipass"] = 1 + + expected_files = instance.data.get("expectedFiles") + if not expected_files: + raise RuntimeError("No render elements found") + old_output_dir = os.path.dirname(expected_files[0]) + output_beauty = RenderSettings().get_render_output(instance.name, + old_output_dir) + filepath = self.from_published_scene() + def _clean_name(path): + return os.path.splitext(os.path.basename(path))[0] + new_scene = _clean_name(filepath) + orig_scene = _clean_name(instance.context.data["currentFile"]) + + output_beauty = output_beauty.replace(orig_scene, new_scene) + output_beauty = output_beauty.replace("\\", "/") + plugin_data["RenderOutput"] = output_beauty + + renderer_class = get_current_renderer() + renderer = str(renderer_class).split(":")[0] + if ( + renderer == "ART_Renderer" or + renderer == "Redshift_Renderer" or + renderer == "V_Ray_6_Hotfix_3" or + renderer == "V_Ray_GPU_6_Hotfix_3" or + renderer == "Default_Scanline_Renderer" or + renderer == "Quicksilver_Hardware_Renderer" + ): + render_elem_list = RenderSettings().get_render_element() + for i, element in enumerate(render_elem_list): + element = element.replace(orig_scene, new_scene) + plugin_data["RenderElementOutputFilename%d" % i] = element # noqa + + self.log.debug("plugin data:{}".format(plugin_data)) + plugin_info.update(plugin_data) + + return job_info, plugin_info diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index 7a5903d8e0..7183603c4b 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -36,7 +36,7 @@ "scene_patches": [], "strict_error_checking": true }, - "MaxSubmitRenderDeadline": { + "MaxSubmitDeadline": { "enabled": true, "optional": false, "active": true, @@ -122,4 +122,4 @@ } } } -} \ No newline at end of file +} diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index 3d1b413d6c..a320dfca4f 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -207,7 +207,7 @@ { "type": "dict", "collapsible": true, - "key": "MaxSubmitRenderDeadline", + "key": "MaxSubmitDeadline", "label": "3dsMax Submit to Deadline", "checkbox_key": "enabled", "children": [ From 3aa6e9ac9d78ce3404c39bb91ca8e367014cb6c8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 20 Feb 2023 21:36:37 +0800 Subject: [PATCH 0696/1271] hound fix --- openpype/hosts/max/api/lib.py | 20 +++++++++---------- openpype/hosts/max/api/lib_renderproducts.py | 2 +- .../plugins/publish/submit_max_deadline.py | 7 ++++--- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index 14aa4d750a..67e34c0a30 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -136,18 +136,17 @@ def get_default_render_folder(project_setting=None): def set_framerange(startFrame, endFrame): """ Args: - start_frame (int): Start frame number. - end_frame (int): End frame number. + start_frame (int): Start frame number. + end_frame (int): End frame number. Note: - Frame range can be specified in different types. Possible values are: + Frame range can be specified in different types. Possible values are: - * `1` - Single frame. - * `2` - Active time segment ( animationRange ). - * `3` - User specified Range. - * `4` - User specified Frame pickup string (for example `1,3,5-12`). - - Todo: - Current type is hard-coded, there should be a custom setting for this. + * `1` - Single frame. + * `2` - Active time segment ( animationRange ). + * `3` - User specified Range. + * `4`-User specified Frame pickup string (for example `1,3,5-12`). + TODO: + Current type is hard-coded, there should be a custom setting for this. """ rt.rendTimeType = 4 if startFrame is not None and endFrame is not None: @@ -160,6 +159,7 @@ def get_multipass_setting(project_setting=None): ["RenderSettings"] ["multipass"]) + def get_max_version(): """ Args: diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index 00e0978bc8..6d476c9139 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -8,7 +8,6 @@ from openpype.hosts.max.api.lib import ( get_current_renderer, get_default_render_folder ) -from openpype.pipeline.context_tools import get_current_project_asset from openpype.settings import get_project_settings from openpype.pipeline import legacy_io @@ -74,6 +73,7 @@ class RenderProducts(object): beauty_output = f"{folder}.####.{fmt}" beauty_output = beauty_output.replace("\\", "/") return beauty_output + # TODO: Get the arnold render product def arnold_render_product(self, folder, fmt): """Get all the Arnold AOVs""" diff --git a/openpype/modules/deadline/plugins/publish/submit_max_deadline.py b/openpype/modules/deadline/plugins/publish/submit_max_deadline.py index 3e00f8fd15..b53cc928d6 100644 --- a/openpype/modules/deadline/plugins/publish/submit_max_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_max_deadline.py @@ -128,8 +128,8 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): plugin_info = MaxPluginInfo( SceneFile=self.scene_path, Version=instance.data["maxversion"], - SaveFile = True, - IgnoreInputs = True + SaveFile=True, + IgnoreInputs=True ) plugin_payload = attr.asdict(plugin_info) @@ -143,7 +143,6 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): def process_submission(self): instance = self._instance - context = instance.context filepath = self.scene_path expected_files = instance.data["expectedFiles"] @@ -187,8 +186,10 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): output_beauty = RenderSettings().get_render_output(instance.name, old_output_dir) filepath = self.from_published_scene() + def _clean_name(path): return os.path.splitext(os.path.basename(path))[0] + new_scene = _clean_name(filepath) orig_scene = _clean_name(instance.context.data["currentFile"]) From db75941e00dfd9f503b0690cda7334ab71b0892d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 20 Feb 2023 21:39:31 +0800 Subject: [PATCH 0697/1271] hound fix --- openpype/hosts/max/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index 67e34c0a30..53e66c219e 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -144,7 +144,7 @@ def set_framerange(startFrame, endFrame): * `1` - Single frame. * `2` - Active time segment ( animationRange ). * `3` - User specified Range. - * `4`-User specified Frame pickup string (for example `1,3,5-12`). + * `4` - User specified Frame pickup string (for example `1,3,5-12`). TODO: Current type is hard-coded, there should be a custom setting for this. """ From 7b6fb46cd191ab39ae43b9664365a0422203ad58 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 20 Feb 2023 16:05:24 +0000 Subject: [PATCH 0698/1271] Fix colorspaceTemplate --- openpype/modules/deadline/plugins/publish/submit_publish_job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 89a4e5d377..bd5933926c 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -922,7 +922,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "display": instance.data["colorspaceDisplay"], "view": instance.data["colorspaceView"], "colorspaceTemplate": instance.data["colorspaceConfig"].replace( - context.data["anatomy"].roots["work"], "{root[work]}" + str(context.data["anatomy"].roots["work"]), "{root[work]}" ) } From 5e203ec6306da50cabca6efab09c4a679b451228 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 20 Feb 2023 16:06:48 +0000 Subject: [PATCH 0699/1271] Only submit version when running from build. --- .../plugins/publish/submit_aftereffects_deadline.py | 7 ++++++- .../deadline/plugins/publish/submit_harmony_deadline.py | 9 +++++++-- .../plugins/publish/submit_houdini_remote_publish.py | 9 +++++++-- .../plugins/publish/submit_houdini_render_deadline.py | 9 +++++++-- .../deadline/plugins/publish/submit_maya_deadline.py | 9 +++++++-- .../publish/submit_maya_remote_publish_deadline.py | 9 +++++++-- .../deadline/plugins/publish/submit_nuke_deadline.py | 9 +++++++-- .../deadline/plugins/publish/submit_publish_job.py | 8 ++++++-- 8 files changed, 54 insertions(+), 15 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py b/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py index f26047bb9d..83dd5b49e2 100644 --- a/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py @@ -12,6 +12,7 @@ from openpype.pipeline import legacy_io from openpype_modules.deadline import abstract_submit_deadline from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo from openpype.tests.lib import is_in_tests +from openpype.lib import is_running_from_build @attr.s @@ -87,9 +88,13 @@ class AfterEffectsSubmitDeadline( "AVALON_APP_NAME", "OPENPYPE_DEV", "OPENPYPE_LOG_NO_COLORS", - "OPENPYPE_VERSION", "IS_TEST" ] + + # Add OpenPype version if we are running from build. + if is_running_from_build(): + keys.append("OPENPYPE_VERSION") + # Add mongo url if it's enabled if self._instance.context.data.get("deadlinePassMongoUrl"): keys.append("OPENPYPE_MONGO") diff --git a/openpype/modules/deadline/plugins/publish/submit_harmony_deadline.py b/openpype/modules/deadline/plugins/publish/submit_harmony_deadline.py index 425883393f..84fca11d9d 100644 --- a/openpype/modules/deadline/plugins/publish/submit_harmony_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_harmony_deadline.py @@ -14,6 +14,7 @@ from openpype.pipeline import legacy_io from openpype_modules.deadline import abstract_submit_deadline from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo from openpype.tests.lib import is_in_tests +from openpype.lib import is_running_from_build class _ZipFile(ZipFile): @@ -279,10 +280,14 @@ class HarmonySubmitDeadline( "AVALON_TASK", "AVALON_APP_NAME", "OPENPYPE_DEV", - "OPENPYPE_LOG_NO_COLORS", - "OPENPYPE_VERSION", + "OPENPYPE_LOG_NO_COLORS" "IS_TEST" ] + + # Add OpenPype version if we are running from build. + if is_running_from_build(): + keys.append("OPENPYPE_VERSION") + # Add mongo url if it's enabled if self._instance.context.data.get("deadlinePassMongoUrl"): keys.append("OPENPYPE_MONGO") diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_remote_publish.py b/openpype/modules/deadline/plugins/publish/submit_houdini_remote_publish.py index 6a62f83cae..68aa653804 100644 --- a/openpype/modules/deadline/plugins/publish/submit_houdini_remote_publish.py +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_remote_publish.py @@ -9,6 +9,7 @@ import pyblish.api from openpype.pipeline import legacy_io from openpype.tests.lib import is_in_tests +from openpype.lib import is_running_from_build class HoudiniSubmitPublishDeadline(pyblish.api.ContextPlugin): @@ -133,9 +134,13 @@ class HoudiniSubmitPublishDeadline(pyblish.api.ContextPlugin): # Submit along the current Avalon tool setup that we launched # this application with so the Render Slave can build its own # similar environment using it, e.g. "houdini17.5;pluginx2.3" - "AVALON_TOOLS", - "OPENPYPE_VERSION" + "AVALON_TOOLS" ] + + # Add OpenPype version if we are running from build. + if is_running_from_build(): + keys.append("OPENPYPE_VERSION") + # Add mongo url if it's enabled if context.data.get("deadlinePassMongoUrl"): keys.append("OPENPYPE_MONGO") diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index 2b17b644b8..73ab689c9a 100644 --- a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -10,6 +10,7 @@ import pyblish.api from openpype.pipeline import legacy_io from openpype.tests.lib import is_in_tests +from openpype.lib import is_running_from_build class HoudiniSubmitRenderDeadline(pyblish.api.InstancePlugin): @@ -105,9 +106,13 @@ class HoudiniSubmitRenderDeadline(pyblish.api.InstancePlugin): # Submit along the current Avalon tool setup that we launched # this application with so the Render Slave can build its own # similar environment using it, e.g. "maya2018;vray4.x;yeti3.1.9" - "AVALON_TOOLS", - "OPENPYPE_VERSION" + "AVALON_TOOLS" ] + + # Add OpenPype version if we are running from build. + if is_running_from_build(): + keys.append("OPENPYPE_VERSION") + # Add mongo url if it's enabled if context.data.get("deadlinePassMongoUrl"): keys.append("OPENPYPE_MONGO") diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index ed37ff1897..22b5c02296 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -38,6 +38,7 @@ from openpype.hosts.maya.api.lib import get_attr_in_layer from openpype_modules.deadline import abstract_submit_deadline from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo from openpype.tests.lib import is_in_tests +from openpype.lib import is_running_from_build def _validate_deadline_bool_value(instance, attribute, value): @@ -165,10 +166,14 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): "AVALON_ASSET", "AVALON_TASK", "AVALON_APP_NAME", - "OPENPYPE_DEV", - "OPENPYPE_VERSION", + "OPENPYPE_DEV" "IS_TEST" ] + + # Add OpenPype version if we are running from build. + if is_running_from_build(): + keys.append("OPENPYPE_VERSION") + # Add mongo url if it's enabled if self._instance.context.data.get("deadlinePassMongoUrl"): keys.append("OPENPYPE_MONGO") diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py index bab6591c7f..25f859554f 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py @@ -7,6 +7,7 @@ from maya import cmds from openpype.pipeline import legacy_io, PublishXmlValidationError from openpype.settings import get_project_settings from openpype.tests.lib import is_in_tests +from openpype.lib import is_running_from_build import pyblish.api @@ -104,9 +105,13 @@ class MayaSubmitRemotePublishDeadline(pyblish.api.InstancePlugin): keys = [ "FTRACK_API_USER", "FTRACK_API_KEY", - "FTRACK_SERVER", - "OPENPYPE_VERSION" + "FTRACK_SERVER" ] + + # Add OpenPype version if we are running from build. + if is_running_from_build(): + keys.append("OPENPYPE_VERSION") + environment = dict({key: os.environ[key] for key in keys if key in os.environ}, **legacy_io.Session) diff --git a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py index d1948d8d50..cca2a4d896 100644 --- a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -10,6 +10,7 @@ import pyblish.api import nuke from openpype.pipeline import legacy_io from openpype.tests.lib import is_in_tests +from openpype.lib import is_running_from_build class NukeSubmitDeadline(pyblish.api.InstancePlugin): @@ -265,9 +266,13 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): "PYBLISHPLUGINPATH", "NUKE_PATH", "TOOL_ENV", - "FOUNDRY_LICENSE", - "OPENPYPE_VERSION" + "FOUNDRY_LICENSE" ] + + # Add OpenPype version if we are running from build. + if is_running_from_build(): + keys.append("OPENPYPE_VERSION") + # Add mongo url if it's enabled if instance.context.data.get("deadlinePassMongoUrl"): keys.append("OPENPYPE_MONGO") diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 8f8f4246dd..e132d7323b 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -20,6 +20,7 @@ from openpype.pipeline import ( ) from openpype.tests.lib import is_in_tests from openpype.pipeline.farm.patterning import match_aov_pattern +from openpype.lib import is_running_from_build def get_resources(project_name, version, extension=None): @@ -136,10 +137,13 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "FTRACK_API_KEY", "FTRACK_SERVER", "AVALON_APP_NAME", - "OPENPYPE_USERNAME", - "OPENPYPE_VERSION" + "OPENPYPE_USERNAME" ] + # Add OpenPype version if we are running from build. + if is_running_from_build(): + environ_keys.append("OPENPYPE_VERSION") + # custom deadline attributes deadline_department = "" deadline_pool = "" From 72134d4a211ee325799c439a31fded53c41b04a9 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 20 Feb 2023 18:33:15 +0100 Subject: [PATCH 0700/1271] Fix changed location of reset_frame_range Location in commands caused cyclic import --- openpype/hosts/maya/api/commands.py | 49 ------------------- openpype/hosts/maya/api/lib.py | 49 ++++++++++++++++++- openpype/hosts/maya/api/lib_rendersettings.py | 2 +- openpype/hosts/maya/api/menu.py | 3 +- 4 files changed, 50 insertions(+), 53 deletions(-) diff --git a/openpype/hosts/maya/api/commands.py b/openpype/hosts/maya/api/commands.py index 19ad18d824..018340d86c 100644 --- a/openpype/hosts/maya/api/commands.py +++ b/openpype/hosts/maya/api/commands.py @@ -4,7 +4,6 @@ from maya import cmds from openpype.client import get_asset_by_name, get_project from openpype.pipeline import legacy_io -from . import lib class ToolWindows: @@ -58,54 +57,6 @@ def edit_shader_definitions(): window.show() -def reset_frame_range(): - """Set frame range to current asset""" - - fps = lib.convert_to_maya_fps( - float(legacy_io.Session.get("AVALON_FPS", 25)) - ) - lib.set_scene_fps(fps) - - # Set frame start/end - project_name = legacy_io.active_project() - asset_name = legacy_io.Session["AVALON_ASSET"] - asset = get_asset_by_name(project_name, asset_name) - - frame_start = asset["data"].get("frameStart") - frame_end = asset["data"].get("frameEnd") - # Backwards compatibility - if frame_start is None or frame_end is None: - frame_start = asset["data"].get("edit_in") - frame_end = asset["data"].get("edit_out") - - if frame_start is None or frame_end is None: - cmds.warning("No edit information found for %s" % asset_name) - return - - handles = asset["data"].get("handles") or 0 - handle_start = asset["data"].get("handleStart") - if handle_start is None: - handle_start = handles - - handle_end = asset["data"].get("handleEnd") - if handle_end is None: - handle_end = handles - - frame_start -= int(handle_start) - frame_end += int(handle_end) - - cmds.playbackOptions(minTime=frame_start) - cmds.playbackOptions(maxTime=frame_end) - cmds.playbackOptions(animationStartTime=frame_start) - cmds.playbackOptions(animationEndTime=frame_end) - cmds.playbackOptions(minTime=frame_start) - cmds.playbackOptions(maxTime=frame_end) - cmds.currentTime(frame_start) - - cmds.setAttr("defaultRenderGlobals.startFrame", frame_start) - cmds.setAttr("defaultRenderGlobals.endFrame", frame_end) - - def _resolution_from_document(doc): if not doc or "data" not in doc: print("Entered document is not valid. \"{}\"".format(str(doc))) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index b920428b20..84c6e6929d 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -34,7 +34,6 @@ from openpype.pipeline import ( registered_host, ) from openpype.pipeline.context_tools import get_current_project_asset -from .commands import reset_frame_range self = sys.modules[__name__] @@ -2065,6 +2064,54 @@ def set_scene_resolution(width, height, pixelAspect): cmds.setAttr("%s.pixelAspect" % control_node, pixelAspect) +def reset_frame_range(): + """Set frame range to current asset""" + + fps = convert_to_maya_fps( + float(legacy_io.Session.get("AVALON_FPS", 25)) + ) + set_scene_fps(fps) + + # Set frame start/end + project_name = legacy_io.active_project() + asset_name = legacy_io.Session["AVALON_ASSET"] + asset = get_asset_by_name(project_name, asset_name) + + frame_start = asset["data"].get("frameStart") + frame_end = asset["data"].get("frameEnd") + # Backwards compatibility + if frame_start is None or frame_end is None: + frame_start = asset["data"].get("edit_in") + frame_end = asset["data"].get("edit_out") + + if frame_start is None or frame_end is None: + cmds.warning("No edit information found for %s" % asset_name) + return + + handles = asset["data"].get("handles") or 0 + handle_start = asset["data"].get("handleStart") + if handle_start is None: + handle_start = handles + + handle_end = asset["data"].get("handleEnd") + if handle_end is None: + handle_end = handles + + frame_start -= int(handle_start) + frame_end += int(handle_end) + + cmds.playbackOptions(minTime=frame_start) + cmds.playbackOptions(maxTime=frame_end) + cmds.playbackOptions(animationStartTime=frame_start) + cmds.playbackOptions(animationEndTime=frame_end) + cmds.playbackOptions(minTime=frame_start) + cmds.playbackOptions(maxTime=frame_end) + cmds.currentTime(frame_start) + + cmds.setAttr("defaultRenderGlobals.startFrame", frame_start) + cmds.setAttr("defaultRenderGlobals.endFrame", frame_end) + + def reset_scene_resolution(): """Apply the scene resolution from the project definition diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 6190a49401..2a730100de 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -14,7 +14,7 @@ from openpype.settings import ( from openpype.pipeline import legacy_io from openpype.pipeline import CreatorError from openpype.pipeline.context_tools import get_current_project_asset -from openpype.hosts.maya.api.commands import reset_frame_range +from openpype.hosts.maya.api.lib import reset_frame_range class RenderSettings(object): diff --git a/openpype/hosts/maya/api/menu.py b/openpype/hosts/maya/api/menu.py index 791475173f..0f48a133a6 100644 --- a/openpype/hosts/maya/api/menu.py +++ b/openpype/hosts/maya/api/menu.py @@ -12,7 +12,6 @@ from openpype.pipeline.workfile import BuildWorkfile from openpype.tools.utils import host_tools from openpype.hosts.maya.api import lib, lib_rendersettings from .lib import get_main_window, IS_HEADLESS -from .commands import reset_frame_range from .workfile_template_builder import ( create_placeholder, @@ -113,7 +112,7 @@ def install(): cmds.menuItem( "Reset Frame Range", - command=lambda *args: reset_frame_range() + command=lambda *args: lib.reset_frame_range() ) cmds.menuItem( From ffcc1656d2a4641bf9ae36e4a28a0c831f8d8a4f Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 21 Feb 2023 11:08:07 +0800 Subject: [PATCH 0701/1271] cosmetic issue fix --- openpype/hosts/max/api/lib.py | 13 +++---- openpype/hosts/max/api/lib_renderproducts.py | 23 +++++------ openpype/hosts/max/api/lib_rendersettings.py | 39 +++++++++++-------- .../max/plugins/publish/collect_render.py | 2 +- .../plugins/publish/submit_max_deadline.py | 20 +++++----- 5 files changed, 48 insertions(+), 49 deletions(-) diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index 53e66c219e..6ee934d3da 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -135,17 +135,14 @@ def get_default_render_folder(project_setting=None): def set_framerange(startFrame, endFrame): """ - Args: - start_frame (int): Start frame number. - end_frame (int): End frame number. Note: Frame range can be specified in different types. Possible values are: + * `1` - Single frame. + * `2` - Active time segment ( animationRange ). + * `3` - User specified Range. + * `4` - User specified Frame pickup string (for example `1,3,5-12`). - * `1` - Single frame. - * `2` - Active time segment ( animationRange ). - * `3` - User specified Range. - * `4` - User specified Frame pickup string (for example `1,3,5-12`). - TODO: + Todo: Current type is hard-coded, there should be a custom setting for this. """ rt.rendTimeType = 4 diff --git a/openpype/hosts/max/api/lib_renderproducts.py b/openpype/hosts/max/api/lib_renderproducts.py index 6d476c9139..a74a6a7426 100644 --- a/openpype/hosts/max/api/lib_renderproducts.py +++ b/openpype/hosts/max/api/lib_renderproducts.py @@ -45,28 +45,25 @@ class RenderProducts(object): if renderer == "VUE_File_Renderer": return full_render_list - if ( - renderer == "ART_Renderer" or - renderer == "Redshift_Renderer" or - renderer == "V_Ray_6_Hotfix_3" or - renderer == "V_Ray_GPU_6_Hotfix_3" or - renderer == "Default_Scanline_Renderer" or - renderer == "Quicksilver_Hardware_Renderer" - ): + if renderer in [ + "ART_Renderer", + "Redshift_Renderer", + "V_Ray_6_Hotfix_3", + "V_Ray_GPU_6_Hotfix_3", + "Default_Scanline_Renderer", + "Quicksilver_Hardware_Renderer", + ]: render_elem_list = self.render_elements_product(output_file, img_fmt) if render_elem_list: - for render_elem in render_elem_list: - full_render_list.append(render_elem) - + full_render_list.extend(iter(render_elem_list)) return full_render_list if renderer == "Arnold": aov_list = self.arnold_render_product(output_file, img_fmt) if aov_list: - for aov in aov_list: - full_render_list.append(aov) + full_render_list.extend(iter(aov_list)) return full_render_list def beauty_render_product(self, folder, fmt): diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 212c08846b..94c6ee775b 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -23,6 +23,11 @@ class RenderSettings(object): } def __init__(self, project_settings=None): + """ + Set up the naming convention for the render + elements for the deadline submission + """ + self._project_settings = project_settings if not self._project_settings: self._project_settings = get_project_settings( @@ -61,9 +66,9 @@ class RenderSettings(object): width = context["data"].get("resolutionWidth") height = context["data"].get("resolutionHeight") # Set Frame Range - startFrame = context["data"].get("frameStart") - endFrame = context["data"].get("frameEnd") - set_framerange(startFrame, endFrame) + frame_start = context["data"].get("frame_start") + frame_end = context["data"].get("frame_end") + set_framerange(frame_start, frame_end) # get the production render renderer_class = get_current_renderer() renderer = str(renderer_class).split(":")[0] @@ -78,24 +83,24 @@ class RenderSettings(object): )] except KeyError: aov_separator = "." - outputFilename = "{0}..{1}".format(output, img_fmt) - outputFilename = outputFilename.replace("{aov_separator}", + output_filename = "{0}..{1}".format(output, img_fmt) + output_filename = output_filename.replace("{aov_separator}", aov_separator) - rt.rendOutputFilename = outputFilename + rt.rendOutputFilename = output_filename if renderer == "VUE_File_Renderer": return # TODO: Finish the arnold render setup if renderer == "Arnold": self.arnold_setup() - if ( - renderer == "ART_Renderer" or - renderer == "Redshift_Renderer" or - renderer == "V_Ray_6_Hotfix_3" or - renderer == "V_Ray_GPU_6_Hotfix_3" or - renderer == "Default_Scanline_Renderer" or - renderer == "Quicksilver_Hardware_Renderer" - ): + if renderer in [ + "ART_Renderer", + "Redshift_Renderer", + "V_Ray_6_Hotfix_3", + "V_Ray_GPU_6_Hotfix_3", + "Default_Scanline_Renderer", + "Quicksilver_Hardware_Renderer", + ]: self.render_element_layer(output, width, height, img_fmt) rt.rendSaveFile = True @@ -146,11 +151,11 @@ class RenderSettings(object): def get_render_output(self, container, output_dir): output = os.path.join(output_dir, container) img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa - outputFilename = "{0}..{1}".format(output, img_fmt) - return outputFilename + output_filename = "{0}..{1}".format(output, img_fmt) + return output_filename def get_render_element(self): - orig_render_elem = list() + orig_render_elem = [] render_elem = rt.maxOps.GetCurRenderElementMgr() render_elem_num = render_elem.NumRenderElements() if render_elem_num < 0: diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index 16f8821986..7656c641ed 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -14,7 +14,7 @@ class CollectRender(pyblish.api.InstancePlugin): """Collect Render for Deadline""" order = pyblish.api.CollectorOrder + 0.01 - label = "Collect 3dmax Render Layers" + label = "Collect 3dsmax Render Layers" hosts = ['max'] families = ["maxrender"] diff --git a/openpype/modules/deadline/plugins/publish/submit_max_deadline.py b/openpype/modules/deadline/plugins/publish/submit_max_deadline.py index b53cc928d6..417a03de74 100644 --- a/openpype/modules/deadline/plugins/publish/submit_max_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_max_deadline.py @@ -160,11 +160,11 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): } self.log.debug("Submitting 3dsMax render..") - payload = self._use_puhlished_name(payload_data) + payload = self._use_published_name(payload_data) job_info, plugin_info = payload self.submit(self.assemble_payload(job_info, plugin_info)) - def _use_puhlished_name(self, data): + def _use_published_name(self, data): instance = self._instance job_info = copy.deepcopy(self.job_info) plugin_info = copy.deepcopy(self.plugin_info) @@ -199,14 +199,14 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): renderer_class = get_current_renderer() renderer = str(renderer_class).split(":")[0] - if ( - renderer == "ART_Renderer" or - renderer == "Redshift_Renderer" or - renderer == "V_Ray_6_Hotfix_3" or - renderer == "V_Ray_GPU_6_Hotfix_3" or - renderer == "Default_Scanline_Renderer" or - renderer == "Quicksilver_Hardware_Renderer" - ): + if renderer in [ + "ART_Renderer", + "Redshift_Renderer", + "V_Ray_6_Hotfix_3", + "V_Ray_GPU_6_Hotfix_3", + "Default_Scanline_Renderer", + "Quicksilver_Hardware_Renderer", + ]: render_elem_list = RenderSettings().get_render_element() for i, element in enumerate(render_elem_list): element = element.replace(orig_scene, new_scene) From 8ca7a719046bc2ceb2075db20a959ccc88797447 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 21 Feb 2023 11:10:02 +0800 Subject: [PATCH 0702/1271] hound fix --- openpype/hosts/max/api/lib.py | 6 +++--- openpype/hosts/max/api/lib_rendersettings.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index 6ee934d3da..3383330792 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -137,9 +137,9 @@ def set_framerange(startFrame, endFrame): """ Note: Frame range can be specified in different types. Possible values are: - * `1` - Single frame. - * `2` - Active time segment ( animationRange ). - * `3` - User specified Range. + * `1` - Single frame. + * `2` - Active time segment ( animationRange ). + * `3` - User specified Range. * `4` - User specified Frame pickup string (for example `1,3,5-12`). Todo: diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index 94c6ee775b..b07d19f176 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -85,7 +85,7 @@ class RenderSettings(object): aov_separator = "." output_filename = "{0}..{1}".format(output, img_fmt) output_filename = output_filename.replace("{aov_separator}", - aov_separator) + aov_separator) rt.rendOutputFilename = output_filename if renderer == "VUE_File_Renderer": return From 373b1abffc44a2be38643d28131ee0abefe3c52c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 21 Feb 2023 07:21:30 +0000 Subject: [PATCH 0703/1271] Ensure viewport options work ahead of playblasting. --- .../maya/plugins/publish/extract_playblast.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_playblast.py b/openpype/hosts/maya/plugins/publish/extract_playblast.py index 1f9f9db99a..fe7e83a84b 100644 --- a/openpype/hosts/maya/plugins/publish/extract_playblast.py +++ b/openpype/hosts/maya/plugins/publish/extract_playblast.py @@ -119,6 +119,24 @@ class ExtractPlayblast(publish.Extractor): pan_zoom = cmds.getAttr("{}.panZoomEnabled".format(preset["camera"])) cmds.setAttr("{}.panZoomEnabled".format(preset["camera"]), False) + # Need to explicitly enable some viewport changes so the viewport is + # refreshed ahead of playblasting. + panel = cmds.getPanel(withFocus=True) + keys = [ + "useDefaultMaterial", + "wireframeOnShaded", + "xray", + "jointXray", + "backfaceCulling" + ] + viewport_defaults = {} + for key in keys: + viewport_defaults[key] = cmds.modelEditor( + panel, query=True, **{key: True} + ) + if preset["viewport_options"][key]: + cmds.modelEditor(panel, edit=True, **{key: True}) + with lib.maintained_time(): filename = preset.get("filename", "%TEMP%") @@ -139,6 +157,10 @@ class ExtractPlayblast(publish.Extractor): path = capture.capture(log=self.log, **preset) + # Restoring viewport options. + for key, value in viewport_defaults.items(): + cmds.modelEditor(panel, edit=True, **{key: value}) + cmds.setAttr("{}.panZoomEnabled".format(preset["camera"]), pan_zoom) self.log.debug("playblast path {}".format(path)) From 38c9330caef225e4ab632f5834c92fff5a3ea8b9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 10:41:48 +0100 Subject: [PATCH 0704/1271] returning nightly builds --- .github/workflows/nightly_merge.yml | 29 +++++++++++++ .github/workflows/prerelease.yml | 67 +++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 .github/workflows/nightly_merge.yml create mode 100644 .github/workflows/prerelease.yml diff --git a/.github/workflows/nightly_merge.yml b/.github/workflows/nightly_merge.yml new file mode 100644 index 0000000000..1776d7a464 --- /dev/null +++ b/.github/workflows/nightly_merge.yml @@ -0,0 +1,29 @@ +name: Dev -> Main + +on: + schedule: + - cron: '21 3 * * 3,6' + workflow_dispatch: + +jobs: + develop-to-main: + + runs-on: ubuntu-latest + + steps: + - name: 🚛 Checkout Code + uses: actions/checkout@v2 + + - name: 🔨 Merge develop to main + uses: everlytic/branch-merge@1.1.0 + with: + github_token: ${{ secrets.YNPUT_BOT_TOKEN }} + source_ref: 'develop' + target_branch: 'main' + commit_message_template: '[Automated] Merged {source_ref} into {target_branch}' + + - name: Invoke pre-release workflow + uses: benc-uk/workflow-dispatch@v1 + with: + workflow: Nightly Prerelease + token: ${{ secrets.YNPUT_BOT_TOKEN }} diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml new file mode 100644 index 0000000000..571b0339e1 --- /dev/null +++ b/.github/workflows/prerelease.yml @@ -0,0 +1,67 @@ +name: Nightly Prerelease + +on: + workflow_dispatch: + + +jobs: + create_nightly: + runs-on: ubuntu-latest + + steps: + - name: 🚛 Checkout Code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + + - name: Install Python requirements + run: pip install gitpython semver PyGithub + + - name: 🔎 Determine next version type + id: version_type + run: | + TYPE=$(python ./tools/ci_tools.py --bump --github_token ${{ secrets.YNPUT_BOT_TOKEN }}) + echo "type=${TYPE}" >> $GITHUB_OUTPUT + + - name: 💉 Inject new version into files + id: version + if: steps.version_type.outputs.type != 'skip' + run: | + NEW_VERSION_TAG=$(python ./tools/ci_tools.py --nightly --github_token ${{ secrets.YNPUT_BOT_TOKEN }}) + echo "next_tag=${NEW_VERSION_TAG}" >> $GITHUB_OUTPUT + + - name: 💾 Commit and Tag + id: git_commit + if: steps.version_type.outputs.type != 'skip' + run: | + git config user.email ${{ secrets.CI_EMAIL }} + git config user.name ${{ secrets.CI_USER }} + git checkout main + git pull + git add . + git commit -m "[Automated] Bump version" + tag_name="CI/${{ steps.version.outputs.next_tag }}" + echo $tag_name + git tag -a $tag_name -m "nightly build" + + - name: Push to protected main branch + uses: CasperWA/push-protected@v2.10.0 + with: + token: ${{ secrets.YNPUT_BOT_TOKEN }} + branch: main + tags: true + unprotect_reviews: true + + - name: 🔨 Merge main back to develop + uses: everlytic/branch-merge@1.1.0 + if: steps.version_type.outputs.type != 'skip' + with: + github_token: ${{ secrets.YNPUT_BOT_TOKEN }} + source_ref: 'main' + target_branch: 'develop' + commit_message_template: '[Automated] Merged {source_ref} into {target_branch}' From 78933eb56259045a27c1c56f06a31dfb39f38e37 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 10:44:10 +0100 Subject: [PATCH 0705/1271] OP-4643 - fixed subset filtering Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index de36ea7d5f..71124b527a 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -273,7 +273,7 @@ class ExtractOIIOTranscode(publish.Extractor): "families": family, "task_names": task_name, "task_types": task_type, - "subset": subset + "subsets": subset } profile = filter_profiles(self.profiles, filtering_criteria, logger=self.log) From deaad39437501f18fc3ba4be8b1fc5f0ee3be65d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 12:12:35 +0100 Subject: [PATCH 0706/1271] OP-4643 - split command line arguments to separate items Reuse existing method from ExtractReview, put it into transcoding.py --- openpype/lib/transcoding.py | 29 +++++++++++++++++++++- openpype/plugins/publish/extract_review.py | 27 +++----------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 95042fb74c..a87300c280 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1092,7 +1092,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1106,3 +1106,30 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 0f6dacba18..e80141fc4a 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,6 +22,7 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, + split_cmd_args ) @@ -670,7 +671,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) + ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -723,28 +724,6 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) - def split_ffmpeg_args(self, in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args - def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -764,7 +743,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = self.split_ffmpeg_args(output_args) + output_args = split_cmd_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 83cb0b0b04a59a5f4acffb05659a893f2318c91c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:02:41 +0100 Subject: [PATCH 0707/1271] OP-4643 - refactor - changed existence check --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 71124b527a..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,12 +161,12 @@ class ExtractOIIOTranscode(publish.Extractor): custom_tags = output_def.get("custom_tags") if custom_tags: - if not new_repre.get("custom_tags"): + if new_repre.get("custom_tags") is None: new_repre["custom_tags"] = [] new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation - if not new_repre.get("tags"): + if new_repre.get("tags") is None: new_repre["tags"] = [] for tag in output_def["tags"]: if tag not in new_repre["tags"]: From 909f51b702825be0fe23fd946023279787d28e1c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:10:11 +0100 Subject: [PATCH 0708/1271] OP-4643 - changed label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5333d514b5..3e9467af61 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -294,7 +294,7 @@ "children": [ { "key": "additional_command_args", - "label": "Additional command line arguments", + "label": "Arguments", "type": "list", "object_type": "text" } From 71013e45052bb0775cc80e799cf4556a935372ef Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:11:11 +0100 Subject: [PATCH 0709/1271] Revert "Fix - added missed scopes for Slack bot" This reverts commit 5e0c4a3ab1432e120b8f0c324f899070f1a5f831. --- openpype/modules/slack/manifest.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 233c39fbaf..7a65cc5915 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,8 +19,6 @@ oauth_config: - chat:write.public - files:write - channels:read - - users:read - - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From 12ba88e9573a3c33184264e2b6c86468a26b3a3b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 15:06:16 +0100 Subject: [PATCH 0710/1271] OP-4643 - added documentation --- .../assets/global_oiio_transcode.png | Bin 0 -> 29010 bytes .../project_settings/settings_project_global.md | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png new file mode 100644 index 0000000000000000000000000000000000000000..99396d5bb3f16d434a92c6079515128488b82489 GIT binary patch literal 29010 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 literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 37fed93e69..52671d2db6 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -45,6 +45,21 @@ The _input pattern_ matching uses REGEX expression syntax (try [regexr.com](http The **colorspace name** value is a raw string input and no validation is run after saving project settings. We recommend to open the specified `config.ocio` file and copy pasting the exact colorspace names. ::: +### Extract OIIO Transcode +There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +`oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. + +Notable parameters: +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Extension`** - target extension, could be empty - original extension is used +- **`Colorspace`** - target colorspace - must be available in used color config +- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Arguments`** - special additional command line arguments for `oiiotool` + + +Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. +![global_oiio_transcode](assets/global_oiio_transcode.png) ## Profile filters From 2894cef94c15b35f0ffff4676278a422d80a0ff6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 15:11:48 +0100 Subject: [PATCH 0711/1271] rename color group by render layer variant --- .../tvpaint/plugins/create/create_render.py | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index cae894035a..841489e14b 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -145,6 +145,7 @@ class CreateRenderlayer(TVPaintCreator): def create(self, subset_name, instance_data, pre_create_data): self.log.debug("Query data from workfile.") + group_name = instance_data["variant"] group_id = pre_create_data.get("group_id") # This creator should run only on one group if group_id is None or group_id == -1: @@ -193,15 +194,10 @@ class CreateRenderlayer(TVPaintCreator): ) self._store_new_instance(new_instance) - new_group_name = pre_create_data.get("group_name") - if not new_group_name or not group_id: + if not group_id or group_item["name"] == group_name: return new_instance self.log.debug("Changing name of the group.") - - new_group_name = pre_create_data.get("group_name") - if not new_group_name or group_item["name"] == new_group_name: - return new_instance # Rename TVPaint group (keep color same) # - groups can't contain spaces rename_script = self.rename_script_template.format( @@ -210,13 +206,13 @@ class CreateRenderlayer(TVPaintCreator): r=group_item["red"], g=group_item["green"], b=group_item["blue"], - name=new_group_name + name=group_name ) execute_george_through_file(rename_script) self.log.info(( f"Name of group with index {group_id}" - f" was changed to \"{new_group_name}\"." + f" was changed to \"{group_name}\"." )) return new_instance @@ -252,11 +248,6 @@ class CreateRenderlayer(TVPaintCreator): label="Group", items=groups_enum ), - TextDef( - "group_name", - label="New group name", - placeholder="< Keep unchanged >" - ), BoolDef( "mark_for_review", label="Review", @@ -280,10 +271,44 @@ class CreateRenderlayer(TVPaintCreator): ] def update_instances(self, update_list): + self._update_color_groups() self._update_renderpass_groups() super().update_instances(update_list) + def _update_color_groups(self): + render_layer_instances = [] + for instance in self.create_context.instances: + if instance.creator_identifier == self.identifier: + render_layer_instances.append(instance) + + if not render_layer_instances: + return + + groups_by_id = { + group["group_id"]: group + for group in get_groups_data() + } + grg_script_lines = [] + for instance in render_layer_instances: + group_id = instance["creator_attributes"]["group_id"] + variant = instance["variant"] + group = groups_by_id[group_id] + if group["name"] == variant: + continue + + grg_script_lines.append(self.rename_script_template.format( + clip_id=group["clip_id"], + group_id=group["group_id"], + r=group["red"], + g=group["green"], + b=group["blue"], + name=variant + )) + + if grg_script_lines: + execute_george_through_file("\n".join(grg_script_lines)) + def _update_renderpass_groups(self): render_layer_instances = {} render_pass_instances = collections.defaultdict(list) From 1d0952014314217b022dffea7340db8776041a54 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 21 Feb 2023 15:18:07 +0000 Subject: [PATCH 0712/1271] Update openpype/hosts/maya/plugins/publish/extract_playblast.py Co-authored-by: Kayla Man <64118225+moonyuet@users.noreply.github.com> --- openpype/hosts/maya/plugins/publish/extract_playblast.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_playblast.py b/openpype/hosts/maya/plugins/publish/extract_playblast.py index f85772886b..1966ad7b66 100644 --- a/openpype/hosts/maya/plugins/publish/extract_playblast.py +++ b/openpype/hosts/maya/plugins/publish/extract_playblast.py @@ -163,8 +163,7 @@ class ExtractPlayblast(publish.Extractor): path = capture.capture(log=self.log, **preset) # Restoring viewport options. - for key, value in viewport_defaults.items(): - cmds.modelEditor(panel, edit=True, **{key: value}) + cmds.modelEditor(panel, edit=True, **viewport_defaults) cmds.setAttr("{}.panZoomEnabled".format(preset["camera"]), pan_zoom) From f26b44a2aadebc60ac92ae1a6de81e9442dab429 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 16:19:30 +0100 Subject: [PATCH 0713/1271] fix 'creator_attributes' key --- openpype/hosts/tvpaint/plugins/create/create_render.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 841489e14b..6a857676a5 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -513,9 +513,9 @@ class CreateRenderPass(TVPaintCreator): instance_data["group"] = f"{self.get_group_label()} ({render_layer})" instance_data["layer_names"] = list(marked_layer_names) if "creator_attributes" not in instance_data: - instance_data["creator_attribtues"] = {} + instance_data["creator_attributes"] = {} - creator_attributes = instance_data["creator_attribtues"] + creator_attributes = instance_data["creator_attributes"] mark_for_review = pre_create_data.get("mark_for_review") if mark_for_review is None: mark_for_review = self.mark_for_review From 08e2d36f199b617a35120b73fd9f0de2b429fa60 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 16:29:25 +0100 Subject: [PATCH 0714/1271] ignore missing layers in unrelated validators --- .../plugins/publish/validate_duplicated_layer_names.py | 3 +++ .../tvpaint/plugins/publish/validate_layers_visibility.py | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py b/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py index 9f61bdbcd0..722d76b4d2 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py @@ -20,6 +20,9 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin): duplicated_layer_names = [] for layer_name in layer_names: layers = layers_by_name.get(layer_name) + # It is not job of this validator to handle missing layers + if layers is None: + continue if len(layers) > 1: duplicated_layer_names.append(layer_name) diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py b/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py index 47632453fc..6a496a2e49 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py @@ -11,8 +11,13 @@ class ValidateLayersVisiblity(pyblish.api.InstancePlugin): families = ["review", "render"] def process(self, instance): + layers = instance.data["layers"] + # Instance have empty layers + # - it is not job of this validator to check that + if not layers: + return layer_names = set() - for layer in instance.data["layers"]: + for layer in layers: layer_names.add(layer["name"]) if layer["visible"]: return From f1718284f9245dd24160d8975061d90cc6f8c11e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:41:42 +0100 Subject: [PATCH 0715/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 52671d2db6..cc661a21fa 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,7 +46,7 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. From 337e695c17d1d5314ffac99a83330f875053703a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:06 +0100 Subject: [PATCH 0716/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index cc661a21fa..8e557a381c 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -52,7 +52,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. -- **`Extension`** - target extension, could be empty - original extension is used +- **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace - must be available in used color config - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From 931d0002ec80b0cabd1bbc0b1d74cbdb06ab2d88 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:29 +0100 Subject: [PATCH 0717/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 8e557a381c..166400cb7f 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,7 +53,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace - must be available in used color config +- **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From 14a8a1449a6e46d82192dece2fb08c8aa807a032 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:48 +0100 Subject: [PATCH 0718/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 166400cb7f..908191f122 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -54,7 +54,7 @@ Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. - **`Arguments`** - special additional command line arguments for `oiiotool` From cb7b8d423e1591a9d0995dfba9d3cb697c551a57 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:43:06 +0100 Subject: [PATCH 0719/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 908191f122..0a73868d2d 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -55,7 +55,7 @@ Notable parameters: - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. -- **`Arguments`** - special additional command line arguments for `oiiotool` +- **`Arguments`** - special additional command line arguments for `oiiotool`. Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. From 748d8989496d6cbd4a5348fb9a78e75617a69cab Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:21:20 +0100 Subject: [PATCH 0720/1271] OP-4643 - updates to documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- website/docs/project_settings/settings_project_global.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 0a73868d2d..9e2ee187cc 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,8 +46,8 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. -Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +OIIOTools transcoder plugin with configurable output presets. Any incoming representation with `colorspaceData` is convertable to single or multiple representations with different target colorspaces or display and viewer names found in linked **config.ocio** file. + `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: From bd3207f2b6a317e0d77e6eaaa19dc60390f76220 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 17:50:47 +0100 Subject: [PATCH 0721/1271] Modify artist docstrings to contain Publisher related information and images --- website/docs/artist_hosts_tvpaint.md | 154 ++++++++++------------- website/docs/assets/tvp_create_layer.png | Bin 29058 -> 174149 bytes website/docs/assets/tvp_create_pass.png | Bin 36545 -> 132716 bytes 3 files changed, 68 insertions(+), 86 deletions(-) diff --git a/website/docs/artist_hosts_tvpaint.md b/website/docs/artist_hosts_tvpaint.md index a0ce5d5ff8..baa8c0a09d 100644 --- a/website/docs/artist_hosts_tvpaint.md +++ b/website/docs/artist_hosts_tvpaint.md @@ -43,13 +43,55 @@ You can start your work. ## Usage In TVPaint you can find the Tools in OpenPype menu extension. The OpenPype Tools menu should be available in your work area. However, sometimes it happens that the Tools menu is hidden. You can display the extension panel by going to `Windows -> Plugins -> OpenPype`. +## Create & Publish +As you might already know, to be able to publish, you have to mark what should be published. The marking part is called **Create**. In TVPaint you can create and publish **[Reviews](#review)**, **[Workfile](#workfile)**, **[Render Layers](#render-layer)** and **[Render Passes](#render-pass)**. -## Create -In TVPaint you can create and publish **[Reviews](#review)**, **[Workfile](#workfile)**, **[Render Passes](#render-pass)** and **[Render Layers](#render-layer)**. +:::important +TVPaint integration tries to not guess what you want to publish from the scene. Therefore, you should tell what you want to publish. +::: -You have the possibility to organize your layers by using `Color group`. +![createlayer](assets/tvp_publisher.png) -On the bottom left corner of your timeline, you will note a `Color group` button. +### Review +`Review` will render all visible layers and create a reviewable output. +- Is automatically created without any manual work. +- You can disable the created instance if you want to skip review. + +### Workfile +`Workfile` integrate the source TVPaint file during publishing. Publishing of workfile is useful for backups. +- Is automatically created without any manual work. +- You can disable the created instance if you want to skip review. + +### Render Layer + +
+
+ +Render Layer bakes all the animation layers of one particular color group together. + +- In the **Create** tab, pick `Render Layer` +- Fill `variant`, type in the name that the final published RenderLayer should have according to the naming convention in your studio. *(L10, BG, Hero, etc.)* + - Color group will be renamed to the **variant** value +- Choose color group from combobox + - or select a layer of a particular color and set combobox to **<Use selection>** +- Hit `Create` button + +You have just created Render Layer. Now choose any amount of animation layers that need to be rendered together and assign them the color group. + +You can change `variant` later in **Publish** tab. + +
+
+ +![createlayer](assets/tvp_create_layer.png) + +
+
+
+ +**How to mark TVPaint layer to a group** + +In the bottom left corner of your timeline, you will note a **Color group** button. ![colorgroups](assets/tvp_color_groups.png) @@ -61,63 +103,29 @@ The timeline's animation layer can be marked by the color you pick from your Col ![timeline](assets/tvp_timeline_color.png) -:::important -OpenPype specifically never tries to guess what you want to publish from the scene. Therefore, you have to tell OpenPype what you want to publish. There are three ways how to publish render from the scene. -::: - -When you want to publish `review` or `render layer` or `render pass`, open the `Creator` through the Tools menu `Create` button. - -### Review -`Review` renders the whole file as is and sends the resulting QuickTime to Ftrack. -- Is automatically created during publishing. - -### Workfile -`Workfile` stores the source workfile as is during publishing (e.g. for backup). -- Is automatically created during publishing. - -### Render Layer - -
-
- - -Render Layer bakes all the animation layers of one particular color group together. - -- Choose any amount of animation layers that need to be rendered together and assign them a color group. -- Select any layer of a particular color -- Go to `Creator` and choose `RenderLayer`. -- In the `Subset`, type in the name that the final published RenderLayer should have according to the naming convention in your studio. *(L10, BG, Hero, etc.)* -- Press `Create` -- When you run [publish](#publish), the whole color group will be rendered together and published as a single `RenderLayer` - -
-
- -![createlayer](assets/tvp_create_layer.png) - -
-
- - - - ### Render Pass -Render Passes are smaller individual elements of a Render Layer. A `character` render layer might +Render Passes are smaller individual elements of a [Render Layer](artist_hosts_tvpaint.md#render-layer). A `character` render layer might consist of multiple render passes such as `Line`, `Color` and `Shadow`. +Render Passes are specific because they have to belong to a particular Render Layer. You have to select to which Render Layer the pass belongs. Try to refresh if you don't see demanded Render Layer in the options.
-Render Passes are specific because they have to belong to a particular layer. If you try to create a render pass and did not create any render layers before, an error message will pop up. -When you want to create `RenderPass` -- choose one or several animation layers within one color group that you want to publish -- In the Creator, pick `RenderPass` -- Fill the `Subset` with the name of your pass, e.g. `Color`. +When you want to create Render Pass +- choose one or several TVPaint layers +- In the **Create** tab, pick `Render Pass` +- Fill the `variant` with desired name of pass, e.g. `Color`. +- Select Render Layer to which belongs in Render Layer combobox + - If you don't see new Render Layer try refresh first - Press `Create` +You have just created Render Pass. Selected TVPaint layers should be marked with color group of Render Layer. + +You can change `variant` or Render Layer later in **Publish** tab. +
@@ -126,48 +134,22 @@ When you want to create `RenderPass`
+:::warning +You cannot change TVPaint layer name once you mark it as part of Render Pass. You would have to remove created Render Pass and create it again with new TVPaint layer name. +::: +

In this example, OpenPype will render selected animation layers within the given color group. E.i. the layers *L020_colour_fx*, *L020_colour_mouth*, and *L020_colour_eye* will be rendered as one pass belonging to the yellow RenderLayer. ![renderpass](assets/tvp_timeline_color2.png) - -:::note -You can check your RendrePasses and RenderLayers in [Subset Manager](#subset-manager) or you can start publishing. The publisher will show you a collection of all instances on the left side. -::: - - ---- - -## Publish - -
-
- -Now that you have created the required instances, you can publish them via `Publish` tool. -- Click on `Publish` in OpenPype Tools menu. -- wait until all instances are collected. -- You can check on the left side whether all your instances have been created and are ready for publishing. -- Fill the comment on the bottom of the window. -- Press the `Play` button to publish - -
-
- -![pyblish](assets/tvp_pyblish_render.png) - -
-
- -Once the `Publisher` turns gets green your renders have been published. - ---- - -## Subset Manager -All created instances (render layers, passes, and reviews) will be shown as a simple list. If you don't want to publish some, right click on the item in the list and select `Remove instance`. - -![subsetmanager](assets/tvp_subset_manager.png) +Now that you have created the required instances, you can publish them. +- Fill the comment on the bottom of the window +- Double check enabled instance and their context +- Press `Publish` +- Wait to finish +- Once the `Publisher` turns gets green your renders have been published. --- diff --git a/website/docs/assets/tvp_create_layer.png b/website/docs/assets/tvp_create_layer.png index 9d243da17a099cae576552193711c2898e76f8e9..25081bdf46ace8070b001c9b87dddd6314e26f41 100644 GIT binary patch literal 174149 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 LU9L#R)c1b?NI*?J literal 29058 zcma&NWmFq~v;|t+CAhmg#l3;x#VPI-cXv`ecxjOqCrBw;+@Uyy;_k)WUGwt4_se}B z-dZnf&B{zNbMjl0V|$-Hv6||NIG7ZeZ{EDYQC0$Jzj=cIg>Pr*sPIonMweINZwOx6 zin4F2#;6Y92gr6nHQ<{!HOW}dmMHLJ40k0%uQzXSd;i-I2Hi?OzIo$8stf|^`kS4s zqWDwKhYA1V@osA?7PL3Eh%X*V2Qc>;I;y(5ptSXoQ|kkGpjIaBRLXr9@sDS zcUz90TKprmO8Gy} zDn%9CVP574*^0;_u(7H%NG}Rx*(ka_jd=f%W(ORJss(v93S-May%1~Ll?EjT^~LZa?}e5>nSx+N3v zeB)oLW%UDCjN-NVs(&Jn7-8+W@DiP=W*N+xeTelCx#4Z_w8yU`-q~kY>;5ul#YOCb zJ^zuOm6e)VzU4B==iuyG+eJ#q{-7M#Q)bndoY51&cV`OQ4aPOP!1%|F4*&%8K&ul4 zAP~rq7a0fy+SbBmU|_f`t>Jb32>&)^&snlzXEkDfR%E^Bz`P@NbUhg3sPkqi)#Yh7 z-v1u9=DW-S08AF&(k#B70Fl%oeMj_WF4MgF&@3!XvC1r!=Ca{vH0wsndkpnUK4@v;CzdR zW=O}i%`D#kfa%f;7NaP)z3#=mCEhdh`0uQasHXK$LYdb;$@tWSD8(uWcFjn}O|Apw z)g8x;?X0O885=2zv;z-U)#f#)Y5Hb88;ofDf`b12tp}NU_&Yhz5}tR@GRQ@Zn|ZLC z^@~{WBh%}Rj0~3XIv|zmrAwLd2WP~-P5aF*%<8N$M6~_Jqy2SCu!_FJhsJ;FL;K@y zz_Te=0QLOQ(S2+Pih$L`yDxR#4QCeh}53{&Mj< zVs9n=2AMth^(aOra{hSn{;{cxn>R@A`tCI7Eb5PKZ!-9{4(~SXIott7Yw^!IW0fy< z-u=Acebe|#oW|;DBKWb%_&j#}NVDVB@?&0L=ppcWe%|A?c+cbYhV*>N2|QR=-5Ix@ z3)V>u`tYo6HgHre9g|`9!1&6Bdpj-z^UJ0-PTF`a_TOyLKc8=U_GLas);{x#^o z;*G;jgxb%ak389wrn$Dop>7(->z`1(_O>q8pOx#~4c~OW#<5+B0My<7?PQquWo?BOr{(<*(&}Ffl|Ans1FjnJv{uEN4dxK=7d9?KlrQHj%*Yc>6}cZnSUs1nvBY)VvS&w8ZHC7-YHop80Qy?(_SE zW}aM*pri4w-VRmNU!kLp&)4llni(jQd7;VopWkegHvJ|~3*URagt=S56p(`gdY46z zedLbLv(d|Vb5~I_7BsobnUZ3?#>%Zkc&V##M%zRV1*2477gGm(qsw2Ago5r!- zKJL!`qul&D6kjbB6L8{E3K{>@`#B-+^&U(P0*QR?dfw64KR8L(7zufyH1{zcm@Z1b zni^6P$MPNg`~BXHpWw?}GiQ)cl`H6)7Wl0=4)jY17xwV*{l3p`W*QG_m)GB8LaNR2 zbiVBc?&hTpk&PTRz3)|(>G`gB%h+MV9s1aWMS5Zuif~tL8r$zhy0Hunkw@;qvg*6$ z3Vj$k+h~K`AR~T=y5F3(61@J~l(HRy`~;ZvZ)wjXzUFA#{q~UCC_ByX6iFXk^Q!^oG~T1dxosy!eTV%!T!1Nt~y3y^`$Q zTuqvGA#}k`%vs#>Akw#?k9D+im0F=i>VHhtTUnTLlCJP@qsKn>$R$h1>B|fi$Bw+` zgs7;$B2<-(UZU?~c~vy)P)BKm&Y|Tsa}wB4=7Q#al9m8KUHx%WM9va7qMfiMV^LV! zI->*~{`$j?$azCvFvi7qHG_sL?9RIg+KmU97Y}Xxmy_A9jh{id^dhW)BqagYE&ckc^`8Id$`%-T0QiTYI#5 z&JxZbbrWzeA&Qv+L2Ree|KG*n#mV3q&?g{iW!`WNAnJX%#~{dy0TnSD6!4=31=+q0 zJukDrt;{%IMf0=XdVKKTZjzA?d3t-idX8)sXeJ)z_vbct-};QS>zVc;bwvaGXx(%q zb>OnGqi^iRvFs0zq4 zPE_o?PE-b7m*>4=t_Gyt4~x(aoS8o`6X;N!r@@0tB=kWb)n500v-&lBz2lXmGvpaR z+G+TF)-E)ufH;f4^_uSWdM|$6Ef5#{^fEnBR(rAYV{Iq<*cd$EM@v3v;YQjhbIT}s zcL>67f4+>rbbqW|e?60V3^t4W)3Q~X=(ka02Md=mL9gtRzB1-=JMCTdJ0raTN5w4x zSG?B2TZ!f}Uh=EaMP$J@8ocqIy#F|3nk=^NgM)8)WfZNm(4jZxBtY*_PZ$J#6Cd_q z(NDC^8lCFft{to$=ToW+;C*}oZSCz?jH%iyx6ZI1otMNPcXl?e7wh~F((t5LgH?b` z^9`yi@dX-#SHKcrmm zXABa7{wyOol6MceI$G}we>6&{r05?&$|?|M+Tb*Y;h4eJVZdDV2v#t&nx{X`x@3T^ zw9s9I%qz{E%&U!e6Sno}g81YuLh#w(`apcOWJFRcP?yyE&qxxLRCsmB*`WVPPRh8d z|BlA`VCTh$(h5=^k(1OBmvu0i`QxqM`9R(HP{`}%I5=j#{I%!{6gCWKVXas09dszdQ(>QvxhNR^4Pb7^+dw`&4YZ@{VN`r z&%t-a$*nzUFfW+ECXHA$=uGPCp-Cb;idG{TMzQN7O?cLw+8`gxA zHhhCtsZ(6MgEz6yEJizN#Nx2i%WT~)f86MxmYTk`bq@au0vlo1@B#dQ^AP-YG znWygz3{wr>YcmHbDoQN12G!V}IXpiI@nb$d=omCOvhP-{#?rzz(fp4)bM`5zexJwr z7<71$zA7gCT6?~t{pI2;7W(?5Ya>bo9{#W!muyi_vhj90#GRYHi-qq}WXBF^%bicL z)tig+YrnC&VC?C5f+HiXPk}W@udEk#lgp}4ua8&b8`)$9*M1E{JE;Aor%&K#%C+{1 zy*$Y~(r44upsVM+^WHPb*o`qB@A|uZw+Dsbo2T_2kv#FFloTK~mtj;5j>N@F-gC7- zZ*K1k>s(=#6Y}zyq-2Y$BCRm3c+6gIhV8}+xt)4$|9saqV|%D2NC700v<`cP@%gRs z~f7N!?I zpo9?OMjMUZ#!V+a?oh;}9rJLXYktvmX@o5TeiMmv$`72YR1E)FgJ+CfvU@HT0FJ>9H}4tgvUfO$g+9q2o{xXE|0F<;v3h~)koCoxo3lvKbOR5aOH{8ax}nd&yz#3GG^1}RCjo8 z4IN=0S}JCEYm%+JucH?_brlZlX68luz*EBO%?tVgU+%Fyzi0522fbFtzJ$uHxoN3J z9xm_6Q;(%HLMbPLP$#ddyJ8y$&A#8NX{f5dqF{BhP=8u&QU!s$CxcK^uKzp-fkUy( z19ycaFaA>M++GV*Z?b~BoOOY4L^TkE3V=hTm_+yih^C(oj<1rf{=-&^{QtqO!vBR` zS;B6=Szqr5_uj!T!6=L;-6Z5_K!jhBi+{W~{@+F8T4*tE{|Bfs2-^NJ+@6l)vIZY4A89k z7y^({4h2T62OBr%KzJ0RsMoEau-hVJxXhF32UCwTe<+t6yKZHeP*2o#Ju*oA+KP51 z!%Ok!Ed5&^jDtTHzsS=x+t}qm-U6*9K%$+vcOQIE9?=3X_6yDXuEt?FZgBX-+jYtC zpKm?DG{8Hm2A#p|hm?+hJbccW^tMLnxb|7x)a%Ngtvl+z_STPN*E)F9nqG*ziFrMw z$!GFv?YQD<-iCu->;-8hXx;F5 zwTY5dovK%)^FrwPLSua6q-nj+{PmGFpol5)o8wX0&a~g@Ql2!k><974pMsZ84dU41 zK76TG0l$pUqKue%r%UAuKEyygjau3jia#qKZHt#_mVQtP=#VW=KN{6r)S7K0&{&fI zfG9d}=^bm#D!wkwJA{MU=ruVSG~>icm%p@|<13#;{$xG0*R^_5sWDmVRA64hjxb|> z&Fk zvZS$7OJ+nLZ3}+3qEerzl~(h>J0W86C`=uD zYr3$YpLc#CKi*Fabv7d0zW(BZdM4V3fbXSjyx~&PFw+R7!g+?D`IsLnS1)g!hT~G$ z9<#E)Mb?(EeEh*oug%#rND|wgDO|5l+*^zj%`Z*L%pKvG6TReF)VBm`a|AJyF#O1; zdO&F@=#-t0f{I{6Z;FXF1B@&M+zw%ysyBpJ1&~w{ZQw{R*W3e zlS{TgrmOK4!gW;I*5!Ibj3wrM|K55qH5_Ek*En3E78S9yR6cbIQZ&CkbiY3x zboZaPrCO;Y#8Im(b+8|33_;`X43H3ZTaMZa8qhMw$I20)Mmu|;7&~5%-D+#H9^Cla z{FM9j3;KfwKq63E{^b5|UpZSM1b_LCrZ)*51M@WEENCgx;z&s4ep~fqCzXaRh)=ht z1Bb+%pD9uz=b}ldq(hL=@Q!<4xsOuJCjJRSQmwX2L<}bzym97^#Uf3E?lxyqkiM-O z@E}$oW+mnHIszuXt8>4NAE~+PROIWl!gk=VG2rjScuLOciTiN$%Wa&-*<>oJ@0*gY z4_ALT;}4I^-|EL}wG@VbQsDXGaR^R_C^M#obxTV=9ampmj___CnZr1Q-PRzdr|L}1 ztnUgxv{c@iZEjjmOfE(XIq7ZW295oTB7IHLHxD6SZgguYOG(j2PGKxW861ywUu>Yc zAtqMhbnI#ds%4QBkFv*XLmN##4^=I@Lj*6mJHc_W2931S#vI|WALnh z^=OY?Li9UB%z;>-^fM?5a!kx1xzZ=)rTu(o;2E?aEL{k1-%dlUm2TcRO2W!Qu*!t7`B)x zD5^)tlk~@}Zh!m|+RT zUyt@f!}N6bSl^N7vZ77?tc(W~-^goz#GN}4=Rng^_7x{NZr6xhU?Cd#xpTx%95Qmz z7lO7;S4KtFo5K4-g(l41yZ;WW6;)wkYASxEl*@ZoWNT(o%HEMHLYWvfO3Vxw>cxr+ zWtzl3iK4rlUecUfiK_FX{T-K9XisC1H2vdrV6q_u66;A{=tz^4-Ad{-&qesM9^WCM z2XxZ_s;-lDUZkPbxJ;`AfTN!t`6rCx`uad(kfhiYZNGrGT$yhI1pDY1pvh(BxC{=m z;ewtIv(=$t)uA^I0mxr-Q^YGB7N_j(Pg-aFpJNp-)?qJl?bnxBo^A6C5Zv5Q?DEGE z3tbc-^@jvvCy~t!FSHu_W;~%{8?r+eIrTs)qA?TrZ&lEOA_G|v8Lxi%&hv{w-V$0* zv|7=s=SsyVVr6NX%|j+nSTAN{l1G54nrl%V$ zj}N#@EB|CX`FPa!Fydu*xAvRvpPk9NnTt$0El+lQB<_3~qOZtaPdq&3U-3GV6RoWx za%N>E#`%3 zmvo}w;z5zH@l(`ue0rfH6u@;Trm0wBwN(`%+8)YoB8)}g<@n2H9zpVYLX1i(7||HS zCh2&dn-@fJ zN4**z2XctcMHR2ZX{baJ4ZM>R4cZkP%NCDTD_8i8Pa4sh)bEKuOUf!&joW%D12xl=#`m>=iC5iBM$NReavoIQW%u&3{X%-Q!j0`CrU> z?=#BP=bW#xL#6IwebN&UrApb-F=ho+c1TV6Hr7{^M@!!kw9ko<|&5e;{j=fHRqnAIZjjSs|hL z9uZ3LB_$i5S%Kg8+Xn^FQI@_zL(2wrlRZo=V+-#R24QKfwO4tJTla+-aZzi+4T6w( z9}wf5tDJTus;w!+M5F^soHZVdQcmhi-+TBn))zq+BKfz9swJn-Kf`3ONC)Ub^5P@U z*h9piv6?k|0xXJEA^AH|a zYyZ=KXp{g9Z8GKcqn7)Z%vhUYzjW1V-m13d;7g}s(FOSaTozr3qVl8b-K}t&(|XRI7XQ~8i;(`G7WOb2n;}XCsaOT2l0!Gw zo3F+MBhA3KCP9+^olwhK8^KLc);ATI^7Sn}G0V$sgyMzMJ{;ZEG_v-CNlo@q=F`qe?5s!q-;4AoU#0o|k-$9#{m}EZ=&`K)Fn-@Ab+B3QE~X zH#W{f2-o}=HHyJ@9MXUggA!uUlyz4Iw>bk?WXSYid)80n6z>R~gOTdJu~km+;!SSQ zxXF&1#s@y89gE<-P-k;V(%BaB%gaSaEC$Tw8s8T(^%Gh-4;4gbOC1R*QWD27y_~q> zLM%=5ub2_tYGRcXVk<8ihi;4u=hnLZe4PNizEb;@MfIO>^q>x zx=O$z)#2Vt?w}gaQ~dO6KSmQu%*3%zwT+h8qj@ojlzRDe%!ato)W1~TxMT8Q7ooLx|j$wdoU6Ij;FFr!6OtkArq@v%aXXcXX(uhN zwSV8)RY6}Bi@`;>nP78*dsxY)E$}gg#wps65&~He{b*~`Tcq~~?^%_H?Om-uUbdqI zX8e`btU=gXr)7Y7oYz)==#yIDx{LZ?(Mx4H2QbwXrC2?N;X?Wr@7mJ6l=cdBNxp4^ z@J5$|Ej5sA)QWP)W}iBf^R65!il()TZ=b-jo%$jEyn4W-B~+xgRy!YS8gfGUIXf-v z^feS48@sTi7CFT#XsQ>XxKU2-WTJFyVoEpMU6E}j-92?qxS*wz#H;^lnP+XJ`Cha% z)wZbE2C^O!vm&GH>r0~&$N399uFhpD8J-l)v5-iyO_ULT=bGCNt{hcUimiM1L!Xge zz&sdICU35f79AbfzwTA#PmALz(`#+Ae|(md9u=t;^J;RMH&h>bW@81; zNso;YGJDJ=SmlCyG=YOIQ&h_JyY{@->%q(Ye$#uC!_{y$ZYlZD+li*m8=UsXrIE|4 zL|m$snAS&%3%uY>jPmK?nbHDfj^9J~Jl=v$mN(%5B6+gUWR`MHe$}G+jTV!`LPM&` zUnAH!nDNq1BJ3T$$9IQmIU6prSGHCFZrf{m&T3!k7 zL{ZgB&q8mrz3pq{#e&)wQj^=@l#*`DFSg)Q4x~d_WYi;-xl#9g<0Gok0b121l_*{r zJW*1@v~pN_)}?hwA=EBp-$gz>4x{!NX!o&sqs%!QMs;`E*3S|5S_%%&H-)GAf>?Jg z56XnB)=?+s>d-0Y60M(VapCx)Fkl1KdEpn*Ra@Ys*@t*0JEq1T4UbYKR);(V}jkFPscsLG@ruqNojuVtrzDn_ZvrVTy7$? zN1Fvn7DSm5%);rYo#kY!KhF>QZcfB!y^@{tZfK{6!cM{>qK zn8&-Zp;5>&y`|%QDRPvbiK@c}(!weg&w6{pLa}}PEN(LU_fJj;>C>Vb)6M?Ul7nLQ z^JWHhFGRGoXFdgu(U6QtOlsxEuyr6}1Q3_H&?*=&qo;ElC8UfY0)E>Hj#KuQeb~A@ zbG_&RIEvqHb;vWb47$`dzMiQnG4zP=NV)o4KZCew&|z%I5)SPlq~FE(fl^SKLZmKIbN<MdC;$zZB-@P#sFb0n(?t<$IoK@J`4E4B)}!kj;8Rm3=TN58iwImuMc5Cf z3$azJ5EhE&+<&qd2wOeUGE3z-Yr6h+{qEvmoq{+BKH?{N%HmQmQYJI3A?DOih<&H@ zN$muyJdmMXZq^9daC(9ccYra(=U3*;r5xfO-d%oEF=Bc%DB(mRuI#TfC^E8}C@=g% zD&}+gxA@x9Y{z!xpbyi&E;253EFiP8ktL+CV z5+1|Y_I+f2qvO6yA4*LlGsJjhEHwy*y0v5y)e0n1wBxziKXEOx>`|e@po7NRTH+V@ zjwT^lxR=~U{s@}3?{GxW<;UOZrC_-@XFBwU3utD`K1RKmv<-#HZf)Ze6C63gr#K97Uz}EY%1$5q4^|G$Dgd_Y}TQr|GgxMe))Q0LW7X zK^xS9LpfjAT0($=f@AM+*!#0%1cd)HMSGe~0ed6`I@e8R|D_s`$YsbX>x{iTH?C+I z_nKpe`G*I2a!O6&^P!T0pByU*x7nvJQ;w-=4VLpI{NB2(^b4Dy(IJldxjQIRIijK; zmu!O6oBq|S8s9UI2LSO-zx?IFa6;evzHbD4!s7EM4L{#S;mkpZ5~@t15|{lfS8qEE z8a%}grx-134tMw*DytM_zhSNoD}jjotj}6olqSYMAE}BIn$`u1#l>N*3&5*C#}E!F zP_Sk-k#cS*StcVCSEsz-SATwE{Gla@gTol zSnS55Kq0V(QJR!wd+puSotYhH6NH;TL>%)iU-PdAljTK{_0TZ8oZB5;QrstJB3izp zyRup)$_I9JgM?E>P+2omg^V{gIfLeL0YxO~jO9mf?EVcsCJ>0evJ|tI(W}NIm|3Nc zb0=kgQtoJ{)k{#ghr1GsH3*>b+Df3sIBY~}9o3)e?h2q3>rnGYlq1}7Z}4gwXu-=|70{)=U$ zZDWA^$HP5TEl)KtJ6toN!0Xuy&V%~$)K^p9j)MaL3IBNT zfZ|&f>MW-av;mG`LT|C!Zch|c6bNK>Nj`og-RQe5R!Xd+Z)r!uN{>gXo{sz0ti-j{ zjCnGfLkO%75ujM&ESQPaR37Rn{Nb1XmNgKH$AKf5Bv^mL?cUa9DJ~HF!MA3ef{B^g zVtUx}x*_$CPw`n0yElKS!{NYl|CNP@0{VqPn={R$qkv_chbaK0>PrO-P~pf_QK<mCzMsBp`?Vtwce+c zFIn`rlL?uR!W9P6n28Ee=k6C976$|s<}0`{WPlbd=+d+7cB_^9^Nbb~%iy1oFIBPM zy@l%=d@cKzX5IT+w|8tm6IWy+9c}rh@@Rt(EfCd)kSazBqXPU*=rF%LwvWEn{B;92j=qoAmF=mc|`mqB(2U8JLl6a40#W4Eir7uf` z_*bLzUP#Ppyhr@5f{&9_1+>v?B;)4&yet}cNOVgj9$t1V35tI?q$cl92|DXvYB2u% zpGY%i# zh>P~^%R0_G5*7l=F;F{a8a?Lt8YaR%uhUsSAO@OO1cB!~c9FdYOlu z#Qg>=cRWjI2zYz!8`t2dw#I#?koH*R;g%wHp*|VQ zTu;HMDA_bHP04y4dof?QMLcH^X+ZlhsrV zcHJmPX!T~pJvCY1ddik;+tibd@WJZ^P3&ZhQDxXDPw4y_C&4z4BrJ32<^P?9D1S~HUOE{DPuL<8@F7Cq}#es^gppR za5fYWg9%BW_VI6uJf}(>t-!q3s}2#9bi?so%JHVpT(ce9XL0)qcLm6k@>A%_f!h&+ z5>Xh2sCrZ9Zj0nfL-H^K-Ji zqNP}1+Tokx zY2z#3Rbc6oZ&V9+SKJ2Kr7z5WDfx)T&W*0+w6o|m_0~bWWv0q3WFx+j_LDabhu{om zqByd6r8jrNk9f%bQoJSwTnGi)7W@A_KKwtj(f^$`&ZLGb1-d3^J1DwZ65mqELb+lQ;l%*ZWto~~8Y zkbdzaP*opqchvk7N`xF6J6AK*4FtNr5Z~e-9@bf36Ve;Rr9SFDDI=dV%OT6aawJjrK(H>^4es z%$xgRiAs=ZIm!%3B3d%TtbMd;fr6EpSmWtRL`{lWpS*f56MXni3)aD8NK;`BAAt}=b$FwwhdP$6wL-%X%x!wa!xgB)H4l)$^0}xwO>~8|bl42)e3lG&2Ny@Xh zPPp`7aA_u7sgkImFa=4um8VEA=FbSLf(k`sSp&M>(e@c3c`E%`D#n~Weu-XhK6->g zajLnhi&AwObQC|4f+7m_Q4=8QsHH*3m4N}6u?*Cji@ZN3NZ=utx5*^5O@rCIFu+}- zjU9FJrdYQ;zlpW54h$I3;{n=vaa$!Y{7%g-dI$0@d{9D75kwR)pkm{xeCMHzknJmQ z&B1S_t(MRS&xZpS035i;q1EvJL&7jqAE8vKMF7`&v-&+;xRy|XC@L{+;{gDz4fp z8Zl%))ob4%Yvaq%l234f(g4qUUyF!z>6EPDeGf*C*_N;dk4Poml<^5Vzao7}KEeiy z%YWze5zkmA4WJtPi~gspsFL{~f_laW5Blv{+@9e}zXIb%IYBC5LVmB?aDu0;Jwj(b z4$>3&ZE6@`%M1EAHnSXu?_)|%tQl?hJ9`>a5g7BIhQa&9^o~yDFSne9JzIN(H~N%3 zgTcY?$~w^E-vKSzR*;t4ojp#O5Mu z);;#$=XBGR*mxh4s1KTLZ#`l(h@b-U-+L7L;!aq0XGd;|#Z#h2t+r<(o7*vXCS&&7 zZTpyPEEGQZYU9fjLw2B{&cKd#7yV9jiJ!by-?ul})R&TGTtAw{tTe^HWPo;?{ygD- znA?YKTqc8bI0tm>djemmsMctRAw0SW|89otIQS&3h`^`7hd77V$rT1`5^(ZSI za(?xfY^qnkp-1nGb0^a=2BgelrZHmG&64mtV)UKV&7cbw_ogg!H97b)|M5BfR`}Z# zQ=%Qw3h;Fn+0K+McHk=fS$b_OI z?Kj-i(iF9n$>887JCu4JWJBgB)0i~NDl zZgf>cW^ypmm*jVlBI5GsgOy&dC>1XQw30N}DkmYCjd<>Ak4y}Es4KV5Fg?i`^3A*7G? zM_w-6D6gZJtb4P@!5U12@#}}W^Y|AttRQtl0v!&%tt5Q=DMw58%?e2dXnYk5M9^~B z$uE&jya5|_fg%GUNrfPR)5%bxVzq|)ly;yC2}Y5qW+Wgl&{OOpe1P;XPo^@8v5;`G zrQgafyV*rS)T$3b=6(5KWtf7P3D4M2JO@cP-EfEsE;Lrh8ou1oCW#;C7RpxL_1RZS_t1DH2Rl)$ZnXiHN;kGy_bBUhSz)1nL3Y`_6 zHYX+E>kNNJtdfS91T@WR)Et1M82AcF`^3-yeiDAAX21k##=d)+H?~!CzB702XO$uRM5o$L zc3^PM3k%3;3_7_0SNN4|f+0Y@#A>?g0x{Rqb2 zWs^-MqhEb1hZf?<-Sq>W(__mq7+{~o^l3p;u;PQ!sSKrJBu`mjB9+3}3Qc4@;ET7{ zOi?&TfT43}p=2Ewiy{Sz5TgomhVGsf$7O=DpgG5}2h-Z-MJJ|2dFILAR`f1*A&>HDN^IE%JZcCTOSoNp zE)r5Qq^}%8?c7le5SFSZrTH|TRd^=X3!vTfGTI6GeE6Tb@O7gVZ8DLLzRM_~iXr^3QZLIS&Y=zvmdv#&wO9o!JKH+t%Bfd&4aaR>XJ4koQDaW@PfSfp=^jrfT?$ zmC;!ea8vtBEtzzl20l7?3Yev9XVl#4`5iv%nb+uQ5)~yi`-@z5 zH67mgJDtIor0?7wOIi*tubbQ7Z@JF#C_gWudkoPVl)o7lvrRj-ODZ=oxxlY zGg{yET`pX`ux94I+hpZEX_X3!WLS7I$`)s0fZln&gb$g=#}zb`vT;PNxv`k=aH?mE zzX|$qtKB-*J=V#`%4zN8|? zdRzEsLz7R_ck9X4tTeyxGjYf$t0LZh6mFkU_r6Q~SD@p!RS-0u-&Br50hXGnQzKh$ zMkF4!@xSlfA~Wwmjw^l7WgyLiruDCaoX=*)GQXO(g+%6~O_6o*nMZc#o=3LWlPJlZ zzT6Rv_8Yn5@52)`_w|;b^WQCP9`4JSeS^`A^j!pPc(fTAGM=ZR3aw=Y+Q{mGwDA6} z_5e9$^c5kZ)c2J|@^&KPey;FtE9@z$$5n^nKIt9kkO8jLVL^7fIlt4-r<0&oMdP&a z_%oqMc6cRtW;1O-zyx$678&@4^v-L82iDBwr~|aHJp_<0%=ONT=a} zV|)B|6kl24SXf~igQ48?sas=oc8`RfUl2RUuPUOSDh{0MqUg9P84ObfXeP^iV$BW4 z){uF?zjdRK{}@a(jH;mdK@)lTuVJ&)yZGB0YNhv_ATuB_Zq;e{+LO*#^-(b>_deBf zbWGl(c<19nq5om5;*RiBCYpu5!oP`Je$l^#gcL=PB zFf-k^??b*V%OJN2qn9n_^-|IPR{?^)tK+X45(VOfvsL5c^70Cb=vk~O;k|}55-Z!R zE_Aqxc>_B=4Q28q^3NZzpJks{QMEjc5H2r=J?u>qJ_XApk_&Jtsc`iy5$F6;{H`S= z89h9J8##K>@9cJvIr;v6l^ne??;wuuvcb;6qF;~6s6o?zHJmSRe7NqQUdw^Zw2{Ig z_Ro1HF+(!sbWJK+w1d?3dvQ;tnaYBi9@Rnv5?T|NeB>$@@7*Piyqp^KsYEAf1UIv0 z-r&Tx@Efu6`@ByQIG{HTdCh3>hIUa^5 zC5ZXY&!Jk0Um<6qpnm2a@)XGH8p5%`B32~>5j(S2js1hsww*kEPr(-rgy_J7f{R2V zV7;R!zUf;-pcGsN?|INBk7VJQ)*GCb|$2h|@H#i)krX1J5 zZ$ao;JN>WRV-_hQk0`xYKO(#~tGXjjpD1?9qM^a!EfhzX)vOMH*lJ8;o%U3%s!%K@y2itGyJ zs-(W%rCI0?ZXe-Yb|2E-SbljOaY3D{B4qxW=AB=;2A%5#MrTU|5$Zi7i5uhh~!U;s*adEW&YBh^zd2e)_N zlM_Z1TU-B9P_UjaD75-m9N8mc;kjCQ^qJ62*3eSCHBh|ZZM58(Quk|4)}&^7EP355 zM2W5M(dPf@?5m@qT)XyFQgTR1DWzK)0bznh1}Q7fPb5{B;Xkd&0}@*p8; zAfX5KeD`qPcdfI2@At0ttsnm}^UPh({p@@1eO=dnpoCxBZ2r}Kb;&LYA@Sj~&ZG6O zVftr3a@S9wyYh0gM9#j~&p$m*XqDosT;LX)(=|T}@wc`fEb#M{*ZI%@@i!2rCGo}- zixPL+GA`WiU~HEb9LQ`~Tc|1>MyyUjVnxb^OAmSu5-loOj(RH12^jDfEc`rfYc$A< zmn8sy?3c+9>r9@dy};scxd7_nuf**pODf!MH9~Jt3N{$jo!yhZ(gtb?ED~IoXm?@!pb6m zG(HMKl5(dK;}L-q$6v^hmQ{4U*2UCHHT9OiGU0T$ z?cr{!Y|%s>6pR-A{O`@b^J1nXpR=7auZNZ&Nu}F8zb?-px493uV@d6@(`ug>zl94SwgVCWm1vBYsCSJ`; z?c|nb=*N!<5FwTpuV1_0`ya9duSB~S%ABU}Fvqfb;o|!rykxS=o|6Ri39=7w!Q{JJ zXYI&#eJ5cr7IRJ}TQ0x%zyIj{{l^;HU;F~EMkmg5*=~F!8&6dl8oOmGZ~EmV=cXCb zfMIZL=P*<@kV~uT9fi%>{Mq?=YTBj$WRltBNoHI}=TDK<3$pRAJ3hcCljj99145g& z{{JG{`G-CEZxS0#F7&5p5^ODn=_c_U){}aylfB1~r3LwM;D-W5C2-|8|F=>6AKLv-VNx*_$gy3Ch8AhB{PBMZ zh%&Vxs#k;1{QqkEAD}_$J}$*me>Gr-nf{}f57v0#ZvS6IO#f)`|8hB)8!GNfJoT@+ zp=1;bwIi->&I*_tkDdi~{o9+R9tZ!g(kZa?Vn#IoTza4c_`m>9E<4pU?3RBc)E;lrEjEF*`@91c0aG@!aC`)KzI-%Z?dRodE@~aK?W<90XhCjv`!_XStzK_(sBq!RXjSW;C!G+T`9)?AS*gE zKFCS76YqH5PetbvT0`V!{2PU9-$XcnXhBLE%ELeIOb>Q|Tl#oJ837VKN~o?ue;`9J zC0>Iw_R%{}Y~@A{FNs={QMYW%y*1AFUV80ZIa)R0`jT!9x(*pI|Ku?JiV#Fa1U%VC zYS6s|2T?%F;KePrNs<1>okY4$1TDspr_Z;39u8a<62Po&>P2JPB|OhaIQT?B59Im9 zQlxAQT*(W%PRsQ-OCB;0jyxK2`!aQXdpns`w&PkP`SD=cqoM5`K3V~o5$w*4sAw&S zaG0cn@VT_sV(&~+1v7fZC+K?hmiKTMk#inkEZR*@Oq?FZ|G5FcdKQ7Y5vr=SUq zZ;P2;myBI&bfVz(sIx94rVU`CoaGaKzNutwZPVkl5R({d|Kus$%ZAziPk+cQ7MxH; zj!3oSyG(q8r6mD*5i#sqex7rCoG0S zSIv_Np;EPL`BU|ULf6kD z!Ll*|yd|8Fy+{Oqp7eO{lT)g)#Eg5@NZ9X0Jp1zU;71k8XdvwC4M|L7J6i6-(ewK; z#g|Mt&5)R$8Vs~emeUxU;mMl8cD;&e9pmd;;dc5T!<=JXh@K5!$Q1DvGAXjM5$>66(mzDTiT|kA#0V^ zr)OSPfRXU4A6i;g6FO-Bz*H->)5+BrlG7zOtf3zI7&qpfE!+mmj30r&ebz7MjBecf zqo$6%tM&#lficn`m$(b@4v^kqlEQY@1G=1i;3x}};`f%lGS-Uj2{a zZNM&uJpbta+6Qn8_Kd8+_5YT-V)^VG^o4dYQ~<{**}vasSN@8oFoM*dnctsk9KjLG zDF@EUJ38_z(iSred5^T#(oTQT9W9jMCF++%nAX5wKEDQh&_9mv#b6N~9+tikCG!Qw z{70GsXm(2sur)GW_zYE}#}VLR7c7#r-x+sJ<4Vgqp)$gP+G2YDVM|806t|0JFzpSD zZ>1Y3L_OXPxIcaB>-P?krxcFKl!@gxi|>WUP}oU6*b3yc_5HD>RrBCGckCXMDLNJ{ z9QNaxY4sKhQ$#Gra@@F?slT8=B_SbX;iLEX*X}6^uuz4*^5}7`*kd`|wATt^J{!_? zXCO1^pF8qO&YES3az$Ld2L5;Ti`4_)^+T!`x zNaYH~klOFVFcF;mET0tfp>fx)VrS)PFJg>r7mjO|0k^Fx8;2>u;8;{K%>Kknh!UK0w z_NEIvS7USBXud4rna5-{XqFpFb9A3R!<@_PIqWA2e}jNmxgYeJ(Lhs*qVL_niF7^s zPOk&z_;A(6Dd3Milfw9D-OneqA34i}Tk*{swFD^{JoDlU)su+2%hKdu1^Jj5%=X0c2}!c!G9sjoIxpQkcR4v0N)gD#Yb(1|FW%*b z=SCPM4@Hg^+ zRX;K_69_3KQ?uFo*Nirc0t4tIO+Pj-&VgA(pG#{H8`77m4>1hf}K|aAq=oV=Sb!x$9Oa%``h& zo-Zwf5aW-{gecE}b&aw8(?R(8AlbA1-Ysr5FOAH~y!1^RO|_ftAFE-eup7HAl9+0- z>kYO9TM`OvNgSlGDnB7E8Q)TcSGLKH9h zk7a{XaMav?3LFARXmkY`W;v*VqMQ(#H$$XQMD;D;Kx77a{b>VE1q=fJLBQ%%Oy0;d z$d1-$HFbo0s9r~WZ0gxpd+%HZe^sP=lD=-zOKb2?{;J8A=BWerZID}(~vh4 zFMjYD^gpViwxzMnH zmLIsoY}9)m6`fAS7fx*xB%h_-bFO`klIh-{@&cU{?6jLp#x`t$wc1#h~1P}W?A@4YfzXZ zv2S?Y9owCzq!4Hb{ITF)xr6`dT>o8!0HEc`sJnf0C~m=Ls;UKK;rh%snq-cm>nV%W zNC8=4A+p;M_(7Pm1az$J(yMQYCOW|%k^lqLhE-tYTGxc;W1hyoqgq4E%T$J&q4IN@ z*o%2^6Ln%=P#f{hco2!SyGt)rn?;H{8dw~cBXw!*dh9(ttYk$fb;986k|j5`9F_m5 zjhP10vtrdhlc?Vqs>3|D{NA$?>Fq--$6>}FA^H;)!Tr=n&1*%;?C3tF4~bcNp`?fS zo1U#glFZXcBswNKu33Tz^zlADtut&bj?o_!zFn;#TQ z;>E_X9R47VV$*Ge?&$dKTCcpAa4@bAK4&jZtKku|B4z?Z*BlVo&ON2yje1&nYZyX`>#c5XGQtzVU>!Oj#*9A1245ilM3s0Fm6Dr@1)-?@}LY(b?Sv$o_9V#dw8$h3Q6-&8M9yIvtI%?AY{YFR zc{c^r*eKFe_s-Q20=P71;G+5Ah%Y{TU|Z@XuQIJMr{=vt0-d!Zl3MIyEyJU8G=edib^ ze*Z={uWT{{^M@_VI&alC_7(WW14K>=U+KX9H`gUVALA;P==PO)TISh7);%sE7PI~3 zBro&_c{YJV6mmhzzOP=%tIV`QpzL}w=fev@Qv6S>ZeNHgm2fkx_Bm$jIJ60H&E-21gRGu&)_ z9h0=@TCb63Z$w^d9ru5w3aB&pg#gkIHBGYBYu&SVu&hHURaYYdbs zX3;y%#Lml`u7(3>RffQ_eDOWFtd8-Ff7VhwGHAO}OQ^|)#->VmZ(h6OkPCPe(;xU z*xY$@j~PW6eS;mCIcXoCCs~r?2~ykm&k47`3g}^es-*|0u$D=Fa0~>miQCa)VrG$R z+F!x9#UvOZ`4h-boZVIvkmq5!jX#a|{`Z6=1!V7|H#1mIUpm+=?CkGvSuw**yEnd& zlPNvuaT-6*_>r(9zaeV&Mw(DBjJ@aKPyU*VkVz*N+;IGnhhBQ6)L$;Xr5s|j7FRzt zBh=2f{KP9pW6SP-E|7jK2Q>{kIiE8bZ@=eozYUWHcqSXh6X9J6X0nb+ElbN=0O z+)Rv<0QvIbI6Yq#YG5E_bka|;eUGZUyVp?o4U0_?J*f56J$BW587QjL8e!7m^T&S8 zGl!&rPjaCLnWL&Ua<^eV-@k8qzrN<{Xer|J^}r`&cqltGenL>UWDl|zNYL47Tkz%$ z&Y9QU@0$tTLsyOMXJ_6sj$Nwvnw4eqJkRYw8XE+{r4tAet&0=LT^8@=`;5p)qi+Pd z4DmZk7_F{q=M=(fCk5Qwes>Gb&FlXWsen!2`Sp3$P^kJWZPdCwxqY*Zm~BK^6s^?x zG_*#LeqBzkbkf7Jx#rfV6Vkyxi?8kcR0@BTNrPvjHFJb$Mic3Xs#vRj1yUl#r(p|@ z6fg@@vmXBE1euwcB2%Ve55mdVc6FT?j9-NKg0(bzk3E|GdggJbF9ABHnh*ng-uBst zQM+vwb&iL$PEYCxE7F8|%%2j6?0)ZC93wWBxrLursic~u7>n+Hk+E^TR>p*mE;oUu zVyMo$UqAL#tUv$DppSH57J8UNenWAa>PC||{fGno@UR9n$oBpWRW{+tdB}Typ*7mP zaJKsl`hyw(El{k!*Dp!Pt4^si)9^Ae7q>(|b0v@f1bV|z`2|I>u_5JH_h*|sTX)}_ zpJj;j2GBkWqnGc-#7f#;uH6+%8&&anlCC=xQ2b0S!3QL*PGS>)ib@@83ntAWCOz1@ zLjshDjwn3RZHy%|H(%!lhJm#`?H%0PPri|M zU3Tah54!MjRMls#Ta0)k0Z|rWS_F5}IBgCes&KYTT9 zfBgiAdzcV2!}jGs)r!fcKu5>Ee7f%85Yd_CfQOC2nF?KNmj%%7@Zo}jeZmCS9`m0= zdk4S%3yty0L9CW;Qsa5Jy2t9rAR{P5krCxi6nS{3k4+vg&wVEz=atAt|LzetNi_tAxi4-I@s)p!n6=RVj) zb%-u5T%OIZw~u&Lo0&zSt6zhA#zT$oW-ifZEB>J3ribzFI<6zh)MMt_QoSj@5`&9! z?nZ6CGk3GI_L2Mo-LiaL1`APipon8$m#e|5aFJqI5shsJj(!Q%^oKMX{Rpjcb=axf zg$Ox2m6OD+%q*0-sKZ*SpCnNMsrWl)6~dN&S2?mzL^*`&R5B}!6vysbMkNdO7IpCa z`pz5TG;sFH*pn0O6pHDa8TL<~D%;t~wi_EmxYh+tDrZZpp}v$9CIl>ptVEEaA<5Po6=-MwrQsrxb-!4pSuNP)5K!Ljj?ra1OPJjz;+UO6ep~CPHoz z=O9Q!m%|E^tqY5b*+oQd)4tNrHE?NMreJ0a-5$U#EV|3V$w}#7MTROe@?44CZlu1$ zwR2P2-hn@SxiCzr(g%G$3k%rC#Pi80RTt>(Z+|qE;yALDy@^inVH>B8KiHj6RlH+z zZze+n9hCqtFMaOTVbn!IQEezS?oX%7Mo4u`KQ1xQ)@K2E8vSnnfjcU?lU!sPDaX zPotb%;Xc>_k${TG2Ln9D0zXLk(o^#geQYwRD3t^V~vJ2%E=vM50^fb##!d z$7mxrNb0D>oPK^?K0~7SEq)mSqQUx=w}>_^_+uIwhB0&TvV7tMs0$Jgr8cH*Vj6u? zuQZ^1x2O1~av+p>v|L?V4~hPnt;_vR>&M($ks@`mKGZ4(=C5^olur$*rx($_LbTLJ z^)k^QG`i9{jPDmko+mq76>i+!AL!<+MB^vXN$zDr;-$O-2%}CMBMRM5=Zhu~ljP*= z7e7Ou7&_bjXi~>@bMuovjfjdwyjze#YOh9Q`%+aNEWbJcl{KE2ykQm1Of*}K{m`kj zByJSOdseGFVDV`RGODJgZKxe~P)_72VZg2yEB3*I2&wxd!qx4IL{z)YytoSbAr-+_ zYtLp0);^0DIu-~gPonmfa`T3L3V@F7#>?wc)8;qUUaP8#<9;WkSja=987Dr>rxI#f z6NX@p)-KqJra+b&;iQODj_g0?793Uvc-@MO@t{N!#3BKz45i7&7dfHicr|lCd*>}D z8^>K7Ig=c29zjGR;e2zk%v|zL4?jXF?NbdRKiKqM7i)BM9NglubgVzK7%$$gJ)AIx zbu_M_ul%F~BMPje#(y;R)c{j{89<-B}HBR>qMR>NnqHLQu-r0Iv%;)3U0 zl$^W;-QC?cUw#@c7h$icB#YvaR#6##5a4{BX+`=~HN|ocqL5Y}qZOu(^d>v0Z`hXD zDzMy~TAea9h)B1mn#+v(-P(aD6p22LPBe6LNVy*}@@^6iRVGd<_+Vi`hZI1lI+Wcc zOo)Qo1XAhZ-x6(5pMF1s#t>h=OnVivEyZ;zF;@6XuX{)w6C+5@UQ%idGCWy(X{PzG z7aLKc`dI_5znzJaheEfY{7~71ejWUyeKmY|jJx|(nX}`G+KD0!ygA{YRt$|dy zeQa6CVWO_XtD%BchdvAtchZ-$oQWPBF1xv@&(R2~y1@f7g_*~BWf=}ex$uy+B1|do z)>TMaqMRchRFDM^qTu>LcasLVl?(LQ**S^4oI!zvc$z@{7;`lIv2!Th*JM^94rZbz znwwD>r8PB?>n^WW=cA6*F7qT9*6@+)0etLFs4n>exl}UiN;-sbKAo`6(8CNDyx4dI z@%`(BkRsFe_9$2wa%hAI=Gp(n5TZ(8n=%>}+j&FG%M(-9cKuzP7x%3KqF0KaCD8Rr zGZbZ#Y_gKK;;O3bQT9+x8-bMrb#rl4UK1y#wvjf+`OMzAI;Dnhup`M_Um29CReVc0 z=R;ah(}c%t*`awxGfcgil!^@7O(knn*l9{BrO8iYqPcq^t>UQY7AX5?7HU&;f9JDE z#`xy2kbv?dW+BhHW}?+Z*@gSz0Vp;>6i+?{m6PUSQLWNBaYmtp=! zvFJdj>O|#784h3BJ9VeN+SdQeg3gqh(-zBJIU_Z9a|8U*Uj~kI!cjp8lQmG;s$$8fiEiGc?hQ($JlZw7Te<6zer9=9+Z5OS z4^7(rvv4!?Dcm4sF8~&{mGf&l=0aa!<<*n5TOdAd00_0@wS7-huaCm+RZo1LquG@%t|52(Qj8~{_F&p@B+ffaD$;Bw6K%y<6z|mHAGDaQE7Vk^juYo9dC#R@ljRRlZTKjrmh$tt&*A%b z^K)R#Sx}V9@LZ@$1(HKTlwm$4#s0HBT-k0lAZb)gY>rKihsM7O;5ZzBVkpH}4NCHjtTc z0>EH%6ziyl>QK22U)|G>w_ChjmgA#8)IY~XHe9k2-CGfUIw&>jE^7Wr3l|^{iY{(E zyFv`%@QMy_?k|)>vNED}92Z#M+J_{9H9gJmC}&N&9hj4v8#2!~w)keqhn(1Xwfk~k>~sFF#Ef{lTAK5o-!?eWDngOuI`8e9)S^k+n!8DqXy#zsC zLCDduej{?0^IjI|6e<~$&7kksW@0N=$|Yx`SEjF6vZ&crt?$Q%mWW{$?V~7vflO{0 z^E#&TYSQUYL!Mi{u5=~hY`A?saED~V?8SH~w;?HK7+ns=gvu!zLN{nXX4!ylJ`#O} z#ECNsdqt?wFn=TK9sk9otz^uF2FE!xc1rMw03;!fIGADm>iWHT>JJ=Lan6s|gZm>d zwx%|5(ihj`a4SgnrP~ZUtO(9oq~b#f9$K~*L68yB?u-_^Rdo3S>~AXF4!kt4yF&!% z--lSoQANemc}~7gkc*7p>CMBmNMmF$KVykE4Sjx@!mKO6`$nR=@KZSVt zFWeKKHeLi*)9QMkV_E{QeI?=}`WC%&d@S#Ye;J?MKhw=Sz}T7RW@COfVYMXRO~yrj zJk0P9vUV*5-wfk7 zX{FQiqS0n_a&6afeYS|AzH6Kpr8;!J^-()B@p^|=OnC%~pE)PNx8OPg@8L(V`ky%h zt8DSC%(eDM`LsC zjf*HXR5Zr2{MU~RrO~vN?}PXDEcvrLm@aOwg&%{O4mQoshJt-%*Wh|IbX1pV+p#Ze z2u4;rj{Z#2?QZyR)~VqBY>1dGmQq$M;RF66a5#cgYwU#hpjQ9G&6o7cJ@zfhe?nuYo> zm+cwIZRns>J1TY7o*DJXgLv^N^qDX1pQ{nIz8kFB-VIOQ781Q28!WPorkD=0zBACv zju4O$;Z3S$R^`ChFt%{a@`ixyZM3Za)QmK_g9x6X$n)FfL{xBT(pXyA=6E5fjRh~T znJ)eYa)Giu64#+!ICRGCeN{!o%89R2F1tbS_8Bs`lMHISr0gA`l&!3|+GQ^59Q+i3 zqYQD1hUsXrWg0U~FU^DmC*tiny1-NKQJMt)N>`AGSU9Kk3w)|6@}xa#_OS)(tnm;* z&&kQBvWV94qTp5)dO3DVMm}QG&cK^^T(4vkiwOKoM;lL)SJ8wo_s(WmH71xdA2(+4 zb6-+7ihB2Mff6Vh7N^p_F{iy4yAe)|d@q6i)N8ZUhJrYFKL;tf_0wXnnAfqfVvQ}$ zeHIP9mZEI(60|(3sxEiAI7fG4{pHl6qo?NfyAgj;qtJUsf~;nQ3G@K!G`FAhmL z{o4+u?#zt6ON z-P!c+ss(O~W;%*nRH1WW)_1t>vhzsyR&8&?+B7Vk?jMm={M2ywai*l0ey3SR1WsEM z6fb%EtI;pPfW>lZTBtaohs`QXH@VJZ0VRwh_7K1#=pXNdYicZ)bI;jjNUWnxI-MgP*O({1MorCw>u$xGD+AR zYn|KlY^TZkto9ETMB1hc^{3Ed)Sw8GI?aKkV z=utP8=H()mk0RYI6{tnD*IMseVv94c{nZobdlXD%DDsAw^VNj29Jbz^jQA~@URF%j zBeW5*hm%ZdHT_T@{M*i(r~54N{ScCp338NI`l>(d^fzd_SUVoQ$4P^cWNYw)sHgJ7 z-3loLY|s9b z|4Hzp3(dwl|K%rzaK^mZFs*-&LS$Y!{2rj#Sb0Zl@3X)>9uYIC(RhxI971!-mb^aC zF3G89BLQO z4l0a2vy(Xg{Ftod5f|cdS*gpx(1G5!#MIQ-fdP%m#}ct^Iq+KJlJkVry)APi4)z>C zY?I_FSPzyFY2W{Jpcre4*M&Mk-x0e{&zPVP#zX-@vRx;fDJf`rDt$ZfyHq^WN_gNU z+17Ayj-lAxi_-6gx;`Z4L)Gh2zUD06%nD&tW+`vd_wmA=uUBM8z&UE$*=Ii zb-X3cK048{G#dwa;kxEc!sxwSYRpXU-ssyeoj;bOBtj<6@#hxw3i7LO@Z3aBwOZDm zZPOQKRk`BFuyE{zdcD1aBPFP&c>C^T|L=Px ziIoZPO!4c+K9$gi&$)DF)UAD24k%0K%?qdRy%ot}RU_NEWwg&} zN0fkYIAq-wdK^D}H>TEqgth#HD$h&f3DkX`v^GpjV`qVmL}j?5=MRjH`|=KPg!Zc( z+^&h}&U%Sh{wH^YYAX1Ylmk+pELJbDmz3i{iPJD${I|4XZi4c6bf%rUf*qE>uSLJY zm`}BSW&xQ3zy&n3FUralJCo4{EMy~rHwzV^=Gp}8x&??@1WqVB$)Chj^wcPtkU9Uh z_*0`2qMY>eDqRkk!wa3tv{Xm2UeU5@v71WeRGbGYHsa;A#5F=r4{1c}vfmqH7>7ag zE#E<`;pifxIFOYK?4vj~dH-*kBVb#8F9qq(_T8(9g@U1o$S9nlu5Nl!9t}uCmRlH$ z2IIAAE_U{$5bnNRm_{S9%&&n(rj3WTOVm&O%YjX<%LK%=(86t8&}vJeV$6usOOJk& z+$S!huV{dA`WMfzl;`~wZK?ldb?#Nb1-`gR#&{JT4drEnGCaU2lZPYz2DXspDObQ2 zKi1&+X;?CqJf)&YB(N#|{z!+z zx>wC;2QgfA$1RkB4cKlc4`JC?+p5`>AU}M2P^gg-@TtDiDuBRGM^@uYK)n2-Q$iBqgRJCLkanReB|-MLYM)4k>@Qw`p+T?>)E zCstcAPh5cS{_~^3AagAB?Z0e{zYX^pBiVzLa18Z|_?DXe@Pjn^gr1E9)X2pUlgjjMeF35OKBG ze0L6l{%iKuE-$BYj2CoF$od^)D;KACmt8=-SAm@y!qetNM5E>`WO?OZ%NpHIHn#da zow`Rh?xzRq+N>!WZ$zr*Ic@oa^Jmy~=#Zvw-)OAksd`uWK)#xkcwpQsm zO~s}-kYiNiS@PW2jwKhH@?vL*8-0o0p7+5=AyD>fTiLRD` zRqc+Du!?}mX0jneqLh8znsuB7OC0$`QuBYEW%=f6S9$|P) ztg7d4jSHah>$PQXtop3-7`Z}%&~pq9{G7*!ky0ayTc7`)L1I z)dNM8vzJwaEfZw<#`aPszp=5f6iwEcW0Th5Yh`kfyYp=l5)xp4Yr;HrZ8coed7yWL zzMI?o8>F*ya|0@gP&&bj#qgmSh3o_c38yJMswFWy1gzxcg%miZ+^iI%7fcd_)j<$s zi|hJ>eqc0<2eGVp){70n$MAUw zs-5gXn84A|HmAA4``yl53nWhnb<9r1++}0ENyK!M0r~y=`6H_|+z_*bK@kzc>dE27 zAKTg~Pv-ISRZT?;%X4_I`i~>?j3O8MZv(%Ao^}UZnv>w)%%UkxJd}Id);vhFdYI0{ zTFo}RHYLP2zuWr-99`63eMfV>t1sL4+O?nv$p9w*y?aG^Q8fc>Y9Fr$dwF{eeJV^% zOihl~{gne=wf-n>inNkA9{M-Y(#skcqa4L*T`m?Fucs8hHef4C={L+u=6NkUYF6B*P#*&Ne3v4~xmCZU;=OH)?a0#|P3 zjdoXDGT_h-8z|V#9#}$;`qJ^YI*AZIP=D4!cmnDoh(eo`7B75`l(b8TEIM5}!Fdw-51GRPd)8Kuw7c1$a?&EgoaL5!|C0f9UEW0Vx!K& zK11JwHLxQ8zbVsK3^gQzgR04uVzRT~a3Mn-p=54 z{ogtc4WDA!)TwLN#@gHS6e8#Z%mnB-W_0XtN6-m3pKjF8&CWV(Zzu3BObulPr(;f! zLmr5_&VR~I8u|Ln+$P86^q}(ALW+P|sAagA;nC6u?+Uy>`U3gdKcHI3J@NrAJuNyW zCN0_tEx01Gqxqm7B+qnuIY&WLf(elM>>|M^S`#e-D` z51w^ZWTA9F_*yF~@5vzVE6sho!zB-Wtu+x7>(U+0RO9~e6wsV)f`DB`2#@j>@nEm9 zfU*|YUU6h;811_jEEo4g^Q(Ya6DFRGj$Wt)bAw~ejJ*0+A@cVC6|rmSLQA7_K#)y*cn$@s`swog@EBO>~iYcH=- z|D9%MiBg;6v`$80buaTN}P21xqI#LFI zBt8E`kvCLw7!_#2jM$)hX$VP{vv$BG#;{uZi%`oEK+jdLn9$Y)KyO-9!2wj9%`6P1wO=(XKm$<6q( zC(xfmEBy^Vi$>D{`w}o~6;)NlJtn4frogjks^@EAE{i({Uw2dqf$K6YPNuy|EfbTX zRqq?R8f_Pdn}4UHIK42GZfAcq^1A$J(*GDtHzQ7BY; z;6CJ^gPeEezHWn;JRjaCuVee z_lp4khFOC-)jIPirydX9>zLQ-D&J0uoD5Ijo9M*2^MT&x{Oj1Z8f7J-ZARSm#&Q7( z8Chh6RByqLN#6q_6&3gVa(z_ zmV*(w+-7xi3;HXb2hEHQoCHHUL9j_8!zUJ<2C&$`K~8F$^^8RLW%@^1`8IS)ziakQFKXHcl3kZWRa@*ogwQkWhtDn9FU~)(I)+;X|!V$6ydFn2S5D8L$#<^OEH@~>5>v*Sj*<7mS zX7!J4*^oQ_Yujvfo}0zxhOx1+f4rPG)jplZ0oUQI)&*b3&5#D}UK~T0R&7e#I8;eV z>2SiIY3R%6;!Pt{$8^uz^2c=>EyBs<`&A(0Stf*v5YuN^8Dt}avskgzZs#FB|U77VtX3RJ=P&nfQ zxF=C5gZZlf@7tgVQ(j?=3{eGyYGvuyBKw=+6@L!vCh?6nB1-tvE|H3BT|ykt$k5Qv zj*g{%1c~a7D<`UfSLE43T!3vm(d1Rai2Nbfe4B}~{f(8b@E01PddOUf??H=Noq;k% zw$~%{7ewN zUwK}ej~d!xbm^@3BI0NnMv4rp$ICn<|1YBtHdUwjZa*x z3^PXBME~ZjrT`?WBzC?NH5Ap`TdXr$(8tiL)^BBb74ePSx*AvTVf1?K2lr1%qY4d= ztIY;0C;x794(H&27OycV@mY4uc~eC;!AxRyC%HiLl19Z}D>^69zl$aRwVW_3bRAzS zpNaGXOLo@X<$4p=88lh>?VgQafw>`*DVr z%(&5{&LBOeN2i2+P3346)RdX@{3M;ebjmDl|8^jL{m0P1WGfCtrIze8@1Z{_w>UmM z-GVEkD*W}~MAW)UczMg}%QCM7KGiSIy6$i5iQB^X5cjs)c&0Jk7tp3?#?T>-XpU8} zjU*v`)ar?3A;Y0p{WhkqE9LM?jMblk)@&Ciu^MeQ{PwAjPl=?F50&`1T*!&LmWcAO zoELT3&@LLi7)lP8ykiMU={gzb8k&CdAsH@YO0q4=H2U{R+wxU7diOh;2Q5`d*VMd8 zr^_u3qa{3+?bcF2v`7Y>N_m1IHL&{D-v_Vp&~4;DF2p4DisF;~cH`HT6aSOf5=_`d zJ^h#B64bD4iL1H)n@9ro2R_OF$vY>=Ul1k!2ee6VNAdr+d2l7|-=PqE* z2>PE6oJnl|bI)7DSO2H0m*cnpwfs%p|92*UtLh_a9^9Bn+;d$~J(J>eJ{wzh7jaT~ z#S`pL7L%+@z4E?aILF*a>Hwke_lJ{$CB*%9_+55vBTC%1LRAB>=$CUe7R(-+F;76r z-j6%hHO5uejzw&3ZO6*2tFF%Bc69ABsr-Cw_cMVVGnQBr z8CMylRUSu^QERuPB=%ZbS}x=yt$TtCp-6XS==q zwJ?WmOz-g~agesUh}b2=zeg(+FrAieG~uI$d3Zd-z?7dRL!gV^hIKrLTE!TP5r{}5 z)~XaIu_i=JcX(Gm)n-IF%UDB`A(HrZKvlW~CwHH0Pw{_;_ZY2ah)GF=vq7mDpY!vN z<@o9H_mTwpdGnK|DiSZd)jg2#DLsixPu14^6eG(1qD8wz)5U`t<8U){j^us91&O5) zGg0t&JI)1OugLg@i8$j~txDbCd(`CfnIpt0OMYn4e}jg9F4wU{QDS0+a3Y~CR%Jvf z4+~!qD`B;5uG4)=fiyLMU5!?_3l~al=W9l}UzEVEAwmnk*$AkmilW_DhcRv}+MU8= zfBVYtVYhawc%l?3xya=c$Ipn;J;BrF69-m*AusLxHLM~p?@8Uk2mOHHlAxgdN|G2qS`m6{-!E9plBD4Br+moOvU5-@o(cyDsO(j!&o0Ey#+3s;2b8~rsl@cPW6uI!~X<^Qddmsv3 z8KhoOe}BK3xq0034xNbW{LVsG`)68yr>O=J*y8;N19Hq7qJO|i z(-RRIsu@;1zrK!L(Yb~29O;wlF5b7)rx81BDtFauKOQ&7+kXW65BC=A1%et(kWNEc zZ>+`<;uT{(yVPpR-{ZB$@$z9@C-RKv2)a(S?({^2Bz)n^u6@gHWRgd}0ctDoc!4IV!Fi?y6UUV->}1j$;GYCA zMCR3wlkq%;RrVviith5A%Fn3Ez2m;WyeTx5qZIo%(7+{)4aLOg^U?$8#ODjPww!&+{;&mM zh;+BjsYX0=V}VR~TRGqti%gwzPFU3>wa0-imFIta{B9smUCL#}lruD6_Y|1q>`4F}wI-Qj0syaEO@kpHi_zKMfclqoQQ0{Yh0C!D>&r&-RR9U{ zq{M^l6ombEvBLU%sm;EJjua)Nq@<1TD3IZ3t#@jHcAiyIK5?G#jR7ryk2A6;Kr7E9 zPz`5R=yk7=U4W#Us^3fzbq}kG2dyZM&=!gB)HE!F z5|t$jK~MavQxJwM#vF&;BhwC9Cza`dy&z{$H_09_2zWij3W67E1(Fvrsoe}T#w9Xk zt67%WSy?6l%kq%$q=WRfoslV7t)N%c6OW>nhI%6=fmHaD!ot5Kwa>eihSV*=X>Ehw9n_u6o$u`pf3V1{E8o1e=7V{`l{fwf6u+i z&FXQew>Py?TWBQCx(mNv=+HoulUd&xX&|fT>bc6H)p*4=IPiQw;YR^GTVTy$_RTvC z2DN6#e9dR%_a1mF|7@~`805(7y6k`Fm@ zcHsFz!~}n6sFF_l0Srj_$I-U(1s6$Zq-}9RwJ^&l9U3ylsV2YbGk~r0`+NaEVP*Xh zxYNldb-p#(7EU8?BEJk|GlRK#A%M||T}b%Go9J7-`qrJuA9!@p?tgla2xY+T?>yD8 zr>;+}s**j$EyrDKpKfx&jVDjuoo)iIT}drAJ$SfyAeFCSt3}H9;$$iNOHZnZ>gLOV zpAqcni`|mu|KE-lm)EmD8^^cPqZ&>SGKX#sV7yItvsmDHDltz(%A}5duYT5YRn&vMYp)e)uNod#~88!LUwwb-rFWzaGH0w-% z?v5GXx4(>Ne6>BJn7){}UIdv^fQ<Wrw@AV$;<#XpoOiH4VT|dLhzMb zC-m?d3LO3@pr9PU^G-A#-da~_H0d?5edhbqP--_-smC{0L5|d97VO^{Qw?n7z_oGz zigvu8u_*dbJJo}<#@6~p@qhxG}tm6X&+P#g*B#z$7l#r0z+4@!BdET_U*E#1x z({p~(rH&jaN#N`AxPBUF*UyqHl5S$``f_%Rgl0@R`?c@;b!$><b-lnHup2vL6ikJug%1+KDzK0t{ z5cJ7Hf+_n#H`N{r`3C}45ed~}9EX%BF}hgMo^SQY>V3Pm@Y{#!(8hQroY;5?YzHH3(b`~}^?XKu( zUWp5;ypF}P)f6&DQ8~U3aZlUg2S|v>$nvO1Ex?DLVg6d9kj_<3@0;-^MD^qD*G57A zso6<)dZ`hUkVcnR9IT%SChTf~7%_o6h0e9~iUB@X?fC_JJ9 z>XFO6@NrcObr{zo#Xe}&BNecB7Y7-p=n7(;cW$7rqQqTuP_|h+YYRtNR&kT)wXBpP z42W?2s1$U1TD;w}WVk+Oftfj9S&~C>f6_pmL16t*%;E44{RF?JD!tZR{zX17&yAbt zYAgz(fm^tT{%=lB~KxC<4SHd&TprCrMS(*DjJ;{8Fp!I+N4avqD*cU2z*|CQ=A zf~dIkRFVWZ*z#+84E9-jiE&9TRBoyD2E_#pdG_G4Q_03vyW#q>u z(8r}xl`SW9drIwrdPzAoTyy#dTozIDH9E*B=KBB{GIQU?`+K3zujQp+_suE)fm~I9 zrr(%uzTBE=H8!H9b=4;$LSuGhjqIYVTIeBQQUo6qPrcMyd*w$9J zqSfYT!ZDPuDNDpox?=nJGYYDUp~%?_ph7pmGUqa4>Vy>%9QzjqbPD;q5K5#{e;K%UZWn<#wKj` z_mp3({QULdJgkqH?aoAS%@-QJzNOo$$rw*ttHx{;p*4z0P>la{FjPAtshYyA3AI@% zyQ(1C^iYH)uF7bc!@XrVCN2@S^JCd89;BM<5;yhw_H9wOtxpBgwROo={33#lZ^Cij zjnT%>isrg0MZmOq>JOZ}4C$wSV-G%>ln;`Ufodq3Xnf!8|KQ}}B^)jX;c4%ZfA=Ys zAk{+RR`T-L47rbK!6NPIX;!Od=Zy}=3B1Nx*+Gq3!!5_VG*ZVF1u}WIQXVqwQt+J1I-)~^>$y*EktSmCp(n|bA8C$`uH`Y(lt2c@T{D`amU z9_r`vap^l%{NxOg;k=!EQ&D#4f=MONkLRMar5suS?c$sXaVI<_Bh0fbluLUFuK8R! z?xqAXI2fY!w>h*nSu8nXP7I~_1U7*K74 zf5d)$Hn%@wIt^@xS{QyXCE_t3*ZR2#|9Xnu?0f;gwMt-)XNe$EQmp3C$x`ViUQ%V4 zYz<;IQ_1~V=3bBf9qp#{uQ3ewe@o4_&1_=5@Tr|nbd3_$YU%OOam?lR4t%E9x=&t% z?NOGlCwt2aNIlODpI+25zrvls?V}mlbY6(f93k;=Z1;+o6Io+EEbogL9~JCqYgRsd zv@YoVIZynVYPlRac&7IA_bAQjn2Lsk>fqUDEW8>ht z&*dy?zV*)F^3&etcy8n2LhSlx&)5AG`jX2tq+e7i_@8G$yy4v#S8tN<@_^sr&_+-+ zx|L5l_EJ~E>t8kaeH7xu@Z>ZLATlzfc{;zSh7DYITke-%@#4dxk1e(9EYq7mVHO5l zN+Pg}h=Ju?^*~AHXF#Ivt*0g%yvCvtuPp1K#Yyi(5t)f7Lgnr~fSbgb4Z9*MOkE;!iLO4~+QRzP}oOhe5L_&CKmI6Cbx zTqs!%QBvB*CnMHHA_)};?p!}QYxUdo{EoLtX?jvsWvhZwDUBvYd($4}5_1hV4X820 zJyl^t)^i&NpI?&O^vd1v0n*>FVEapDYz@NDMNbE1Y1$^@S^QFm%nn zde&I4#)2A2+BZD8@4qT+~ZZPKCoG)k4F~4ZEt+ zC$Y&x)+I^kpC{&HtCoUb5ySY#$B3LC60*zeg!YMX9VNOku^u7}C5Uj+`s3V5 z>?IXm<0umk1H!^cnljq0O2f|W1g(zs!JxSGhIXWgUU6+im$BR12*3e69xqW(~y5!ou?K;p??gO@3=N`e$bM zdvSTqV!MN#fli~5f8oK~!pB@($UVlMh1#lG=`LaDIQ+}4tPfxgP`9|-Qq*^yJr3=k z(vd*&+4JQ_$wPf6?#w zK;5YBqB+-}k+IWcIIGhq9wRO5(TpTk|1Xb>2}xk^%yDf!R#0A0`tO^L_g;8Y{q6RD z%}D=O_C-+hKPS2U!sFKg(lPPs+vEuR6U%=*cQIM$T=>~mF1cm*cW=jimE>ir#rgS% z=Wjueg@qI5pS{?MX~>n z9|(D73lqZ2e*fuT<3KEA;%@1pKpZ;9(QwR7Z22+VC@#LG%PRXdi(ayDutJ<-bgQq{ z(Whfd2i`d&MUJ|6pv#p);`;pTj(fK$*W77qftx&POAmWqYidrQJpbqk2?#jjcJi=& zeSKVdC<7RHy2(F|RyY3p>!aWA$*PQmytZCjek75SYF3v#{_qh~Df{LTxXI6Z?k7;e zQX^hv9}w19HZ`WwnwN)8)Rc`*n)n27POAFl{p~tKi$i_fFFG7GR@R#&?CX>!nbF)T zdv0n_ej#S*K^vMC|FX@H1D=)qL>-}29ZK?3O5*IDM^U;D-|Uo&x_goF`{s;pkC%cR zDZa^!*(RvK{`dfw!6?bV^cU$oUZu64VT_2S8?FKF(3{v>%g@i66q=&@Su76(&HbB& zcl<3BBbzy0j!#SW{Ei-lnZ!2xt))8Ma4*n~4cFxq;lNUtA3Bco4Z1x$vTD@+J7A3h zAU7NSM>NK;C zu8GS*y`{p|e&<-VFmLU&ZGC-%K&Q?WQ)9g7%dU)ux$Qau`-pri9=(8yL}>5a;UI~Q zDfca#r^^E+G*~as_S}*&r1{86RV_XZJ?&q8fSYDdThkL(`D8A5dboDH3*;wx4PyR3 zQ}l}H0=L`x$0!sh;r5fV#Hi4~P4sm_A7O{6?wdD+me1CKX>piRagrHvOm$0oyF4K2 zHq~MxYjWa;JaZ*Bz3010N5A(b_v*0KJH^p?GW~MTt?Hum!!;jzuD?=~w}x^eB~Ue0 zRl#H|<^zS3(Ts-7mE zNWAZJzBX3DXIv-e4l-Ez`D@v&!U{WHZrN3+i;U+6m&5RCz&`Oo;HsShk7hzl{r86e z8C1!rvKpAZxC(*6E09gi2lkR>qyRP1D(d&xFr;yH((yLO(M|KX&kxtZD7*%?rG z5OiD8sFHYplgtV?ce0YtibW{ty$YuscIeL`qUx^~vBbcSj=b!w0T3NYHKkI_SL6Xu zJXOvMm^guiUtC!&X1{uFgjV><2|j}$pve-k2y;tx+3BP2Zr zlvGLgp_7Im?>Cve@CS@5oi}lA&TwF)SuYK;|Lg6=bTLH!*_&}UYc(^CUgK_j02iZ9nWcKd0X%oDm61O)!n}^4JV;LZ&u~_9g`YJZI!9 zsT`@s*6`j)iHpO?xx|Ct)PuR$_|w&d^wY6j2R$ZY4~f!U<6);KqIK-z4v*z4;;}7u z9{_sBt}C7gaDcmqWF-_!b1X@t)F!{}H9kMs>tLEL&Lsxcy5E*GMW?%cX6aq4D!ssk z-CTkyop4rl^2@}3DMeaC9tpdGNbyd8X4=p1ch*Fp7M%{-c&KgIhN*1fZ3k#oc0G)p;_RP zrW3m|YxOA1G3(!O_Kj2fS6oKcmq%jip zmeFlx=+i4wcJlX)c-W}vM5U6Y|JtkdlMkAQv)Gs7%PBp*8jEw{0oO(;V$W|J>gU)~ z+E;Snza@u#%_L#1Wi|bT&izhXkW(bm-aRmv*ZOE0VsGPyRht|$lA&Z@6H+IWwMi8! zauwh_SlKb-znG+ebvD z#YC5iG8#dL(WkMUdmHtekqja+(D{`iA8!na73fSCT)y<7?@we?U^g7ylHnS zbE&YuDfDjyb2i`NfmBt%I4uk7qQt0Rt~U89vS5RkkFC4EmO1{|gkZX)?^U;*-d=ykPwvhRdOb0%u>hS z6y@z|XnEUXsfO_RFFi0M?7#8`=Ll!fe=RxjZUAn4nKaO$Fj{*bnox7|E4q69(Sn5p+y_@kW|Hho5xp8Q;t~=S`~w_d@^v905=R*~ zaT@_T8N|W9Ho|L#a zOQGa-k7~xx&f%qyBGyBi5h>C10eFdE=4{uS&YZwJ4|T+4V9`FAXI-%VN?N?m>T&n0c#yGE(P{ zI5=bc6V1!44X=6JkTS<`v0<(2P|WbYomV&^#(@nkq@91AKUhmgf#!HCDfIyp?lAHe zuL*~1_MTuDTgYziQ&%u0C|dS`Q^r=z4hQ@(%|?7MCn@cnKhGphOSxQB1j%gps1+U= z`9R!j3(lc^B6b_7Xbu)=V;WX7fRYzxbO_$GxE~c4Cm3+%MaMY+L^Q^iL#_LuWELg) zN7I^#G|&d=%-Jt_Y_WhhjYrV zdU3%tY`-d*MN>A~uE0{T(%@wI2Q?O+TtSyC*(4P}jR>Nr!^=QTd(X*2g6ytBZknOX&fc&-XMZzu8a zeWB;tN!iuv$&(Np@mpQEmzI@?{%O~feEhK_M0MkoI z)Muv9-FTIj`Nxx3)6~%L;R#*ghuvxMR%E*+1K#Ud!mB4#w;HfkSTfYWMK)i6TX8R> z0w>-m-YRus;gik!#x}@h>!^Bcb+T@eV*FJVYTo$pytk^3w~KOYMwbO%)XZYsK4pc5 zOlHVjql*E6oj1VL@DqfYKfOO6qT|9qYMVm!$5d|xW$4+W=^7P~$lIAk!|>Dqu!Dnx zhsQxhuOw2%loTz^@r}P%e4sh-auZ6M*IY%0_&6Yp1X@!omX?k${ee|YaR=x8ZrZApd89~qSq!T$7wgg1_z76WZ|ZC} z=ys5r)7IWjZe|Ac6VCVaJ$zDczT3B|7uc(fmD&##s?!Uhba&z!-)Wts6{TAna#PD3 zE!78VH$bXNGKZcp5k-D-=*ukvlp))ErtRcIh~h8a(}(G+EXq5( zrB(f5n_TR!)>sM|A+ohJ$@*w~IKSR;S4m?3ToL8Beb_$Gm|TULVSH#=O0KD;y>{V< zm~z#$-x)1iKD=(CHS*a4W?Va5ef`8&VMNzTOev!G%dO9jW0}$USnKwp-Eu4Bw5e~8 z?Rmosb@9su){ITkR2lrja`ph-(&;=zw41BE8Ysyv|FQ(Q*Xl6`0OoPKwytA#GmOP| zXn+;Z=+;3rV@y|gttQDPSyWO+t4vk@mDu>gzxJ7L82(aGjQs;XS!c+OC z%}rW9f`GRZ6BA=+U)oz90CJTF08O7HXb(t7lq>R|0%!FAb#rdaLRZ2_v9Z|mavSp1 zI=I*%L{04u;}gu@5T)bS=l+5$gM8--)R}7=D_I@ZIRcs7-V%L}WTqeNF`A(vq&Kel zBiVhM*EpX;^Bw2R~uN0mCFQM_NiAk(H;vi?p#u&yUnu<0q-cTo%@fJ zoO}&Zu+jPpxQFO~(v=-L=;1tm0hE<`Ud1f}56(0STT=me5sKlGdT-PIG_NM*)g*;S z5tG8n@;0Bo$dqtf&J0mH08W8tRR7g0)r##P7K!r(e472`o`dRcQ*62oW7OVA!GL1A z|3<;PYfO1deNKC9_MWBG_W_vL;?3cCh&`xERt=@`Hjf(0*bB4urU;hr*DfXh$Y|^x z)U)yC_HO?Io3VvN>s6<0c8!9Dm$DI@ao>wm_*Wt6abmmrrdkV(fhWr&H*eq9ueMwb zIU&=rg6DVKDdEg?@F(QxKiVe&^rxMAQTiFPx^zX6xp6$~;j3QI3gj-n*V@vuOwE=q zvA4d)>cee=zi7xdKBxgO#>2_FF>;*Xla|D@9ESTrE!>(`~FPDx67dis0!WQKs; zQh*K{&8%qPZDo~fuT*BwQ4UNt2lB(}8ldSQyR}7h>uFEAl+?qA(i{sf=se*^+8T@q z%0C|c${zAF{S{lXqP>6L3*cCXPshsDl42$WjkM4#dHM#a5JttLr)m`d`smyeSwZLr1A$XleGuG0?q zCL0fQ#jtj-z3y`7auECcyb^2p_=q8Lkw=GzndLihC=};X!DX0||Aa$I4Cjz{+<>*% z{pE9Xzmd4)y*rPKv2sRr27WiS`iDD5&9kb@Eto6hK`-pxyPt%cy~IR&xx!*kzXkT4 z^0!=-UaUTDB-zo=h3YACo7N^d?{tekzD>&Rrh4X z+nrpedf_x3YBX0Gu%mrx_-whC1V1OT+{n@l5F)|ut9mG-+83)!SDGh2`xU*4h8LW^ z!EdVrJY1RMB+23A!D>0}CB)CYFV&zk%{>-Jm{)Xu5OSMD%IYK`LR6{?a^ce!0$m9? z!NEM8AgeIkwSkdy$v53yq$`6SCU(ohm&*5ANA*NTQY2CuyuKE0uE?=B(#W|zgcCaRLg| zSP(Xf_5QhzQc@7V=~=wEZ6`1OBu(pz{5yrLjA<)Mhs$398Tsq>6Wc=y!~E%Ov>f6` zMqz%P;XzQM34usoHqAwq>8A@dJ&8_z<%|mv7@g%`q74lVF%%bK%HMx115p6+908op z9cP!xmDQ#kv~xdKhz2UjiLCB!Zf=Xm6yDz6neHozD!Dn=kMFGAxQqfkpwFirDoQI8 zwG$Zmtd_R6y>Owb^1>ozWBKE!0?(g3`C#(+-j6k)48CY^yD~mE!mj^m+;Hkwn;7vh zxXpQMIEsaD`F32$aIt9(i=L9cMM3!n#cTFvQWBCbELJy{kK_*5MI;Gl&&K9v^VWId zl`9v;ijE+=Vc;MedH*hadG-ZU$Pm3h->O?(Uxi7;r%y*7v-tq2!;*ionhwDK{0DA6 z@7v6x<;2mI<*z+w%+v$SWc6l^iuF1@Zw*j(jELl}%=?;ZI)YyaMo!8k`8!D(fRDUiXao8U>e))&_;?Zol+-be1UibchE$#)e`wpHPf z-Tod)>DZcgWM!Csfjb0S8><*Y&Ii;&a-SGuy30U8LhKz}Vq9Hj$0c(K{jN^U89t95 zSVuBRdU7zc@JRgX1=!a|p^y2%v9SX&vD&gNF9#t|8Ap%kn1-vcXtc#b(pt*+5?;TC zg>%ZpqM*CsYINQc9INwQw7kBxZQr}4^>x+DagKomZy!?^Ts7{g-0hVGDi?dO{*GeJ#uTg+^&K`+o0|s^>wHdvD4LpCmu@ zQeqXfwvOf?)KFC~ej6!V0m?A%Rma4aOyTDZujLT^ylSG(;uF#6UtQcZHPs$?F3H2g zqefvtUU{Pv6#q9Y=uy9+Rq}37;JrX-j zr=+CRC}v{dzKqBdSVEU6r(U~uZ733%;V#OEmS)yb6Y(x%VL>6Bm5mY}3)&A`^d|8# zyjy)DA>rTs^_i{hGDd(U%+^(kKaOiaH@45lsKIL+(DDLi+i2ECOqbb`pP%0u%T~ev zX|X#|HnvCbCMlhqV5)UA5bDZXfPmIth9Oq!R23-!VpT?=b5jW^!MFX_&3+@*{m8FV zR+}}YO zHox(*akjBxcdGu`-e%i)Ee!t3W6GUJ2s!4hOf2Ufjd&3!DJ~vVF{a9@Y+#XZT)D6q z?^gKqspUGOlC>3R8n;%HWPIFZ36`aWYZmkF6#Fu*++7$ecVQN@ z2F??<^s;rAsHVb9Z8k2Oa7RN#%2;a8+sk(wF28K{y1ExEGVa&-po2UZx=TNu+%ZtK`P2)Jsm8Z)2yuwywkF1Cc}mxvmRpjgl9Yr%yO z9Zmms7Lv7#H=LSNUP@8zV3N;s0;I=3i0JHCL{hC-sN2Wx*tL52UwG`!}Muf!7M??<2QHT1b{&8PTDhWM7-cXp0&C=v# z9d(+i1}kgqasp#BD=RDK03segU;!MSD{7^Uf;~g~^=7A|JF>j9r#U7itzfA>!FHo6d5@Iqm&`z)CsJf4)B$J`%FyiuzoXu zUGY~?UL*g8M^r7`|GC` zq8pzhnhAsW;}r0CyuQXmDc5gf*mY>YeC8?8eZSP{7eL1dm$;h5SiPom8AFs(W6>E? zRg|QbCcd9sBn1eB=EiYG8KGYVY_+<4(*CUAYRNU=RLgq*H|t>(_-0Lf+ksH8w>Jj@ zBP#{|y}k~`ol2nZdK>xz0(-k1dm3INueH<#Kt8QDakvhgJg^%o8Oo4(e_T?OD>|p& z{7QBYII1PB`vD*%Yo;8HoGKp#N6Et_Y*sniu=T!;9r8yi3RBBx#35JU{`c6)yE@T$k#9s%-nYkhd` z6Zz8ZtBENogZRug$0#N=Rd_gwJkVfwTyUKao7G8YXVUESGb;1M)}?ds9T+FT%7nCpW-%_^UdH@-UcnR$RDJ^MfX)B(b zS?%e{g}TgKyK>Z#ttQ+cKSQTPOO|!%ID5XaNhGD<^~t=342VKl7dm;rFDyZr#0Mno z5nPSO$Mez1hK2iu05Zsk$<`3!RyiL!Q|wN@I_h#Zt1( zW8@`hETFdP_i;ra4CwE)xGpA02}YtZ8lWwCjcScw4OfqvUb0t&Mkr=ws2se@l)vDt z_d~7&%3@(7Jj?C9eImc3RauCmwdt}CP-aqbrEG0%{D!E+)n>m1LZg?$0s>T*CcR&g z8o5Fh>y!sr^cJAJX$3YHfhFZ9@Kz90aqb^-E}fME|M8_y z>JqpDlcDB2=-lYN$Fm})lk#?ydG~4)--;h$^5}(+{&OY1f-&5_T6Rc$D)R^Rrt+LZY`P|H?sV)gvd4V1-A^K8@`?Rp3hpUwdVy@ixER@V9ixvUm7>0Fuc$ zjfG7vqF(4!3lY{eRX~+qXY$apqRqF2X6`AJjCcX(4UVmj?2nB+w}34 zG&n~bZi?7`I6vs(oNXp>!h*+=c-Lp^x?SCE0{$G1K`^JN)LK z(JM(gCpW9wdd?YWkNzqLxcP))=~x2# z2UgQ9GCwZBSV?iUN%gv4BP8u=JGs@taS%(4N%-uZNYr*E!JF7BO-27Uzex z@Wz|>?%liA#_{{?Y7?`vC%$2MDJL&Cw^A79HNTmY8XK#gJ2F{Wp7<{5Tr9hDdYbaA zYfQ+|%BzpF=d=j|75|Kmfke}a)V$pT{CVD%SbTn*o58#H_HwF&WI+NzTsN3=MS#uG zX{Nv&@X$c!ZVf}xZj{Vz_56CI0Y8!b;D6;u+YZw}B`vcQVuY;s z9bcz_K&e{9qW8acDP%8v3s#G3BGZ(kV9}wa;Zn)hJfYB2%`x%N38-Ob%Y2i?o#^6} z`3jb0@&d?doSdR8a!lse4Ohm_J#l1aST_X41f5(Fb3@BjA}!8a4~qD$aoG7Uhkok= zcki3?)V4Sy`AWC&TwhPN(Wuz*nnLV6iC1o$G1AJ(dTlh;$C@S4ZH4l&`qAjCYGl{_ zMr1hI1jJiU)}DCoZ%&;Ju;I6UUhJT)+0W(<&}pPZaR$+!V-QKzTfUzKoo^Qb`!CBI z=*{aB5J;k9m^<|A9^1h^MT#gySCd>EdeJ6tAA4D_I%Q#_6sH0_)pA|30)Is}Xx?)k zbHKQdvQ@%obH`CZK~AMCPkuSi{yHbTDX!bPqD(yacle({gGaPNZaF}|5F)Uc`+a+R z8;t3dvV#cc8U93%wc#+i{9Zs;j@LNWo3(_4Ba1!o)rx)d_O1F;jw}U{ue2gsy3++A zz|kX;D9Gkn>i)D^+3}Ko*Df>Slbzp8Fq@Ei!I$HzrBk_ux|jwsVym8HhZq^a!sdTd zd;rp<&eF=j9b__`L;#RK=ySrpdZS1F$9qkl-wy%aZ}@ki$R}xq z(+i00kB9P@29Fown}ORk7g_UdG;M)$XWYIFU*n(jT$Wy8qaL(PW#`|NJ*?Jdc792b zi&dEp@@nvEZo^_oqRVAt84J!&LPvj0RGk6_dh{!`^rCYhUy52!s5XcZnBEpuy3WfCd6w!UzPB1qyf?DzphBq*@|fbTr7cVNI1f&p#Nr1Z*dBemZM+11p}r)9W=Y<2PS2;C$)c zE?C^kf3NQiz|9%jaR<=nw$(BYFO|WKQNRPbjd33IwQdxGj!t;=&w^gv|9q{8@4Op3 zPmD-+^@@Q^?CpI~avIF(_;6mGr2BUfN9 z-0OGz-eFO)sFp(jv?pRz#G{osq%40ZBNHfgPR;&287B|Z<0@2l6De&YjI}1tNT5QV zTFlk^8(&!47?=3CKOcF#yGA2@NGakQz`2>Boyz!dwRU4bF#NFFRI%ilpLz3-<@^w; zX}|3D`t6-#-S>YQMf_a8GgxdMU)K<~A63^lpDV=gV^YrLj>7IUrBzk`5cBh#+IOr< z2vYpm>1i+>^G^iSHgd<{vzl*GJ<@f&=&s{3iUu`S@5Ln|ClBW&$R}}$fe>)-i>Y|W zc=n8w>YCo=I|AaRt7R#zI&uOl&mBLoeu z4r4ALV6F2j8?A^ZJJT{MRvO_rA%i-Pzy0=N?09FM#Ts~U5o!zHVqz|Z|35wKEpeUa z6Jujz>+9>F095McjMZ)a^d~m-V1Z*4Xx8)nJgTIaDRZ>n*jV(XiFJ}hnd6&3@9M~X zX4clQ5~RRLhY%f&oa0ZCNTjURtAwlNq)vIH@7G;}2joZ@b>GLx0;AbOo3#-9^ zCYGv30n4!tAYMt47gYuehPii_oi5BM8)@GYsE5izHIk94$DD%Np5l^nL+@4%PbV%( zc*K*idHD-31Wh6Y&i41G_{pEM>hv;$`n118{PbdDM}eL_d1|Y|+_&wBJUz02mNK)O zIr~yy=_+mhGN`+KPK?F>k3SLYR_UYqLA4kSimaU6Z(N4VTO-8lpPsr&CS@tm=b43! zRjQHWleVY|iiV0k6s7JYR!Tr5;?;#~?+pj0-fY<3DAc5|7f*Y%T4K=5o z9g{zhFZLFR3m(^3P;S@2@D-EAx~(5C%rR{G;zk}}gJ=&IrWz&fbWC3TgPN&ov42oJ zaX2+tM{7qNkiFJF(6v_It2tt85s}Ye39})ZoPJTX02||ng~?)0Izx)ID^6APayhD; zCVqO158tk~%%z)b=?th{Hx>x$Ep-e$tb1v@Ix24$6do9GdP%xYBuCp)sfxF(uL+h*7$#F{G3H+W)L^Ujxu!xqRm3{>{eJa9PnS4vGju>U-wRY0_U zSp4UYK|P+;i`E^Xz53x~ClBT9j7}4WH~ZC;r(JBMU+w$)&jWNN=jDUzCgleW2nZ1I zSmX2Ew>gA+1Ng~n+9z*kmp>gX3k0o~1|{x?fG80^S-4Nl%G;U0&{E)kvb$KtX-Uoc z&y!n#aRmTYv3edEsqI;)SW3xN@H%abS7S53(D?$!1n97^g3{uQ)>z&A4!7Z(TW9&nBGu78plExXuq8f@u>Nsa=a zx|?b;EI2q=4$~t~(*Q_cP%5jPXVPTC-?LDub{cAEZ6;5F>Iwv3ph)W1dpdd=z3MoH zt9?=f$Uohv6-6i{j-rw_H()(f&Jjch@=p%+>7gp`54u z$ER&qNS5ox{G>z2k)&yAAIIa8l4Cf+l8=MbNm6#^m^LY^PaJed&HKP*Ag!@Z?xhv~ zL92s0jVLCwmx$dv_-fPCI>k{f!BlQUZdtIoL4$Kka%iw!f=TTA!8Hw z(>8WrA&R8cFo-;78PAb-c~N4XL_9NGexU&`_x-+?-+6yn9N|mZ`!^<+jLQ>KsZ@5) zEevL^Yi&AR9(y&P3r}*f#q5zm#At-r+Vw@cF+U}RnzsqLv|b!Hvl~Kmi$Qjo*+T!y46sj*Y%`-E`FjilbFl(lpW!Bn1 zk&%(n(Gotp7T?oGK%oSvFUC5(L>DeohEA6VGD@0j&617)Z**%EervQJ04$afvA=%( z0{9WMX9=j+ys)E@l)^KkKPW^H@1A0_CMzxFff~U!Mr+xLg?e=$`zD)$ygbM#OeiuF zSr~-L^JP)gsg4?IwELXKKVy2qCr><>#@?`9J%;YJd*Bv875B(VOG7dM=!;fVOet-S z2B~f3f9k4Rn(XpXvG8*{uOKQ6H3n_NlvSxiUYRii&R|Gbv9G z2T+2*8EcQJgz=DqACC)+#*4`@mSq@?K0+SRWz*ZSu(Xs_@Oh-bFwek40AZ(HoS$cK zJ)5j^zZh?f?j+Q!&UtW^?;hSn4;APN#1zwX$ghEV90n)dep^WNapo$xpY~0(*~vk8 z9~+?1UCI~-qch1-4A;4WV9-i6+DKmUc3whW;O<`_9G>>Ey@qe3e;I%Wlzvdk2?(=Q zfLiB?pC8Y!$Dq@fNiaYq*zv({xd)1H$K6S$DT|>`B3zX68#&!gt!lO(Ru`H2R`<<< zSG}}l54{uPU2AE$@C>w`$2+q(cJV*q;YM!*keJ6_gK0weI$&S9z)jqRQOLceSlrqk zozf!%QlP_kCxLG3bn65v)f%B9w#%-&*~gd;xir2-!DZ>RIT=o-^h%1(7lYhWYC~OJ z*q+-*f%^k*Wmhd5Id2dU8E%Q#!gtiHss-SPlkrC+q2QrA11Jt?F1Cxx8ij$Au+yW! z1*og_YaBA0^3r?@0d6W?x|l8M16lzZ0jC@yzRk&77@&3l?)!poc53INpe?tv*k2jw z4d4N&j72<)D!uUg{CeWFIciEM?|rXD0(4X(C!PQH?;;~t()jz+;sp73R8>`Zt-3SP zeG~v98~13l+5xls@g|)LBhXb9jN7`u%f6-gZ8MKl;-SJ%e4b*(BO}rY2$}O8Oe@>Y z!cyH;rexw>@lz}78`=tb_6wEFN;?O|9-EFE@$b#{MAKrKr!Nj|3i})p=-t~fgJ;K8 zsnCSn4lV95iozT;1tYS#P5a9 zz|D7#U(Oq4KE%(}Xel=ws&s9;uvC0Nt8Qn7OZMn9H@vxjopKbl;fY(6HPwLS6ZIlR z0$$7HPB{sv{LmB)`Pi4;|IfXUde2=Ua-z|wdOvr9Uy7>LLrb%>vl$r~nnhZUm`Y1f zkuC?W@bIjrl$4aF-noSZ3s3%35$QD%xDI<&Ezg|A_U!K}8OfqUPoKwR1RB!&(mP8~ z=l@aA%MJAWf@HT3)y);Mc7S4{nW|N^0ixCLh3pNsO(3?wrV=3J`?1kWK&A@B^1a1e zzJSlEwj0S3mI@`Y9WBNH$W$=|C|;@(6VZRd?qk@gLDFeq_K@!;po1_h-!{je4pS5< z$PIOo5(vnQC_&-(?)TD(2=Xw1|;bht<+Du-^Q#1@gJomdUCR&$v z+hwA>#hE<7cYm@hNc^B_FZ$-OY?pSisQG0MTmescRL(W$5MW&wa2PK@Z3zdj6H zMQlktJX!xWK!5HlxK%w6a8(SRI`K%*Y8pqIm+pJ!6!fXU7OOS=@e16S=Aie7Xii1LACNvgT;ifG);ia7qylFs$D);luPV&E9x=LMSUc zKqPT)<+QCH(Z4_@!AR(coua(FIwL2L(S(rij@7v{iNv}B@z8?^U{3oGUr$6#46uL)(O1I%jJ>HG3)m{#2 zP8(dOrV;4TtvZf3M4fYql>AW7HC%;PN_fxr+#naL+x{Yfa5}{mm7%96hKFYS|CU4t z+MWDbaYi`6?%Y#8Sz-zw_a};&ig?zahCfYY`^w=LPx{v-;)TI`T&mvxJg8<7fc+{YC$U^Cl_WeE4UThw zk3aWW*?1Nr`$DC>u9&vET@`>b&o*Y<=@opI58Kjcj?O#J$!m@lhjbMQYFC{m3G;tw zRHuYcZ*&T%-UX&gDMF?`{s`?pi4sU%9y+TpFEf$ZdVCsE7ozhtB~(VKF}CpaH|4CF zAHlZF-tAPZ*7^%|b{?Fhge?lxt*&l;aG-mWs?kX12oAjwaFx^%;sezce{2_zeQrk^ z{iwWa#**=MW05=KvV8L`KO$22b#iLSlXptw8HHmsJs{2I)uNX>{j2yAVp?z@#-m<< z=vifos|C-y=!Subr6OPr1$5$Hh}Q-tzs@}9pA@twJfU7f)8qd}a0N>zQhjS_5*386ut%AudFWM&=pl-; z{pLD)X&TR8a&-0XxmB%BP88m*SlykNnUaGh8M-`;I(Ud}UbDEJW@Ny5-JWB|-^qx2 zW-TqsN4Q=uecaiE>Sc3+YvanJ*^q`{LLqYG7Yj8gd}z@9&28n$Yx(R>@16Im+hZ2$ zJx{jXF1XDXcx`@gbci8U#4->du5VV$)7=m1E>wrg@rm0a=4yDQFF6k>T8vc|mlY05 zY2{j769Wc=wY?y(%}F8Y4aaIdl(FFA*~#8Z|7O69d&x|ejo6WIC>{eIu?>Y;zGP8m zWclM8gm4w@?eB>5KpSU#@tYlv@mHG3ADgxcNcFox_PD)rv-mg`m`CJZ&Xaw2!DelJ z>>Qdfh#$17rF?Q38CV-XHCa_>p*#Lh(|duB>YMA=3DB}IIcU*g>VH5z0YNF-|HKSx zFaKUtpFPF}f*&h;GZ3^?88Ns?r73_KV@fWHdW5HH+u;}BC4%90XS#iV*Krc zwULQ(Q$o`C!QH{t_aK&m8`u~3JuVf$%;rE}!apG4ed}6Q%r*RHj<5gzh++O;uMjxZ z@&piTc60o0oURIJ=jA%a`IZ=?emX)&7tileyxkW!mLopZOeIm6 zzi#Y#{7ip`d(c=z(%=7FcTjuTWDd!Fr=L#vya|*(Vx#_GYO~I;$_H2%_sI|l?B3Ws z%E?e{uSE4yh4`Nn(D6EBDjj(CX$ ziRYMn*H}=#qUl~TfDxz!(FXFc0+4P`M+ycUUB1IAUl>z2MZVc$R~o#$0G4yWgkrMd zH~TX!$1t~XI^3Ti-Xd{DdWTGIz+>K$vTt4NTZ%R79X?xEAAUkC0trvb?@8>#f4_!~ z`6hF>@X3@iO(6G=_ovEGGu{T@ngJQxDEWpr-`P%7YB^!W|5{>LAK^OK!aM<;R!FNF zcYjGPtBI%iaATrOAJN;~JinTkF7CaZq2^0V0y_1GW-u}|Adv;n85yne2`L9B&E$hW zefks}Y;%_tQ*J7iD(sP!m9?;xQp@?TEhRny_UHb?D;aY2bo4F-4a#53epJ;hH&1D^ zU+{?tpp;SPe&V%dqQ-D7^y~SpuoT|%9z$~S6)_tNrKqzfMXwH|HG99er9T`g zI@(^Tp`{C0@{yA|R!bj7DBzd-<==m{bhL8}t*H;n1hz8+^J05bF#W5Z;857#?< zq0P14WCt4U5U`OYk4nl7Ro8W>-kwR<(e(x!U6GO$l$A+<`LL32Y|DjWGh@xg0b zUQ*H&QN&nPQs{}@NkA6(C*y0yW)047q3l!qZn}I8zq)AKdtcbr+@jXoKjF&(|G_lG z$FM?Dkd)y>2Xqk9iJeQJ2sKCk8MIQ3nZf9JEVO;7PunZ=7ahC#waqvYu&;&W;Q5|i z%gyWgFWD}LJ3~`QOP!7`D@3wIw&e!b4k)Mcne4M~W;ZB=J7i23`6=$@WpTS$5slLk z@#bZ_aH!sXKvcr4T3(&xLmHCCtEwYN8-i8q44(`A3-n+ji~;yF-7xcGdf`93JUnrP zsVLl>(G%Z^=d^j1;iAfKNp{M^jHow6c#SJ`;eE#MJckRvIzYH0Tm25ZRi$f&9Ef&y zzaB5l&r$W#|76CYgv>p6%TW2CO%r+MhrCEWG*tCbS{AK2OoZzSP4qpO%WvM$99 z#ehB)qg?&e#+EK?Z=zGtMn~|gTBTsvvmUvE!V|dI^Wr*aVWn{m86>o}yNC!m7%TQj z;rdZIp@!1`X^lrT0Nt6H^W=LUZp)(&-StNQB(^b8SS;8}qd8oUsFMt)vu(dPMQuvSich6d zA?qF!LD0``xdC)lc35RQIKBxqK_);Bqo!J0Ljs%IJU+8H*m_jM{XcAHaLcDA59j*E z`Q9xSKY=OQ$gg__o*MNca+sgOH7LW`xcP-}^RnTMa^X__-b)(?0c$2rfFtj#4%WkQ zE8Ixw&}u8lF$U36^`q=L_dJopi+&pCxSYG7W9gd@z}Z&oIjeU9XyYhNE~hS7xd&W@ zFQ@Gs)+r)9VAy;P_jeccS_CUloQ85qyC4$AeC8m*zfN3{*uL^!jZux*{ETT^#*Lgt`k3aEJCNu-%JCc144m@m+A_PL&QTy;vkiAt9~on4f!H|ZA+Y8 z6(2&3Q^9Au(gCf-#PN9?ApQ?%B;YRc2|?v1AXbxs5oxq;Z-I;Qi(V6mQ5rm^|I&AS ze4HY(j|O>|y5f87vC05;HXGf)%v{|4eSx3KQ>I6L=`pMD7xZ8)VUr@sa`Zm>gjmHB zRamp@Rjt%dy?tCFX&dk2KiC*xzb5F*)r#ah2H=*u(ZxS@N=DmC-;wxNGt0gQYm5Ht z2M-ao+hTa@twzA!EuQeP9NJhIM6Z3m+GiF$Xso;#37C5O37f)Ij>Uli{tb9ZNSjfb z^Cz{M=BQU_<;Xp)E5Axe&=A58MbJP;USDXYaM!o14fa(I_kTcLXK8S%MhjkbzfDls z($V=e8sRGCnHGtQA;%SOXO?z(l9Y?q`)xtStvEb{UY?rofPukzf6m{aOSAA>jicZD zTXN7H2PUn?9fbU9VQ8^cnK)Nu&|Y&RA>H22k%_$p-q|SaWa=lx5^K1 z>?Nq^NtdAy_(N~AN<{ceh)N1kzK{@s1idW2Xf9LI$Dia%IqHub>{Kmct~wNMmvS_n zPd7g6T~C4$`&GLCspp!W!SA58oW&1u_I)?5%L$23_cieEoO#hOihIt)swX0-xAhp5 z{c05(wdvzx>gR`?_Hu3!v3pk<=TW5x`21y4pFjBdF_x)rb7KRWIOvB)_8k|1(4)z3 zZJ*Bgdbd_TV~nlyscS{3G4<=d8gPa`2h5J~{7O=PEt=^YZ_Q)6+B2OF&cfCik6f|B_FC|4`6;BY6amJDk%<$WX~nHy+E9?t(b$l+W!6 zdDxAX-PM9}vpWlK8#`k#UkI@;rj@jZ z!7TmXx0xIf8L6nK2*5KKFjj%@tHrb9CmWNluI~TA?id$>vA4rI%_lVVgB%delxcWm zqOk$~{x)|lag`YvPfBWjw3XKt^-*B%!K&B+o;CyIhTwpVmKyp29#*a(wu$r!EGTJK zi6y$v?8p<);PoC@2-sMWoMT#n?0JDw!XZWJ$MN5xgrTB8GtFV$I-+~C9s?`h-} zY$PuE`&FDv#nP<|54W=*O*c0uY1m=Ooa++b?%$a*7i9BU#?%wKMg3_@110=|#VG#K zw78`9ohOzHIwiLsJ>p-N9jptg{EvdL`zg1GpAg%RBZswQ9h*h_zOK6&_5AsB%!c*g z*)!$JwWFQy^eXSpMeakv`DW!wHoH9x zZHrowHkWdc?64pz&jaS3OpK}%p`N1MxTILT zCD7SU#tyHEsNyq~Tyk5H-H*Nr_frXp4 z5lEx%6F)63cLDRMhC7fN0|kM32ndp( z`lS^`u=GaJjS-zi3Asyk8yH>~0=_CK}@CQMSCnP7x+VVktVQr3y7&m=dq z-UG9K98ZiW)yH1ZXl!f*!xXicFJJv1r~l!J2W&K~5);$YXFzB?;4O+32w(ZAHtvI% zBmhu<3UpzX2DeW04`6>+^0@2wB=AkZ%J32!Vr9M4k8%YBZ{Y05o50(pnq!}=w%8;| zZ5{Hb`Yvk{hya_YaV%H@ebRxi&=kjO5W$4``&lmo^cGWIsUk4jlxp{p%anD2)b`t@ zs)7-b8WIAf2#f_0tm*{NR#0INJGpT4$i`}`zBJmucw>>Qd=PSj3PQMmig7af3bt=Y zbd|aroX_R`r!QZ=+$nql+xPtmpD+ZZi7~q|OhKxpU>e!;{__wjaZ7;~4hX-Qi?O0O zEi%Ah03Xq2ehKjMZB6VYFvAkN$el0VgFel>cC#ddhaN`Y!p3axyw~sQ+|t(OY<%=6 zl00ztHxS}9?6$he#w_(cbUEIhBiG)@1S6zxg_2%p5(_Pi1pYa1v|e0cx{fE{irV$JEirTf zTmCn{sj>S15|6xg?OM4t8m{?R%(u!6eVu@EGxrcu&s-N}7?ykOZ`=J#T zaFLWM;+1w#|0Qtcg2zz@(1Lb0&ClGU*XkSahZ9NljSsm^Uj5YO4j|>AmT&?*W75a! z{N6tIP?c1|8nVE^K$?W8EH3u2`Q_f!dOz&lPrteCHve2kgX_#R?#FlP4>2kx?=R2o z1BsF6CUg3o&7MU7s4cIqrmJ_%mn;OTB3Ypc_(Sw3KC(rrQxx^l<$jV^9i3i|drbD8 z0HHv5*>m7SMZcjlz?-xuGxgSe@5RZqu(-h?@Ge_B2+N`t$EJkI#+{?1qKx=9m2i}K zwgdA(D4{1dr`f3#ZJ(9zKl*EX^p;xr=+5fVUNDtWM`x!Ue!mJ$>Nu959G@0Ps3)lXY*h50--faFepw zfd*hvfPruZP`Mz3C2>t9{1iY+{c75V-#QTv7#g1#K6t`Ba^IZefIaKc(DE!z;hMg5GE!kR_crK`Z0) z>R>sdxbbT;RuE|(F%uWoni74>GRG8Sf68ci5b{x7M2 zpHV?D;e`b95rJH&J+x~H2or=;OMytVb3a}#O+VO;T_;W2j`^ew@!xBM{9Sd!9*`jMz+@fuEwgOv8`w-{*!JE;}ylS}f?R z;GXE~cE7bdbA@fK$r}#G?PafzoH+kN>jS5caaRS=;yf-;QywOoFN$P4fbWOD*x8lu z8eASO*5&1HH_k3#H?|RmyCLX4tjSqkY#lwc?Cl)$uo~^KW-w;>ejlt?MY_qIIs~aw za6FKAvSN&^Jr^-T`ayesKEZkTzIEIHYRH*1#2c3DAByT$kv#&!j!K$cN0Qv*i%^HU zOQ`OKQW}=iu{UPH&){@BiEn>#VNO~+CG@UmU7kQxG&7GtLXm7U_1Sfw(&xq%2A_aM z3f99%qG6b2n!x`x^g)0)DT`xn<`?PBF9NoU0w2VVS>JM8c#`mTOn9ntNT`U%M8ixU zSHH!&<@CrXoyVHaO+CqYd^ydjNe@}!5IHi?{QFEw7Rwfc60DtHp^DCV+ZiD(ft7pfT7LPCE?}1&i=Li(LA^B@L}-h;-J-s7?6gv zmcyFcgGsAHOnK@zW!C28Jdf82>!szkUqlsM{m`3L@GDv>#DFnCzwdDI9TX(eclN&1 zWaO-;EOor`p%U;XE`V`SN!4hn!XUl}xw%!cKoVQoG(l)myjI8MkctJ3;o}wW4<2x_muLH(g$4`j}Gzl9^GAh3b8lCS&KW|r+ z=5w0#2B%_OYmH#!6Nm-UNXGt~7{N-I)8kU~&mzN3otv!xMK{Gi0-RVi$Vb^R{Q`6hm*+7sa5oZjIn{&kmOj~# zOp3V#6BQq3#CgoMlLV6}i4~1?j^Ab{?Vz}h{-cj_TkMn?C5z*B*56+MrRAvSiZb;~ z`TMx&Xz~*K&B?lY1RY3^%wAGD!pR1R=w@VLEA+}ALp!p&v(KRAXlXQ@2ucH zcw7su@cSP-oqpi8Nhnv5Yl%#|qOm-2lz#a}>7x9sx_|7T2X#SBR#XtyJXUaC=BQ)t zAb)4>OX{-YV4M&*n+T1k;D*$@HGI@o#B_Y!RErz?+Fj|lVwokh`r}_bkBRE=U`Xo&6y-1$ zyy*E+odNRPF)1#pkLru}5!cKw%$8QqB&DP@#RKiC{UuZh^4A<7P%#mPmoEo~QeMZd zPt3|p-|Mbx;J*LbnRhY^&<07VsR4i_?fjOfkhBX_3*u+n%^(DC4UE$|RD70QhCko? zfN;Hhe~|SDM)d6D&oVYKM79q!Gwd`lh78lw)1dE#C11us4(l0J_{B*N{r#+VlqvOP z?EnXQz+51e85Zn;J&jbq9cg^}Gb84*L0>ggXMAjP8SlNn$u@+34aW0`;wlSp;lR6Q z4xYGp+ndQblF2XyLV`&W>PSXm;c+yYN{IJW^=o=-oXXH!sQ9ehKqTz%Iygz-e(Coh zqa{vT5@s%Nw&ww~0bpkb7sHT9j_PODcSl2^^dyW^=R>(;BxRhASMhv1f&SyYit@VW zb#o>Tg+XGkDXH&bDkHW3cJ#PkSK^~u2srHNCF$k*P7WEi)$Z%6vfktQWT7PL_e=<_ zc2P3{qM0i8CY1LR_CQ>hABth?1-We!LrcbD7vq zTul`l4DB*In5yThaV+{Np4OTEZi6oEK(9s^8I=Al5!&nn=d}3?WHbxJdPDTo(;%Fe-=>LK_q?y7#eKO2Hw((`5m&yYqWzia=EW`6Ow094 zl^|SJHEh5p<-hq)yIALC0B#?|ssMa@7}$KrI)zi60CGewx=o;&D@k3M3@)`J80bKL zO8&Rjf*d}w$AYikQj3&*?0;urVFB_1q0d9!a)DfX;&_!*kOKs^sE(O%cc+P+et%1s z*+<*su~B>D!_4fYRR!jxWD&rAk!oi55B+cKcbKE(x3M()QAC8p6z{AVMHmH!_h){xfv*Vyom zlz??cbxC$>k{Vy;4k?#+8A@fQi%d$i>!%iF)5y*2+P#m~636QzM&)YdfhJlrseArC zdWJV6QA}>VzaU;>q8UdS-{Vr$eKLOdIQ1#7;i7L=)A%aW9(*iYB5vT|UmUD1CT1lc zH9wp1v42(c<$zI4x^ML3=pHJy{XP)fPoTMIM=$0!16q{>F0jj?qI3&+9aT3c#MYt$ zvpV*_PlYP$eEWvY(^^(LcEQY)7!S^vcIO#;`fkSiQm1G5cXaWh^wGKh2YP;tlRR%t zx_`8RU!LfJ(rrqtZ}`Z^DYB^MvqG>l>49R_jsy0e$8&QZPQ$C;yDBR*-YHWRCx&XX zd#`2I3m@1JMU2yX~gLdfgEW@l1m?NOXxP z14k|5)bM9V)H$(vps!ml4nz4_Z`pZJD22d<@RlA&KzDo?bR+Z-a*D%&cu~V5%}t~1 zp;h1g6;r;1_gyJPJP%3e{kN}RCkdD@mE_xE?dWjd#aXlHeeTc z2cxb8H;X#MW)6^w1v=QJA3$P3;|(Zta%#SR48s&(P*!s0i=Klc4Khpz1fFJo2M3** zmlH@%AD;3O!9lco(;lrsT~PyowuSRo+D#W1%fUW2PnZ~J4?Dmv@{;b_LGYrc2xQy| z_&fH398g8%jvXFIZyEyDPpy zDqzM`8iDoXFw1dCY)y%No?@zszSY`qLtv!HGM*8)M;A}mEY0)N_gMtVsQy2u2g62r zgUoBy#>-c@{Oj#NU`N!xR^#95n8;v5)$Xj~zK33iT_2xEXycPT>{YMH7x-9Tm@YnQ z_#T{^gFj8+f|Tj09KTJM)ONUCmRp;^apgm2^wg?J_n5Y{EPl84wq<|~F~KWKcST!w z6B83KrN`Z70x`Knw{NR5cIHd|%#ha_le&dS26B}WgSwbVDhi6PbYjVJHlhp+=D$8% ziGu1#TXF7W;0U7SdUYpy>s3%UZ&gALOgx2R|S1jw&UEr?&4Q3 z9xZl`9UbAFp^nelr4jboB!>#9pVb`fr5QS_Yuy+jt1nENa{pmS-dB2UG~bkyD=JSZf zcczs~Lu$C_^efW}KUEcm2U08gNE!p&96nn}vOzCU1wJWRfW97|Ue@#eFenIOH}v6e zUr;aA*x8utkEApk_`)u$d-TnEUmu)$Smg&|TvNA_Y)^&Ws$c`E4R0i_UH4jvaT)t) zAZOoe(CVuewm{;-Vf1BTAhy4n=4e4T4izcqNh|G(dlB&hT2q5&IP&v8nRsE~c#A+T z9nN!wKqcN#-FXkR!A#+Upv5KP;@BK5naGiK#%v6NX0Ey{}TC z%h(6v6A9}xsB}meQJx|cC|v10MJ%bgYF>Zn{6K~VOCe1_I|m4ukkkbgj+WNe*0wgV zEGBibpQPOS1g7LcR8cr_HP4g zfaoq(KqJN}GH3X2Gr3G4*uE%%fB!-5Os|GkBJgb8nkh6Sqz7z)taY6?8(i!~1b?^z z0LTfUfOiTC-Lr`0HXvbYJacCyFAbUj6R`j%t?X_17sbYYGV%tbZk)tB`CtSBv2y%L zsYLkToXzW*%QHNucIm*f{7=!r6P@d|JEF+4`2$9@?59%FK>UHWIT>fnO)&12{Htgw zOW8dOmQ$+D|7w^!u*UvC-u8T>_bv%AQDkQV3<TLdRvH{Vw&O_kTu1Iip__ajQk9 zNg-NA9CLm$eamd-am{j_VJYb0356O!Q+REjvQi?ZU&H2$*!#7XSad9c{C6*XiH8gkcyU1N$rzmmbv z@YHi$r!zJ^T^L!8NUDes1{kUJCP;SMGTy;}*ng*D?7&0{!$Yw2$o;zL+ES}&(B0xg zIZWMmzq6oDosr8#V&iRc7E8xV&ck^?i8FW~ZLU}_aNsEZO?1*)STb{;VhDcRl}7Ih{RKB_(?Y2_e{ zaVX3fasF#re2(t}vDX6nivCm{bQm~Wm7FI>9?06Z@Oqv4q4Sdxqj{1@yJ28vaXEhxTbTlH^XX=o+Zib1DOkUtyyR8 z*wP&AlH6)(saGO{Y$tNnD1efg*-o%@|u>Q#=_0R~gh9%>n=1a#?ZN$>I zr&3FeB5V>8^tQaUg;_wWMoe-YFiSE)&XKuSeY04@d)d=9uMtqT7xS{2qd%OFaH@~9 zhd;xc^z?McDGz#{cr{!dvWk~G=$}djwuK7d^?ys}lf&%@LW7R>#3s~3@D+6cy?0fD z+DprfJJdvkg>~`)IO^U&UblcA-$N3uK9c0PYyTHlXBkyx8+B{CI~9@c?r!Ps?rs#M zJEgl@r8}e>q*IXYmhP@|zu$Mp`EeNhbl~34-gm6I=DH?7J-{R

gVQ+e}lCEN997 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 PpcSf0YWMQ*J`MaAgSm76 literal 36545 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 Date: Tue, 21 Feb 2023 17:51:05 +0100 Subject: [PATCH 0722/1271] update plugin menu --- website/docs/artist_hosts_tvpaint.md | 34 ++++++++++------------ website/docs/assets/tvp_openpype_menu.png | Bin 3565 -> 6704 bytes 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/website/docs/artist_hosts_tvpaint.md b/website/docs/artist_hosts_tvpaint.md index baa8c0a09d..3e1fd6339b 100644 --- a/website/docs/artist_hosts_tvpaint.md +++ b/website/docs/artist_hosts_tvpaint.md @@ -6,37 +6,35 @@ sidebar_label: TVPaint - [Work Files](artist_tools_workfiles) - [Load](artist_tools_loader) -- [Create](artist_tools_creator) -- [Subset Manager](artist_tools_subset_manager) - [Scene Inventory](artist_tools_inventory) - [Publish](artist_tools_publisher) - [Library](artist_tools_library) ## Setup -When you launch TVPaint with OpenPype for the very first time it is necessary to do some additional steps. Right after the TVPaint launching a few system windows will pop up. +When you launch TVPaint with OpenPype for the very first time it is necessary to do some additional steps. Right after the TVPaint launching a few system windows will pop up. ![permission](assets/tvp_permission.png) -Choose `Replace the file in the destination`. Then another window shows up. +Choose `Replace the file in the destination`. Then another window shows up. ![permission2](assets/tvp_permission2.png) Click on `Continue`. -After opening TVPaint go to the menu bar: `Windows → Plugins → OpenPype`. +After opening TVPaint go to the menu bar: `Windows → Plugins → OpenPype`. ![pypewindow](assets/tvp_hidden_window.gif) -Another TVPaint window pop up. Please press `Yes`. This window will be presented in every single TVPaint launching. Unfortunately, there is no other way how to workaround it. +Another TVPaint window pop up. Please press `Yes`. This window will be presented in every single TVPaint launching. Unfortunately, there is no other way how to workaround it. ![writefile](assets/tvp_write_file.png) -Now OpenPype Tools menu is in your TVPaint work area. +Now OpenPype Tools menu is in your TVPaint work area. ![openpypetools](assets/tvp_openpype_menu.png) -You can start your work. +You can start your work. --- @@ -67,7 +65,7 @@ TVPaint integration tries to not guess what you want to publish from the scene.
-Render Layer bakes all the animation layers of one particular color group together. +Render Layer bakes all the animation layers of one particular color group together. - In the **Create** tab, pick `Render Layer` - Fill `variant`, type in the name that the final published RenderLayer should have according to the naming convention in your studio. *(L10, BG, Hero, etc.)* @@ -95,11 +93,11 @@ In the bottom left corner of your timeline, you will note a **Color group** butt ![colorgroups](assets/tvp_color_groups.png) -It allows you to choose a group by checking one of the colors of the color list. +It allows you to choose a group by checking one of the colors of the color list. ![colorgroups](assets/tvp_color_groups2.png) -The timeline's animation layer can be marked by the color you pick from your Color group. Layers in the timeline with the same color are gathered into a group represents one render layer. +The timeline's animation layer can be marked by the color you pick from your Color group. Layers in the timeline with the same color are gathered into a group represents one render layer. ![timeline](assets/tvp_timeline_color.png) @@ -135,25 +133,25 @@ You can change `variant` or Render Layer later in **Publish** tab.
:::warning -You cannot change TVPaint layer name once you mark it as part of Render Pass. You would have to remove created Render Pass and create it again with new TVPaint layer name. +You cannot change TVPaint layer name once you mark it as part of Render Pass. You would have to remove created Render Pass and create it again with new TVPaint layer name. :::

-In this example, OpenPype will render selected animation layers within the given color group. E.i. the layers *L020_colour_fx*, *L020_colour_mouth*, and *L020_colour_eye* will be rendered as one pass belonging to the yellow RenderLayer. +In this example, OpenPype will render selected animation layers within the given color group. E.i. the layers *L020_colour_fx*, *L020_colour_mouth*, and *L020_colour_eye* will be rendered as one pass belonging to the yellow RenderLayer. ![renderpass](assets/tvp_timeline_color2.png) -Now that you have created the required instances, you can publish them. +Now that you have created the required instances, you can publish them. - Fill the comment on the bottom of the window -- Double check enabled instance and their context +- Double check enabled instance and their context - Press `Publish` - Wait to finish -- Once the `Publisher` turns gets green your renders have been published. +- Once the `Publisher` turns gets green your renders have been published. --- -## Load +## Load When you want to load existing published work you can reach the `Loader` through the OpenPype Tools `Load` button. The supported families for TVPaint are: @@ -174,4 +172,4 @@ Scene Inventory shows you everything that you have loaded into your scene using ![sceneinventory](assets/tvp_scene_inventory.png) -You can switch to a previous version of the file or update it to the latest or delete items. +You can switch to a previous version of the file or update it to the latest or delete items. diff --git a/website/docs/assets/tvp_openpype_menu.png b/website/docs/assets/tvp_openpype_menu.png index cb5c2d4aac02c9187d38420947c3dad25cc1eaa6..23eaf33fc3d2ba30452454eb2ffb4abf1f5337c8 100644 GIT binary patch literal 6704 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 literal 3565 zcma)9c|4SR7as}fiV`U^3^BQ~WqGNK5m`nv5z0D7$d*AyWDB8?WQ=KyOekAq&DNlF zEorP}Ng}R{b&O?fjjeZP>b>v%-249V{_#A&&v|~o=bZEXp7VUq=SjF|W^!P^_X!jp7$=*SE>T%oH(JWtOPf572;*!uj?(l#>yP#3#>mzwR}Kvh9C z0Iqm6gsxrtw#{5wnrKTN-&h>ut;^Rr+4s4#!UqA{1%P&HL4@xTg>G?g>TfRIT^wr^ ziu}HN`H5OlPIZ@m77@irQ*kN+JywZhJ?0@yH>X(oX@L$*!-L9FCYdb51oT*lWxT;;9yDM`y(m%iycN4d#1krzC}npGM(e=0H+TB&M;FR*PD;p7 zRb*1{oz>R>MWW`HO_4SI*$UwB@=e6ikvF5dJe-NhItd8Ziab4FzfsAP(;;lb@*Cw5ZgVr*UJHmg>|M-WPDRl{Fa09xzPgB$HaBe51{sP#*(d0P! zxLoVM0?)fm1cDMbO*S5EeNsTeN=U!*7gnfKoEjq}R%TsDk4xjM(!X!^zYAKXIJO0v znU?nrHWwsXl+&!Tz*wK)&A#jU+@9#Hno89)v8?SZ&BzRBZi}jknJM*UNuq-yuq@S< zcdp=}?v6G+j^)&-mx4hV2fC4YlM5dn&Lf6UA@KJ(r8N?3S zcqp>ylfE&!FAi^rkTl7MyV)!DbWYQv%A6WQ$3dltDJ#cO&A9(mY!%n1_bQiUTi7LV z)ec1}XQ<1}@N^Nam}_>Pkxh)*tVv}vgADaU&H415;QPcvy^h53mT8~9IFTZ;$eNT& z|F`QtMveR3bgaX_r_d@7=A0XCjC9&DkH2J-C_Sz!ujEQ@Kz1IZOkt26`9z&`ly zI^6+Kw-*<)u#D@wA#^r5m@?}i577Mz`MxyH4RG8;(cd_Wd9RBF-4K%H`@;Rx9Kc0x ztsjVbbuQpC>qMVt>3Jz;j=y^G;>YGB>mahY@8ZJU`ubt#z;uFBsc+oyfE!LoifN36 z-J}5tCYGc8onBFjVh`u>i2gz2Zm`E4^9mK-gw|8^@a~E!)~~h$ct@A zCan8xOXY^WuqEeNc(7kS6Qct3HP*LDvj!=Yi4Lk~ye2Q!$N4UAS_{BITrju&R`RweKT0^7~T966y^Jcke`{fMp?9 z-kWUqAuY06VxPK5mUH*1)*ne!E41|2x46Wuw;;m&qvygfN$0d`PYB+bqmRm>5 zJO(1e4E8Y`Ab_muWJ4In78O}CbAAOJoNxBMo0O2|A4vbu+3q}aF!vR#O30`~OljRt?Rosh_nR0GX_9 zep$W?IpmYKsbLoy#&~ksQJJqoV27lcAhy2KgQ7)s}&v{c~^*ZzB=b3?jcu??|PMSF2$KqHK>%^C4&L8vSrh(t|UGMrN4 z()rLg>d zjj@w*`f9j*Kz-dY>)M}TV)EH1{|Eje2w(PPsWUT@Fu^)LR%_p;W``9)244^DS?cFB z0-K&c@jD#!DZf|qc3}@a@(&>lhljgIe`lhWGXhr?4sP(_{G!>Q{GxT14mwZXkF2*M z?P0{oRO;T`ECFZfy@ISBjpFce0cN&v{e(U_cDuhPUQdGBE@6RGO&xrD`XU==_+fWQ za@sM9U3bb*K<05XdEO{$eSr%Z2P;)lxk~b^v6gKUh9q)A?d3n+osThf=`ExV=}V99 zo9aLtGZ=67uo--JokVeu}5j6_ZR(5(dYN{v~NMYVBsN4Bik4KJ?MB6dI=Og^m0yRD? z!dIn3UIbPfKWf8|NG8I0vt~DlLX=uo^Y!LYx#ZQ%hiRh2Z^dPARF=uK#MivqyhSbc z?M?OqjJ&%wZvQ%CNFw}?+?|pgpZA^)Do=dI*TSgH*}2=d^zP)6PtKlW9NLhYh-=Ct zcq!a1^AnUqVBTkcFq<%x3!c|4@k}khlO7&u8mXr0Sls(0-gC+9czuM0-G%yRJtD4i z9?O;2XE%VX&ey^@nLJcwTU7RFCW4Ks|Mg2JCT z8Z+q7j@>7$*a*xo{8z!YrUG_vAvZ5d2*3HL?($3>nMXyb1^FqH4?;!p8(N2(^KybL zsXlr?-Z8+0UePxTg)QHoZ=au^yjt!yhiS!Y+v><$T`DlfXk%ZZEjm3z)CNjY8v1<| zS2n()*X-7XSSiY>hv+yMsf+NRnj9;ΜdRthcI%Jgf}%x5NHEtv;k^Aby4zi}pSE zX^T;r#Y2J95#iRF!a|{Gi<^5X`mWgR4~Xv9V(e<|#ci0WrFz5hL4W@qES1gxO8zF{ zR!Zdct|L(e|BwqC+{%UXbKUhOO{)_hHsY$PE3VnF+G#UIXUO;Spkz{rRLBEghCl$130pcakJZ>XE!$F)^q{0R9-;U2E#vhNCUdul1XsSl7G- zZ^`XZa{qeD!CRPiYn;Auh{bjs&S9Hd$y8akoNClwn^2ghvzl5vl#S-o ze2-v>;jA$x^wjdBfY5A4HT@J5dDKKd7o9=0#o>rD?jLg}t`#Q_uU@#Ppm!7h5n{9T zVw%OzZ;vRmw<~RQGFm%3XF8g=`&T+Fr(FCsapB@vhEG7*YqG6I#@5m8t#ifpmPtaBVF5&;DghigBLQ{#sk#MC+Vq84C6?)Ws>|KD5=cpqH z%x87A#w0NuoFhL+6hb~#89p6AyEGwok8)&A(Gn9RC6n@?fL^^GpcISOG3%%`fx5%B zdBJknoTdz+xN-+x6)+&|x#+jt1ObkEC#5BUA6o zm>xS9lu-#4(K3UL{gR5pT za^^bfj_7GcrQ)lf5va|t@~s*!^+a#nO=C=|-p5wURu0UhLxi(GCJ+Hj3(l@%HPsK~ y997S?#{lekeon!^F!dMW+Tem`8+CxYLZ^#+FOju}V_3gv08=9~!%~AQ(f Date: Tue, 21 Feb 2023 17:56:21 +0100 Subject: [PATCH 0723/1271] Revert "OP-4643 - split command line arguments to separate items" This reverts commit deaad39437501f18fc3ba4be8b1fc5f0ee3be65d. --- openpype/lib/transcoding.py | 29 +--------------------- openpype/plugins/publish/extract_review.py | 27 +++++++++++++++++--- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index a87300c280..95042fb74c 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1092,7 +1092,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(split_cmd_args(additional_command_args)) + oiio_cmd.extend(additional_command_args) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1106,30 +1106,3 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) - - -def split_cmd_args(in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - Args: - in_args (list): of arguments ['-n', '-d uint10'] - Returns - (list): ['-n', '-d', 'unint10'] - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index e80141fc4a..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,7 +22,6 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, - split_cmd_args ) @@ -671,7 +670,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) + ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -724,6 +723,28 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) + def split_ffmpeg_args(self, in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args + def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -743,7 +764,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = split_cmd_args(output_args) + output_args = self.split_ffmpeg_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 7e15a91217d4d74d2cddfe6d219742d5146ba52d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:02:17 +0100 Subject: [PATCH 0724/1271] OP-4643 - different splitting for oiio It seems that logic in ExtractReview does different thing. --- openpype/lib/transcoding.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 95042fb74c..8a80e88d3a 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1092,7 +1092,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1106,3 +1106,21 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + if not arg.strip(): + continue + splitted_args.extend(arg.split(" ")) + return splitted_args From 0871625951f2054392ba74cbe6ba5ea47b36d44b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:14:57 +0100 Subject: [PATCH 0725/1271] OP-4643 - allow colorspace to be empty and collected from DCC --- openpype/plugins/publish/extract_color_transcode.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..82b92ec93e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,7 +118,8 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From d9f9ec45a86dec05c2b5f602951c805ef2186c61 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 18:29:37 +0100 Subject: [PATCH 0726/1271] add missing image --- website/docs/assets/tvp_publisher.png | Bin 0 -> 200677 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 website/docs/assets/tvp_publisher.png diff --git a/website/docs/assets/tvp_publisher.png b/website/docs/assets/tvp_publisher.png new file mode 100644 index 0000000000000000000000000000000000000000..e5b1f936dfec1ec4f35fce41a419e53a72c6468e GIT binary patch literal 200677 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

;r>#l_M$0;nH4viktX?A?B}GQ;WQaE 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�d92;(`4A*w~J*L5*xpBb*ZM1C~{kGgc3F zLep--dV1-iorZn~irQxFM?AVrN%n6!L11Lc`qc?_DNoZMy98=LY|rI$QJ`cQnV78o zqMx9+U+p~wqKLCzL7(B0Z?WgV70bU~-T*w$_kS9q|8m8m@(tn*OG``l)dpGD%>@l^ zd%3&A@P3`DG<@6~7^>prW_xMa&p(VbF0JGDq+I~}k|P|b1%{|T8&+mXmW16#$bIlPNN|ung24GgF@@PbHYT8K7}$9zzO=-3>#WDgW+m`!!B#!wp77QYVl&Q{RXx@Khyq*1f;P%IEfBZi17(-zdgGZ3}h@&eB_mAJ|Oh z5IT1+WJXTXY{E$LLwzV6b-a^G7ds9nx{R|LpnvJp|7VLsJw-3)E zYsdM>8zmGTg)a9wPd-{QP|*VFh>ufNw@x=|yV)(=M!8+H>!k-=1kY~tXOnw>vFx8w z33~7}VP8n^7TEQ7ryNwg{G$`X26gJUo+LqDnsJjLnr552QH{@5!F1K1pj(C7i0X;2>}?pC^>$`dBD7 zAB;i>b7l2zsqqJcceCKBUSl%&L(V!viA@3p59^mD(o5x1b`e_&c>sP=E1NpE<=S)g zQeXd_ot<6l0uu1`pWh`+mT8k`2+YW(``jk^sY*$3+3PXhZJpb?T5h+~)#}~!4Mg#;U; z$4{CEKJ@cikX7hwMaP4zExK5>gCS~IEy2)0usg=RpFb6yxWn0GdNG}@mTsHji76IR z8OycWFCrd^BaYaBJE#K;TO~j~kt;QTSR&6|V4!QFj4P;v$I)YMX^8|!c*P$9S6{1J ze%&Ai>4jmA??*KF4z_UVvC*U9pS->nj4u#zBn!kLvPgqpC7}y;*`t}Ym_s;UA9Sz$qe$o0kk^_Y@`F&#w&X-~HR%e{$f~kP1PS9t7RoIi@-7x!wW`3LYN?976P#aNit! z;FU19B`hX~qB^05r-i45ZfU3!b>VKMC1F|66(W^Rx>!854VmfIhmW4Q0hk4a=7r`K zFhRk>bAPfc^tlo*vz%Q%@)4;JIk4ktDDvy6L4DSw!Z;^CDhLDJHQFbOwy zx9?S#AH)oZ@$oS$F3wy>Yx^y3cYlk_47NAaINL==)|eMiUteFH?@lOvIjT~@1WYEy zHazOHz(Nl{UNto}`X=2SQ2AM&Fq#y(*Rm$1)r5e;+}&TJO!M!1MGF8LM3!nKG#~{+ z|Bukle!xTg_qiu1Yd365L|F`2b@&>PMdj^JAYj+H8Kw2%TeYJWcYr@cCz@(K7L%|wsE36~ZXv!lk( zl3d^&Or*t`rG)jLmjpfd^{C<c!?-cETtyWJ4=JvJA_0>l6)}z|=B7aqBB5CwGdB z;CEJX-!As;-8!!F)`(j2-92dav-Mv2?)TQsiDI!_pCiB0+!=_jr3HIJSU6vMD;xXs zk`vop*)A)N)?j`Bj;&UKd>|s;v34_JeqpvtdNK2p9^3#cHJrh=jOh{5)7?!x*Oo}e z-gAVD6*3NQ!(C@eWF=3kD^0ds4IO6@Tq(s=BpmN@tY>0kVz$1O&GX@enAp^An-e&d z`9;fQfenvKnTT;q7Hs1rUj(uITh8@(HHObs)eN*0l_#XLK-ieagS9lOgpL7gQ^;6 zPQXb1lozWl4YtMG31<}Chyv$k&Fm7@)~zrs+VQwbF%Kb2>g$bP$N?QIRlfn+uT?3+ zk$zJ{iwaW3=EX}v30}*u-Kwdr^sG>bY2W~p{`^VZWX1Fm@CG~@G~p8w*>a|UleEA0 z2b&Xjmck#65Vj9Q9Koczod+bN^-OsRIm#|JSY5kRwnarnyXC}ttml8s`N-9Tc{l%P zXwO%uS!zp8RmLYKemFRjDV}&0iZ-5}nhJOB?p^|#K2>O_H^2kcWPw|+s#*vJO6{3j zFRkO-*!~qNavd~UY7`KGwy>agP$AGmg6tJZe*L|wSK9np7b&3P6OzV*1SEluCR?wQ zR7kLrn2ms4>{O86{DdfbkTFEKkeYTPB!cI`@;&=d8q@+Up-OG zxJ&%}`S*cE zx3^({5h6ls_`a0~5+2sx-mY1;xlWdhAJP>IoXKfvyNGoP>5jrW34f=B{G;DQ>{(~u zgv=&$`&4yMT|a~Tyc8dIO?`%;=DPO0%a)GS(Yjx%gUzghP8E6#3o(hBFz%)2C1>M^ zkH-7A#;=fJMaL^|0EkMyt7 zQn|JLNtXMPPCJtjkbq|eMEOy1?j9ZrUWbcUSMC`sBpm-=F3kf8U85$zn|*LV{1x0{ z2(2v1b(7Y8gL}ccJAwJ`NQurW=U2>)75YoW@3T&wIL_J5*Tm(ATI(LAU16WAouW-f zt&9EB$k6K7wTp|3FUj6Fe+xXkUdC~!`!qu$3-O@}uAHs@xLybY2|7f%#4S1_yx2mU zzqo(Oi2GyjCA>s1L;JmuQCoEe#JbPTzGQC9+l<1ZNDyc9K`Q|Nj65(u0*-z_$M z+_iuxR@+M?h&dQf>OR)(a=n|q*$qY!0&$}A(t`p4RQ&I#fU|>|96B zGO_UJVuTCS`>P^B7$Af&u$WgdPU3-rRiKO5O%?Ug#MBCs`>!JmEQG{QQfrBTR^=M* z2RV3hZ+wsu(ePgb(RzYe4(X@-ECgglP#L=r^S|ASfnGEX2rW|NUxHrpf7g?`!N7Qe z=~e>ytFyhr6{d)cbit3`JWjV>%08n6*Wop{?=uDDOkMuyheR_l-SToG(zO_jw1(}Q z0G`%iEc&S_izv!RO>Q!WD4HA9N5T{ul=Mk@j}kq}L>OG1VK`wZY!~Ii_gWxH5D))8 zJ8`tI4x`VU_Nil5*e9S)exuacpnpdv7!R)P#O@KJE^j^tpI2Ibw*P}1BIYBZQgct7 z(?rENS4kajbCy1)fq-zr$5RgaiEn;N@u{VUT%;rQ@Ygf)(Qmo_Y7Y5llTd#?FvCd8 z!SQQ)+U@dSk=hGL*D>4k03F+{1pkS{N}XX6^aKN?t`>b z%8aFyW#(PW#k&0abuZ&o`P3R@hlm3lSMdy4bna%|mc7l#cDwb$7M#+liroWSu=+6h zhJn=R{_ZXmv*gR`NStnpdhZT1yS-Y5D7+c>)XjOXvgj0{YRl+0>dAt&2tUh zV*oq{CX8SXCJKF^#gR@MvRG@6Pe_2VZu{M(@+%tgxfe1rvNx13ao!mBP}pE$Ruv(NcFjzbIkr|VD406Ryy{~9t@si0r9YuTt-EW2fv3t?E zgXDK5@JvSeeB3yS?P7U!esgUR4p`|K2_}Rv9!l){S5;K-jL?p+`jx-*XWwq^ZkGAn zS+9Jjppt4U)Eu<_CeChueSJNY&Vz;7{q!cJ5WVThahy?KV)jV(=nD6x+lq^cH=bln zvxhB+^E>v{n;CF|-Il^xD|SW+oY@pK62$PLe<)5>J%F%~`iC0nHY_E^uJE4g8-$V+ zFpN|^|IerEaYAoenO0Sgc8KeyTXZs|jbpfk1 zXhW>aky1O{j7`w9`qd$V5f=f-n*A`}Cq;EI!2`#En)`Mlu46h) zCemo$Iqdm|za;?e7iO;qtOce$--J@R91dhG!9J~wL5PEgw@qI!i~RNQc+#7Ab)_+YYS(=YuR^iYj}8hzZus^X6JBk-JI{v zH&le=XJDSDg6z!9rz&!>HTv51??rj)407g=&jI}fBWGx62>iEjxiI9rx^aO>Nt^oL ztV(EgG#Jjl@oS&NCs0W1pqsn{4w*u(3WujXRT8fy6+?#_09)Ob#-d{0|F^fVVF~dw|>d{S5zESr|&$O5s@PdiS+Q~ zYN!H1pw<501bft{NHqji-GPOK_07%mp=6}i7Im}>reNHS4SdN~R#sYCDP;F?yKan*W&UTIh@x7{}%w&Y9` zMH4^19Zd?>93m{9EJfS$Z35=J+;6c6Cmg1+b636}x`Eo4QVHYxlZd?}%5AGyB4`>s z=Au||ZVmnK0Ms=5(P|kCHq6ya%UY-kJeM7kGDh@%kJvQ1S6AJrS1}1#|EIS!0fayF zSiK+w)@M9n*1w*E>B?OZ2nYlX8tuTt4*m(m|6dvX-Dirjgm&jeP~O{24d4jQMjC|) z1}uk0$Jckdj9r=Fj+~rTzvz~`?*vE_0ePp7HvTlg#(D@a}C^RlOdKH;8E#cEjk_hbBrI91(doNohbLH2juYb5@Q6sp@Y* z`}0F-avNDR$}c{9pn*lz`YN4(>Uj&Zr!0!-+JgCHKyOH>O6q{u$juCM@509{iZ3hXz7Edl_t2BT`{7>o96AmRnu zS;Sq^=c@KPlbSn#A3Z%<$vegk*DNd7s?3DYM}jLKm({3npF023ujW9Nj=uP_N0g9(aL)gneATyZSHiQSol zC-aHTSbshCRW~XXuM|E<2P5=WC7xYojtWG6!`eepcu_!bJ^wDnjlT4Sf8|2@rY<%8 zxP~CyhT<5F(({tasaKj_1hHZWt6v21UD{x=@KY35I_u>~g_EgrYJ$eEZZ0Y1rd4AI z7n&{ghftaeN~t?(okF!;K&Guxl&a(fL&PC*#vFt+e<`CyXTTu{Qd*Og1JvBexGkYb zbg5})RgFJHGkd#WkaHIiV7JK__j7xl94!RW>@Qe$h;T`pi!xwU^ndw6T;71z^}Xl) zA(kb{1pwP*sP7s&KUPB18P(|?JBbXu(0v)7xWX#|juO35N(>j2>r7*r%uEJ_Skh-u z3XXv9wOuT=*44dnXvPEuV7rqQIhr6nW!`1$Cjq7#^o1&bS(=V(e%S<7#?%{kN#3G8 zSl7(<9W=XzonKz^xop6+cws+2^gCkK=?WN+r-5Kw2PJPR28KTy)e(q6CV#vpKz>Nm zc5`=U^UZkzafv$b*7@{?)3t47)St1uUUm{)pc;I`+zaytMyrGeIJ36BT%MR|Pft%_ z;qSBSMXTzgq=M`gKY!oqO@sM#)8e~GemF-P)=T+O%N?UxrqW?x#mU^cTsvOxRoc5H z`BSZ`Y8l~fX5%njAGJFv2&{`8EdA5L(d8!K2s+!UEupcq?BI<;eJJ1CG^PUF-JMfr8%*@&0|S!B#VpJFU(=O( zz~)@7jL027VR^d0vm}UXu;F>`xdp%&i(3KO7l4BPfH}eNgu(UG>tv7dxZ}k{Iv3@@ zHCqZl}6l3{%xSQG5(o%pyqEPo4UE&gN}uCFFzAGxW{zkjFA)* zVGt?$DMX@vHkUmmi>aCrx!dZ54xAxI7SyPzJD1+e^lF8ue{NMDx8eCG*rW5h@m?7k zt=gunhpuAe_$J+EL^!LzeAg)#Ym>_wd35{G8-^A z3Hyu@K{sXm=C`wh-sAMUhCZ|LI6WU_P`Dy}?e|y4;`m9iJP)7SevPhu`@W)fjnC=w z_AB8L^gr;{Ku5iBm6B;g(<19XFrq~?S=A9McKeg_L8?3M2|`8-6Q9hJiegxuQ`%zT zYdjo54)W|HGDYcqxH>+CoyR=aheICWk0Y;1C3LewC*7*~-6hff_)S1+b&R`ycQXL5 zAl-YRPcd1E*8AkTH1MOZ>)|6YZ6Yz>`;>yjiC%%rLqqs2pSIP*2(IAPnd7q*tcKaq z&oiXeIl<;xZMW_B4U>L#hBib;+lNH&%-C7@qWf3-rTHz|$Dr%=k2W9gFK)>=vqtL$ zZWsAaihizIO;KRZ9nCJ#c=;}?)maBjEq=4pyXl$A(##lo!?k}VKi)CH#y8nwN}|O# zf!((6)$U^Y_@X4k!%Oeb`$MLHXSL6Lp*~SxTPm0N{snaQ<7Ht~ycJ)4rRTvy|B%-( zJS_b7-Tge59PYg<|9pYc&ttoVuODyGC$zGD-knSj;>KReRby-`lF7uvA`{*6@;E%Y z{gw<_3b1)i?@Pw_dA*VAF6y%rVZDvdrlgIeEFuX1dOL}t88P+f*iUzoe!W$5t^4_I z+nC4JcB##;J!UKZ67S&dgH+N2A8)>TXlqc4lM{&#rpEULnftPvGPDDM zlfzBMHk4eOH})PoD>^~af(La=f{4A>5hb#9Zi&2pOy)9or=KWjyT0>FqwV&0=D!XJ z4oSrhN^?rE&`}SdODt&QDaDV*oycAGS!Uiq$|D9N$e{tH$X@{_Qenx-TEK$}Y8He> z`vYHKJ#}j6B54U?Bldtycv2!F#*J9Sm?4X1aLENaijhnKBsu9*p@e_>5zs-#SxiI( z)sy&oB!yk7{9y#G79g`Az&Ba;@VY)BJTMR@BIs~IG+CrvlZ^X4)OblriDzq@g#yJ^ zMcGeqQ3ZB`y}{P1Nt>YO!~Qg-FrOOoK*d6w>uMYGdtlElP~G{{2N^Op91R9^3cNwJ z@c*IObaZ5-^6EQXkm`w=3LPl4rmS`{Dg(C(Q4NVYh}mLHpS~gg`{5jW0GI=1-5|(^ z1^_6$j`FuNkam0y#B*`+*G);8455>CbSvvve^-+Bg>!x+b))r2~R`NjIpSi=RHgO<)Z9I3!Usu*N87A=s3lui{xLQJb#)|$j}Z!uY# zrNwvsP?XiKbKSnwdZSEC1yU%DO=-<)OKn9>jt6daidr}~3m5x@RgKuP^2~RXnVuvK!+33;wFN(IP5|$eR#~56`7ZPF-Ksz><&jx~k64Fr{c?lCJIcIp^yc)Bmum zTmD(UlcS=9^53=5%`x_a3>K9|8hW?W@pIb_>*9Un(y-+JZ=C8PFJ2^%cXa_sCwDYX zl=<)PNm>GSX-zW9GqI2!07(W9D*B(vma5zh#xwg zN8~-$>!yDw{1JLztU4e}a5-xnY6vY8Pq>$k9cUH0EKXsb-i8Kp$k0)A-ePMiqK;Qt z{O+C(j`G|`fhe*LZRquUXcQ%-HwoYhl}|!7iQ@%{u3k>`SV|}%tWe^VLvR!qAHdS# z)rDoV{E?9Hmh0WzH|Tudw3j}v`W0QdM-x8nKE0{sUiEf&6s11mgqyDr!%zN)d%tf{ zoN_zf>(75|`%kUq;Fq*SkTJe{mMOgfIYqpv7VM2?5R#OLVR7RM_vNOmYIMTM?-_yM z_|N!rsGUwG=*D$KOM1~Xn`<6DD*RAW?t&Xz&u9+YFo<%3l>CV5SOtv{WbgR6Lbt$-P`w;)@=e6g^O?^26gdajr zA))49OpVgs8?G1pO5h`h-Q`dJv2o3X9H-qf%O9|ql$0j6lqDn3y)H?r&YY~0*!`Av zGgT*njm!BHN7rIZkk?Lb2F8bvXSt&^=PBq1O!-WEQyk|~Axy~h1@&i)E z!zdZt#X8d>m8iZ1W_m@+2MfVM4r~VJo{=8rMrA+FZ!UIDhUZOfLN`5p^|{SZs--Sd zI6Oy*x^5<4oHOLSQr#4`jw6b5QD-O{!@~s2siu(dkXTwUbyKI0r#eC#qDK30bGs)Y zqH*l5ZouQ{=m-=TgGtP9K{OK+P(f*wuH}m1K@%_`o^k+#&4O$ zbv>AIee}*`>#lN3LA^H-I{ItS-D&)8L**@qYq-XuRSYENp3U_9LmwpiEQ|c|cmAUd zAS^{hIzVXd(NkM*N@d#(qH9b{%&R&O*?>S1h%WjjvBOBa@u(FM4jb3CS;MuNq)oVU z5ryFA=AzrO{Y+I(RXGe@3mDu%gj;Oq5pzN$g=&ZF%FBbIET0o(VrW3i8ZF~6YI4|4 zMQ&VOUw1GN&=*n?qxJ$94IZ;D%x!0~B{%;}!x!Gw;e-V0lxEj^OY(VeDtBd&*rl?W zheI^#O@zK~m(wL~OJ%Z}!Yv4aEY!qPwKVxcP_Be`8zwD4)A6jqsm!olu;dink*g3Z zXF$kL-!sWPM}Rj#K6M_sE(VxxLUGFsTvvTx#AT2BhqY7WtQH=`)ab7G#FQ z>Lduh*hivY79gQR2d<3@nRX5SnZ_JJqU`zi1gTA@rq8AsirWuSLY>!U%3Oqi99h5F z1jQgk65}B9bmF!_!~JNqxG=u_TupjHhmVBvW-<*9D<7_lD`-HbHF zw3UDj4ARC09M|svKUEdT*at$Gja~!}ZLTe2(X=q$&8s@%vFLrGA;$53-Djr>bG_j% zCaVBeOQ>PJLCX_e2`Mh7?2MnlSaF5CTg+hTp8}# zIi zl6)=s4kj(3X+FE2a2zde9~YY-uXKH!9<=wS*$g-KRZK67U$kk6Dsa)!`%tcgBYUhn zjTF=1oG!DMvyi#>T8xmEPwWVK*}{4UFvlUFeOaJV$4LG32`W|-h9B+HJ&-N{bk0vn zza?9uiI_;r%xrTw^aI*Nw!I-CwOImhXoA5o&=;dOW%-&WwjHB_+bP4b(b9}XYEnt^ zU6C1pd{tC~fpZ~t3nzTlt5Qa#~v#-D4SdjW`2 ze99j@Y%dE6MePgP&hNVC5BwDE5?a7fZ;UvBo@>fn5=%EnBy6)NN)9g@y*4@dqPO{u^We;EL$OGgC3)G+kmK$J zYnt5SXp-TNj(#U6Cm_XO1GtyvOCe;k=`P2xqaUaQ_tzEv>AI6VsV_7S!-O>vbHyWQwX6x)Gl7*PZbO&lf3rEHM^gGIisN$oPEPhPnL zHp}%i-t!BWBeD7U`CdiVneyk0t#4hx%hmP#Aoz9#@(1KGGY+*QE-zWOTwg$=@xOok z?gwrHo<_=sjh8aRjf z(h|}o-5{~)R#NHiknRTQmM-b;4(ZN2@tpVn-1u<6@L+D%T64`g#;=APKb7kByNBh2 zS2{@#>rul^qZX}*FE+l|DClQSxU8%T!5Q_kj!4!`z6*)BZ=&-D#F2IQ7Jr32QaSbS zc!&HW*SF(Q(Xj~;F(W@d`kak3tdMysraH?OoY|q%teQ{Ll5$6IPa-* zZ=|s3-$O31OP6kQpw78+=3p(;!*o4d6LI>bV}R5_3Nz1_fs74(BMjlmPlh?s1~h`F zBdh47o}QPz{vSpIb$(_gzPD}>IIP|>iMTFEO}?L8tfk*({zXu^=w`r~AN8xbRvIVS zFEBktHehBzg0BlLp4ZkefHS0oxaIE8YH6kVh4x+~Of)W!uXO~vZ;8AnBQ`D^EJ0J8 z?e}Zlgh2b=d~K)otCN!xK(;E=$MhQBobjyang-Ex?SBqa^5F}RSR`&K7vdm+k#982` z)?*2ah)8nL26ZYtT%T>BTxR;6cE|);_Ayg>hX?C9cfwSYDyir zzXd{p9@NfG$8{&$Ved&Ce2)9v(RZP#o4Hne`p-p}zzr=kv#eT60|*Uv(@V^osf=S( zS2#ZQIAC>tIxLtW+xs(5GaW+smF$rVJc}TFG;vbdYb37!jP7~rz|e%h&m;igc8(60 z*!%YYIMfGHc2Uk=Fn_DV-7^?U*>?W1DE?Gy86m(1H;3^ZGBw# zr_0yOHPEt12nTZ43ab@$)NP8A8`vHs4$H&Z6CkAYO{YoU99uV_w_QM&Gp%cIoz|}T ztT-ej;_^8U$WL3x8I8@hs5|`6#OZ$ElTDajfRn?`|PJV-*xcv^F9hp&4Jfl#j_Gu z3X;ZBy$*j|*_ecceEmvJ>&0XmR3d&?_7vB0ppX#Np8-I`=<4-XUr9x@XkQY8zIY)J z1IDSLU#&59e^%UUc*4HXdLr0Ce5e6Z!+!tlz6aJJ?0!Ku<#6aKI8%Bu9q!7yKWhqy zMpX+BWm~!#B|&v(-mp5ietE_jDEJf0jkC>SCxJB}D_@N3bKkS9m)&eCvT$>@|8QlZ2eu z`{mwzS;k?=GI`G*HIE5gdq%r&l2M|z(ad**q~Fjs%Mg8LYdv$6b=tsg)bd<%xJ z2i;y?5hRv&9}uV3<>X5!4l#A)KODpLiw z7B-7`rEzFJUoKK(VDX@h@1;pt=S?WP&F zsE?i%MuN|-@762*_g(<=&cFZh(>KIdUzDqDKa#8Uso^mO$pnk8Z*JcBs+J69Q2)-& z)&)+y;|u(S7eK(a3(u|k^fZ0x;Yt!>%=?bfk&N)&QJ$yQe>a+$FR~Z-imvv*%UgIy z=z$^Zpm6TlYN4VS+MoSp86OX}_obpNVq`PcFY_haDB{z&{uMvJBFtnI5;UpjZKK$} zL6|~4rpT}}8e`8F%w%&3$u}dqmu@z~tUd2v=})j6hIrIA^zC?KASp)%#Rz@FC-M#V z`+GahnGE#EK3#l*7tXuz4&!uh^^4h+U$eSVrk?=EFwdGl2%scb`K>L@VG`+eaCNmHE>AZd0+v%$QOcHX~_uAo{i zb`UTd8pYul6&S+R5clL25mb^tyZ2gCMdeV4V~SKZp&*qbQ2i1}s)u*fk_cs+HNTmY z>eZujxa6^wDt2#HgqKd}`A3><4@UaU;H9lT?E}A2ij*fEN5^RA2~{;jhc#@nO`#*< z3Pgy45NVPdGjMRxhf9&>;(Kc0U;cQFs{&L5X~YW_pcca$MFMewU8uX|Ku>N*aGhls zqAy!_4fHJ6*MOSF4H8Y453ed)G2XpU;{lG&Pv>h>pkqnCnXUq0Qib-^-z@xHW2$y`<7D0{s`fMlkmY*X~_ zDtubi^4YB%l?qSa9l(JXK(PRqNdw4ch&}P2x^K{$gZ6PdGi=dm$s=n^7U0k6@lH;u zCaEs)fe{}LBjsZnq?%f(?@omZT7ByD*ptvBig!dQdc|l?!*qs@R6r*KxgF-?)z{tA*R3i8-7hu8z zq_FfLw&T11-dm;H39y0G#S`+GU!gb!eXw!vsC*G;hoZG7>tVoGP1oqj#KSYZp^%3Y zyr57E;=`<)uXEJL@@`i2>WsHWOFgg7){HP3F86mAa&v=6e#;U>`(ABlmOk9#k^G1;q_dwo*?qV6a14FWVDXz*gOL?qPEgnLdD-dT`=!nXnR4T7AX{qbMUX-nf6 zX-q_0uY=}^)a$hV&_F{}LLGOt|JrUbRp2{P=)lVts3I~Q*^_t3-&IQ#nRj;F{~Rzd zf36)W{n{dD46!pxdvE!gP$5*CGw@C`>g0cW+U}Rc>F70k-YHN{flHSYypOj3`ee1P zn(K{fSENxXjLDLrRd zeBBao{`tA7U_M!v`x{{1mseA>1&cu_KS3Djf4kEkRXdWhRodCpe_GPg!rlujLOKuz zPgcY>qx5NU$*Vk_4Z%=OzzqauOHUQ)m^kqOy%X5hG05W(+lO9bKn8`v3F&3~c*u&P zA>PKK7+-noqDwpm-+5ZK_j$!s?hyZ~<3<|d;36xO>ebTvD5mo6{^n1E!)ZD)Ss*Qw zZ|Klm<%-40@|<<90`#6-gdk^p#aSw~cO50ph8Zz(?H)8j|Nf{-2;<)6&@2Tr5I^m7 zxcG{M1kjmYk#uqW03JsFespxSysYcf_G84fNTsl2<;y^PJbt;_KC>DUE%*%>7#K3t z7^S0U4B^SiC~grSorUK~11t+ZNqw#WD<^dVfX#pcQO;Ka(*!oaG%;Z-Wgo?x7d4BuM_or^)2yOrFB};YX%T6hRr&+90 zJLvx$2pwl@4t34V#q{sYa>*XcO;SB?t`ndMqvqw6`T~$-6z}m5qce1LszWJ60&O^QU9>S&EE2HGX=5h%}%a(D8yyCXC!NHr+ss_Ys^N3c=5Q1 z{I&b^@?p!Zf;D@Xhx1)TX7_=7DQ?6x>`G6si}`%53OZAGjn~c zZb)dYVgM*$p=U>L)U+F&dB8-W-R#q{3Cmo$WbTMKWzrZa&7WpvAOyJSHdHtvV}7q} zLadiXyAj`Mope1pNDx3(t+NDqe+KF@%k0acYN>+#xqqS|yKf!V|A}slOopYRV|Y9Z zjH25JU9jYoo*zTE#a^0Jj83z=yE-=LUe#5P?yp(n-d4zYUFsN`L7p9%7E}+(dMQr6 z64p{Qq9)bZLWAk7O3%fBmGC7Qf-}B6)bJv7&5I&fZCATc%Pnw$-(`-es?C0Pe5a5_ zunD@c`}lB4n@nRgv0`7a0d*PH^H{c2s`brHkjk#w00lY4u*%(A+mR2Zrg=S9PP{2I z%*vqKo$!krP7_=*IBEs@V6BGO7e?4*dH``0Jne=roG`a{V&9v{jngg_1nqtn+kh5W zxG{BcQIU~KO!b$4<}Qb=1p+SXR3Ag(;NZ4O;uASP^IGZ_0;FSiu=>a2CnoZk<4@Pi z17&l69{B~I00K_^k33e_TB6%OM-H%Xh6O++#jQb!SvS9Qf3*7@gPc2XWXIm<{2yBS zb!%S+pO&GrjYR|RL=PmSPT_ptp!C zy>gxJz+$Vc=-&yWh%?bB#&fh@6ia%gAIikB?3-nIlUDOef)z27&xOry_b4x(WUA;> zrO}=r)1ec3-@WDe!CU4p4!vX&1l9d$de}jdd}u#-xF3BpBlnQKG@JJy)KB9+^WAmx zg^}EPYFE^e22~GemRqb>g*iQ5F9de7Je(~Ii`Ky_5dYmI(%nK5B82mjnGwm5(`UoL zWPq~pA}THB=JWT|%--2i6!#nlKY;bb=)G^5f?K?z0x%l+#T0b4&`5#xa`{uM*5+BP zjS{3gf|wV!$TgO7Wd(Zt8J9dEaJY26#T_5jRc-O)?J7<*gGz_O-f93Xv*i0z3NAWY z&%%%mFC=R?_NC(9xV13FwxePu*#;Iyp8OZF%&~`KdKjl3F2Y6bs978V!FC@s>;{U^ z89VhU81H#Jci-@LPsvLhOEYzPiB;Kur6EzqiSd72Xqz#5jo&!@1{xblm7r2eelS!Q zC@*PK(*9jzrjS7xaiM=XkuFB+&%rhAk#)_J+S$*~k4e4ay;`XxW&Ib>Sa|?EAtQr4 z>WVtkoYWp!O?Dp4&iWY~%1ZbBF2trYVlTL3!L9{TeOQis_`o4N`HI@9yqeipMBFL9 zWeicsIGCOw>o7h`2LtD9z(ojPMdMV-X~(?@8H%#gwiwG9bVAlXyUW5=!2%<#SCSNm zlX-wvgk|A2rrQ{G)V#W1kl?nLCM{W@|B}7sRe~06;|(W%oa zoRdIxHU*PdBNb5D1+U)O$02ZwO}v?Ryz8-wYG9CWRy*$*&kvaq7XLUhM3GCHYk0pX zUSj-NgJ;3ZbO4n6rMc39O{(oLp|kpee2-SYF<&6JFukOxXmiW9oiR#Q{7@eKCf8%w ziojJ93Wbjr&of^E&Y%A(2CfuAuSKgG1UNV$jlv1f<4!!mhV$(a7^orZOpcca~qurrz4b`>Dk&z?OC z2ZIhE`Om_oT8q>xeT#?^sO%7(J3%Pw#`-$qo)xI){5}i|-V1|Se`g@l%Md5peFPc` z;l#OpBR$Btg$mjV7Z(=*BpKqQ^Zr9pR{z4iwSdyS6=`7&6exdt^r!Yw9?qpCHpaDe zm-iK`#Ux`|RST$UQ@Tw-G7%SLeH+MpACe$!KJFsQFxVqonl?WQCSdb)LmX_{{^y^^ zGs*WtN-MU76TE+Xrc4t(FV8Q@?w2XTKoGuTTRYdX=5bQ#iA#2(WAd}8-nca%aBhu+^f&@$!@ zf)me6fAv?Kwkusr_#vYlaV6pJ%t6oQ-+RR7|^7Lnqme9tJ#|c<=W6Rl3@S zo3}gLjw;hwIs76&?PSV#Jd-AJ1_@qH&Y{=o0oPv>1husmTL&!bJQy(OC3RYtrdP|l zIg})rC?Ws$xx#rGA^2~+?TJ^CjBy#uG>SoCE+8NPN@6@q8A-{Jy>sAaCHDhm{&P6e zmw0%&Bc_I|2~K*z+zdAlmmjF45WFQpEt7aQD$Rm}b8mv52owoKD0fu_JUl#L1}|{6 zl3xEX3>DYa{c&r%unK&%D1xP0%mKx~2)fkhQdzdr@|Hy^`t6B=*ML;hS4u$+ju=Xe z0wQy9;Eq&}iQk-S>BJUPYQTE%Qa$3AZ>&$N<@SV{vNo~vz$0{S0SDQv8Prl`llhe< zj}0qyf9J~qWIyI#2;&Kcc|xYF(^X~FX1f5FTH|c!3Hl1vmg9I35xvLnd2_atKIBeo zU~=W9wN&nS4}^x=n(P)pE7fqB>X)1oA!9NS|HkQ|2s~=ES8pBLLbN#tHGD%z1Rpvs z@w$HfdV8_oAD+2B|Cxk&2Z$V<{-`zBth9#-YC^&2`GCD8Nu?E2{w8Qo%#u_@`6I|= z?BZv)#LvR1deJo((-bPE;d@S*MQSIJC;Engw5RI$a{_u#1e z?0#-G&7jy979--iVaccZk9fs_L9^<}z;_QoNH;N4q#_|ByICC9$ZzJ``hX!|EA}TF zF+UZLGPd$#TJh-33^baS7i5_HSWKskT*s}&9IzH&x0hz8Q9i3c@ z&Jl*iHITa1f z7T61bZS0j-$L>U-WO?L|GN;2xz~M!fC{Y=KU`{uX{13VVv*}l0Ks&3bsHhzO`FJ#) z;%wDQni4%nH%w>%%0KILky@7n7~ zB|@DNPHc-{GeniZG(S)M-70m8ZrMpmE&3th@9tuup7ujy?yWJ~U^l>XeCL4Q{VqHz zBpV+wG=r=sU&4;vS03}wIJPj|@7sZlI>49~?Qgu|!A;`6~ z^7?hKN6)g50AY0s?w)tYy40Ia?oTSxAFa@OUqq?z+QPvHP-Omi2WpWlLZ6S3al>!{ zlA@!JZ%a1TY_xfxRSt1CGIMYxGuS5fu#xU3QMj8Xynn!@5{0?{;}z5Dc4TqpeN69)1_7s({N4M$)PoXAQ|W}y(9MVnc3#8XuhnK|W-6bT0WRoNtbu$jk|XPl-tvF+ z3MxMJFD?-4D0OYX2rkcL~?IR4ZeVOJ&hyOIm1xI4e=P4@y(3c@ig{FAj zA-zEE6ckktHYtLPDlr;grG7Pzvj$9g$1ip89?6x|l0h_wu3rK7K5pXSUXSfvuqs*( z@2%0ieNnP|4T!K*yScvpR0oBNeK)Pe%&HPvrl4ocCivEK0iPkj=(;UQ3xx|9YhO|X zfc&38gJqV!P1Ltt_lfHtC0G72UD09cYlGflNTUp1zRWbkW90W10_*Z$ zD7&c%Tq7#+|>DK3s?s^8;Xm#|q2?&?(gFHz{^}&u}A{?^{vh z;lg|Qa>bta6_4dbYJ2Fe_b4_1LmBdawSzdg+OOPB!`h?-s_dlHQ$iAaLa= zU^bdnVSFx}7hAv~!=qpByq@=F%;fD-zQ-P_h3BBtN3jTdo6FEr8LyMxE$tl+U0+}5 zyIRv(!^&5)$GMT!G+CSLGp1d1e&M4kh^(xkUq{C+rg>S__)TUXQxo1WH0l|GtMrYd zBXVIK>y;$a=VQl7s0`JSU>!l~%U&vT+z3cJKAsxlf7Nh-BHxG2D44E1sPq@k3gQ>y0hSG1`ZWNIAmUMfd=VZ#_f4 zM5bs$d}pvxBpwizOfH4|#{DEXCh4+Cpw5~*W!1)B$n42Wcf{v+5ak1e?eI`?u=LL6p8G5~2-ltTp`@9ZQ%6e>aNzqIH* znD+(FHu$6;UmA4jkdTl-h*Pdi(gk>3Kh@jQF)^XCf}Z~B;jI;rJj}PD5ex_@XnhF6?1?1E{Q+mPly%A-d@py=)8`V zaNAI;Kbm}0QVdK~R4LK;-UDVEy4&IYs6)SWG%EJTaQX#!?R86&IZdd$3-3Eb3tScY z&{mcWG1ja5R#z!8L=aHXjtXn6&gGI9-t!tc7m^TC<9xRZI+LvzYbnp>oQY2SF4b9= z3$IIy>zxcG?}HO+FezIV?@Cu6wd>}uZ;8&PL$p?rlGPuY_w%xxMJe|fOB5 zWS;VSs5S0SOPaD$yS2OBre^JfuXgA08k!rU`Pdf0L!PcDA&uivanza5w$%qi7?o@p zQA*qB_y0v%i~l=ZeRsdia;eB_XlPhjSpk{>3llR~Yz^qxK=dwITz|jI*@*6I@Jsl> zmta=s7pW&(2Y6_0yKKX6v52+Pn;n5KLY|eJ+#4l`&8QE%TBBHro<=V91Jw0m{~E-d zb$oomH?EuNPvqxK0_+-r2RZ+LP%C*$CK3_~iZ7urP7QE|AkzJhfT~WTWo1>6nAA<~ zw>`cy^U*%c0PUFl^=fC)co6xl%dWzSptxKzZ^fbh-A*MQ^Nd#2*a5fKP1eGS4Qp_R zUO?4M+xY=rQX+JxW@(C;+co32?)Ee#`|)bsX}-%Ngva)-rw#BP1m|}stc;)f7>SK5 z)mJ@v{WIT-WgegPW&&Y5KlJUv(Qz$X?&`i{!;B;tPy@_26i=+W1cLQ26=8VfrSeM!{t2Re#2oag&mFf%F-N!92#RkHR2Zu4`2^=rK zEjgKL9zafm zdxmS16WwE==+P9Bfe2{$Wkww=lB$@2_#thQmxl{pw{WHp%b{05eN%iek?S<#)t@;>6Vt2rqQ$88`Q0Ui z!s4z%%}0ahU>o2kb*7i!15HRk)eB+5{I139S@E&amE0FGR3Gf*s$I(Ic}Gh-Neun~ zJ3H9>z+<|9dmZlzz^RL{kKR z5n;nH%%Nu`M3apADxntml~g2_n*hl>t23B`?Y{2>N5`}AY-~7|>EWOsr;d73n69VU z@Hdu1<&i$sH+2C+h#+m)w7&>bC=0~aX_N1wiJy+aM@R<-QC01*Ji6ka5WB(;fMs^KrRLw|hnAfji8V3ZM z1KZEbzwp6`?xUCJ;K&{cS$o9mPUB>5(4(+sO1mIr=$1P6DL(uDhtKW8l4Fp>{3Pa= zs)lr)?S6nq#w4t5{hWzr!4Oq5PYcvX&>(M z<+9r&**CBi+&e;Wf3`+4FQ1mvC0AJLN}ag+O{Z?J<<_^bqgY18$4jxw?XNzbCu&AOj8E0Mv@d>p>k=le_S%U8?a z-aLKpfazYf0>fOSgkafM_hM;|V%+;*r9pkSM7BGPg|B>yQWMG16I+(?9)lZ8@l{Tv zP`bP`i!|oMvRC)hIde8h_DNUDUmklxH2Kok={J-T1lms8O z6+`_Lg~EmSeTy&wBPBdEL*~oK!pPu<;s2;hg`qHuwDN-zL%;6@8TL83%57-US$vCD ziOsQ3C$7i^mDwnncb4?-4s{Xf|2A&K3C|6VjXb3SnlyLl)zM13t67%94mjL9Iy(H$ zF@Lmz8M`L)%VwHi&HGF1AY~N!sODV=@_+Hqzq*4%+9tYyvz(pjW*H$X72PkTVoY{* z*cke=XhNe|;=wVo!acM^usi7@#`5Gae&5s*(Pxh@hl-_RJ#IR=1goqFCg^!bh3Qlt z%METxT6*aivdDb%LF*&dD-#EYOHsg*VZyjaRzj|ixMcIK-KwM&NwnX$jWA1Tgc zWdm%$Ia)xbt%pux4ptE#G6q&7EV&dYtsXXZ2 zN84CygT{6$vSg5$s7T*{04LK0OAxcdiLi7M8JC30F-@1B3x87Y!oyR>(I^C?Mops= zU}SH93e22NJEN$5YN&NzV$8+Eh!=9y*MKlhDoZ~vcid$esI9~6sZ=ZyhI1ygkBA~9 z9@wfKMO&>&fWBcgs+lfyO^X??dtR!Wp}~#V6B2^E@IoQMMjdc2>yQT9ocjjqsNVUV zqz*eAWs+HsjMbR{3}GDcjs;YAp$$dKOfCA5uyA3-2~p}y;#f#0nA};hDNIK{fTMuZ zsbexdDwcGt;#vyfFOEgvK>JZ?IVHExolp?8OYOjlfjJ8=bkqON+YTRK(NPHrPfaDj znP&uF*NT@4glqq_oGEbda@=>&G4v4R`3(L^6Tt2a zNxjq}1E>DxBaEBFZ2f@=?LVEa=Lavf1;@pDwe(}A^&X%*0Z$dJ-^&7w(66MqBs)i< zId8~Q+D$|KF{H@<_kt)F43t9&IW6N;Q>|*|6cwI0m3{)$ZjsCpLu2Eogh0aBaDSCq;Khm#t{6emwn_0cce^MVYj zdC`Bji>G(o{Of2El-*ptFjgk1CdkA6X1+T|AYJ9U$`t#@4;e#tNTTgK#7gnH1{%bM znDDqbaZEnPeYN9W{ck{+kP;nlh-vtqNN)phbFSM*i;Euh^f=8BhPV9`Tl;7psfq{I zQQHz|1Uvp&O)lCsjPB18{Yd$JBv{Bm?akEz)fb>O2CdUl$=yY+j@Qj`oK7zG=O_={# zY7tcTQzZZzMH!vTNi>x7lk1zCi|r8u5ZJ!9pzXZk2RsyJ)f#%IOkltz7}fx^fjE=@ z+mX^AGk}+wk+KPeMBuHUU|KNg8k7#?`22cAoo)7L`0(nr&GNfMoKLkj=^wutgLzmx z)V4rvVqicHW?i*>s%(;sqDDvCZaWkm1Gl%<<{?sv{uP_m71(!%yLB9=1}o)}rlrMk zTBWh&c_l7^`QRjl`4(s=BUDU55Y(AYp%+`ihYk`5ig?9VtKM|qrR@*eh!32gXxH`9 zX>ROnG@mH=z%~J}l#ej=B4{oP(#OZ)2eXA-jF$(3&-=Hxb8ObL{C)VQs3WhJRcCa7!o3Z}(^)zvmTe(-Kv6zB;7t;hbwzBe18zrwL0 z1t%v?$5*hgU945NgXxw>8`=n z{N5X^?G^mF&mcluvEifznUAR%Xv>SX;?x*pKgtQBq`l0K8J6R1dYT?^JtY6vp@4|$ z2=DVX*}zcJpO->9L=2`sur_mxUU&mAS+YvdF7WylAOZpA(?;ZHFZ$q!;6Lvk9$5Cz zXP|_NInhkm?y#%|u`NfgvJqvW81)SmY*!z(mM2RAyqYbW39Ep~f|wsjHboHNW*!ZV zUW{G?MvgOaE(LC{Jle4I9XstXCq9}>NGXs% z=x@0=!>Z9X%f3?nr~mnt;QWP+`{z3mxr48Pdil_LlGnC}(y5;0sBf~2S)I6D>igcE zUQT4eSH#Rq^O-#q+uPoqs;}GPDEI_!Hmrk6hA4!kn}mmRLkS5e5@(}fc-A)>lc3Lc zC4UUeJ#wM<&C3D-9smm|_n95+?a#JY(4R89J?-3R-OW0l(^}?U$5m>=IPfmq4KG~- zA7RNaDk0a2Tsfhe)&yQ>VigLY4tx@@0{I?>aE!23c>&O89&j{kHUH`sr>f5mDi6v$ za~%UV{fXWrG+*;(bIU$D4_2r(%9`&o_ZwIUA*@U#2D20jSxz#BAw5atX3g;*I`?o} z8*Wd}w8uLo%)S>wVtHgBBKYnILqE_@mmn0~32i1Q4foq7exM&Ik0Hw;Vb9*8#3_EF z-xp~nSjnghu2Nb?87^`i!V!E&dA#rO>(b<-FH?77@a!P^=hOWey` zpZU>K5)AH3^|<8K_eg$KPKTbiX!}*OmdOpu5oHQcC~-6EWwEMu@_EnIaTEzbQc6T( zWa?s}>fxS?s5P_uoyzB(+?Ix}dfSND5JoHX1OtZ&%vjPBgUpZuXO0MXQ* zZsFkL)A>-RyQiEZ)YKD;F2-|Naw~MGzkiWIUEGkhF&l}c*t3^0hcKm*fHEZcfV5Vn z*((}wl%+>kJZ-rC*TbOopNBzNRt$}c<5Pn@G5e3)8OvFx*9RI<4I>cLm^)FxtRo{U zDchdo;wx1l`HK1Fo_X(8HIn?#sXR?HoZ|7mSVGKZ8HYsNSu`dMbkC#a0RK>Y-u33i zd)J@etjui)*-l19OT73pqq7NVC|X<;hxpNwpi%{>GCa|@7gw@+>JQ=3Qnnx(_2O_z zuX08$6hir!xc)O4tMA*qk)trdVGbm>AyeJb(h}&ZQayH$f%u{X?9*9Ulw$M*Z=G%4 zXNrNmuzePW7G+xO4iaZw(0igGNV_Hz(58Weba?1f$S7A{5JZ@c z0rwT0-zUwJ^&oT`m-kBcUDNGZhS|+oKHFVkM#1snf;B#bkA6qgg7yro;ylU-;dww=%1{8nVuSL) zi~s!Q$2Xpr^Ze)5Q{7Wjs*=4c&61Qa_w4H@|IER|!fG|y4DPIxz|;R#`!2#gfu32F z_z9D!5A<%l>aAOK<6%|b)@W+`hK2y+wl`Cq;)|;R+tch-{FqR&j>o$DiUu=l_nk)0 zY31Bt5&HYIp9L>2xirt`tN^3YC-``B)$9=v<|Bc@mDq5j%f^J)vnkDu2b*731oN+6 zkmH%Vg|D^6y1K7tQ-h$uwHFYN1h}8f`#6;yypU_Mn_oIB5RzXL3mZ;ep}|Bp(4P+u zQ5FKjVTGDG3+X6)%dSf*=kBvczg^prAfHqc&~d=>5FfgyZ+|X(jfu!%0p6~45BcBJ ztSY@S5o_w~UK;k}#lhSk(L!V1O}}S{PHX=w*#5)h?IVc)SI>2>HbO`%@fb9 zo93@2=5A-hR8S$>db8jNm81dq6@Dj9Adi8$cSAwi&;Hq`jJu+vC(4MLZWCBvbHKg7 za{FqOo*U}`<7rdDxKlo5&lxxTf4~A(Ge9tj$EbERp{mmsDm;k_K=`4-K_H7N1{?C< zdL{xq# zTh1t`(aC^7c);8F#UvsmL9H^5E)c7K)cfUp!dNGf(@KW*w)Z?ndmvH0B1^h@LKsZD zo{?o*Zh8;gk|ek+#hYk!*}FN9MhVyhYPu)HC>sMfr|@Xq?u+p})o+GoJ|&$6V+zx* zhy}ux!D(%Z<}&}zm?TJkCM)?qiu9wTZOm{0qDi{X%=zPZ2`9pbUQ9>-0K)wI{yBw@4{0F4d057`uML$5q^k9 z68_mUp=BVK&Zvfi2kKcUhx`Hut#ysw4<}FER1fiU-6{QeQcBz*557#a|7b-lp z6-3mN%4oN)Mc03JI3li=@A4!UHmea-LEAqd!wm{zk`!Lw zTul>gzP3@MTCo3hI43056v2>nU~A(zW69aa&y&cdu-T=?<4$l`4(%zMUrA2~Yhhnk zccxl|uSwgBX=VG%GngT*+Q{f=!%QQ96Lb=$iwHY1!9el@hIdE`!X^IPK+7`IN7J^~^@f$7(!4 zs&Ft;Y~biP^fSc`BywqR1}PPCv^+FF0F9;L0iJUVSglN2NB$bcN2R$yNVCuk6L5Vv zaS%uH_BH40*UM=l-PXJ+XznkS3ny3xJWKNOwma3w07LDzYU(YV?z~Z~O5zOYP=v0{ z%}wfTJkZve4ZmfI>reB%=>=Of+RofsUkt;HA8sC5mC!gtY2(TA?`FV>_4oF^Ktkg6 z*r#m7sgi2~luxMVax)l5h_lg-9ziyG)pFF1+~p3#$XET@6O?Xb2E+nDh<)5Nmrl%g ze#jlqTAgOH6dyady1M#7RSF5`nogG+-I;Ojmm?aHRURo7j!hJ7ypZ0dh3 zYTYqIA6v~lRWcuZ0@?~u>`0V*ok~UaIFAk-4o2=k0_99y@|HvIZFq77I*0RPV-|ew zK+QM>4cj6ngyAb&-KQ}8ywlm0`!bHSf~~zhqPgN{^LwuZozXli6K_zThB27!)8^IL z-NxF4ZV+7I<}KbM$i=^2Nq;F4xFOzcp8%#y#<$)3`1oXdYH&A{Km`8bNQ$2$biat5 zF}(Qp4Kb+TtUan!uF;*d<; z7Wr12Hb)s1;$1(T)!1GcPA! z%_aRjWY4p{T>R7!>S{>;tLlZw2^%JAUl)nT#ljj^w*af>-T2o*FU^O^A*^GuSiaPC z_nXek)ReQ(WO(w1TK@uZMZiyUIyKbf*kCYM!rxLdV{_sD z)*apVhD?LYy3D5SZLvnLyt~SmmXgm&lK~Gi77j|S&%M5LG*rGK<(=K zYg2$7&aQZ_papOJT5fg_mkt{mgpcF!H*vxO=2(rx`+HXsmK*2W*kV7VwD{>;=2sHo z&=Uf=vle^kFoLw|pBXpjBN22Ph2D|-I5myy*?jBSxLH$!!Dcxz`Un!M4hb&|ANoo8(snVBe#UF>e;r=5;ZtZP0mt>ob?T1>}} ziv$=ktL8S3*r1RwX_dR(E}`=MJa}WeTrVd$bJkxlCOR2x#hTonEY&S z8SW8}ynGeMn}BB$%&4-pL-7KnR5q4Y?^inMJbd2s>}g5dU8bz}^dL;-bJFiv+7b~K zK_0fcg?4t&h2l7$l;W5h)L-mi8DH=Fxb)X(ReZlA{{GqiU_ISl{r9>_8rk7o%_HUI zD;}~)NkO->cj<*!Yu5bwgt?!1%v9m~?ujV7Y2Glhx$6e||D?oI!!Lg9jmk~`a1!IY zFjsSTv6Uaqc)Di5oA%aro0n+C<#O^s+CC7wyeV@C@UVu;&f0Nz%_Stfln%CbsYJ^A z_i|3KF|O;NAU#BsP?X)XTBvG%c&71r?B}ccUsa}ajE%P8qY7D%dufas+J}QYYlQFx z==%cp=c7iClb@3u_Y+-g;(iru-T8Fv1sw1=PwP5~ah@VuKs?>Oa)dM7Rua)LlG_PG6!;OWPS$(Z`Z+=5G>8mC%$A-83WYKv|p44f1~%(@Gt6*iQFUN0T>!EKkWg_eCMI^U*rYjqq!Y)Y!0*`ZSaB>fshO?m%V2z&EL zBNd!WvA<89TZ925)j~gK&ECMee3{{IijnB5CzT*5pWgmdmwM`Vl#N8zXeY5@v_bI% zt)(Yg4wT&*^Yb~}U_IV0iF9bzn1U!E3xR_N?nMw`w9r@{CWg9n=39icR>Ub{mFkmD z_S~WQK_hJo&z<`)lcibvO6nFPL{6<%oszl|FM?HA9y3G-!-N(?GsLj9dtNJZLMj12 zVL4ifrjd<=Onj4n=%oKH;ogTVdwm-=hej_6@NqLo-)N(nZ{|&+5w(yl@P7jOD%eymQ zJ)2e?W)0TII=pN`j`4lNJXbQI2j(V*d)E0|wLe4S&k^qI+xwNU@;~j;(^t}UGuF`T zSdT6m;#Eq{aAe37C>f>N{fGGZ9-yVjUam)ljj_WWZ)ZNb-TtYW*yRI>M!A&^!s;tNhoBau|iiVO}8_V)IEBKq(B^&J+`@?Z4C*qv&ZehLPN{>q~=j`)AeT3ViQ!#dy45A zEO)wnc^qK&ry1YF#lxEdITgSb(-VMqF0fft#Ha=j2iJzVh{2t(Ff+jjVqzs?HbKAv zL{;03*79kHRGGr5uA=P1mYbWe#sU77FF>r?+uOlRJMgkfw3=U&6cBo&%*ozthBLpW z5}^gQU~n7c>+t=*mWDBQB2XGCym61Sk9i46U_IwF%)yqTOY`(;u){=ND)n}(NX8eQ z-tP=!sLG~E=CYu;dHbRHpG)((o`BJ*HKhHWV|YpV+XwU)qSEr;0^gS;TR#k$PG2TW z$4g8HjbpTBOOA$hCb<)xdHSxu6|8Sa+v&SzR1{EL{6~b?uS%|=xx<4}pJ0!0iLHIf zos5DXIm0jop_0HuHwokWVAw=ba_D}BFslewe!!V#L;yRpz7oz(aa5`)$GQ|R+P-DP z+;aEV8sTYq_?WR6=!KS2(hsqb^Pl%Ax{g;-?yLdP&9qZHug8KjRS%h^{8-k*i>YGn zJa)9GEoNg*mL4^+*6Dlr?Urrm*XE$xRdj0&omCKi^JQrk+0z)fz4_1WE(;xtks9|0G_!pSKHZI0!1;gfUDcu2R4(*h1{wh89I1+p9y?vg1Kq> zN|X4?i`-YeQ?VyRPJwFQYS&DLQe`nJ%!xriIsrVWK=u#xXdtJ4(F({@fddRg&8bvv z{&1WB?CJNHFgtmTkAjTXtlIXaz1^y&&#K1jd_wif7P)_M&~OOEC-T!O&#|?^fS^+C zCW#eq@8_)k5^xMWE+^~2&c?3S4y%9yI)T<^FpArBvWTD;WWxGP=)?Sq?pU$WYI1cL z$q+5GwzOn~5btZMqyW`y7G>{z^`0Qi4A`8XOn7BxebIEnDl>uiJ*hPr5CQ@MkV^!# zOP!t;Tf=Dqn#53D)`X(FVtLkEXq8-zw{EmySUKy1=(K4wnabWei=N8I3HGW53(}{ zBbkpM2mt@b{>mHxBza!+TO-xQlwUPngQx0l-Ma;1SIMK|mXOtQF+Gv(j6-IC04?1h z&ToDwG}JJDdHi!vM)6$ z>ANhPXQ9jwy{I4IL6%HW#7}`_Roq-AetwUoMHnDzH=0iJ$^kesJa;S>7a+NdhIOSf zUcXa-i`W~GF)?h;O?nc}~zKg6~14sCp_K$$^8WD_XSbzc-R_Sb8b0DW{v(m!TkC36IB0nH0k{__@3f}aC8 z7P(-i1`0m+f!T^4e>zQ&k%N9dddT+_2jAW}T%J!b-{^LNz@sv{tpkQ=f5F@R$`u!J zARfLH-)x4t^hx!PR4!Ec6H+*2a0-7}U|#JyIZwcG0WQDN+q$bl`dZGa7dTo&4g=Gx zPNY2*h+2=+r~z#)4X)}%|NU)fYox&y7B+Ke8MdOVTBHCsW}vkp8PALo>Hes1y(^ej zq!s98__6E64R};(KI>fxu)farziygNRAF>AfTleu)?TSbBMl(6o?6+TxUR2c2d33US@k((&;JgC* zd#Z`)oaDQ^JAgFaUR}LIJF@C~c2plu5h3xEIY67*pabq+b;_0@?i25`fTG1#2>zhe zY0X;TULrqwakd&iwAJ#b;Kcy)(Y;nrJ%UXUtS9pDaC{@sFJ{sVy{K~vN>Bh@Wu2yl z=Qjw!=Q$)5q7_fxQ z5I{o@Z+L*3D+$SRB%`zT;S*_h7w=QxRV{l%E?uttD;|)j0Mk;$p725J6KR&y{T*Lc z<}4>6x6O~ie7{+5j%y&=lVLa4Efx5aB*ah9`$8Vw>>HU3&)VbED=E(J`>;HCvVXoN zp3(_AFTA;L5DATm1+h02*YoOEohSrs?K}3INXXxVg65#hMO)LDCrBW=_#kbvz0hfU ziiw&U{z)SE^RwFUA;*o-ur^I3 z3YkgJdHw?}8y2s%Qf~FgNs)?b!(o0UE^t@-&Hc!@x&V)1E->87SOn_DdIBK!Z~+i< z-bF!IPV%Tu(a)#`FeBIUKMiUf7|8Z{q23|<8ej~uf^WH3nxw(34JE{`9$|J7;wzCx3p0|J@?v5qG6a zKyH^F#=}t|(hyTH>#wpfuKga@Q7|CLMpW2f`tId?OxtRvpY74_)pl|j+YJ;9?T_`P zp>B-P658@MDJlAEWK@ZhvKg_7u_;M_y~h~8$!<-W%8#Dt<~eC8MeyD|Wrigx){^IXDqqn$BPn=)PC>lT?+sP4%l*D_2p;!aW>PgQb!KtJ zTFZ6Q%vFgi&udlt?>FLwbo8|FJ!~6>h*1%3Pj^`*6jn4I+$*gy|3t-bZ}6J72ZQ)T zBToZ^+!i-P0&+@sZ2E1sP%-qBm6h+YuoPDT^P{4|Df*5Gba8^STOq`ce~p$TLzpt( z+-+~wBz7Y*;MXGVlfQ3%i7RcdRM#g_KRaeJp1Z%KZnl@BB6=rYpkvc%!LU#$36G}cWlP0Udo*6$1_#QyogY@;w#`!Gd|b+6rBT|AZIUB7~2v#^~v zm0_LdJ%7u{Hy;Ylt-Hy2euDc@0P4*{QCm z&IRgT2d*)M4r{gRQ3q%AJ+!5^3H6kSc^?!n&SO7&xC+mQFqDUzcrHql^J-YT&(MY1dBt;a?yv@m!4BkZ*{33JoP-}do61e7zMYjlq@ z{biykCi2|sU|+HB7I$^JN`g>Y z>$9En1VA|l`14(z4&jHd#IGN@UL4ze#B5l*uU;2qeww|%q9Bjh&TY+Mw7Mt7w~^QS z4Ch0^P#$(QR&KO(d3EV8%%GC){kvOSBXsjI%fN+uqLuE|Bmuq=sb#(=mwkC#7=i*Q|2$CDm?2IZXaO?nmdHY}l` zPW$Y{SJZye<(b8=l!Kn(73V3*1Z?U?na#6*9Ugr5Ca3)ojkfx^N2rIfb+5XYRps68 z5%;^Ht*^bR&dUhorQ8P17(mGM>b&n^eu=eOlwk|gb%OEA#tI&V)-i%4w}1mf-( z;m+y*{Ia2Aaoe4%OkmATJYXUhBQ$}AHuvQtza6aobq#OjZkIvHZL<@!ScUBse>$iM zNK3Ef_Sja@r$kerWi8Oe?t6M@aqzR>@cpkmXbQq20^44n+je8jyY35K9_Fh3 z+>+*yBu$PGBSmMWKUV_=;MPnYB^@1Go%h}%a#z$qmRR6iYzG3PFE5zZ&lfn`1aWNw zpEkF}-wxOB{`&Pxo8veY7V+}xee{Y+rD?5=PPbs#skzH>#DcTK1PP|#0 zaNlVPjnGJ8N!O-f3s&YG3nuhztp112+p#W9igEt(sZB(ZRk^aLO#0kCw`30X$gH~> zu}JTC{uz~*kJB7`oNE;)Gy39uT{_|B3r}2gsr}|a%h?lS?k)&y4GRf5+?-UAxam-C zG1oeFrKG=qlk_kOA49`~`rh#A#Fo_c41T#VrdxEuiX@`FyUlD4NF zR3>0Kqfu8=a|RSCxLITU{`(5>AOvKREkv67inLz4+x+&MFc)UTgYd6+{Z&k3d)R+J zC{~vnXBm3l*C|gew@SS8_6gC81Om?ki3Z@fr`1y*T*_L_^8ZXp;+(mY`8yZa2%Uz& z)m)!Bc(`Z`rT1~*Mu>4zi<{>-gUGw!?(P9iTR+%PG9h)vB*c--JS~ReS~FRQ=^>rk~v*Dp9yyG>a?1w=&`%QYTxO zR}7TG8*_LB1X4+Nz)FMUSSNU)gH23Ertsxcl^z?Eq;xe}@SvaWuNeCu5|XNh$tfxE zkYK^gGK`1dBjDKL;^XD5V2eakqyA2Y?DfyzYs1@%h3lN#RfUST!-IWR5OOJZ4q9Y7 z+Tiuv^^0fYwWOurjGX^)>+SJMNqPS)*{8cS^4J^J5$A$HcGUL;$r4w=w z#(qS+{mDx-Pwzh$7nP;hU|?051ByKkx?9C>-~_RUcP{B-7Mf+BOP+=mB1FICR z*Ykc~{z;o%BeeJODNZENM_O7cbvs+{PzT#`om}Gn)nU_h_nRE@tiUx;kh`k|!((Rc zse~mHdU!XR`^r#VOCGih0HHLsYlL3%By+dw@=StT8nj2+v;tZGpkCc>wWW`sn*Mc1 zO+;Dn0mmq*IHi)nBG27)&2iM<(|6nMqdgQK?*41`w*cd9n&P9_0~ z7yG=#5@ma1ATsk(Mmf3qI&K7F@+=gYe<^HA4{{V%#3#}wRZWZXG7s}2`%j(o&7nO% zeabSM0FUk-OttBZTuQ%Oed^we-#-?5cey|eQUxC7+E5X?!SieA|xaVa&rZWdiy_q?n(Im{rj4=svt$wpF4h) z`ZZRFSm+NkShU(vcSI(263WImGTzH|UvSi)iSB7bAeS$^J^bJa&MVWGUPqtaDADPp!XSfrLS90<+C~w=TeRh z=Sr@P%79>Jf}*M&1>_Fym63A^jxq?btbFwe>u@>iB~%rT3egZ?4iDvm4SaIw5*_=k zuA#{8i#O$wZZDa~=)TL;KCw{}vEw56Q8Yx(VH==1P-~YAJ-g@0$XZ8%DwPO+mfd^I zAR2SH*<-;#(D9uo(2C1Bpqjw%+v$zc25wXJ?5?$JCb)aogZ@GR^%2@Q7SK@Hl3txD z8`L>A|EWvVDH&DjTSyI9Yu69!5Lbk}lxW8ZHd`2XU{(oma5%M_Yx+K8&j2}UOnhef z_{P2WO~6cu{~|U50;ac>cvX5zsjfn>r@M74HZJb^Vf>|NmC+e!*e^i4SoQ5%d2V?YI+xy>zv&K%#GBNmx8|0n-Sg2a=aPjCd_`yBV0lBkQ!Q(e1B z^%_-_jIV@H#RxI%KteG8)GQG?szL!6O;`%m+ORL#%MRqevB^_hG$GaLpZrW>MAC^B8hMme`RGHVdo8Kkb>OBH`UTy>s~jo&-9&&UNNez6xO5ah2cviX=O0avOvbb$BzQlgugc@@ISl& zX?9#XY~@QvQ-ROA2CJ_mdv=>2m8Q@aL7SUEG}2}4?v4j!$s6^B@X^(^mBC; zZCoD&)!oniOi>U|_wfgKQZM{Ez{5NT`ZQBym_4Dw1kFSDfO0=3A3ISek8`EOvZU3h z>dWGN=euaijnqt0&za`ei`pl@$ef;^9!^Ch2UI=XB1-E+NfS(kyOBlGYAA#wx%q0e z`k0z;OZ8c?6cEU~s_<|^L-AgXNdTB!(Ut|n&VWq+X?tA2OlmC7LZ#mHI~*FVC1-*% zwPF+fQl!k~o8b6_IZ4=>4e#f@*I6wBErV}4D_Uy&rb;Rv5X}z?X4s-f$lX|wT%!R; zOE5xgn(X}^MwylOYszpbTZ(FiUfKwwWM2)!RJ4X^z!59 z)Ut*eRc2m7!Me@XCYl3fjZY7YzdxbiGz*5*-fD$+2sRn3PZJXZgACix)jBQ`EZbV4 z`}gsNwx;ShPO-EAtlQcuwzi~s+!igf8x-k{$XTOvcibMhLLXIIUV5aVfBN@hB>%t2 zCb@Mg(EraX?o2tgia1USXxx4XAa!ZA6M#O_`1`!tx-JMCr2Wn7HcYCHtPNf}I5u(7 zwgO3JO&!AVxwlvdMZ9-SXu=(P+7^~T&0qMIErxG3w@kl=A?t_|tl{Tkfq8j(`Zg_z zVF`Pt{V6FaI0MxHUU-m}1EXOPGlRCVgH=^9K+L3hr6}b``9GB$fZNJsB73zBR!;N| z?m`)X(QzBMWUT!$J?PmniwrgN z^q6uTpN-#@oYi_YVNaw%Aa*+{rXludc^7$kx~;l2BEF4^k68Qc!N!VOv48N$mW$x& z7>`a0uA(t_V4&k|-7^z4N^`FLt4Cj4asWM%e9o$!2UO2{vPz-Aaq<$N2)1QlOXVFGY?uQdRz%j9ju z)ztm^eLIiyYdD3IqtADqTlyz|>=T1cKprzocaWw2F^ZJJA_|Q*=d#dJj|qiMUNb6gsjH%=;4~lKSOr&cC_%FKq|(n2m2t6gl|&%mn{hR9$8I22E>25I z8_U`8xayI{kc^%J#W}TgX>i5d&*kI9@CXz6^}X!VF(NcwJE2PF+G1ujM~~Ta6NX%| z0GsT`6&#WQk&kNMOvU-?1_Fy zaQaVqTeIN17+x8KeAE5p)`*;(oX4qq%dnXDPk97r;knnwnMc=`+t_yxy~oIRoMsvl z2ZV4Bn@)ZJKPktAl-KeyudINyR`2CuzTgzFN~AR=_0o*@M1Ne zZ4T#tM^hMEU*O$A>1$jmSZ;K!sW6K|9!Ms)y2@iQ!*L}#p z0cXj=4*Rnkr4sp-q<_n^Y+-wdgQe*5`1K8G2>K`}yAU7$gj}cMG4?4mnm}4mmy)`{ z#_u${IjgF+&JI@)hCKdfzn|}`&7Yqhz%*H1T|GvA(nYPmORdz)+d7U%BCj3Z-N%$u zP3y-iPOZm>`R-{I(f1CbokacYPD6M2GB$VHJ`>Mgt=CkvK0EPSc#nfEMN|+)h@$Fa zySO^X9`-daD}bxm>%CZ_;ySY{ieBe}(?HtA)_9+g$2LuDaW6~w-`Qpie?j<#-O>?; zCPU!`WbZ9AF>O_bp;l{f_X|)n>0r5>rP2x-A5PNt2i-$Eh-WX-Z$ZX+YZN!~84D5z z%|4^6*auM`7^X0dmUmdcGxQkv%9l@{H_`9eC>pptk7ONnS@u@B*8j_tFTV2LL5xp9 zbI|hm4z0^@C@bWxr zWUp@a>Bgg2s%5>;=VF?2dh8D#oUe>CK0KRQ6@#pU(>E#6lUqj)b7caq%P&W7V+P__ zbNI50iOr?=zqg;@x~xzI^lf0Q?HI@<3kqHzBv|MNU{TU)`aSm1I-ZB0=Qs}{Rp>V8 zxI$6!kdY3HhNbxjTYQ&h@yBG}ITOf&N- zmUfx?y?f9eB`}U=?B3PU>n`ha*Sn(>cet(Otg$ucb4g#edTnu%ef38@a>5&x0<}() zq9{*pJZ^`ob($QxGdFyneRG$>aH0Z7k4~J;YNHomi#W0j~CP`$a3y-l{H&)qz zh$vZXOpu$~?J%hXHn+C;8`eBzbH0}~a9w%rDee#xGdc~IgH9AJ#K>9_z>Gq1r&Vs4?De4{m#-Oa==n}tU&TuV;T*nxB%b>HP7>y$bUz{qU zl6#EOgkx+>)&6x+G;5@LDLx6&LKZu7SB38=c=>E$oF411aG} zOvs&1i4!NN)Qyo@91nZ;>oyQj#Eg#;BwMDGAGZ7;IZg{r*pG zX%qgXW1X}^3yy$;cef^)LkAthPr+I+M8_ukrL@cw^iSE#rFarV#im;Ck#b&+J^U7l zEFrOHYRZdemh{3|-X8C6QfQ?*7kcu@b(*R^Y9+HoqSF#7{_eCSm}ZL?~iMBo>aLm@23j*R|f?? zGoD}0WuqsXuGP#cH!}QSJ4*eaiFz&Wv&~H`%v;> z#IEMmgV5f?yHoLih|)l+&VdvRvo+IAFW!9EGT_5hRBc#)dn#+gGN8bG5hM}xt9?3B zOJrrZOm^o3c92u{9VZ~N7lOzeub5@K%Jsxr!_VV9{wdwo>->+P5Xnv~m|k!-Q3bq? zHWmB=cjSg=SMS2f1q;D0RRdZqckuanp7qff-=DN zRxMOe+uKW{CLMKY7Vd#SBg{`(8gj%oD8IdY@NhUa-j>sPB9@%xN+xu)^ zx>{!UmB9xlCZ=wym80#MsajR1Lcxt*&L61*Hzay8)o)emdU|5oHxJzKIa1Ew@EtK@_(D2?SCVxws1;zNzoa;YPdz3~*)#L6yy9fV!pGRUx0 zMvp7!;yb7fcH;{#>9*`;s(h(+C>4p_uGFXK8Nf`r^j^3>d5PndUcS5{%xc zTQFA& zx`%ARaO@o(m}~6+9uAL;q?_@)mMib`V|lF;8KZi1ShcIB4-#I$lpTsiy1280>jB?1 z#X6&9uNhxjcBG!)#?pXR{nxKwNBKW@c4E{MdZso0XQ6B>P^86@3lRq@5OJXB)LZ4M z@X{(fuABO&M%?V;P%*s~hT-lCZtEQZPX;EsLdgOg-^AB75pggmU;pKEvR&u@h zpQJgy3rpwOTpCu6K`TCP%Er&mk&5d%5+0Y5jbEM^ovR7w#WR!FJ)!q}733d8exp!8 zGYZlYn81;v_LJ|?K<}-!_0yhZS7O770jJv_3(jO4L)Bmr8f`WGgi8TjKE`YF+@jUqkJOXZRZknzYSLo0) z?ZLEySVmAK#gs#=+=NtHack=p5F@hV0O+5gLQF&i^&N!r1m8|vb{B%}t5b>+-vA`4 zKL!pF!H2{zj&(57WSVFVyyCVVu&}h;GpC6rQP+Mq1kn@>jEuPCd{~dofWHWfulCSa z_5CxqijCyr^D6&C*^u~kM3D8Vmko!4%i`r)dp_7330%(~G_og4+>OO*Y#oL=b7<%$ z4I@vnHryJEp7zh&`IfrGBoe*xdSyM=&;$di>W83HAezhpZ6GNV_A-G>0|W<%-_wQ^ zTIsKKPE%AMFqnC*A|eKJ4o9^?qdvsub@vlZi-Qd z>J^AuVrbL5hhalPPVRfQqYQHhr6)YO_*ainY*aN>U|!YHOpb_7oNAAD)cm|gZEZFu z0|bit1RM6+fjBtx(_Gu`@vo3zq0)pDlb_~jCOZ{nhIjJIfQZ;cjGGja>h&F7wklBDKz-WB4iR)6|5=~FlVX96+@zLGMy$YqnA?oVzz zZ|~0S75V5G5RnmK8XgQ)2rAX9Xv?ks_#<3vH&LvuXn4dc`BOWBt5mrTiLNUq-i&kk z{^Uy>qdy-a(dFtrCmx$kTZbtzb~{f`6S71P#Z4oN`;EE%8*b-`Le@(eGazn0U7v+F;w8@v-jICJN0Q)UPnzLbRm!#!1Kg zGni%f9t-kC1zuB7ByJm0*i(K$(WlBme|ygytA=9kqombc-L|Wl_}GrPgv}rIv~0kf z+U}vT%uUBnlIFRp)b61$j&iE39O^_1Y^i-L=z($6i&Qtw+GW9XTLWF2()GEHogc)r z6eG`5d_gcY|EAO+eiLmQ(5A3ICm!L1)L#e^&+&&H2vwvj`>cd6wtX*>Hn?TXPG=vh zFwfmsvtDtkR5nW&#nL9IU+aYTJ%5{EE4lO9fJ~z1EIA*VXKG zsWT3fzcDm|k^e`fiKpI=s|RFnQ|xTv=*I5}lDD@#$!Qk!>9+(xUfTK%tIuF0fp!n~ z>1u{o(m*OyI%@RFg0Svfi$er&+Iq5IfOr!S%H}%7Dm|$G*w>T~UIJtlhT(=a?OX*$mdFE19i)FaWA9PIYy> zLMpLnxP$=^twtbx!k8djy-|wgoB03MZU5WP`jIdUCuDMF76bew3M9XvOXoF&G!~`~ z!P}znZ#pI_7fN;gJ?RO&=Ky>v(JYU-`up4Oc89xKfy+xAal(Am$7QzfX8t9ThW~-E zSb7r-HRF3*K#I!oV0Rc^jIP?*d}&+bDJMDTj!?{k~wdAzRYT^{Bi%@?`i}yl;!812o|~gW#dC{|O#~ zyZ)e;t%W(vBT(Oj_1xpxJLXiPmJq+)@7D0)8$vj~qL2Cz3C>a9jY>efapQhVq#P*X zFFRy9WMAf3cw(@TN3;Whc~(x7N_2)V459QrzPvyveIK2ReT|Q^gkss80<@j`sGSgMpl)q5%)*w&7ct@2j~QG^?_R z@QWC2Dyr;6O#U>k&KAO`N|V$wRAf_Kk1`H|bd@4jeK+3dQ+anTINY5lRDETT^pq~y zK|oA*l$O8w|Nf4=3Yk}r=f#HysF?58yxR8yVsvK}8D#vKw8J5Up z0XMOXW8?1bK6sX4ld7YCQou7GkD|yZo$m~OvmoELof41Cth*MeDwCcIqnk5 z&Cw;s8fBfE5M|E!Ia5qDc`Du>XXbg3Zmtk7*rGHIgoK<1Ln|CU+ULjCzv#p}o`RL5dOk48s$7vKuD)B^F4of(iHwy?ANg^t< z+tbrW@abex`9~6z6((rp24&YiRr15%m>VMn0mM`hkIa)eDXzYS!XlTBVy52cYmm828)^G@3Y)ni8+cJ>r={`O z3^CLJJi(Ude78qCZ;=-b6_pGNwIq=$_upJ`)Bz+ykuP83>DbnT8mH-CKsdsQM996c zG8+}azxzDvhw1>LrhN_p&P|`>7d4-O0w&_uRZn@{2#iX@9Bf0Si?I&1g;pZJvsqCms1Ql~i2QrpugB>0(~21#E&MLXKeJh` z8?H!q9>Sv9p5@d?3ZeGLqe!?R0ckhO>69YO;dfi2kWPH1t9+42oVil><@jqW}g~ z0y+XtkgKx)YFVJUFJ@MRuAB3!KxF_)+%+9wPvU9R-Kq%%UOxnVNKiP{BnOSX`EZ2uI6k2XSa&fZm8#j*N&{+7W|Df9>qD9hu=!$1YyoHWtdSPj$*~YtK7Kecfo33~Qe{MK4d%p5BWh*aP9~>Bsplt8JfM{K9rM_tI}oD`3WJ?^j)Y5W%zB+W0kHY zKIGiT7Y_i|0SH%cPc`$E_Lg;*>8bHCA94>L z=*9#FB1Sg*Jf-Q?5}+lOVA7KC*VQ@uBsLI{GyBMQ|MysQPC`*v#rlC@AXkzy&C1Q} z9Be!oJY_E^;W5PCc!7vA5y`boR%_hE6B61P`d&tFcjUEpd~wcU2&Gc z%(jN)lS6%~!2p?2j~`)Ei;Expf3UM%rA$y+uhm*ohVPpFd)A2-h3mg11ZSVW-(Stj z{Czrf4E|=?oZu=WmcB2{)aP1@rpJf+yJO^39H%i$9~m>f7nidyD*98aEG7zqwy8d` zBnEEJ&6WDxEK*|6+q7jKZPHJh3B2k6ac13<@CeQDd@)f{&y>SDQH9rbM{NdBIwue<%l zn4wcYxI+G92H{3N)hjiVQb=t=t}*sLeO-Uk!0VTc{AjLI)UM1svY>Z@GG7yThp;=i zT&t%4+$p2`L6 z%!dQ{IzT5oz(prNsrhjvRsK1r`ImHhy#{!o`osoB2?+^r?~h<7 z`VFOUO?5RmL2@;i6X@nlC~?r%aCZ6|SwL!u1d8z#i$sdt8ON|*C`8^QB-}`WFg%t- zOxq)NWQ-rg3S+RLDit!7^*-*UBFxjuOn%lu&9QZHdp9T3X}-|PwZ7Nxj85}=V#Qs{ z;s``5q~YUy+wK+%Qu#sOP zixJ7jpqMJQV9n3s38g6`GMRlITs*;z!m%TN%BR2g;2+C5JY1IhlH`RIac`C_J6K z;9#<@i;5BT+Z=&!xVcr={gusBslH}Z(3HAu*KEgC-LGcau0=f+rZ%-lkPVWovPI*+ zySoPrp7QCyT(9quN(}H?_}Lx%u0Dj}3-QP>RgBo zkPmku$T2?bxSiM(zbsB}rCHcZ#P?_EaLjP6W>-m3PfE(#E{?P`FSJ=InL-YP;iE#W zIAv3mqJDadm7ZH8Yqqu;GBTkdnh0t_a`ZF?7%y5`Mw#}bwqXi2vcm=9Z?G5rsE z(RC{O;Tl@TfiduTz3z0mICFxdy?byl%&ug3jet+!@M7o$l8Ha@J#Sv9rEDv6;LHmKf!DaOQ6*($Q2nZ+qoRn4KO!kS^{T3;RKw6hAxzR_ zq*x|*YC`ft0vTsx#%b}Z<{N8)QbUEPEUnB-aD+JTFqbBve%PL>Qw97>uYKdiI;tt< zjPH|F5M?>v!*L7xyShCc0L1`6*i+$rf(>kz?{@v$x74PRgx@&@@ywz^m~#E8gR%cj z3P(xwPeXB&0|fn#>+j0!Dgq*ZV12K5a3j@r#0}K)Pavvi z?rgIX)V4xN(N_L3ZEK7GA1$atlX9{^5li0T_F;gn#ncTVA`h+r68fP4VwzvkTrPImC#vx$1DPa@{ zjDptl{XX!>rarI|_$Tc2>IUcwn11yylUJvZj0X^i9e76AJr9(IZk)oL%4W1U(K?qT zy4tg6a8Q>e5jF!s`)WUdU}IZL#?^xu3!a08i$Uql!=vkvv7lz(`HB$k9$G=Lmsc8W zuJkTA3(N=`ci#1eaD5`l12To?7-t zs4bm6~LR1nVCT#|&pYrW%y8xUFlJyERQaP_j5!FKV=tcmBoF<-B>HUtI%HYP;z8Pvdulq>H zOZ=k=q;*x>@1fy5FIZ(v$GE1TKo2F*)KhzeR@G;#oq z0fzlyxKyDBm;(jeCyC)3jbzwWA*-dBF9F(BdiAKKk?$X(8M<$zdGN-!NMTc$TMa75O{S7 zvK!Pjsz3vyB3Z%tQFzit0@^!bF6ZA3LTkQ~!*W(pR!f}Xsl)s()Bk9~|Bu&~&Q8Jl zb)S|VsXec*=1JK@i1+(LxHDzXN1MlxruJGQzhWrZ?(`0Z!ACSgpXfBnhV@{kAV?i0 z%D%~%wf+0|@8&;8St7n4?q;}DbCq}8cEm=^-u_EzOsW17aCoAsi8C$NL0P++UEI{v z^z?8&_BX!j1M%Va1tN!8N1>LTKcI>8p#mt7b@H70QV6U}a)@o3}zK zuMZW_490Rk!i+m}ZT0?Ntz}+}VX5%+~o&-pPT6AC5R++=w# zV#G<#kr1M|Qd50MN|%YkbF)7>eE(rfxSK#?l~R~7>{)tP*Oe0h5I}WO^{x6Rr_rDH zPK%OjTl?RT-raGU`PlGwPS4{;8^f&NqBwv2z9)!o!1fEhc zu!zDji-?^cE>PvS8{34Gj#e1M%V$xVQgP%e!v+(&!t4fY#~;*>C{V6G!f*`jCT6`OW5 z31XIDNtBE~{DoPYxqEN_d5qx-9uMoj)N4wruC#A}=4M=d%|DBdjZGLX1s7$m!K0+W z3CE_(XZI7D5xefwr)LoQ1zqy1!(LLZoMCt(g=Kh0HxAL*3lOHL<)02@uk&qI`M?D3 zDQFMhPJKcW_KGk6;Z(g_!S^~Npd9AvhXK7wUo5{hI(qPvNMJmsx-uRSk<75WfkA5J zr7`$HDY}eW2lyWn#iykiYH9JnE3L4Qsbmy$4cybs`@O8-qY~O|uV%|74UJXE18y|z z<5sT3Ph=`6_MM67UYT07Y&Wo?`L4Vbe(tB*{x5%z{S^Tn9E@SvYZni&?|I8i!&g zFln2wrF!}+8{Ts}V-!1{>7}KmbD)!%thHlFEQY5ch}Hn`r}vX@Zq7FUnGi7q|L@+a zv1}@gyIMTZ6RH|lf3*6E0&(>wGV0-My_A&xT&)=iurbxy{Ek2yi(pIn|aYx}Gi z#isYURL@xYuJa}CZC%g5O9s#;vk~3wxDTooP;(?oEpB5JX%P_-f&FrLe4LoCXmp*B zRcCp1RZ_c>FsX6}qP*34#4vxnj zYR}yE55pq8zDhz6)T~pghvfUI#KPj{yE$D2{Q}Xh@a##_ngaOyyNi4Ad};lkMlJ$B zJ=6|sT}$k>*e=kt`CnR+)w0U|i%3U(DEJAT^3^@wTAuer{>yj3Hg-2+Z_$+lqu&2e zx@k!tCjF}u3Gz{k?_UaaG*ob};5nMfSEQdFtU@X)@pca603Sje3xbCMxgp+Lwd2%i z`=+c=V4`NmH2(PPwt7J>{V{=y-i|{~S!?|TX$VKZSa(nO6SU9ubaoHlIUS3%pOqy}sk#y}olka6xeI906gpkZ zLL~O~WFvquZnjwV0F_C@miV55ggNfufQK=T?Y>DGwPm z@B0S_H1}fV-UcwCT9et_enC&lDqAt$5vBICli1jAqpS`Fpk53#Jw}!*2^6ed?fc4n zSX&8wE5p`G=K4odD}4}eoxa%BU-d1IpFt>{OyG^BVgX!*fUc-`2}etU!VO3%zB*6l z=<4b+CPqQ8A7$(!B%-9e*p4ro^*iY2St`R~_UE7_e)2CB3uozXDwcR=3B8}l7m)f$ zNWFd&NwGZzW4ih$-*Z|o&se#+H5uZ;P}8Q&kxPSkIBdGakFP^P5g)Iqr6qN#gET~n z#?@!rwa0Kfe0~{_VpRrZP|JeA_mQh>b;rUt+u9h_NtfRC`&dn%;Se^JpL=twRRNYk zh@7m;qbVndq8IYqM90wc|NUJyr2k*{0&t<%$621V#>U2J{U1Pn4xM|2eog4oM>zZ` zhhmUVE47)zwy~YBlVF9y3CY7R*T%9Yr6~@P9d~29S6oAUm(5kJ)2jt2a3JnSqv$OO zkTgRa>iVwQTRdK4M8d#_^nhFqs!lIdPtlOO4tyBD!G<Q%+*#lhqqj z9)>%1+r$G_R+Wby{b-+JcYf^mW!bex*xX?;6-0Nn|2JLqd++;O72(_{Hr!)>=-gK} zyhaj=NJRyb>@@_5e)%RnUIPLve*j=KQ2GDKgOpD=7U$r$VWB$p22`5NyVzW8)D9T1 z$u;7E5Ez2Cq(N;0{U&hVmMPd%~{gqF&== z)2lQAn`X?Kk^SQ5aL7K(6n_g^e29Uhc7q>Wxo%|m=+SiJPK)7X*sb;FlO|qFJAzu* zF!VycnsGoF!@p1Bxsc|HYGs)i`JJfGJ1NAn%J1bD8E$a+l8|VV1YACEc#2d>hhZ!h zXq=Iy^tjXEW!(`6%kxLuVjC?i)f1aoH5N-D_R3xCuL+;KGfWhFR_aD)edW`?7ny6S zNaYdZzf%!t+kYFmAjH{gF4h%Ee6Xq3WMfEI!}G#DHmaJxr6NdZqx}m#R_7#JzSH;) z^IP{^KFW+b>yGhLhN_U!;15TMp#t&_rGm~K6=Bli)os7jG^**6@Fh1-m)z<_dVYSR z6!4ZV)*QCgA11PGy|ythcd22Jx2CgJM}A4<`H!ItQ>>|7Wx+$%!6-GG?nkRzbbA=D=mICY73uDu+}ZP*g%Hns5iI$zr& zkG|1QYIer8-JjBP4j(N($_K?}5J|@Dx%edxJT$g4wlZa<@Lc?o ztNy;KVE=bKx-FwL{jU$1GT3gc`AdGe;EedZ9;23_m+LOr^O)Xee93~Tb^c+k7TbcO zs2}g&yP?67%{Ly2^p-@eF6faWq7#GEC_O=34cHF3<$CeE;6Z?R!|m7ndjN@#6Sob; zr2^L)K0ZE8Mp~L;L&HOcwM67o)F31s8l$gpUqb=|bb0!6#H?dA)~az#`Ffl`lACy% znCRebc_YPH%h^8np+3ur>%UtObHYgpB?7P5xA+)2N?GvNWAU?*A55gd-SjVFG|TMz%mEHt-+os>xFKpgdam@bRMq)rXsXfmTOd`cKQ+-yHlYh%kJe{=wev z(R9Vm)S!d18lrd&T7khivfwxM1=YoANvuoHRO%Q%HYZ?Bi#|G;G?yW7%P zMd)vbB;2j1C)Zd>#UtT?=lA{;*ZZ*FK^FJUgOlVJ*Gyug4x zpCKDjL44phNj4%@0?a%O`C;wj7_8!p2g51xH`to-Z)|+2DnCE>OKWaA?dvn+BETrR z8nT;ehStng^Fex2SwaX$^Y5Wfpm6DUR-G%UT5-<(DzZAo5r7~fwgdfuQi?dL&EBxQ z#D%P(YN2xzo8&Z+(=`Gy1(!Dv3|^#!&bFahVZf!m(i`t-^6(xT;rWo zL0&#_A1eJ+?`pYFbQ<@MkWm5ZrU8rq_Ti{5uDq%Zo}KaezxK}_lR=EU&iLFlca9)8 zy?5VZYQR{lCf&xX$&wfs_^RUDoMRM{t+DZso3|wxB#w*I0&HzN$8UdB4~ckIRf%_d z*;lkLT=9RK4v>ajFqo(5z~s7bg{7AR^pY6Z7&;}VjcV_{zsZa(8sDf55Q_b!v;8H) zMc}h=C$a+ z;b`st^J#5GO_fm|i?(Jj(5X2)JHt-!y}`X{I%Ie|TeLGUQ4>fP8W0i@wZJ%x3f}i& zkeG3{Q9(?YIJ@XZ{53;3`D?%-7~4Lrg9Tn*^xvV6J}QFMnM5TasD-3GoM6sJS}U50 zde|X#J|cXD3LXgi7uO}!IoNej7jj9_x#G}Q;v`~jDFsW=zp)X#(V>o)o0}LX8ACy8 z8C2FM?r zml6xY+=w_d3ms`@1rBsXM5ANm^w3R;|<Ra5}TRH?{~6Wo?)m+ z7nktcK6@IQjWz7ts=wpCfy31gB#33HNsWNYu`HnpEF{Ocn`naKNDa`4hbIJp!eM*t z78jJ@GY=RU3);G`BC%=HBrl96-(zE8CG=82#my;PY3`%E9MJnJ=n6n}$s0Ok6i$;g z*x82d#+G}ff`FvL z0Ma>hr?g7R(A^9I!!Xp)9p5$k_pI;vzW3dK?6vke1+YL5T2Ou@&ose$O#C@BMTZq6qZ zGe1qoWd2*B?Q(yH#tfLba`1Y@KD}+W7CkL8<^L|EE1&sN&1h};`Con^v)K%vt62hg z(A}!#3-}3TB*CMDSZks-RFbh4h?5O%KYo^OruN2|1OJzS->%`fPE~l5+!WXU9ustW zi>lz0AAET!BGNi;Q~aEmSNxXg75+O##R-%Q9hyRozpwT=E22vV=&Wvf%&`Hg`_g( zUnKRNOU<#{zTO`T@x6E-K%<{ue<{O}2d#<5G`u}4k zP)wWRnLOZY+VxPf>S??<2qHWL{c8{t8;Z)(T{0+p=UG;tKLyU;rwdk0B=4Qt*>4UMN< zixU6xjjXbi7a*wao}{ErfVaW??q$Nvw&!I>^yB@H zPU>1EJk>TOX4}-5c+q8PXioo#r95t(9CS2w@UMY|q5#&;uK~-NF0&Wu;hknx64@%u z3ETlJ#>CPQzQ7b^s`sWWSjg@+IvIq}^> zUq;kaUW#Ql*pLf?J!C=C|0PxPVOT8Ba}7#^$B*s=>%7E!_)xug+^p)^j;SKOdiRk= zSjVjse2K=0qR{-bVvCj2hiRYBy+mD^22Pr*)cl9YHT+xvo5hw5a~a*o0Mo;4h4$)7 zpf@qj6eS$xUinCs#HQnn8ifkYLf!ylO8~q`%!ZA2u(0dZ-ZLTu?8qiJV2^O7gt!`r z$UbfUC^QaH=lOt`zt1&$`}Zh>;qN;^zy)W%fBobt$nWIVGwG_ETmD`ZaKtDJ4pGyS zqEYdbd;2O|hQAh7pAh19J-Gh&X@6_z!O&+aoT``aR3D?L-rvin%``tLL)AVToX(UCInMk97JBU&K@KjTraNiV|1eEx7dQD=Yb~ZuB|+44nU2<>h#J@y(EP2Eiy_8k10?{($Z4syq{}xdfBebH++T6K3rKIa%|p%edmK8j5a@{@i#AK;*z6z%=o2TnlBTgb6-Cx z;bGj{-aazMFKy+tD5k1&@`KnvXf4@_m3>h+j1)Gp5hGZ+ms&S&|`nW8{&N+5Rm?SaiK7#c6lCTNty zfS<9=-MBbN50mW?5s=G(OhjQ8S`t19x|{2hb}&VgO#cAW2bFLu$cI9B*uOWJC(~?C}yef?D3$^DGh;+_{v)(Cw1j*tim!q5fcOt?QlXy z1*01PSX8UIOca!|a1C#@T`%KdaxT#EnMIvs;Vdj?L!TAF(sFJRCXkmbeDY_^okuiaiW`~}B~jW=~V6L-9Lq|ha%VkWrG^uSO3&p(w=&R2&4`i-u1 zecB!7Tp|bkZ^VGa5h{+M;A&R`Gyy;lUmp~4UD@$@?=I;(HX^_<9?bNA0AgVzN839O zfEKuQk!V9oBoPXUtjU0pb5-)xp9!QI1Ev$p*&8e4n|-GKutz=)GE&9dtGqZ>Ckyw*TF9c3S`uP~Q2`@hrZAKh35s2tXLE#>?uuo$zrtC`6 zBq`caGc^TF{6=jx<87cZU}L=&zAx*??VOxZ-8`pktDOQx%AXwd;x}rM7)8` zN{_MMajH|>*%@*`BXF#21&j@kN8LD7MDz8B0DCon^IH5|cBi+2Az{L$+cVEY`v&d- zM!kaleAz_ztXPj@wFHLwhXtav1hBB`JYd1!3kZH*mN)O{?asdf$Xr7->U`d9$fEH> zk+8PoC_oMyiP`g86k@V$XtkSw3)~PXn;#G@liuuN#w!YE;lMdx*c-kA5bDh!qsY6| z-XEu!gF7mfG>i7FG6T8=dGo3KR=>+~;ghQOPH%E-jzaqmkw$r&Lq=EZd37)O5pA($ zN9|oKPucXiS5Uoupzvt-WlI1V366h4?vG^zYU+gBSXXwuE)1VE~GVX1BaxJQzXFu5wGr`}v=92!n-q>_kE4b?InO)@h8 z+3ksouCA_z#K{nFm_GcH7YEji$|72OnI8+80{A@VmZ8__*=D^=xzUis#L+twKL4$n zC|<{TZ?I=TFed_utm)?1xE+%l2foIl=bQr{^$FNzMSaYLJ_rDT_g2s6$`Zh)j_D*$ zIA6T@gs`h4{1~Cr^J{{(j(Yqf5v*#H8&@r>>q9J5IRzqW0XtDuMgL55no&sVWC;Jz z0=)Ki1|PrxQBM2j}lN+5SjjXawHw(yhtr9|!s zjuZ;4<@3D-FPUiat_Ir!Kn0}|bZ2B{-q=x1x;$yW*)}j6v#ndC7E%6pi$z>(_S$~F z(kPuk0W1Rh#o{_a?1G-nqX(Zkt$+OjoYk(bEe(p6-x!ErRN?zZqE4qZld7-Nt03BUl zEMY@}&X1TF0)E+!1{J9E9)@LlG%s|Zy$iwMf<}o_;p}$VXx`I5WX)>7v9?y8Ca5G6XUK?BwSanjQYH67rk^m9d z2Vl&i7Wz;SqsWpdl4$RxjH zM_0Yz3Fy4{(7LsgV{S%F`gX9WsdehgWo#{F;iD%Do6rUnCzt`hYjwq|S?-}jp->!y zX|hvQO+RpEhdQmH^G}c%tj(Z4D=}a=6Bw%AjlUzL?*JliK zWIfq%cbA*m`S%rA4+cG4gFAQN%}tepcvN@O_x%9u_FJf%@;{!jQl9q|TBv>rQP+;4 z8c61IGFq2aqM4H<5Rpm-K@y&CZvsod3GeQ9SY)S78Tou6N%Yw@wMdBAU;$&|99lVo zg&;9S?7&Pt@<)@_b$Uj*pt70bkAAE3f~k-5H)qVt_Ywkh)%&cPv`)D;PYUeM9#6ez z`UgpM)>30-DziG$gNERlQVXs&8kSr}(=BH6B)>1UTRmzo&5E;Dnz6j^fK7heb1YH? zpBvvD{}luqOGO_%?puMwU=^_i>M=#jY}yl_N=lR?my4@74xi@I?uMjZQ&EsumXW!Qb3+vJ~U__Km$!k0)?)0ZzV zTXJP>V7+bX;VT*)%t8UUGd=h;Ffrd%=e9nOhiqd?AHk8|DO#b%9;fz1TY{-p>XkRs z*SJqzF82Z+TV#IWSt`gAb|{?)bu0^sl%TweIU&LBRJ!u322K@Vd0Vbt0B{IWr2QcZ{ z`w%%KRQR&cp0o!xs$I}V@s0Oad%mrp)XT1Umo7$_(BQa`vQcDi2; z)lk?BAF%WTBNZdSwGRRjF__RErlOWT{QrH!x87UTe zTAJICu6t*Y+G?rZZdo-o(lipmGLfYru)Yg)+yDOWYl4bIf`b5@gis+Q7EDPF))6c>nvDCCWT^ze^jJ*NAJwjc>H5IIl%deoA*@qshF_ z2-zlK@Z(WwX-tC_1HQ66vb`sFc6ANjS$mHi=d+v@Gmj?u?xsN@Uw4oWjPF-8h8nl# zej3?jUXNjy3^Xj}Ln{XTW~eC*`oTqWv)fti7;79P%~YUiov4)!B@n*kM4;}8?R64E zKr$NrrgKAElL$LoRrIshIaUph>`4l256l=yT6+LK7zm_F-@nU8K4UgVuu85E&pe0( z#cQPbnG~C%9ZQd{&y5sTL2xt;DXD2r1x1rcXej;yH8xh-ZFj#%ScqW)iK{4+E(w7e zd*~a>fpa5mxi^m^yY@Q_Plf}I3}26MN2(|dR28HZle4z4LL_|zTP$mQ24>-4LhN?F zTWGM9XDcB9I~UK+POFv?Iii4lm$EqAyMMAkdJkW+Jn>F2&+QRw%#zp5ePr78Ku$OG zALK>l6W{_weNzN<0ybW4DcFTVtPnS6xMPTG7U5gQyI4z4o`b^y^W_3UT@zQSb1BfG zG=b}`jr;EMLg5kHE~U4i(2iI+rhAC-ufxCpqOkQr0c&OKFLS1Ctoo8Gn|#t_wR7`` zzKdZV9F=;}OD7j7kx>+_buHxPFcNdt1MGmPy67dOW0}92_9+R8Pe8#wL|5v<$QD0q zXB~m1=yvQlHFowDoK?=4BRTDMh%1(D`5?!eKL;hUewvxtX(E$G#B}4MPG}w^);W=!Ry2Slg$K71A+_8&X_6p)3;W8V+9}!O0G4Zv*LC zF%h+iiHSRNHz%M&{1;CkULy!*E zfdM?6+lGt2`cJy3Z&exiQQpa>EPNptNr@jewV6OG_ZTPm_hUlB-x(z@T?0Z~&2?u| zj8p{oM(#i+tsD6rO zI}CO{mJ~n)sQ{aa+zDF@Ss3uT6w?J00)0V?UBZrCNGBKs%(sY$iGf|2jl2)DYSIKv zx{}w7MbZNJ^$7`K;TQkyDyz@W?|}wFCYR8K@}o>}+u$Ko>D{|RINIZE%F(JJR!Qml z?UIvzv!$#1YOYo!&k}({9QJV546b^+0y}zydAnuxbd!DOX&-6fL-Kp7+^W_4bdd3! ztYX+L@b|OYq@-l~tg-NipN(jzwk>VE4|wbS3`G3tm4O}`WG$#8V@rxDG2dk4i&2HP z8`kjpIGU6)o04A}$6N#|HB#?0W94^z)rv;<54GvrCFQ=PC7`QQ3wC`UuCVFWd%$<- z#6YbQiTMl9?Ux#vKgzJSfTrBK6DZWc2sIBJFNGK*3H^pZK)|!{Tp!NfSiS?&axfqm z;JQOVz#In)Z8lg=n16!G({p|%(WrQiEKDi+?C&q^k;ufy)^UC(iMwD_z@>|@ zlvW)O7gy#|C?TZ$&;l3U<8QyyZ-B;e$6Y@`p5$*_O#YtgpRE}Vp8Hz5^@k0yqUYhU zF)S=h{O-G;-w;uWGY7bcSimM=DS51rXp=>!!O4KE1_?;ZL{v6huKW5o0e1g}W-L4$ zn>LtpKdsQA6~L?vX-VPG1Jb`52GZFP`o}@U<(qQ_C0wpVJ+0Rpqu|_W6Gcz*!ToE_ zhGox><=1~PDFj~CtOGitx5J`?#`QLQt%oLPzWq276dv`%r3Z5JRzr$#KsL4f=U*~$ zKGD}8BP&IE`t!@fpr7?lT&?Gpv0Q^9<2W(^c)@w4a|gVExH-x2F=g?LN;DqpK2Vw@!^PtP*n~qT^ANWBr)Yl@Yc#rXN+on^S)7~-y z*m&hC;u@PRgQmN@{p?%_?O%%Vmd`2w-#f>`!;ug;8Ukq9{pwNYURpn2D zH~Mv7^Z}*|V}GBRCwCZ;g>~YCi7vcv{d*QcpA-4B&Eg|mAB0#-*2IY9rd=dnPti-M z%$C^vvle|`yIN;_LK*XZbFM$-*kl$-&XiRo>pVrZZx$rBW{MY`twA(#alhuE);+m3R?BG{?tL zk(-CO4Y3wus&dsG?IpVDi#5Iw`Eq79mU#`Uk=l&X%2rfRw2io2;M=5A-nxFRBWF07c$^(BA9 zztS9t`eJdxo(d-cd&m^a2`~g11Ngv=G$my^Nr&|SyCjQn%W%7*-?Ju!3*KE??fdZR)rz{X&FQe4L2P` zSnag2q1oU+!(y~ZSmUPBy`;h;5?_rEx-%zA{Vl(zYne&TQk3!c0P-mA9n<8JT=c(# zn8c^6g3OL%1qqBKiQx?kNAFJNjQ2gB9nIrm&qnB$Dmhpn{cs|Aes9GtDbR^WC`Q$7 zxce40IT~Pop(WPG{HVOINOSk{yI7CFs>S%E^YR`8-KJFyCM78p5B7lr9Cnu5?(d*% z>dachDEFK5sdfI<*1WtB zu383+Qj@)O^!qDWYdCx3Dv?K54SQ#X?r`!uE-5)xw^a&aw)OXn`}Emh1WSbK2En-a)&~}*4lg6Knj6Omlm~E)z8_BV#{DIC7+&sJT1Ajh_-sv${k>8i5qv^Z5c^9HAKhEN@ zPiFy7`P;}X0^`ZHQQx_zctA_%G|c4p_wFM=687CHb{rDDV7>FILg0}Me)RqHj%QJn zSXuQO{+zCO41lp+BW*j1xzsd|Qs-AbJ8ByTOw96Z0{yfbBSX-}c+r`(JhmHFq4Be* zOG=BU*lJ%DxukI1Z7IV<>*a%fM=C^E=&6Cu@9EY@=~au2IKA1&o!#BVjgLo0HcZ}o zD6uG%6U}`$LuN}{jHrF>GEjp3@?ol~f<8EVCSS_;QY((3Q*9YnjBeIshF9eGb(D_L zD_h2djao>Kxe#5wW6ZZ-%lvuEyk*t#L@WZ`3@Q)S{UKXwkLV_syStK^W@=^RZGD`D zr2V<+w8FM)v{}?6?%^;>r}|IDkfj+uM+p3ZraFNA{?YC6!nLaFSst6vvHg5iyTNSj z2O8*>WMfWeK(qTyi?*?{qMSFX3pvbtcZo7gMM;E(mG#iK6`bN9YCt@VWauLoeo8=g zXJY#8Nz>a4{7g0eFmp1LIiCZX%ABPou7-8PT#-bjS=q=_>!Go+F{aGKM45|B5-GAt zF?>Z7RaKxH@a?WvT5xGz0CoQ!=8kelje;zW@iUi)ZkX%tk1{-)`{s)8(Dw=~x?*_&v|7)cK6w1Nl zbr9eyQVqw|Fk4yxXURgnvCYpe!8evyFAM5-xJ`ax)szbVsrh$;0OzUK<}!QR>2f9ORU3xHsWD%Flk<-gKpCls z{(@*VLW2j1Pe_=k`p^S%GODbK+AemQ0nvGM6eQ2ZK6@?kL|QHmV+4Z%)PcC4zTj7{ zRz*TFHD=nTUh`r?DeUio_pb-!#DZaaA4#y@I!v5lYw59bf(jl8<1k*;Z`~NoIJnKG zv%;vGf?)AOThmp+fenB`%Dea5FNS)+XSIsSeT3cBs(sUWJ$X`u}>6okl#az!)YH-0_*K+XT&w`+476 zH6(9Umw?G}P-;vi+YaSpp$B7X-IjsLzlk3sKn#-^>*!2_N&r}y$33s?=67FVem80w zmr6lO+H;Tq$I{qhDWIJ7w$7igjiQzFSk%Vn)&h;!_)|=8SMZz?5R$kyma_?jozV=L z0S9|MGyoY>A<-oQZzO0S;RBBNz3nQgr--4yf0R*)>Y($7b2RI1pT{~p8szs-H?066@ov;KOXn`n_Q|GV%^)v~+iyj_z=@Gz58edU`sb@1>l|TV{r}-oGa= zUvhacx`H9p;i6lYk9-$g5!;87XZ(nv{;dFMXBbV!O!EhXRi^eZh){Ungv>v{omX>N>=SI!d)qaoNKt`Z|NXm`O@cCY7I+%kYxey66_tV&~o zI_y5!=rQVND4arlRtPbvsRZL)@>_yogrK=BEPF!*%kJAsWE(}zUYH!esJqi6E&B`& zkg3yY;_`^OI=cotLcgG!M!c8c>6cy_F$aeuu4Hk0k{FrYny3im3>Zms% z%*MLxWYLdmXZ(%-7X!#?>N@# zWBL93SSfkAlk#AF3LP=g4b9Ic1L2LS@9EFm;b;P!AbQ))yaG{fF4r&Lt6zC|N9Cu9b+>(VmPZfH)b?mL zvBU5yX1=PEdB4nRf|Et@i(zmnf}2eEczUHaGgk(`%u*~eGU19_N=9os-_^exE_j`& z??@(Mb}xa3g)xT$Ez2O5oSq&qfk*-n#F>L0Z^8a*{(y~8JdNLH*aADU^<=T+O)_Sg z^~vBBcqvALmlHcS_TAriR%13ZG7SIl%pYu+86few%5iOFxI6SntYFHTyVdUqLd&G) zOiDtsJzMc;aIO{o1$32<6=Fw42Al9GQ0sVT9k0Yr6{NuXW;HTF6G|UQcyN8u?pp^tmBBySk;iI=83mWQn37)7ReIpUt_d zJ!pcQnW1IP$8e9Ejyv(8#JBBf@|A-EVGtYX`kea&%BQ=*H=?|#Y=Y|(=^OD^>)kqQ z-de~r)EI-<{=IGFK*Pd^D4zYNZOgoDmQ_gH?Krctu}uG{iq|>V@1zq!yXnH*tEWUD6?oWadC07#eXo+Cw?L4Zz|1hxQ2zrQC7{f&`PPRo-j31{$gnc_z zsE`HGDi07VhYGk7 z{-e3EDtf!9up~nsL2(+Z!wnB|2D*Bl^C-rV?NL&6TLZ8_2EEo8)7~He>l$1oYYu!n z$u0@gf*1Yf@TmfCjgW6GDjZkb~$T*2*CY0_?kQ#*wv+o2KZ`dY^O9~ z`dh3t0Z`3{_2i0*tLx~1D_<+oX|i+%@Qr}~XrHBW4=`qEHG)bz%gf74Np%7PW2Vgb z_!Vb<0In04?klH*t))gC~KHr|*Wqv6UNWrO2b69rf1cjFz>jRkxbheod zPEy4h)=*x}A|3i^hRbYB>=F{i4$N%q>`ZEJx8$T?MUoQxCcF}p*zjU0hoTp|Tml^up_ZV_#t~#vy(9b}a|+H}onXt)RHnO8N~YW( z?=)M}gS&a7v+3%Gn!r^mpTO37$?@#bY#^z-ar^bq?Ns&E+M+VOll29w)o+VQQO`X0 zq=P@?u*&YsbG;!rL`{fnnrJPK82BdqU0(;pw^I17zOAi%JiSd&=|u-}YB-^>w{TwC zakeEJ0DOjic$EC+>+6wWc7^H7-5JYGJufLl3QKV%FA%+f@%Gv`RcK z^4TmdYL17F0#&yb6pGZ{S2@clmw12$?*NmqEjNXFDmP2R0M%TBxfRvrb}ZG3Q@d5a z1?hpHL-%!nW$SQ-7aRbP0uE=j@naX04zhdIwx(osLc_j@2tR_NwxuK457!p>($_v-D%ppQy1x%4emAz z=3|Ktd` zM9f6G{dS%7UEHN{FkdKYG!cDrH4L#>r%%=(al+>VB%~i8I;ht?Cm;~0c&-1}@?#ZN zfs3~d7~_GBY8n_QI*^~A51RDI*RMw)PLr%>AiTLSzFr`&uyFm?Cv0WWv(?-lHvN2e zTm2%}0aI%loi=(3fLSoHv*(vscwGNDcA;TI#1BI%& z>wP1u22ud&=8a}{e+XO;QK#~RwT=BckU@6wwlDnw^)}gTH*0XKu zZRf6`LVz`3Fp}62&7;xUa;1XK1?E}c9s?qVf%HuZi3AR&&Fu2+s;_d#)k-VM z$?ElLTnG5_wyMLwvV$GlJW2)sV+i9Vm)6#Z)NJGHz!|YGj&|nvgxN@~M_3W?HyLs< zJa*nOaav2GsSG#p$RYafjFu{8p8o1EwZV}Of@Rt8$`Sj&dA8v>s9n?4ft^hulOI|u zIpZaLK6eKDBAezraNQfk?58=6Ry5=K)y)D1Dm_q!??k6!+smk&NB$o89=h$x=zWhf zT75u(d?M5ma0egn85P8_#nSzkA;gPc`BjFwj+!UrvO4=Fi!g3xYtlLtzvbg3o9K^< zya#jrB5|ZN@zz|nIHJ#eM2>U*49CVDam1%V-p!4>iOQF@y!4#MUwiUYz~At0OtqWV zD`lUwMrzZIPB$hjsF1_bf z(%MX&4e|&oX*#?qV>ms;dUm_zZ7&1G2cog*y(;Iy&%w=;7Bjv^zLAqgtHS;tTXUD# zLkI=$N!O27wCibj@@myO|N9da^~i%BM@TYiEj;`kT?BbXvA;G}^bnYAik+>-W2_(n zrGwPK`Id#X=hk#nR6XFXz}<#KxS^LoK(F%xbGN=#9L8M*5jVWRs3elTsjyT;sSwyx z5WbQrd{gkraRpPQi3XfkmTQ8R5_)*(?xBmTakg0jjO3l4m%N{9Vtt^}5p54*YcK=C z&F(M56Z~ZtU`o_rHx-irfXY#O-4j6u1mFtL`-6P0+8v+`u{qI!3nn<$fK$Abp2eWe zfJ~vJAFN|A`c{Z^Y(3JdE3`!{y3S-5cJC}S7sZUBSf>j50plF(lkY!%cn|PQseA$V zbn!za$rP&|g|1~!rwJ#eXlYcTB2S+v0z`dTT3U{ZTc(zI zhZoWc3JTWB=5cl&TmmX>JJ|LS4sL}ZJ2+w&TW>ttZUYMq(8de~Aa+4dYA~sak}6)) zA`==T)O8RxPQP0iJ?!|4X~?oq5mnMPoemq4U4Q4tvs#-}*tqh4dF_LDy3GvRdJ=b1 zgnjlG<6t+GFXUmwMxh};r;PmAU_`QKPR5PVfi=9i=OE_kU(g%JEO;BG)jXK*`j35l zb}s9cR5S#t3DP0&b>Q~0HEetzp(AhsY_|)9Z)hyshJRIwElKITN)W%x-v;73NHUd> zMNfMyESRymtA%^Crz*!xt(2$l*xgQ(cE{H*)AH29#`+~!Mf1MZNZG4}=Q2#_HMA*E z@EIFv)|_QiX8YCgoZr#<`MA0=iY2H}nhu8fD%ZUQ58;03)(<{s%EEh6AM0J%U0h)Y zit0Nw7XUZho~aF$r!>IPcb^=FW^s{p0+KEsIoB2-NuD$(%spkR2=Q=Fi?d>giY*Cd z_zN?gGiP{V_gG_(MEBrjqSe@N%2s$4QHJ)pxoLEEn#As6LB#&O8R)7Hq7C(#nyUQg zazumWATV#Rx0%jq?wel!YuXVyvaN4!Zf;|P0_Z2OLD-a^TiI$F86vs3rj&vEcwL?cxD2_>B3!1_bKe{+~^o!1n8E) ze*#nSX?)tZ{g3AUmIOoKC^|Cc%w8P6;$6TUcBoJ)00a*tM24fs1HEWi=57ffyflRN zqprnMP|iv;U*Fdk9!i2h{I8Ct+|_w~_RUk+jaq;#b$F^Q2N1ovofd_`2rF%Fh126H z>^p6*Wli$H8t^Q`$IE#%p!R}IjV1jrI&x%F4NV;+-BQ;irW^l~y}sjPA3N3X>rnL! z{agGN{<|}J9{G1%&!p7*7Rz1 z26HbkLX8&;@(ch-yFDW#7jWWm+hG4l<>R|TP9&phSDKADa zpI@ByRC8%namz)>42-*V+L#>&H75H6OK9|RR$3uC)XyO!ZQIQR1vfu;-c~`D@cBh@ zSmXwW^cvi|??xWal=%PRS3C4}tv=bBb6vpuz)s`#8u}^MG4tg5B+PZ**Dc=K?H42e z?c1cOY0t8!Hdf;wT@(EGBoTEVIXAzQrw`gKpGpWhv zK4`)wr6XFq-8=Jp7RRO$i;ke8U=`s38SjcFr@)S+x>aGHlgIw?Jj-UG=}7S_uSdBh zzHL01wL{7j)sVpfF~Fwcw>5Y1!3*yUzU22BnfbOOE^*eQn@3ba1zp|UG1MM5U?Q(5 zC@Lh>Y}RAwI5-Y^B%SgfiL(Nm;0h{l^yi*%N_heXhDHQdO?As9X^;mj1n}OvSOKjq`iMa97)k*%H#=ZKw^L2y*}Sl)hXSqOw8zr7YW`^t zU1`zYWoQ0!>VRA6PzqkOx`F_E51ldCsM~DUV6IL*NsBSg*dCC^_yWE*JODSHd6^L; zGZXFs#utT!6YXVMQ=huuHtLPCT)i9-=YA!H-S9p7m9;e;B{C!4~*s^<2n+ z$c`}eXd3{e8x_~GcQ;`y?6ZLBn<%M3*B*EotW?mv7ZV8NJ?b)A*z19)G-+gTiV$_R zY4Wg*^C7b|Rg6hjrAuklprLz{C)ZptSu-=_@xp%Hl#xQT+Oj-7x(ZaezGwJ!{sJGm z&6^0TaXLZNaJ^j;H>f1kB+tR!CFWzpl|Dh3&h86NKRvQoQ=Af+U|dJQ&B9QXKj46_ za&{Dj=OOf&1aJk`J;c45eAJP4GEch1Ze}%jv+iQ01sw5z*1QUH?OvEa024bHQnJFb7| zA?h!ChPmUkXDg)LVBE5{GU{boXANUSDlhX{GZ`4hYKwj-kJ)G6S5)^w_{$9SL}N<` z4FA9|CY>J)N->7ro0seURHPw^$|T9EvfT~-ZpWQ6s)lgPv%}SR7imlJp*%F^gj(R| z$&t?i;}yK+B+#g}v2d7>B-y#AlcGn&dEnacZmcmd|!7$^1hXmcmA6k>e=k;i=lwH}nYk700ulI>{vr$C+J z1`Nx2rzjQOK4RSugPAEZ!?GpxG;H_;AF*vrmQh%MBrZT5R2JkGabOb_T{KhG+v5aD zb{Mu;(C5ig-Q2|68r_yPB7hJJJO}pMZ?4cFVY$eB z1qD+G$T}msTtLtvaCEXhb7}egJg;uk`eax8((d$(YIbX}$pU$7NYX4qUzWl*;`*t7 zE3JiS#H{|>?l7D>;NsExNcMB2%2(w#>wg|{Z25^-&aLb~k4NV(XIo$0<8xo*^qAg$ z{bAVj*G>Ua~YF8_raQVc>6Wh+p-Cth90b8XP#5X*srv5baZsUtOh(( zY=j#1o!l8DI_8Cj$P8Ks9dR0=2yK{s%bOtR;ka?U#E3eUZ)YUG3Vcm;t3>B zWgfS0Y5687+6CtLPvN5g)%(nBZwL~$OFLy}ECBeuxQ@eMt_YTDZpDA2JjnpStCt*K zY8J6v$Db#D0+{5$0+u|O*)CRHK$TmUHF2}YSkVpz4RQ?hbT|(HLKPB8;rI5obA9a1 ztX$8Ci012K#jg3`9&z`cj=y?ZVakXpBPZU*&^7{1vcVi<{%t^MjS76p=$0-AM`ra($e~y)<`rzs_#iP&&sy-Dt=kTv@FwfQy9``8@a0se#q~tel zWB&J#f{4nQ(|9~dY%Dy&fI8H}@oq3ykcTfI*htFU z5q`i51u+*j*^~c$-XM-#957)Jp(eCr3K=i6`71t zV74KRIzH`FYJ{+mKn8<(eg|5u(9yMMFf#Hr-7@kydxy$Y`Z8qD(BsT*5{q_`@fNzn)|jGKBY0@rla!430wmeyE2aYZ zI9jg?Vrx96L?&aG$$m7hhj{YjNns=BQ;^EHwp9By=N&`b5HcP^Vs14uE>9*)cMY+S zC@7V~FY3?|aFi49!xc5*Ca3w~OyF>NHFdqR=RK`*;&}c3NLJIW=`;_tP=kJ}*~`mj zwXplj)(P0r_x1i`aM!|aeSA|KTIM>gG6! zCr!2P9-^|%_4vFD{N(x!d``2qs3mby0558IJ{_Z%f%Djai5_s=f>e$n zc=}^9GCAWx;0b+0ZXC=_yN|`-nom7eMwr*A~#MRFHcy1lle(!zTB`^%3x1TDR-x z*0qG+yorAb%u|NTcuEL75a|ze&H$D*arRv6Z=v*yaa>c$C(-~3ji%^1Xc-meg9uua+9iOcCoW;-RiUec<^9}i)S z0CdR0!tnfwFg8kY)*!$+9fj;1+{LE^S_ui#FJ_1+y(drTzf;W~WJ`uI4{e@ZfvOiM z-yVm&kPKC!sw=!LXrNS30~nS0rRhpk%L0vBLDIblVnsH;lW_iet388H@hy(X3wSB( znwgHz=DnfwE_AZFljB;3a9D-Ilu=wMw03{4A*zkFLd@_1wTt`|oQ|Q) z8|(dNyxS+{q_U6No{P$O(S~vuG_EhaESO}|4fy<)>5yb*G12w=#^QgcBi1hgWru_h zRXPB}j0cy`^tNYBfeF1*S|K;pYC^pTxTE|44*Xmo6+TB%@)?7ih5lj@wA7j0r>f)7m2yA)q-_cjP*2OOszO~Q2Q-9oeD_p?=ddS zj8_0TZ}WoDq&YnYN33UT0s}2A#$pb{YJ>C62S|;_Mn`qhBZ`aPqrdb1-GDHiL_7}l zZVOCF_}{6=n4Kn7Wc}^}QZR@t@L1lbBZl4@TQuy2j=j`PBtXUa8V13?#NUj0ZpF)` zy)n{g^7t74AX+?)oBFo|cEf@vg(Ap4u}Dc7uuNC#IpEp;vjljZ3sDasdBTkH_5zD2~@J6rrJ{kAU^y5 zu=dtbQTFY-HzuW|h#&$YNQ2TXND0W$ji7`yNJ}dSNGlE?-Q6uMC?G?3i->eeO7C;z ze(vA%?sx5Xt-aPef1xg%nd`c~b)LuZIoyu3ELyHq?f2m_)@@9q#O=u0w~zy}VkG*6 zXLl#51N)W}MCC=VrBJ!8+|TUSx-RjeO4qt_eCD?)R}9R$OAZ3+c!T|Z!J^k<{&8M` z_MPRTJv^5uplbr(l|zS?ub(8r8_@UW{8TI}gYt&MqfNVq_!``Uh(;(Gflv2QbQ3iS zvI~y^?bzt6CIGn2N$UnUagCsl3z~i?;0MCqzWsTw#yBdu5bNyYDb6Jx{^E6V9Woum zb6={{Kc9W()|z~$pC(*;us@C|d$_);cxuSQiv&Z)ENsP0DeM~*QyNAblhab_= zhTF$`Q<;qUxhw^*L6fqTota?Zf+{j8UkcJIoC2Dgnk@sK!FpKc5;i#V@r_{^w{KKR?eiDnZgcJYrMm-KjlKM#Z z-IM9duUz*O80ZPGgkV^OKIN@W4r@{TZcq%YI-SgZ$K@e;R^Jc3rd+^;;o*tQptsT$ zm;U&%a}Id)rI>k$gE!Z7B;)zn_|YS2TgnqaU;?R371_x|4O0yU%{V!!*VRP*gT8C4 zs$P?~IC(otrlzaw4r@U1?=(oE)wynf%Ze7#LMYFyQw^HIT=Kd&3rrQ(spb*t8R0rM zbrBqSb_MZ0|0N96-peX9+w z8ra5xUQ$s;i^8APU|%sWPASNH?TWI=QkYaK*dbwR9bRjl)u18vv7 zjUh6Fi1>Q%6+?ggSt?;WzNy*wxxoy+;g{Zr(=J~1%ld7|wtK4`wRd<++v=MBJ1)9XMcFnwX~jOXRV2%~_e2($zlMU4=U-#VuBd6|#wj;GW^`!3(TD@z zB569DKOy`pcILa0Y7#e%Jk(I5S46I4W;GvvL`Ul zy&NbM88j%?9IAbRIq6?IpQA}(5>dPbm0yjU-T3O+zTV#6pmWUPe3lJP0&r3-bwo!c zCKk?_Qv*RDBeO`>_1)106zhMhDu1*yq6H86UwTahT%oAnJx#}A6YFI;AnWk;$ zK`@IbAK&3rHu*`{5i#mn{b;m;^OzMqJAq_zZ2imKmC+07#~@L`wF>r8#2X-pm$a^g zZe%j&#~lwtj_8LC7hsFC;%A3}z=YHBH}vUPk-7Zp>_#nOe>`VSbqzy-S{A}>N>(&X znCbe}DhQ(9N%Y5D^vG#y7kcuBbdE}#3$x47@gzPR@jc{j_VoJW{lo~^ihQW4wFjws zqN29MrT5@}B~{qfpyIe`p_qY3^Z4u+VE1<~oWD85PK+Sxy0BsHSl zlxMG`MurF8YVuDnGG`^U&nnxiJ-wqAVT_V*e-xe;?3o^*m@}lTmU=!QoFmUubUoQD zPkQmeM7E?thoTBMRpZjrxsx9qZHHN{Sn7&lP!@p$?ts(9+^4- z=)B~&T(B^hOX}e=xKG?my59}v((O{4f&G~UK_CR*=}}k6@N3|No&0~DsZvcm`Q%@V z&iXj0tD!B-1c3!|v6q#kSOqS?fN}Ov1bF>~81fli*`xf67~K6(P|Nwr_vU(hyx3<# zRJIq$c>`uUOZ65Y2t!u2gzqsd!E@-|uSU~;yIZe7cUSqPJFgeBc zq{4nrPZpW1#gryFTVRBdlVWd)5EM3AA+5SC%Jh_FVG&V47XdHuzXFXVz)v z%5X>!E8I^j!^FrK5E!@w{}K8yQDI32iD*;?wzkEMPqXe4E$$r->M8<2+qz~Ii0P40 znJw~JM&L9&_I40>q8_>VmZ}Z~UiAs)N4bN~O-!UEB+@c6u3t}-y^@}D*(_a`w$3#7 zF6shDS8C00zRrAjzNJz+Z5shGF*?ExBqClbHz?nnn=Ko@iEQ2GLnN&~d~B&(BzWLe zj~9M}50-R9rqCyi*(h3x&YNZz2KeiyubdFx0H@ziZt@V?z@>>cO>cZ-4WEKH?BqNj zIe@9KnwIYxAF9l)K^8p&u$??suN|>1D0lx1@*iJy`Vg?@*nzfKb744T3T798` zfn(%gWs_G>$du%^jI{^maU$F=a7k4PblaA_2MD=>n>bbjAsmGs>>6c0thBiE7ls!-D;|)H`Kj{B+ zlN6p!q<9qO`EISpmm_I9%pU=w%2zcRuaIWJ$zdxpAAETZ=e&Sm0|8;RwL+*KV<&*j zv*wJDU}31o40C29B=v91_oO_~>XAPUGSs-)IxoF}T?m?OK}H$psU zV8RyWS%^Q-grOpH6I=ozX0@U0tq3>X7^9mxXmO;!dh{|({`(K z2wzqgd9C~GJFIrfKJ4{O88_r>RojV=IrusQy8yLjoor7rvBn_IQ-$~kp-ZXbGcBpA z_u7?Yw4L8KW`1vHWHmRclG(a@jJcz~JJ@vEb+~6Sb@Y9y$)d}~JmmeLK(k$3x>=M3 zyXAf>Z(&!$URS2R$JA-lYx$V;YW@%U79m%Dd2A<|cxr78FMhRs+d5+gKG-TgyuTBg zsu*idI-H`oNmW(TzDlDcdn|colN+GqFDaFfre@x`Z>U|q=#@?MmGC@Kx6&q-wruKU z2UALqO`B_s(jAK5-j#o7KNVZ=N2vG+c;@ z)Ry!2O+0oU-NS7-vL4OT(6_MOpncT5_Q{F8>(u&X&6^AZ&-xLrs`?S0uC0Nx zY!BBxa?hPrDpoCSi?KJ43cAK<8l%M@v?3SgKZg4ew-YgIFvCGE?#qJT8=^@(?oVk) zxC+^}$+^y{V%-JGKnWOLPR~7%o#BIiTMyIxMUyHugb3Aey~p9rYE5)shN1!eP(Hs^ zQw~eSo{dO~d+56tV11V)wFF0qyg#nAQd^&zn`!>^>K~hiV;%)%ngd>aktERTjPLlP z%h?mZcu`7B-OiAiTnM4c_62kLoliM&a+2N;qyoD|~DI{P{| zd@E2|^;-L}*g>UNZh1DM&zfIWx2hc$H#@lHGag5mjE@c&adf_P8H4K4)_5U5R5z68 z61wNR6W7COfH&c+H9x0nVj~d+W3nLbz9-ZObv_kWdSp% zyIppo5~+vTgdc<|Rp)o0GpZbCma=-dJaAEaR#f<5E(Hdk{Q^fHIa&JfvP<&ja$Auk zY4y3n!n8Dr@OEcCl`jpEQQ2i`c->ofQ^zPLEO`{-5BQA1#&?QhOP-`Gv=gJKO~G;$ z$hWg@aGZcX9JsEZmyTv3R8_=?3nF5M_7Kvo9l-=H^Q<4X!x3+1YW- zYGKc|SO0p7(4O8lE7u;H-H61=DuSXAnp~$1auiUjy1EL?LMg;_zSNtczqYDR{)#*x z&_NZ5_bS)?x~e`@&njt6p>3VVi3A%Gh!V^`zdDC(fc&4uNhR#n2Ocz;M_0(d= z?BuuuqX8t2L8D;e=^St6M*@dcIv?JGqZ8BJ-f|FOIS%eP3v(^LWVz)qJJ{NU!AJn>26RbX!6^VND@=S!-733Ts6QNzfY5ClFqEl8V?RL23o7*- zAV4fY#T|UAQ(+WJF%Co1Th>yC73(5#Co|O-nw7l;e{8=kpyhvHG1*5(#WH46sHB;5 z+U(&UOvwd)^_H+te@bleDeN}1^L_eAmt`MYwN7wQul?kC&3>JY)I;gN+UUC4ufzGN zmBNi!>4x;fOTMFA8tDD39__;y6B-gH4Y8N6cwHqQg&mp9_OMOPP`7hBmrpw;a z5yOk%lRO5Elt=nL5+8hqA0=}KualCtH?mnOD=XXC+h@OPwgr(CwC|uPvM_Z)y^6vW z*PLy{@psG&+>X`*&aU=$H1h@W*%AFm?#BMu##!f+oh59dWf#h2<>ZXTx|s%Kn@w;f z6X2-*wlg0xD?hb+LP@dJBAvbdkHPS3an#RuN*5kpInnv*sbojYJ;KbB1};$6x9V-CMfW;lDK)5-irR)W|avahfA%@bv3jLf1MGZf;nWG92ippj`^RN2+!e z_U88Fe7;*IGq(v}6{!t6Gl{Up)Jv#M;`R%#z!-L3nMm!Tk}hXHN>ELgYT#Vh{R2xL zh53>cI8 zPnnpz|ID<4Gxpr%>>K(aOPonc@tMzTiX&vW7>bolPJ817y$zeQ)r{#=9sMi2e;ncv zm96EH>$fH;zwj5J&(`Cszen+Ap&R9=h+xo)oV2cNogwrQ6AO_tG2>IyQ1?guo?VSGnK~5ufLTI`<5r9r%cXv8%dlnqQDR33cjAIKxn7N-Nu@| z2UbxoU98#9{Pc0+{B}mM&|sK&@<}JPw=Cc%EcT8?Wk>lhJ0mVq`N?wZqm$`^<(cQY z6T=7|&$M`=pqtwAx=RhZsXwk@cPru@YQ!F|`Of7k{~iij0-x!NP^>M*F0EB&%r3%e zbC~?ONxwc9&tyr(CW@F7&E!y3SIeyL%-5=S-_O`-Ks&6KBiU6BJOxAaQZA`<#PQox z)>=%!Zs_E2$&%@}U1mMEDHbx(ZX3F8muwUf1JzZc=PIo78Vn?wa6q#HFW1#u+tpj8fFFF2{oU z$NUsy;kY=bxtXK+wxCbrx4(UbA6GyaF%HNRwA5QV49@S-)hkG_h)Z@3ejHx*m2^|F zsc3B*x?7_4YAif#FG5_iK2O3d(4OLOY~w(Mtb^z{>~r+zga}Ffvj?*N?=!S|Cu86p z8pN>d#Ht0()`E?0j63z;?yA-w$DU3?@6W}z3IhMCCvR{9@Dl68rp*#JUhSv_&C&It{7VrZjl1E{ruWj>7+f)VX2aSn*AL`V1q4q5}H!(x;g9g?v1XC|LH{*0J0Iou zYxwt7@Hn1s#WMmocJ?7ZM8bx4w4*hW!k(m))f4xoe}e|tvthdx78v?g{Xd|SwPaRX zG!X}kYg9xm?<%CkI76AyCt|A za_Sbeq)LvuZQDBp?s|46fw>QWv8gq-u(R9r*qe|nJlURqML_uSE@tGMK7fk99J>y_D^i?D^kz)%zyh-YJCL&RfGZ|w&L25O4%@bCx-97LNW zpd0|@H<^NxL555Z}`23rj7oJpe4K5;HW@rMMRdbx8^w$=|5E~y0 zJ1)(H3GLoXAE)m+GFnW0;#$7WnROVKtsu-fJuRIO9icOdh1m6O`a%}n{n+DIfXtqP z+qQ`8I_r4cm-MV4A0(lXkqFz^!4D5-X~|aiqqfZ0N=tcJMAdpi%@u*p@pVtd;#APq zyikuyra-Sn&JdpINdRQ19nbRwBEeEKp!5LdIVFiB(=8_Sj!(36-@QJ9Sp)ATPaX*B z&6SK{3*{hiOAXj(rK6T*mrz%3+@mg2Lnw!H5aDK0)v7Uoj(-j6|#fY z&nURTaHYtHtVXxKfAz{0a^iiyM17YW#R89x;vDOg&`n0?$8lo~ZvLM>rG?6W4Y?Uv zW0YIH(y@g#hQa=^`IS^8v(qb+c~%iUs?`q;DW8{bkvFopxjK@)!JSp%c^*Q>S zj1uqv_+D>S*uUP*^4>4_hW%F7ch;De(<;y5?^fe%RkIn5h6VL2KO0G}-g0o5vevMD z9X6cfCi;?%`tr8xmxBkqmfuU{=Uyfb-y|gbeH`|{#OQgpBU93pwEq{|p9Gk->bWG6 z!D|;WOb{WSCP>1(NXNX*yviza}We2-@hlhvboH@psT`&tLa_(SPj<4q4i;3sqaRjdXmi|i- zh?m{hNT7DPgqljCcJGOxlE42~4_ROUdPqL;htfp`adCY~ej$>^$^4=1GZDRcL=v-8 zV*W|Nn%cz(woJGXvKK@AGcFfywuTGkZ?0Q%u+HYmHRg$(WHX+^!05Vo5Pv`VF{QIHKGH8M76WH8s->kRGH8YXjSZ6^8A!p2 zs|_G(zsZkJ7l}U`$%@qc{Z~z8?{@i6dH?=EIK@Ol%O>kv!Og{%+| zbNus4O_V2?>H&1<=|jjk@21Nr!-1xtdZiC`H%uslk!nW^>N4y#GU8}tAM7?l8je6t z$W(#C{x~BYeB=H8_@2YA=l%>)SgQ|aUQ?J|r^fCEa98c_F**N?Na~|i&h5{(r-y$_ zIzihr`BWZK6`6lp*eeEPv_vFb^n|axO&6T+QZt2Rs-{|N&-NTYHcH!ct4lu* zJrmX2n#Dx-rL>$#{!^T+SEniT#Q8gyh%-t)jEEJw!u0CKtacMJ=>w=vVxl%uZIf-zqwamQ#R2hM# z7!DpW=^kZr?j-T81=+E0_fB4^<;uCcu1!o2>u&AOPqFD8&9qW^jM3B$0-{HMcQWb$ z>!H!tBFeE>>N2+S-w(EzZK7Dxw|8^dTQnuq6UG+3_ZsvnUbZFq? zvgCEJYJ;@CF~%gx_f_vc!P_G3SdZ<7Q-{Tgyq*d3rj@XkP_q4*`P0KcHM3>$LES;} z5q7v2i&wmNUl2QJ-3+6zE62$E|V?f+~{PfY0MmT!ufV!{e>+Z6u7 z{JmZO%@MSbfVdlWJT0Nl8Q?;AurI-MZ`y%K}X0mQ)ZJVZw~`AD@-K_)^4 z;zKnPvr#z}`7u1J5UUEyou1Ant-Dy>qn1PFY{SxCMKNa2ke#Ol#Sp!FmhuzUJLDRSzY_7x0 ztjXK+Lk%Zm^=5~mSClx8v}mujlo4{Z$*a`toQb9?jy38hl?X=5wSst%Y;V6!*1Qb; zETdPsb!?-4-Tr=+drG$P`Ve8lPS6x*+xLkMVHZ68@q)SmQrz2Z+Oo_I9_cIUNm{YYKbA2 ze@5QQxTp87%TD>hWk6j?eO@Le-z0ui+o6*4hYf9z0e_p6oXx$T&LWSI9t5-D)qE=v zW;K4^o)_2T+kx63`^x*qC?>P@!+m9%#YQwewKH$ftZRe2A{{a+`|U??c9eA ztmeIK1Dy(CZAQ?x&AHL}%$DNsszBh~BA)%(7#S>2>9){yv!L2&FSJg&#D1Xp_U`(~ zGNbL}_K)LA5rO-S)Jqe-DN?H8d8uEQ7G^Q%(obDO{F-|&iHI_m;l&A}B-I$nR>q?| z|8%S`S6(!#>t5f$jC(#>>M?1FwDZ`13LsVe&J6_vk{6(|Xn1*fQ7*#mqiu3F^6=Vpc@TwJL31 zqG6P@OGjGTvCwg@C!yU;5x*#cc%?U<2Wj*uK64G{PYQEJp^}t%PeNfA zX7MLSC?26H+$kwpZ=RQymIeg|npX6^;GVqMuiD4-j6ObZJE%kI>RykR!ml}G%-sX_ z!Cw(Wn983Yt7zTqXqUZni@orxMap^E0Kq>(Q7nvf+94d2g1Hx_sky#OqslHwcpzQl5;F1-jLt36L0NMxg*j94Quc+9Y&IU( zM#Y@!5l_7QvdG|EGVO5w6{mkxg#HbP2h|l%6VXu9qR14|s%la_>A=Kz;cD)vuV|7? z@zD84LXs%8XsLGo(2cJG7xS_IOTI5YX0$f8eF$w2Ccl?Sw+()i`)yiQOsHP{6({wN z@&iE@^k;bKJ5ds$DT9gU6F)lyl||;AC1fNwAiHz4mqo?D{^Y(gJ8D^#P5$jYZB+a0 zhu>A`vjhglXR={Mpo5yg60wK!T-B=`s}9A4&dAuDc*@jqLk!bvqCAklDEOho z9DdzqXWw0rBh1Legi6SPkDk8OhCTG{+k2N19r&~41soO>D|i?`UCKh3nPoRVRW$Ji z+5Qu@C?MX=Cv(WYqW`wO7Zj(?ddc*o_i4d^eafkLoq1NG=-^?9JqYiLl?y0Pc2^~4 z-2#sv=rkFfcCX<2jkhM3p`yJ!+W;^`Wb;wVxyxJX5bUR!SqX7*X11-Y_5M4kg z&*b4lZ{_S_V#)Ui+Qe>pnQ&?>*ShaU97dRTUZ`120~Zl!_I!EWkk37Sh~F1d{J+cQ^FAoT1x0`QDeP)abhk*aa4#-! z=8wCGPvbps=z$EPMel1A^D=uwr zDeO%`?~)o{+=a{w^Z25MeLY_9e^@rHKlnVF*1rf9TUCeP;boEwp5jwq~AZ=;%>Aj2R&&hbS* zU63LPXM+I)p;dJ^#H64zM#|)EK!9ykglAx`3ki2uHAwh?k-Y3s4cI2Lk)mgEQEceM z4-mWCfM}-Y!7AWYw_#qnAPEyTqjoAb+?N6U(p4<^En}_f!wS?iVC>xUNSQjsf-QY078dM5m zB9KIfIUtce$nySd61_P`^7tDYGt+u!mx<5A!I>;(ppR4Z2W{{Apt%CapRh5eR)b=w z?a)Itv3Pv-xkBUPIKJNbEpR&wFULaa)m*F%jHvBvYq{Nw1=Nmr(Z~SqZh>AUW6a2A z+l6nXeBjr2;G>A3v}ZE1jkA-p_Zf&j5H~?UXDP?rS$wt(iO$jJaocmP3#V0z7o|>$ z9P*>aGASGUYg~mXGFp_kl>Sz=v{oDg$yKw&&4K-+GK6luQ3}`7u^Q$o2W{H+&D&3u z3zyexUTk`eg?exV*tZ0sK0j8>hN8>K@nP)a4Di?VTS65Ec;m$a5u2`cTxj2d=>as9 z`Ufm|mK_A@Tkc@>%LxIUheaX!z$*5Rj52V3(#04a&cb$lP$-X-yq@3JCik($@&8G|{V9gQ&{U`7!{s~wEDT+YrxWxI#r>sH z{Z2nr){luErN?fR+$CA@JEB>g9_*eHQ5^HK!MwoiALz6;vA@;*ATu-bbh}?6*)|Ri z8UhK%{$(`UuumQRqD4%PNLxHyZ%|P2NzRat)muK(WcRSN598M9HGmElbmXe_pr$gf zw&jTJ+-?C~w*b9|1i{%w_z}1H{}exqVWMsCX@e904uDK}yY1MVt=Ccyl0$sx)hkD45@M zU^kOu)=72$pMk_jdSstDiC{R|uELCs{q$06^;2b@a90fJ+S65AmXcT^9UPKgLS{zB z>>I7wC;W^S1VJ<4J9s6F`pIWW*zW(=xgk*~hkhK_hnIJ{5+CSi_V*s9F!nC*ATuQ) zzva}_Y^U{C2lfkXS#m_0s#-wDZ}4p2HPZUBRn4=iEzv5KB^iQHYfY(Y};t zp1$SA&JniHP>_I1J6?Qh2;p5lXT8Hn0S~&6#T;$4JPeKf{vjPoQI%(SeU_t4p4aC4 zQG7-F*8t+MgTYKF%eTW!`{CA20GKOM2633 za^2N9US?*wU&t3k5g^r)AV>ag(3Ej_oY(l2FEW-`cjlLo)*Y;e%N5z%iz9(dWJ2|X zSxX-Wb~SzR6Q5cS=0sFJZRmWCE8Hp(A5h;Q86EAAk`X`5a_7#Mb!MvE9P=lCFV3s} zsE^@GFZgoE+U64#@wPg<+^b)k3A(&y2%Ow*26-PP!zwg1bW9B?-qMV$UQq*f)Csyy>_S`ZR2xE zC3Yb!{jstZ{efMt4Ro+07G}N7Ig)q8E5Cdz2`9ZYyL!`z?uhhVgefU=a$PzjJ#%Yk zH#7Gli5Y4OHDts%Ld;)yWv97fWi}#wY59${zDM&9%96W09EHeI=f+Uc_UqZkt+d-% zZz46yyDek$>R(qlyW<*c{Z6N!D%@+Rb*bKBsRRhbT7%i{>AiAiocr9Z5L5mSbyozmH z&VQMzyF?l2Z&zjanqCujLQ!>grkdFuxX3mL#eLZHr9*LX2!v+{Xt6Ldj>blOh>M$` zmEmVz;5h;Oi!YDy6NH83a6!CS?nDdeuerI|S#FEL6zG35@=Fq2%wK5l=wNX?+1i$W z)vsP3Ppt*PSz|-1<9!3vhtREY;>*m+lW1jpVqkLQ@e1#4R&Tj!Z?0XChHD*iE|zPT+h};Q z#wSC%Kr7z!p1p6ai>%k>+RJ`HcFiIGEi3xdg<@DEFl*CgbEBVXO81$ze=ko`@+c5LlQ?u0 z`K#BWjlAoFDAN=gN;I`E>_GWb1>INIxB$`$UHTEiWQGBG`mV39O`wA)$ z3;!)LS-86j5q-)gO?}X7P?2}89Zc1cNa^ZfO;oqwh}OKVva3oMha~!ey?J;{Gbu8R zr{F)f$e=bKs-kU=RsCFMsHrxUPDF%*r_r!|-uQm^T#5opBix?a&?~u9&!9B=aZ5PS z_>>nFLq!Bm+%G#MrC_97HZ3(>29NXH_(d&v6tnBneylfeX4ei(&%Knle2DBd`ZiL^ z%TC{#IVep>TOEYsq809)&%u55yq0Z5I=6D@FF}YRVuk~(-tK!qAo^~>($xsarpixRMUjX`}!HxcrD3B2>{2>R4lT2~(A?i>l-p4HrOxEX8_}O> zGx}{kL?J9HDaZICkJ8?`L_3kf=KJ9k;5;jNFvvut+0o#i& zC6)M!8Fd$&G-Y;n$QmXSitSA-&D#N+!z~}szp*smHFQ-tC+}|#62^~Upj>y6KxyIW zy%oSypQPVqnnXkg-W2kU$|4!7|1v#N@8P!blmVn# zgT|RTcMmNq$6;21?s}UM3g3xc?Td5H@Un#?R~Cm=a!3z!EgT0nv_yGe)* zO4}PN1Bf9X++(i|oWK(|G9!MloM11@~`ciAgCMJ@0pSDni>?9k}lTjAObDvSq zEb6Or`NOaPFwko2eekFdtVQ;z(3BgPWefh8cTmfx3()<{PL`75?wv&ZEA7t>M`AVX z`k;@0GKg%hUE;rcKXh;Z0zyM=eA?x`@?%}j08alQ6FG6S;JY%ZgQ#%o%Q@y(&l=n> zhJGYm?85e@*34IrT}`B{2b~o*Nngs*kvj?mA9TXP@hEYVY`lm#FyM2^aZg$sGw{lU z<5BPtfThDX?QSR6+097{2g+K-O$pd1vMfiZBam*AWB27AWloYr2uc_~Y`6H-T^6WU zn8~OEh=N~0Q-%Zs)kih+4g%S4tdf~jU*{D2JZA1&YV1_4x}|h&5y_yD$ch8KIIUX` zYoekuued|&3to!)D10eB;v2!W){92z(LRY9E%m=`Wi@WO9@rS~uBcod5oyThaQetW z*&j{c&S63QMM@1A#ZsxgMjXEzZKHC2+Nt%G-M(hU!=+-a?YEZeA(EJuJ&CnuSMKv( z{CM+43;4x%2`@tvF`_52~|=z4s2nLh><>5}^k ziH-jY_lUA1x9JaBv8fdKLy`H)fkP`y`=GiB89(eo)?F(ibZ*RgsfSUOP3l*@uIH=bHsvso3v z))z!z(8W}J)L&a$i{5(Vir={{(+6i^2(Cnl90;^l=objm{=L|*S}@VFU!+G|jj9Fr zO+y;|wK1z}z&6~p3oAIV7~Tzz6Z>+y^NW*{l8kpOx^A>{QK)td)zVQ_+bqpv#3z8? z;L^nzCUS2Ll_x$*Lm$gmDQ1Ubo8KyYwBqCZy(fFe>}vGN@0aL|Z}Qe>nq2R4*r;06bqcl}nK$*H zW6z<&dw`{YNIbWlrE+je0>e{ELPAl-!J(|0PxE$02@ZKk1{_+;vO%r!+{h@aTtSAK z;OR4D0g2)>P_Jt(EPu7GT%T&tkI%j;a9-e6Ldn_T`qgDW>*wM#7b4*PiMU+4YE$u+ zt#aje36b6$qisnNp@X$sed#aFSN*P%--)7$$UdN!?h%qFHf(E#b}r97c1l#Ukyi)} z>-nvx=QKw#+U+{1l%O2cKXrsIH-{wl3^sfP)(=_+~9-}e;dUhsTv{hSbm z?W~@wM(_p<>yoKT2{?ch{a$S7-)o8{fG+d-$%5`SJEni}N1(r6wRD@%4jf4D_p~ zNJ2{L;MmhK1Lp{`=%XC-p0KC=mqbF}@Y1DAH+d`-O`4Ru6NK9x$Ly{zNZ0}g&p&V# z8Rh5)xUEkb+S?${*09zvAPYD8lw{Jq%)VF)jv}g4A)rk1mY8foBJT>#6wD(R5oFhD zExJ9+)aT|L;eJ-yabpnGzxQAzz1#T#7mTBj6bD-Au zFpor_tcf)nmjwAAt}=b5T&u3zP{rQZB0m@@89M#7wZF|cPpG8aV%as`z`@H~sVHj> z##N!jsQ2%uf6#<~_#hZJ92t4QKMB^UIBEPICvC@dklDH%p!G&pJUUPcT%cBG3H?Ie zD*^6J{#;#zDMO}m+55j@UzzX)zDF@wAd4$*)eOEY>_L_@rtqrNCq6{zc<)N?++x06 zA=KNNPX69Y(W?O|H?S@qoqNCLtKga+s@R3SM294H}T-{@q-%|JHmBOX+EpB*heSN*W`w;RyCAqy0z%INV!vxRT}UmTCYkUirf$Vf~Is{dH#s)MM zL=JL4lyE5fmRNM#^1+1a4x;_vYeD^ZamSV0d5mfDS_PFK;vj?IZ+a&DA6U{Hty|tw z@%&Z~eG80=jyF_)$47#>`k(6{>htH_IY2~M}CF;Bt{Mm7yM7P9k2$L zTMY})&@?G6jnp{X1biuA%e~Uw29a~g_d|0ks7RxXxXAhI16!y{e*J!lgBzAz+B)cd zSbV=1V3(EZD*L+^Qno>ZNlG#fzG3rE$B^=cjX&WGqQGXkdO`O;McQLYs_vPyrrMR( z`OnWa`JvB`fB0U{^|)X8`whEG|YT{ zeH6_q(U6B~hz|so87*>Bao6XYA@0R2o~WnCDNxG?GT! z8r#{@V{|{dGRQHXR3w=)}zOe zFhoFH<()`U7r0vH!L8x@K|M>4n&9IDuX^x_&x`ZLBhWqd{W0coUexy5q#Fv!2qpYM znA?lU(b6&E6xgXPd2jstsECU9EqkxU3&d5Lww3tmyL&7=3hiGNe#tkVs_+1<@Th@# zGJR`*kZS=8lLIzaBwpLS4`l>yGu%cUNhh+TNphP>*eioTlq1?9RXWVwU^Dee_qwQm zddwZe>D6V=CtSaKTN#A++H!CU5(i4n9(qq@ljQ7S`&r;usRqAN|?dd?L8V;G@-c{_Mc! z7!<`&ZZY|@*PqJreS*5#I0d~)^4GDhE9=(=?+;Zyi|U`VNg4daP%MzA7OHhcG4S3W zfif9SbJRc^F}+8Lt8}8pn(WY{`{#Ib7w4|ZhUd*YRu$>!wvDUUJYDrrw;G%OK@;2} zy8iu^i3s;`n?IG-r{{7F7B+I%-Y_&I=DR6_P2udtb@{a@H4o2_5l8dauUV4Q@{zM} zw~K|0nJ#B`l>nWoxPntd*R~mQ^;UO32tP^oZOF8Xh@!5k8KO`S3)D~sXw?PSB& z(4`|=jxb3p=jrdc^an(eSCF1}z}toFw>ESPn*fyo zBqo5)37WHN^S85#iZ;IBKR}1QgK;e9dVDzSui_DzU2l4pcdiGeXJu_RHs8W)g%qXZ zufz%fC$b*MQZipO``bbEiRP+rh!wd$x(X;|OhxHNV&x8EDPoE+_gpksBkbRG=Mo1pKyOG}&8zSJahn3w$pVO3IEii?Xo7+tMcVT06a z@H|Gtu>in*`TRL`O&##+pTW3)fNkV?R~&3K79V`)FYaGFg1>*>@lgzd$0#2UYEJRa z0!BD)@HP+=+oRYuNaHSh!aQz%{YM_wahD2O10dTv8jp&S_?6=>aE2#JDh}tnO-GlW z<460S)np8s4fRg(41pY{-IMB)LiF>$eqogj&g6`F>gp0%eVEBLJ-RJN8J1bJ@ln7{ zx3f|o;oz|SsTeZ$W}~D-=dvUtvZr?eOi3^FVhU6(*V*^iHm1LroB1f8EsSwiEP7wg zBnhvf-v9SH{h)B_Q$%b$XuRpEg?93*-3AgSGv3_ipgVS#8U!*td%F zun-HRbAIG+Zv5smv%zS%S$yTMR}sFsqH!e;GQZ}1&e8<-Osc;*!yy#~vG5U~6isdh z4d43N=5mC1&sA9sk*{Z|V@%RGF~=WR|C^pUXO@geCx^2k|%n literal 0 HcmV?d00001 From c3d02ca051cbbec4bb9d81f5d68bacb66ee8fbf9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 21 Feb 2023 18:32:09 +0100 Subject: [PATCH 0727/1271] Apply suggestions from code review Co-authored-by: Toke Jepsen --- website/docs/artist_hosts_tvpaint.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/website/docs/artist_hosts_tvpaint.md b/website/docs/artist_hosts_tvpaint.md index 3e1fd6339b..a8a6cee5f8 100644 --- a/website/docs/artist_hosts_tvpaint.md +++ b/website/docs/artist_hosts_tvpaint.md @@ -42,7 +42,7 @@ You can start your work. In TVPaint you can find the Tools in OpenPype menu extension. The OpenPype Tools menu should be available in your work area. However, sometimes it happens that the Tools menu is hidden. You can display the extension panel by going to `Windows -> Plugins -> OpenPype`. ## Create & Publish -As you might already know, to be able to publish, you have to mark what should be published. The marking part is called **Create**. In TVPaint you can create and publish **[Reviews](#review)**, **[Workfile](#workfile)**, **[Render Layers](#render-layer)** and **[Render Passes](#render-pass)**. +To be able to publish, you have to mark what should be published. The marking part is called **Create**. In TVPaint you can create and publish **[Reviews](#review)**, **[Workfile](#workfile)**, **[Render Layers](#render-layer)** and **[Render Passes](#render-pass)**. :::important TVPaint integration tries to not guess what you want to publish from the scene. Therefore, you should tell what you want to publish. @@ -74,7 +74,7 @@ Render Layer bakes all the animation layers of one particular color group togeth - or select a layer of a particular color and set combobox to **<Use selection>** - Hit `Create` button -You have just created Render Layer. Now choose any amount of animation layers that need to be rendered together and assign them the color group. +After creating a RenderLayer, choose any amount of animation layers that need to be rendered together and assign them the color group. You can change `variant` later in **Publish** tab. @@ -107,20 +107,20 @@ The timeline's animation layer can be marked by the color you pick from your Col Render Passes are smaller individual elements of a [Render Layer](artist_hosts_tvpaint.md#render-layer). A `character` render layer might consist of multiple render passes such as `Line`, `Color` and `Shadow`. -Render Passes are specific because they have to belong to a particular Render Layer. You have to select to which Render Layer the pass belongs. Try to refresh if you don't see demanded Render Layer in the options. +Render Passes are specific because they have to belong to a particular Render Layer. You have to select to which Render Layer the pass belongs. Try to refresh if you don't see a specific Render Layer in the options.

When you want to create Render Pass -- choose one or several TVPaint layers -- In the **Create** tab, pick `Render Pass` -- Fill the `variant` with desired name of pass, e.g. `Color`. -- Select Render Layer to which belongs in Render Layer combobox - - If you don't see new Render Layer try refresh first +- choose one or several TVPaint layers. +- in the **Create** tab, pick `Render Pass`. +- fill the `variant` with desired name of pass, e.g. `Color`. +- select the Render Layer you want the Render Pass to belong to from the combobox. + - if you don't see new Render Layer try refresh first. - Press `Create` -You have just created Render Pass. Selected TVPaint layers should be marked with color group of Render Layer. +After creating a Render Pass, selected the TVPaint layers that should be marked with color group of Render Layer. You can change `variant` or Render Layer later in **Publish** tab. @@ -143,11 +143,11 @@ In this example, OpenPype will render selected animation layers within the given ![renderpass](assets/tvp_timeline_color2.png) Now that you have created the required instances, you can publish them. -- Fill the comment on the bottom of the window -- Double check enabled instance and their context -- Press `Publish` -- Wait to finish -- Once the `Publisher` turns gets green your renders have been published. +- Fill the comment on the bottom of the window. +- Double check enabled instance and their context. +- Press `Publish`. +- Wait to finish. +- Once the `Publisher` turns turns green your renders have been published. --- From d08c979a2145ea46d66c52408913081d3f4486e6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 20 Feb 2023 08:07:38 +0000 Subject: [PATCH 0728/1271] Fix setting scene fps with float input --- openpype/hosts/maya/api/lib.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 84c6e6929d..509168278c 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1997,7 +1997,7 @@ def set_scene_fps(fps, update=True): '48000': '48000fps' } - unit = fps_mapping.get(str(fps), None) + unit = fps_mapping.get(str(convert_to_maya_fps(fps)), None) if unit is None: raise ValueError("Unsupported FPS value: `%s`" % fps) @@ -3454,11 +3454,11 @@ def convert_to_maya_fps(fps): # If input fps is a whole number we'll return. if float(fps).is_integer(): # Validate fps is part of Maya's fps selection. - if fps not in int_framerates: + if int(fps) not in int_framerates: raise ValueError( "Framerate \"{}\" is not supported in Maya".format(fps) ) - return fps + return int(fps) else: # Differences to supported float frame rates. differences = [] From 17c666d2f398406afacc6e8b5b1d8c12039e5bed Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 20 Feb 2023 11:47:47 +0000 Subject: [PATCH 0729/1271] Code cosmetics. --- openpype/hosts/maya/plugins/publish/validate_maya_units.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_maya_units.py b/openpype/hosts/maya/plugins/publish/validate_maya_units.py index ad256b6a72..357dde692c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_maya_units.py +++ b/openpype/hosts/maya/plugins/publish/validate_maya_units.py @@ -4,7 +4,6 @@ import pyblish.api import openpype.hosts.maya.api.lib as mayalib from openpype.pipeline.context_tools import get_current_project_asset -from math import ceil from openpype.pipeline.publish import ( RepairContextAction, ValidateSceneOrder, From 540a4977014914f4c453031522519d16d7342f34 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 22 Feb 2023 03:28:50 +0000 Subject: [PATCH 0730/1271] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 72d6b64c60..bb5171764c 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.1" +__version__ = "3.15.2-nightly.1" From 8f7b6c6a0e9f8b708e618fb5fec9277305786859 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 22 Feb 2023 06:58:20 +0000 Subject: [PATCH 0731/1271] Hound. --- openpype/modules/deadline/plugins/publish/submit_publish_job.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 425c236b7f..c651782392 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -962,7 +962,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "s" if len(instances) > 1 else "")) else: - #Need to inject colorspace here. representations = self._get_representations( instance_skeleton_data, data.get("expectedFiles") From 03d01430e60f5d4ec37c1d5e1e8df2868b7f6b1e Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 22 Feb 2023 10:27:31 +0100 Subject: [PATCH 0732/1271] :rotating_light: few style fixes --- openpype/hosts/max/api/lib.py | 8 ++++---- openpype/hosts/max/api/lib_rendersettings.py | 2 +- openpype/hosts/max/plugins/publish/collect_render.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index 3383330792..4fb750d91b 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -133,7 +133,7 @@ def get_default_render_folder(project_setting=None): ["default_render_image_folder"]) -def set_framerange(startFrame, endFrame): +def set_framerange(start_frame, end_frame): """ Note: Frame range can be specified in different types. Possible values are: @@ -146,9 +146,9 @@ def set_framerange(startFrame, endFrame): Current type is hard-coded, there should be a custom setting for this. """ rt.rendTimeType = 4 - if startFrame is not None and endFrame is not None: - frameRange = "{0}-{1}".format(startFrame, endFrame) - rt.rendPickupFrames = frameRange + if start_frame is not None and end_frame is not None: + frame_range = "{0}-{1}".format(start_frame, end_frame) + rt.rendPickupFrames = frame_range def get_multipass_setting(project_setting=None): diff --git a/openpype/hosts/max/api/lib_rendersettings.py b/openpype/hosts/max/api/lib_rendersettings.py index b07d19f176..4940265a23 100644 --- a/openpype/hosts/max/api/lib_rendersettings.py +++ b/openpype/hosts/max/api/lib_rendersettings.py @@ -62,7 +62,7 @@ class RenderSettings(object): # hard-coded, should be customized in the setting context = get_current_project_asset() - # get project reoslution + # get project resolution width = context["data"].get("resolutionWidth") height = context["data"].get("resolutionHeight") # Set Frame Range diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index 7656c641ed..7c9e311c2f 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -31,7 +31,7 @@ class CollectRender(pyblish.api.InstancePlugin): render_layer_files = RenderProducts().render_product(instance.name) folder = folder.replace("\\", "/") - imgFormat = RenderProducts().image_format() + img_format = RenderProducts().image_format() project_name = context.data["projectName"] asset_doc = context.data["assetEntity"] asset_id = asset_doc["_id"] @@ -53,7 +53,7 @@ class CollectRender(pyblish.api.InstancePlugin): "asset": asset, "publish": True, "maxversion": str(get_max_version()), - "imageFormat": imgFormat, + "imageFormat": img_format, "family": 'maxrender', "families": ['maxrender'], "source": filepath, From 3b432690ace63aa1d69baff80de3ff3d86c9c8c8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 11:00:20 +0100 Subject: [PATCH 0733/1271] removed settings for plugin 'CollectRenderScene' The plugin is not available anymore --- .../defaults/project_settings/tvpaint.json | 4 ---- .../schema_project_tvpaint.json | 24 ------------------- 2 files changed, 28 deletions(-) diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index 74a5af403c..340181b3a4 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -43,10 +43,6 @@ } }, "publish": { - "CollectRenderScene": { - "enabled": false, - "render_layer": "Main" - }, "ExtractSequence": { "review_bg": [ 255, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json index d09c666d50..55e60357e5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json @@ -204,30 +204,6 @@ "key": "publish", "label": "Publish plugins", "children": [ - { - "type": "dict", - "collapsible": true, - "key": "CollectRenderScene", - "label": "Collect Render Scene", - "is_group": true, - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "label", - "label": "It is possible to fill 'render_layer' or 'variant' in subset name template with custom value.
- value of 'render_pass' is always \"beauty\"." - }, - { - "type": "text", - "key": "render_layer", - "label": "Render Layer" - } - ] - }, { "type": "dict", "collapsible": true, From 7692ff252f21af427062e1edef1852589e904d6f Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Tue, 31 Jan 2023 16:56:01 +0100 Subject: [PATCH 0734/1271] use .get() to not throw error handle_start/end is optional and keys isn't created on Kitsu sync if data doesn't exists on Kitsus side --- openpype/hosts/fusion/api/lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/api/lib.py b/openpype/hosts/fusion/api/lib.py index a33e5cf289..088f597208 100644 --- a/openpype/hosts/fusion/api/lib.py +++ b/openpype/hosts/fusion/api/lib.py @@ -68,8 +68,8 @@ def set_asset_framerange(): asset_doc = get_current_project_asset() start = asset_doc["data"]["frameStart"] end = asset_doc["data"]["frameEnd"] - handle_start = asset_doc["data"]["handleStart"] - handle_end = asset_doc["data"]["handleEnd"] + handle_start = asset_doc["data"].get("handleStart") + handle_end = asset_doc["data"].get("handleEnd") update_frame_range(start, end, set_render_range=True, handle_start=handle_start, handle_end=handle_end) From 9eadff95ed3dcfcb214b22c1e77a3ec15d4fa82f Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Wed, 1 Feb 2023 19:41:00 +0100 Subject: [PATCH 0735/1271] Generate the file list using metadata instead of files in render folder This solves the problem with the preview file generated from a previous render is still in the render folder --- .../fusion/plugins/publish/render_local.py | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 79e458b40a..d1d32fbf91 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -1,5 +1,5 @@ import os -from pprint import pformat +import copy import pyblish.api from openpype.hosts.fusion.api import comp_lock_and_undo_chunk @@ -19,10 +19,43 @@ class Fusionlocal(pyblish.api.InstancePlugin): families = ["render.local"] def process(self, instance): - + # This plug-in runs only once and thus assumes all instances # currently will render the same frame range context = instance.context + self.render_once(context) + + frame_start = context.data["frameStartHandle"] + frame_end = context.data["frameEndHandle"] + path = instance.data["path"] + output_dir = instance.data["outputDir"] + + basename = os.path.basename(path) + head, ext = os.path.splitext(basename) + files = [ + f"{head}{frame}{ext}" for frame in range(frame_start, frame_end+1) + ] + repre = { + 'name': ext[1:], + 'ext': ext[1:], + 'frameStart': "%0{}d".format(len(str(frame_end))) % frame_start, + 'files': files, + "stagingDir": output_dir, + } + + if "representations" not in instance.data: + instance.data["representations"] = [] + instance.data["representations"].append(repre) + + # review representation + repre_preview = repre.copy() + repre_preview["name"] = repre_preview["ext"] = "mp4" + repre_preview["tags"] = ["review", "preview", "ftrackreview", "delete"] + instance.data["representations"].append(repre_preview) + + def render_once(self, context): + """Render context comp only once, even with more render instances""" + key = "__hasRun{}".format(self.__class__.__name__) if context.data.get(key, False): return @@ -32,10 +65,6 @@ class Fusionlocal(pyblish.api.InstancePlugin): current_comp = context.data["currentComp"] frame_start = context.data["frameStartHandle"] frame_end = context.data["frameEndHandle"] - path = instance.data["path"] - output_dir = instance.data["outputDir"] - - ext = os.path.splitext(os.path.basename(path))[-1] self.log.info("Starting render") self.log.info("Start frame: {}".format(frame_start)) @@ -48,26 +77,5 @@ class Fusionlocal(pyblish.api.InstancePlugin): "Wait": True }) - if "representations" not in instance.data: - instance.data["representations"] = [] - - collected_frames = os.listdir(output_dir) - repre = { - 'name': ext[1:], - 'ext': ext[1:], - 'frameStart': "%0{}d".format(len(str(frame_end))) % frame_start, - 'files': collected_frames, - "stagingDir": output_dir, - } - instance.data["representations"].append(repre) - - # review representation - repre_preview = repre.copy() - repre_preview["name"] = repre_preview["ext"] = "mp4" - repre_preview["tags"] = ["review", "preview", "ftrackreview", "delete"] - instance.data["representations"].append(repre_preview) - - self.log.debug(f"_ instance.data: {pformat(instance.data)}") - if not result: raise RuntimeError("Comp render failed") From 723778c3ad92b4a2609996b4c0d6d357335c3830 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Feb 2023 11:46:03 +0100 Subject: [PATCH 0736/1271] zfill 4 for frame value Fusion defaults with 4 numbers for the frame number so if shot doesn't start at 1000 we should zfill to match. --- openpype/hosts/fusion/plugins/publish/render_local.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index d1d32fbf91..5a0b068e8c 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -33,7 +33,7 @@ class Fusionlocal(pyblish.api.InstancePlugin): basename = os.path.basename(path) head, ext = os.path.splitext(basename) files = [ - f"{head}{frame}{ext}" for frame in range(frame_start, frame_end+1) + f"{head}{str(frame).zfill(4)}{ext}" for frame in range(frame_start, frame_end+1) ] repre = { 'name': ext[1:], From 4ad830ffdb9b48ff3455f8c11fd7a681d603c7cf Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 3 Feb 2023 00:21:37 +0100 Subject: [PATCH 0737/1271] Fixed hound-bots comments --- .../fusion/plugins/publish/render_local.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 5a0b068e8c..b02fa55d20 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -1,6 +1,4 @@ import os -import copy - import pyblish.api from openpype.hosts.fusion.api import comp_lock_and_undo_chunk @@ -19,7 +17,7 @@ class Fusionlocal(pyblish.api.InstancePlugin): families = ["render.local"] def process(self, instance): - + # This plug-in runs only once and thus assumes all instances # currently will render the same frame range context = instance.context @@ -33,12 +31,12 @@ class Fusionlocal(pyblish.api.InstancePlugin): basename = os.path.basename(path) head, ext = os.path.splitext(basename) files = [ - f"{head}{str(frame).zfill(4)}{ext}" for frame in range(frame_start, frame_end+1) + f"{head}{str(frame).zfill(4)}{ext}" for frame in range(frame_start, frame_end + 1) ] repre = { 'name': ext[1:], 'ext': ext[1:], - 'frameStart': "%0{}d".format(len(str(frame_end))) % frame_start, + 'frameStart': f"%0{len(str(frame_end))}d" % frame_start, 'files': files, "stagingDir": output_dir, } @@ -56,19 +54,19 @@ class Fusionlocal(pyblish.api.InstancePlugin): def render_once(self, context): """Render context comp only once, even with more render instances""" - key = "__hasRun{}".format(self.__class__.__name__) + key = f'__hasRun{self.__class__.__name__}' if context.data.get(key, False): return - else: - context.data[key] = True + + context.data[key] = True current_comp = context.data["currentComp"] frame_start = context.data["frameStartHandle"] frame_end = context.data["frameEndHandle"] self.log.info("Starting render") - self.log.info("Start frame: {}".format(frame_start)) - self.log.info("End frame: {}".format(frame_end)) + self.log.info(f"Start frame: {frame_start}") + self.log.info(f"End frame: {frame_end}") with comp_lock_and_undo_chunk(current_comp): result = current_comp.Render({ From f7175511f044745f88acb9c927a6f7ea51da705f Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 3 Feb 2023 12:11:04 +0100 Subject: [PATCH 0738/1271] Move hasRun check back to process function --- .../hosts/fusion/plugins/publish/render_local.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index b02fa55d20..b49c11e366 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -21,6 +21,12 @@ class Fusionlocal(pyblish.api.InstancePlugin): # This plug-in runs only once and thus assumes all instances # currently will render the same frame range context = instance.context + key = f"__hasRun{self.__class__.__name__}" + if context.data.get(key, False): + return + + context.data[key] = True + self.render_once(context) frame_start = context.data["frameStartHandle"] @@ -54,12 +60,6 @@ class Fusionlocal(pyblish.api.InstancePlugin): def render_once(self, context): """Render context comp only once, even with more render instances""" - key = f'__hasRun{self.__class__.__name__}' - if context.data.get(key, False): - return - - context.data[key] = True - current_comp = context.data["currentComp"] frame_start = context.data["frameStartHandle"] frame_end = context.data["frameEndHandle"] From fa526b3e4b2fe40a24dc7f34492c5864b818d9a3 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 3 Feb 2023 12:14:45 +0100 Subject: [PATCH 0739/1271] Made line 40 shorter --- openpype/hosts/fusion/plugins/publish/render_local.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index b49c11e366..6f8cd66bd6 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -37,7 +37,8 @@ class Fusionlocal(pyblish.api.InstancePlugin): basename = os.path.basename(path) head, ext = os.path.splitext(basename) files = [ - f"{head}{str(frame).zfill(4)}{ext}" for frame in range(frame_start, frame_end + 1) + f"{head}{str(frame).zfill(4)}{ext}" + for frame in range(frame_start, frame_end + 1) ] repre = { 'name': ext[1:], From eb3dd357e3a5406bae2f6ddb856412d44a78a815 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 10 Feb 2023 14:11:15 +0100 Subject: [PATCH 0740/1271] Changed LaunchFailed message from PYTHON36 to PYTHON PATH --- openpype/hosts/fusion/hooks/pre_fusion_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/hooks/pre_fusion_setup.py b/openpype/hosts/fusion/hooks/pre_fusion_setup.py index d043d54322..323b8b0029 100644 --- a/openpype/hosts/fusion/hooks/pre_fusion_setup.py +++ b/openpype/hosts/fusion/hooks/pre_fusion_setup.py @@ -36,7 +36,7 @@ class FusionPrelaunch(PreLaunchHook): "Make sure the environment in fusion settings has " "'FUSION_PYTHON3_HOME' set correctly and make sure " "Python 3 is installed in the given path." - f"\n\nPYTHON36: {fusion_python3_home}" + f"\n\nPYTHON PATH: {fusion_python3_home}" ) self.log.info(f"Setting {py3_var}: '{py3_dir}'...") From 6482481cf55089420b479f16ab4fa579ec76676e Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 10 Feb 2023 14:13:59 +0100 Subject: [PATCH 0741/1271] handleStart/End is now required --- openpype/hosts/fusion/api/lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/api/lib.py b/openpype/hosts/fusion/api/lib.py index 088f597208..a33e5cf289 100644 --- a/openpype/hosts/fusion/api/lib.py +++ b/openpype/hosts/fusion/api/lib.py @@ -68,8 +68,8 @@ def set_asset_framerange(): asset_doc = get_current_project_asset() start = asset_doc["data"]["frameStart"] end = asset_doc["data"]["frameEnd"] - handle_start = asset_doc["data"].get("handleStart") - handle_end = asset_doc["data"].get("handleEnd") + handle_start = asset_doc["data"]["handleStart"] + handle_end = asset_doc["data"]["handleEnd"] update_frame_range(start, end, set_render_range=True, handle_start=handle_start, handle_end=handle_end) From a9e1140ec9a5c4c0f347e4a8e5afa7da4a89f781 Mon Sep 17 00:00:00 2001 From: Ember Light <49758407+EmberLightVFX@users.noreply.github.com> Date: Fri, 10 Feb 2023 14:14:30 +0100 Subject: [PATCH 0742/1271] Update openpype/hosts/fusion/plugins/publish/render_local.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- openpype/hosts/fusion/plugins/publish/render_local.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 6f8cd66bd6..9bd867a55a 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -55,7 +55,7 @@ class Fusionlocal(pyblish.api.InstancePlugin): # review representation repre_preview = repre.copy() repre_preview["name"] = repre_preview["ext"] = "mp4" - repre_preview["tags"] = ["review", "preview", "ftrackreview", "delete"] + repre_preview["tags"] = ["review", "ftrackreview", "delete"] instance.data["representations"].append(repre_preview) def render_once(self, context): From aa1dd161a5c9add531d53733b77b8cbc5f651e48 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 10 Feb 2023 14:17:59 +0100 Subject: [PATCH 0743/1271] Removed double space after "," in repre_preview["tags"] --- openpype/hosts/fusion/plugins/publish/render_local.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 9bd867a55a..53d8eb64e1 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -55,7 +55,7 @@ class Fusionlocal(pyblish.api.InstancePlugin): # review representation repre_preview = repre.copy() repre_preview["name"] = repre_preview["ext"] = "mp4" - repre_preview["tags"] = ["review", "ftrackreview", "delete"] + repre_preview["tags"] = ["review", "ftrackreview", "delete"] instance.data["representations"].append(repre_preview) def render_once(self, context): From 0124bba40cd55c30517e9a89fc89405dc86c6081 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 13:24:50 +0100 Subject: [PATCH 0744/1271] get_representation_parents() missed project_name --- openpype/hosts/fusion/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/api/lib.py b/openpype/hosts/fusion/api/lib.py index a33e5cf289..ac04efa6a0 100644 --- a/openpype/hosts/fusion/api/lib.py +++ b/openpype/hosts/fusion/api/lib.py @@ -210,7 +210,7 @@ def switch_item(container, if any(not x for x in [asset_name, subset_name, representation_name]): repre_id = container["representation"] representation = get_representation_by_id(project_name, repre_id) - repre_parent_docs = get_representation_parents(representation) + repre_parent_docs = get_representation_parents(project_name, representation) if repre_parent_docs: version, subset, asset, _ = repre_parent_docs else: From 7e4457b241347b39fb1606751ba723e1be774632 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 13:25:57 +0100 Subject: [PATCH 0745/1271] Fixed hound's max-length note --- openpype/hosts/fusion/api/lib.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/api/lib.py b/openpype/hosts/fusion/api/lib.py index ac04efa6a0..88a3f0b49b 100644 --- a/openpype/hosts/fusion/api/lib.py +++ b/openpype/hosts/fusion/api/lib.py @@ -210,7 +210,8 @@ def switch_item(container, if any(not x for x in [asset_name, subset_name, representation_name]): repre_id = container["representation"] representation = get_representation_by_id(project_name, repre_id) - repre_parent_docs = get_representation_parents(project_name, representation) + repre_parent_docs = get_representation_parents( + project_name, representation) if repre_parent_docs: version, subset, asset, _ = repre_parent_docs else: From 44672f3f827517575119d541a1d3c76a3c7fcc2b Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 17:29:18 +0100 Subject: [PATCH 0746/1271] add task to instance --- openpype/hosts/fusion/plugins/publish/collect_instances.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/fusion/plugins/publish/collect_instances.py b/openpype/hosts/fusion/plugins/publish/collect_instances.py index fe60b83827..7b0a1b6369 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_instances.py +++ b/openpype/hosts/fusion/plugins/publish/collect_instances.py @@ -80,6 +80,7 @@ class CollectInstances(pyblish.api.ContextPlugin): "outputDir": os.path.dirname(path), "ext": ext, # todo: should be redundant "label": label, + "task": context.data["task"], "frameStart": context.data["frameStart"], "frameEnd": context.data["frameEnd"], "frameStartHandle": context.data["frameStartHandle"], From c26f86f08c8517c081e35292973b0713f2346e15 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 17:12:19 +0100 Subject: [PATCH 0747/1271] Nuke: adding solution for originalBasename frame temlate formating https://github.com/ynput/OpenPype/pull/4452#issuecomment-1426020567 --- openpype/hosts/nuke/plugins/load/load_clip.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index 565d777811..f9364172ea 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -220,8 +220,21 @@ class LoadClip(plugin.NukeLoader): dict: altered representation data """ representation = deepcopy(representation) - frame = representation["context"]["frame"] - representation["context"]["frame"] = "#" * len(str(frame)) + context = representation["context"] + template = representation["data"]["template"] + frame = context["frame"] + hashed_frame = "#" * len(str(frame)) + + if ( + "{originalBasename}" in template + and "frame" in context + ): + origin_basename = context["originalBasename"] + context["originalBasename"] = origin_basename.replace( + frame, hashed_frame + ) + + representation["context"]["frame"] = hashed_frame return representation def update(self, container, representation): From ebb477e068b19289963aab53a99a2da503d5f52b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 17:51:29 +0100 Subject: [PATCH 0748/1271] publishing files with fixed versionData to fit originalBasename tempate --- .../publish/collect_otio_subset_resources.py | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/collect_otio_subset_resources.py b/openpype/plugins/publish/collect_otio_subset_resources.py index e72c12d9a9..537aef683c 100644 --- a/openpype/plugins/publish/collect_otio_subset_resources.py +++ b/openpype/plugins/publish/collect_otio_subset_resources.py @@ -14,16 +14,41 @@ from openpype.pipeline.editorial import ( range_from_frames, make_sequence_collection ) - +from openpype.pipeline.publish import ( + get_publish_template_name +) class CollectOtioSubsetResources(pyblish.api.InstancePlugin): """Get Resources for a subset version""" label = "Collect OTIO Subset Resources" - order = pyblish.api.CollectorOrder - 0.077 + order = pyblish.api.CollectorOrder + 0.0021 families = ["clip"] hosts = ["resolve", "hiero", "flame"] + def get_template_name(self, instance): + """Return anatomy template name to use for integration""" + + # Anatomy data is pre-filled by Collectors + context = instance.context + project_name = context.data["projectName"] + + # Task can be optional in anatomy data + host_name = context.data["hostName"] + family = instance.data["family"] + anatomy_data = instance.context.data["anatomyData"] + task_info = anatomy_data.get("task") or {} + + return get_publish_template_name( + project_name, + host_name, + family, + task_name=task_info.get("name"), + task_type=task_info.get("type"), + project_settings=context.data["project_settings"], + logger=self.log + ) + def process(self, instance): if "audio" in instance.data["family"]: @@ -35,6 +60,13 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): if not instance.data.get("versionData"): instance.data["versionData"] = {} + template_name = self.get_template_name(instance) + anatomy = instance.context.data["anatomy"] + publish_template_category = anatomy.templates[template_name] + template = os.path.normpath(publish_template_category["path"]) + self.log.debug( + ">> template: {}".format(template)) + handle_start = instance.data["handleStart"] handle_end = instance.data["handleEnd"] @@ -84,6 +116,10 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): frame_start = instance.data["frameStart"] frame_end = frame_start + (media_out - media_in) + if "{originalDirname}" in template: + frame_start = media_in + frame_end = media_out + # add to version data start and end range data # for loader plugins to be correctly displayed and loaded instance.data["versionData"].update({ @@ -153,7 +189,6 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): repre = self._create_representation( frame_start, frame_end, collection=collection) - instance.data["originalBasename"] = collection.format("{head}") else: _trim = False dirname, filename = os.path.split(media_ref.target_url) @@ -168,8 +203,6 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): repre = self._create_representation( frame_start, frame_end, file=filename, trim=_trim) - instance.data["originalBasename"] = os.path.splitext(filename)[0] - instance.data["originalDirname"] = self.staging_dir if repre: From 8eae684f01da394e18407692d89062b3385a3bd0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 17:53:36 +0100 Subject: [PATCH 0749/1271] polishing fixes --- .../publish/collect_otio_subset_resources.py | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/openpype/plugins/publish/collect_otio_subset_resources.py b/openpype/plugins/publish/collect_otio_subset_resources.py index 537aef683c..5daa1b40fe 100644 --- a/openpype/plugins/publish/collect_otio_subset_resources.py +++ b/openpype/plugins/publish/collect_otio_subset_resources.py @@ -26,28 +26,6 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): families = ["clip"] hosts = ["resolve", "hiero", "flame"] - def get_template_name(self, instance): - """Return anatomy template name to use for integration""" - - # Anatomy data is pre-filled by Collectors - context = instance.context - project_name = context.data["projectName"] - - # Task can be optional in anatomy data - host_name = context.data["hostName"] - family = instance.data["family"] - anatomy_data = instance.context.data["anatomyData"] - task_info = anatomy_data.get("task") or {} - - return get_publish_template_name( - project_name, - host_name, - family, - task_name=task_info.get("name"), - task_type=task_info.get("type"), - project_settings=context.data["project_settings"], - logger=self.log - ) def process(self, instance): @@ -116,6 +94,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): frame_start = instance.data["frameStart"] frame_end = frame_start + (media_out - media_in) + # Fit start /end frame to media in /out if "{originalDirname}" in template: frame_start = media_in frame_end = media_out @@ -258,3 +237,26 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): if kwargs.get("trim") is True: representation_data["tags"] = ["trim"] return representation_data + + def get_template_name(self, instance): + """Return anatomy template name to use for integration""" + + # Anatomy data is pre-filled by Collectors + context = instance.context + project_name = context.data["projectName"] + + # Task can be optional in anatomy data + host_name = context.data["hostName"] + family = instance.data["family"] + anatomy_data = instance.context.data["anatomyData"] + task_info = anatomy_data.get("task") or {} + + return get_publish_template_name( + project_name, + host_name, + family, + task_name=task_info.get("name"), + task_type=task_info.get("type"), + project_settings=context.data["project_settings"], + logger=self.log + ) \ No newline at end of file From c103ac19076736ded0755c54737c2f9af2b408ab Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Feb 2023 17:54:54 +0100 Subject: [PATCH 0750/1271] wrong template key name --- openpype/plugins/publish/collect_otio_subset_resources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_otio_subset_resources.py b/openpype/plugins/publish/collect_otio_subset_resources.py index 5daa1b40fe..dab52986da 100644 --- a/openpype/plugins/publish/collect_otio_subset_resources.py +++ b/openpype/plugins/publish/collect_otio_subset_resources.py @@ -95,7 +95,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): frame_end = frame_start + (media_out - media_in) # Fit start /end frame to media in /out - if "{originalDirname}" in template: + if "{originalBasename}" in template: frame_start = media_in frame_end = media_out From 80ac19d4c9af3e24f72738f820b64b546fc9b764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Tue, 14 Feb 2023 12:18:00 +0100 Subject: [PATCH 0751/1271] Update openpype/hosts/nuke/plugins/load/load_clip.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/nuke/plugins/load/load_clip.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index f9364172ea..8f9b463037 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -222,13 +222,12 @@ class LoadClip(plugin.NukeLoader): representation = deepcopy(representation) context = representation["context"] template = representation["data"]["template"] - frame = context["frame"] - hashed_frame = "#" * len(str(frame)) - if ( "{originalBasename}" in template and "frame" in context ): + frame = context["frame"] + hashed_frame = "#" * len(str(frame)) origin_basename = context["originalBasename"] context["originalBasename"] = origin_basename.replace( frame, hashed_frame From 6a8f40f5bb29e8b1bd04497cffe433fc1792af3c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 14 Feb 2023 14:27:42 +0100 Subject: [PATCH 0752/1271] anatomy data from instance rather then context --- .../plugins/publish/collect_otio_subset_resources.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/plugins/publish/collect_otio_subset_resources.py b/openpype/plugins/publish/collect_otio_subset_resources.py index dab52986da..f659791d95 100644 --- a/openpype/plugins/publish/collect_otio_subset_resources.py +++ b/openpype/plugins/publish/collect_otio_subset_resources.py @@ -22,7 +22,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): """Get Resources for a subset version""" label = "Collect OTIO Subset Resources" - order = pyblish.api.CollectorOrder + 0.0021 + order = pyblish.api.CollectorOrder + 0.491 families = ["clip"] hosts = ["resolve", "hiero", "flame"] @@ -50,9 +50,9 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): # get basic variables otio_clip = instance.data["otioClip"] - otio_avalable_range = otio_clip.available_range() - media_fps = otio_avalable_range.start_time.rate - available_duration = otio_avalable_range.duration.value + otio_available_range = otio_clip.available_range() + media_fps = otio_available_range.start_time.rate + available_duration = otio_available_range.duration.value # get available range trimmed with processed retimes retimed_attributes = get_media_range_with_retimes( @@ -248,7 +248,7 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): # Task can be optional in anatomy data host_name = context.data["hostName"] family = instance.data["family"] - anatomy_data = instance.context.data["anatomyData"] + anatomy_data = instance.data["anatomyData"] task_info = anatomy_data.get("task") or {} return get_publish_template_name( @@ -259,4 +259,4 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): task_type=task_info.get("type"), project_settings=context.data["project_settings"], logger=self.log - ) \ No newline at end of file + ) From 2563d302fa0f9b1140b226e7e992b70bae403430 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 12:22:53 +0100 Subject: [PATCH 0753/1271] OP-4643 - fix colorspace from DCC representation["colorspaceData"]["colorspace"] is only input colorspace --- openpype/plugins/publish/extract_color_transcode.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 82b92ec93e..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,8 +118,7 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = (output_def["colorspace"] or - colorspace_data.get("colorspace")) + target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From 24715ff2de8e8c673670643ebb9263f3bd7219e3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 12:34:45 +0100 Subject: [PATCH 0754/1271] global: template should not have frame or other optional elements in tempate, since they are already part of the `originalBasename` --- openpype/settings/defaults/project_anatomy/templates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index 32230e0625..02c0e35377 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -55,7 +55,7 @@ }, "source": { "folder": "{root[work]}/{originalDirname}", - "file": "{originalBasename}<.{@frame}><_{udim}>.{ext}", + "file": "{originalBasename}.{ext}", "path": "{@folder}/{@file}" }, "__dynamic_keys_labels__": { @@ -66,4 +66,4 @@ "source": "source" } } -} \ No newline at end of file +} From 8f5e958e60bf258bb05d8ca72576c20d410f08a8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 11:37:20 +0100 Subject: [PATCH 0755/1271] handle disabled creators in create context --- openpype/pipeline/create/context.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 7672c49eb3..acc2bb054f 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1390,6 +1390,8 @@ class CreateContext: self.autocreators = {} # Manual creators self.manual_creators = {} + # Creators that are disabled + self.disabled_creators = {} self.convertors_plugins = {} self.convertor_items_by_id = {} @@ -1667,6 +1669,7 @@ class CreateContext: # Discover and prepare creators creators = {} + disabled_creators = {} autocreators = {} manual_creators = {} report = discover_creator_plugins(return_report=True) @@ -1703,6 +1706,9 @@ class CreateContext: self, self.headless ) + if not creator.enabled: + disabled_creators[creator_identifier] = creator + continue creators[creator_identifier] = creator if isinstance(creator, AutoCreator): autocreators[creator_identifier] = creator @@ -1713,6 +1719,7 @@ class CreateContext: self.manual_creators = manual_creators self.creators = creators + self.disabled_creators = disabled_creators def _reset_convertor_plugins(self): convertors_plugins = {} From 8fc144b419976675a8f3b7b43f19d5a52c84b616 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 13:59:20 +0100 Subject: [PATCH 0756/1271] base of auto detect creator --- .../tvpaint/plugins/create/create_render.py | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 6a857676a5..2cb83b7fb7 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -35,6 +35,7 @@ Todos: """ import collections +from typing import Any from openpype.client import get_asset_by_name from openpype.lib import ( @@ -604,6 +605,201 @@ class CreateRenderPass(TVPaintCreator): return self.get_pre_create_attr_defs() +class TVPaintAutoDetectRenderCreator(TVPaintCreator): + """Create Render Layer and Render Pass instances based on scene data. + + This is auto-detection creator which can be triggered by user to create + instances based on information in scene. Each used color group in scene + will be created as Render Layer where group name is used as variant and + each TVPaint layer as Render Pass where layer name is used as variant. + """ + + family = "render" + label = "Auto detect renders" + identifier = "render.auto.detect.creator" + + # Settings + enabled = False + + def apply_settings(self, project_settings, system_settings): + plugin_settings = ( + project_settings + ["tvpaint"] + ["create"] + ["auto_detect_render_create"] + ) + self.enabled = plugin_settings["enabled"] + + def create(self, subset_name, instance_data, pre_create_data): + project_name: str = self.create_context.get_current_project_name() + asset_name: str = instance_data["asset"] + task_name: str = instance_data["task"] + asset_doc: dict[str, Any] = get_asset_by_name(project_name, asset_name) + + render_layers_by_group_id: dict[int, CreatedInstance] = {} + render_passes_by_render_layer_id: dict[int, CreatedInstance] = ( + collections.defaultdict(list) + ) + for instance in self.create_context.instances: + if instance.creator_identifier == CreateRenderlayer.identifier: + group_id = instance["creator_attributes"]["group_id"] + render_layers_by_group_id[group_id] = instance + elif instance.creator_identifier == CreateRenderPass.identifier: + render_layer_id = ( + instance + ["creator_attributes"] + ["render_layer_instance_id"] + ) + render_passes_by_render_layer_id[render_layer_id].append( + instance + ) + + layers_by_group_id: dict[int, list[dict[str, Any]]] = ( + collections.defaultdict(list) + ) + scene_layers: list[dict[str, Any]] = get_layers_data() + scene_groups: list[dict[str, Any]] = get_groups_data() + for layer in scene_layers: + group_id: int = layer["group_id"] + layers_by_group_id[group_id].append(layer) + + # Remove '0' (default) group + layers_by_group_id.pop(0, None) + + # Make sure all render layers are created + for group_id in layers_by_group_id.keys(): + render_layer_instance: CreatedInstance | None = ( + render_layers_by_group_id.get(group_id) + ) + + instance: CreatedInstance | None = self._prepare_render_layer( + project_name, + asset_doc, + task_name, + group_id, + scene_groups, + render_layer_instance + ) + if instance is not None: + render_layers_by_group_id[group_id] = instance + + for group_id, layers in layers_by_group_id.items(): + render_layer_instance: CreatedInstance | None = ( + render_layers_by_group_id.get(group_id) + ) + if render_layer_instance is not None: + continue + + self._prepare_render_passes( + project_name, + asset_doc, + task_name, + render_layer_instance, + layers, + render_passes_by_render_layer_id[render_layer_instance.id] + ) + + def _prepare_render_layer( + self, + project_name: str, + asset_doc: dict[str, Any], + task_name: str, + group_id: int, + groups: list[dict[str, Any]], + existing_instance: CreatedInstance | None=None + ) -> CreatedInstance | None: + match_group: dict[str, Any] | None = next( + ( + for group in groups + if group["group_id"] == group_id + ), + None + ) + if not match_group: + return None + + variant: str = match_group["name"] + creator: CreateRenderlayer = ( + self.create_context.creators[CreateRenderlayer.identifier] + ) + + subset_name: str = creator.get_subset_name( + variant, + task_name, + asset_doc, + project_name, + host_name=self.create_context.host_name, + ) + if existing_instance is not None: + existing_instance["asset"] = asset_doc["name"] + existing_instance["task"] = task_name + existing_instance["subset"] = subset_name + return existing_instance + + instance_data: dict[str, str] = { + "asset": asset_doc["name"], + "task": task_name, + "family": creator.family, + "variant": variant + } + pre_create_data: dict[str, str] = { + "group_id": group_id + } + return creator.create(subset_name, instance_data, pre_create_data) + + def _prepare_render_passes( + self, + project_name: str, + asset_doc: dict[str, Any], + task_name: str, + render_layer_instance: CreatedInstance, + layers: list[dict[str, Any]], + existing_render_passes: list[CreatedInstance] + ): + creator: CreateRenderPass = ( + self.create_context.creators[CreateRenderPass.identifier] + ) + render_pass_by_layer_name = {} + for render_pass in existing_render_passes: + for layer_name in render_pass["layer_names"]: + render_pass_by_layer_name[layer_name] = render_pass + + for layer in layers: + layer_name = layer["name"] + variant = layer_name + render_pass = render_pass_by_layer_name.get(layer_name) + if render_pass is not None: + if (render_pass["layer_names"]) > 1: + variant = render_pass["variant"] + + subset_name = creator.get_subset_name( + variant, + task_name, + asset_doc, + project_name, + host_name=self.create_context.host_name, + instance=render_pass + ) + + if render_pass is not None: + render_pass["asset"] = asset_doc["name"] + render_pass["task"] = task_name + render_pass["subset"] = subset_name + continue + + instance_data: dict[str, str] = { + "asset": asset_doc["name"], + "task": task_name, + "family": creator.family, + "variant": variant + } + pre_create_data: dict[str, Any] = { + "render_layer_instance_id": render_layer_instance.id, + "layer_names": [layer_name] + } + creator.create(subset_name, instance_data, pre_create_data) + + class TVPaintSceneRenderCreator(TVPaintAutoCreator): family = "render" subset_template_family_filter = "renderScene" From 2b9e40aece80efa238adf9ee0af9f6d8ea0911de Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 14:04:50 +0100 Subject: [PATCH 0757/1271] added settings for auto-detect creator --- .../hosts/tvpaint/plugins/create/create_render.py | 3 ++- .../defaults/project_settings/tvpaint.json | 3 +++ .../projects_schema/schema_project_tvpaint.json | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 2cb83b7fb7..5239c7aeb9 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -617,6 +617,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): family = "render" label = "Auto detect renders" identifier = "render.auto.detect.creator" + order = CreateRenderPass.order + 10 # Settings enabled = False @@ -626,7 +627,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): project_settings ["tvpaint"] ["create"] - ["auto_detect_render_create"] + ["auto_detect_render"] ) self.enabled = plugin_settings["enabled"] diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index 340181b3a4..1a0d0e22ab 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -40,6 +40,9 @@ "mark_for_review": true, "default_variant": "Main", "default_variants": [] + }, + "auto_detect_render": { + "enabled": false } }, "publish": { diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json index 55e60357e5..5639dee0c2 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json @@ -195,6 +195,20 @@ } } ] + }, + { + "type": "dict", + "collapsible": true, + "key": "auto_detect_render", + "label": "Auto-Detect Create Render", + "is_group": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled" + } + ] } ] }, From 4a81519968796677482ccf1c8984a114ed9e9e0a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 10:29:37 +0100 Subject: [PATCH 0758/1271] add more options to create new instances --- .../tvpaint/plugins/create/create_render.py | 276 +++++++++++++----- 1 file changed, 209 insertions(+), 67 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 5239c7aeb9..cee56ab0f4 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -40,6 +40,8 @@ from typing import Any from openpype.client import get_asset_by_name from openpype.lib import ( prepare_template_data, + AbstractAttrDef, + UISeparatorDef, EnumDef, TextDef, BoolDef, @@ -612,6 +614,8 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): instances based on information in scene. Each used color group in scene will be created as Render Layer where group name is used as variant and each TVPaint layer as Render Pass where layer name is used as variant. + + Never will have any instances, all instances belong to different creators. """ family = "render" @@ -621,6 +625,10 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): # Settings enabled = False + allow_group_rename = True + group_name_template = "L{group_index}" + group_idx_offset = 10 + group_idx_padding = 3 def apply_settings(self, project_settings, system_settings): plugin_settings = ( @@ -630,75 +638,73 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): ["auto_detect_render"] ) self.enabled = plugin_settings["enabled"] + self.allow_group_rename = plugin_settings["allow_group_rename"] - def create(self, subset_name, instance_data, pre_create_data): - project_name: str = self.create_context.get_current_project_name() - asset_name: str = instance_data["asset"] - task_name: str = instance_data["task"] - asset_doc: dict[str, Any] = get_asset_by_name(project_name, asset_name) + def _rename_groups( + self, + groups_order, + scene_groups, + layers_by_group_id, + rename_only_visible, - render_layers_by_group_id: dict[int, CreatedInstance] = {} - render_passes_by_render_layer_id: dict[int, CreatedInstance] = ( - collections.defaultdict(list) - ) - for instance in self.create_context.instances: - if instance.creator_identifier == CreateRenderlayer.identifier: - group_id = instance["creator_attributes"]["group_id"] - render_layers_by_group_id[group_id] = instance - elif instance.creator_identifier == CreateRenderPass.identifier: - render_layer_id = ( - instance - ["creator_attributes"] - ["render_layer_instance_id"] - ) - render_passes_by_render_layer_id[render_layer_id].append( - instance - ) - - layers_by_group_id: dict[int, list[dict[str, Any]]] = ( - collections.defaultdict(list) - ) - scene_layers: list[dict[str, Any]] = get_layers_data() - scene_groups: list[dict[str, Any]] = get_groups_data() - for layer in scene_layers: - group_id: int = layer["group_id"] - layers_by_group_id[group_id].append(layer) - - # Remove '0' (default) group - layers_by_group_id.pop(0, None) - - # Make sure all render layers are created - for group_id in layers_by_group_id.keys(): - render_layer_instance: CreatedInstance | None = ( - render_layers_by_group_id.get(group_id) - ) - - instance: CreatedInstance | None = self._prepare_render_layer( - project_name, - asset_doc, - task_name, - group_id, - scene_groups, - render_layer_instance - ) - if instance is not None: - render_layers_by_group_id[group_id] = instance - - for group_id, layers in layers_by_group_id.items(): - render_layer_instance: CreatedInstance | None = ( - render_layers_by_group_id.get(group_id) - ) - if render_layer_instance is not None: + ): + new_group_name_by_id = {} + groups_by_id = { + group["group_id"]: group + for group in scene_groups + } + # Count only renamed groups + group_idx = 1 + for group_id in groups_order: + layers = layers_by_group_id[group_id] + if not layers: continue - self._prepare_render_passes( - project_name, - asset_doc, - task_name, - render_layer_instance, - layers, - render_passes_by_render_layer_id[render_layer_instance.id] + if ( + rename_only_visible + and not any( + layer + for layer in layers + if layer["visible"] + ) + ): + continue + group_index_value = ( + "{{:0<{}}}" + .format(self.group_idx_padding) + .format(group_idx * self.group_idx_offset) ) + group_name_fill_values = { + "groupIdx": group_index_value, + "groupidx": group_index_value, + "group_idx": group_index_value, + "group_index": group_index_value, + } + + group_name = self.group_name_template.format( + **group_name_fill_values + ) + group = groups_by_id[group_id] + if group["name"] != group_name: + new_group_name_by_id[group_id] = group_name + group_idx += 1 + + grg_lines = [] + for group_id, group_name in new_group_name_by_id.items(): + group = groups_by_id[group_id] + grg_line = "tv_layercolor \"setcolor\" {} {} {} {} {}".format( + group["clip_id"], + group_id, + group["red"], + group["green"], + group["blue"], + group_name + ) + grg_lines.append(grg_line) + group["name"] = group_name + + if grg_lines: + execute_george_through_file(grg_lines) def _prepare_render_layer( self, @@ -707,7 +713,8 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): task_name: str, group_id: int, groups: list[dict[str, Any]], - existing_instance: CreatedInstance | None=None + mark_for_review: bool, + existing_instance: CreatedInstance | None=None, ) -> CreatedInstance | None: match_group: dict[str, Any] | None = next( ( @@ -744,7 +751,8 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): "variant": variant } pre_create_data: dict[str, str] = { - "group_id": group_id + "group_id": group_id, + "mark_for_review": mark_for_review } return creator.create(subset_name, instance_data, pre_create_data) @@ -755,6 +763,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): task_name: str, render_layer_instance: CreatedInstance, layers: list[dict[str, Any]], + mark_for_review: bool, existing_render_passes: list[CreatedInstance] ): creator: CreateRenderPass = ( @@ -796,10 +805,143 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): } pre_create_data: dict[str, Any] = { "render_layer_instance_id": render_layer_instance.id, - "layer_names": [layer_name] + "layer_names": [layer_name], + "mark_for_review": mark_for_review } creator.create(subset_name, instance_data, pre_create_data) + def create(self, subset_name, instance_data, pre_create_data): + project_name: str = self.create_context.get_current_project_name() + asset_name: str = instance_data["asset"] + task_name: str = instance_data["task"] + asset_doc: dict[str, Any] = get_asset_by_name(project_name, asset_name) + + render_layers_by_group_id: dict[int, CreatedInstance] = {} + render_passes_by_render_layer_id: dict[int, list[CreatedInstance]] = ( + collections.defaultdict(list) + ) + for instance in self.create_context.instances: + if instance.creator_identifier == CreateRenderlayer.identifier: + group_id = instance["creator_attributes"]["group_id"] + render_layers_by_group_id[group_id] = instance + elif instance.creator_identifier == CreateRenderPass.identifier: + render_layer_id = ( + instance + ["creator_attributes"] + ["render_layer_instance_id"] + ) + render_passes_by_render_layer_id[render_layer_id].append( + instance + ) + + layers_by_group_id: dict[int, list[dict[str, Any]]] = ( + collections.defaultdict(list) + ) + scene_layers: list[dict[str, Any]] = get_layers_data() + scene_groups: list[dict[str, Any]] = get_groups_data() + groups_order: list[int] = [] + for layer in scene_layers: + group_id: int = layer["group_id"] + # Skip 'default' group + if group_id == 0: + continue + + layers_by_group_id[group_id].append(layer) + if group_id not in groups_order: + groups_order.append(group_id) + + mark_layers_for_review = pre_create_data.get( + "mark_layers_for_review", False + ) + mark_passes_for_review = pre_create_data.get( + "mark_passes_for_review", False + ) + rename_groups = pre_create_data.get("rename_groups", False) + rename_only_visible = pre_create_data.get("rename_only_visible", False) + if rename_groups: + self._rename_groups( + groups_order, + scene_groups, + layers_by_group_id, + rename_only_visible + ) + + # Make sure all render layers are created + for group_id in layers_by_group_id.keys(): + render_layer_instance: CreatedInstance | None = ( + render_layers_by_group_id.get(group_id) + ) + + instance: CreatedInstance | None = self._prepare_render_layer( + project_name, + asset_doc, + task_name, + group_id, + scene_groups, + mark_layers_for_review, + render_layer_instance, + ) + if instance is not None: + render_layers_by_group_id[group_id] = instance + + for group_id, layers in layers_by_group_id.items(): + render_layer_instance: CreatedInstance | None = ( + render_layers_by_group_id.get(group_id) + ) + if render_layer_instance is not None: + continue + + self._prepare_render_passes( + project_name, + asset_doc, + task_name, + render_layer_instance, + layers, + mark_passes_for_review, + render_passes_by_render_layer_id[render_layer_instance.id] + ) + + def get_pre_create_attr_defs(self) -> list[AbstractAttrDef]: + render_layer_creator: CreateRenderlayer = ( + self.create_context.creators[CreateRenderlayer.identifier] + ) + render_pass_creator: CreateRenderPass = ( + self.create_context.creators[CreateRenderPass.identifier] + ) + output = [] + if self.allow_group_rename: + output.extend([ + BoolDef( + "rename_groups", + label="Rename color groups", + tooltip="Will rename color groups using studio template", + default=True + ), + BoolDef( + "rename_only_visible", + label="Only visible color groups", + tooltip=( + "Rename of groups will affect only groups with visible" + " layers." + ), + default=True + ), + UISeparatorDef() + ]) + output.extend([ + BoolDef( + "mark_layers_for_review", + label="Mark RenderLayers for review", + default=render_layer_creator.mark_for_review + ), + BoolDef( + "mark_passes_for_review", + label="Mark RenderPasses for review", + default=render_pass_creator.mark_for_review + ) + ]) + return output + class TVPaintSceneRenderCreator(TVPaintAutoCreator): family = "render" From 5fdadaf454c94cd8e575b5d71a0768cb10385978 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 10:30:55 +0100 Subject: [PATCH 0759/1271] added more settings --- .../tvpaint/plugins/create/create_render.py | 3 ++ .../defaults/project_settings/tvpaint.json | 6 +++- .../schema_project_tvpaint.json | 28 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index cee56ab0f4..129b8dc1c5 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -639,6 +639,9 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): ) self.enabled = plugin_settings["enabled"] self.allow_group_rename = plugin_settings["allow_group_rename"] + self.group_name_template = plugin_settings["group_name_template"] + self.group_idx_offset = plugin_settings["group_idx_offset"] + self.group_idx_padding = plugin_settings["group_idx_padding"] def _rename_groups( self, diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index 1a0d0e22ab..1cae94f590 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -42,7 +42,11 @@ "default_variants": [] }, "auto_detect_render": { - "enabled": false + "enabled": false, + "allow_group_rename": true, + "group_name_template": "L{group_index}", + "group_idx_offset": 10, + "group_idx_padding": 3 } }, "publish": { diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json index 5639dee0c2..05cfd99047 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json @@ -207,6 +207,34 @@ { "type": "boolean", "key": "enabled" + }, + { + "type": "label", + "label": "The creator tries to auto-detect Render Layers and Render Passes in scene. For Render Layers is used group name as a variant and for Render Passes is used TVPaint layer name.

Group names can be renamed by their used order in scene. The renaming template where can be used {group_index} formatting key which is filled by \"used position index of group\".
- Template: L{group_index}
- Group offset: 10
- Group padding: 3
Would create group names \"L010\", \"L020\", ..." + }, + { + "type": "boolean", + "key": "allow_group_rename", + "label": "Allow group rename" + }, + { + "type": "text", + "key": "group_name_template", + "label": "Group name template" + }, + { + "key": "group_idx_offset", + "label": "Group index Offset", + "type": "number", + "decimal": 0, + "minimum": 1 + }, + { + "key": "group_idx_padding", + "type": "number", + "label": "Group index Padding", + "decimal": 0, + "minimum": 1 } ] } From 51f43076b560e858b78289ce9954d814243c6d3f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 15:33:08 +0100 Subject: [PATCH 0760/1271] fix smaller bugs in logic --- openpype/hosts/tvpaint/plugins/create/create_render.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 129b8dc1c5..7d241f93f6 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -673,7 +673,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): ): continue group_index_value = ( - "{{:0<{}}}" + "{{:0>{}}}" .format(self.group_idx_padding) .format(group_idx * self.group_idx_offset) ) @@ -707,7 +707,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): group["name"] = group_name if grg_lines: - execute_george_through_file(grg_lines) + execute_george_through_file("\n".join(grg_lines)) def _prepare_render_layer( self, @@ -853,6 +853,8 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): if group_id not in groups_order: groups_order.append(group_id) + groups_order.reverse() + mark_layers_for_review = pre_create_data.get( "mark_layers_for_review", False ) @@ -891,7 +893,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): render_layer_instance: CreatedInstance | None = ( render_layers_by_group_id.get(group_id) ) - if render_layer_instance is not None: + if render_layer_instance is None: continue self._prepare_render_passes( From a2074308142dbc999ac4175b01cfb7affc6ebe0b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 15:40:20 +0100 Subject: [PATCH 0761/1271] fix groups iter --- openpype/hosts/tvpaint/plugins/create/create_render.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 7d241f93f6..2d4f9f1bc1 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -35,7 +35,7 @@ Todos: """ import collections -from typing import Any +from typing import Any, Optional, Union from openpype.client import get_asset_by_name from openpype.lib import ( @@ -719,8 +719,9 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): mark_for_review: bool, existing_instance: CreatedInstance | None=None, ) -> CreatedInstance | None: - match_group: dict[str, Any] | None = next( + match_group: Union[dict[str, Any], None] = next( ( + group for group in groups if group["group_id"] == group_id ), From a44f928804f21b893bdace9b74b186b079426b4d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 15:41:03 +0100 Subject: [PATCH 0762/1271] handle valid group ids --- .../hosts/tvpaint/plugins/create/create_render.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 2d4f9f1bc1..c7b80ae256 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -653,6 +653,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): ): new_group_name_by_id = {} groups_by_id = { + valid_group_ids: set[int] = set() group["group_id"]: group for group in scene_groups } @@ -672,7 +673,8 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): ) ): continue - group_index_value = ( + valid_group_ids.add(group_id) + group_index_value: str = ( "{{:0>{}}}" .format(self.group_idx_padding) .format(group_idx * self.group_idx_offset) @@ -708,6 +710,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): if grg_lines: execute_george_through_file("\n".join(grg_lines)) + return valid_group_ids def _prepare_render_layer( self, @@ -865,16 +868,20 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): rename_groups = pre_create_data.get("rename_groups", False) rename_only_visible = pre_create_data.get("rename_only_visible", False) if rename_groups: - self._rename_groups( + valid_group_ids: set[int] = self._rename_groups( groups_order, scene_groups, layers_by_group_id, rename_only_visible ) + else: + valid_group_ids: set[int] = set(groups_order) # Make sure all render layers are created for group_id in layers_by_group_id.keys(): - render_layer_instance: CreatedInstance | None = ( + if group_id not in valid_group_ids: + continue + render_layer_instance: Union[CreatedInstance, None] = ( render_layers_by_group_id.get(group_id) ) From faaade284ab14d8cfe4dade4011981c0f0d26623 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 15:41:44 +0100 Subject: [PATCH 0763/1271] added type hints --- .../tvpaint/plugins/create/create_render.py | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index c7b80ae256..883383ec76 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -645,22 +645,21 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): def _rename_groups( self, - groups_order, - scene_groups, - layers_by_group_id, - rename_only_visible, - - ): - new_group_name_by_id = {} - groups_by_id = { + groups_order: list[int], + scene_groups: list[dict[str, Any]], + layers_by_group_id: dict[int, dict[str, Any]], + rename_only_visible: bool, + ) -> set[int]: valid_group_ids: set[int] = set() + new_group_name_by_id: dict[int, str] = {} + groups_by_id: dict[int, dict[str, Any]] = { group["group_id"]: group for group in scene_groups } # Count only renamed groups - group_idx = 1 + group_idx: int = 1 for group_id in groups_order: - layers = layers_by_group_id[group_id] + layers: list[dict[str, Any]] = layers_by_group_id[group_id] if not layers: continue @@ -679,25 +678,25 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): .format(self.group_idx_padding) .format(group_idx * self.group_idx_offset) ) - group_name_fill_values = { + group_name_fill_values: dict[str, str] = { "groupIdx": group_index_value, "groupidx": group_index_value, "group_idx": group_index_value, "group_index": group_index_value, } - group_name = self.group_name_template.format( + group_name: str = self.group_name_template.format( **group_name_fill_values ) - group = groups_by_id[group_id] + group: dict[str, Any] = groups_by_id[group_id] if group["name"] != group_name: new_group_name_by_id[group_id] = group_name group_idx += 1 - grg_lines = [] + grg_lines: list[str] = [] for group_id, group_name in new_group_name_by_id.items(): - group = groups_by_id[group_id] - grg_line = "tv_layercolor \"setcolor\" {} {} {} {} {}".format( + group: dict[str, Any] = groups_by_id[group_id] + grg_line: str = "tv_layercolor \"setcolor\" {} {} {} {} {}".format( group["clip_id"], group_id, group["red"], @@ -720,8 +719,8 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): group_id: int, groups: list[dict[str, Any]], mark_for_review: bool, - existing_instance: CreatedInstance | None=None, - ) -> CreatedInstance | None: + existing_instance: Optional[CreatedInstance]=None, + ) -> Union[CreatedInstance, None]: match_group: Union[dict[str, Any], None] = next( ( group @@ -885,7 +884,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): render_layers_by_group_id.get(group_id) ) - instance: CreatedInstance | None = self._prepare_render_layer( + instance: Union[CreatedInstance, None] = self._prepare_render_layer( project_name, asset_doc, task_name, @@ -898,7 +897,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): render_layers_by_group_id[group_id] = instance for group_id, layers in layers_by_group_id.items(): - render_layer_instance: CreatedInstance | None = ( + render_layer_instance: Union[CreatedInstance, None] = ( render_layers_by_group_id.get(group_id) ) if render_layer_instance is None: From 07c87b2efd8c6eec6c1df2ec5d8110eda6d5af4b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 16:18:40 +0100 Subject: [PATCH 0764/1271] fix filtering of groups --- .../tvpaint/plugins/create/create_render.py | 108 +++++++++--------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 883383ec76..2e17995b7a 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -646,37 +646,19 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): def _rename_groups( self, groups_order: list[int], - scene_groups: list[dict[str, Any]], - layers_by_group_id: dict[int, dict[str, Any]], - rename_only_visible: bool, - ) -> set[int]: - valid_group_ids: set[int] = set() + scene_groups: list[dict[str, Any]] + ): new_group_name_by_id: dict[int, str] = {} groups_by_id: dict[int, dict[str, Any]] = { group["group_id"]: group for group in scene_groups } # Count only renamed groups - group_idx: int = 1 - for group_id in groups_order: - layers: list[dict[str, Any]] = layers_by_group_id[group_id] - if not layers: - continue - - if ( - rename_only_visible - and not any( - layer - for layer in layers - if layer["visible"] - ) - ): - continue - valid_group_ids.add(group_id) + for idx, group_id in enumerate(groups_order): group_index_value: str = ( "{{:0>{}}}" .format(self.group_idx_padding) - .format(group_idx * self.group_idx_offset) + .format((idx + 1) * self.group_idx_offset) ) group_name_fill_values: dict[str, str] = { "groupIdx": group_index_value, @@ -691,7 +673,6 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): group: dict[str, Any] = groups_by_id[group_id] if group["name"] != group_name: new_group_name_by_id[group_id] = group_name - group_idx += 1 grg_lines: list[str] = [] for group_id, group_name in new_group_name_by_id.items(): @@ -709,7 +690,6 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): if grg_lines: execute_george_through_file("\n".join(grg_lines)) - return valid_group_ids def _prepare_render_layer( self, @@ -816,6 +796,30 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): } creator.create(subset_name, instance_data, pre_create_data) + def _filter_groups( + self, + layers_by_group_id, + groups_order, + only_visible_groups + ): + new_groups_order = [] + for group_id in groups_order: + layers: list[dict[str, Any]] = layers_by_group_id[group_id] + if not layers: + continue + + if ( + only_visible_groups + and not any( + layer + for layer in layers + if layer["visible"] + ) + ): + continue + new_groups_order.append(group_id) + return new_groups_order + def create(self, subset_name, instance_data, pre_create_data): project_name: str = self.create_context.get_current_project_name() asset_name: str = instance_data["asset"] @@ -865,42 +869,40 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): "mark_passes_for_review", False ) rename_groups = pre_create_data.get("rename_groups", False) - rename_only_visible = pre_create_data.get("rename_only_visible", False) + only_visible_groups = pre_create_data.get("only_visible_groups", False) + groups_order = self._filter_groups( + layers_by_group_id, + groups_order, + only_visible_groups + ) + if not groups_order: + return + if rename_groups: - valid_group_ids: set[int] = self._rename_groups( - groups_order, - scene_groups, - layers_by_group_id, - rename_only_visible - ) - else: - valid_group_ids: set[int] = set(groups_order) + self._rename_groups(groups_order, scene_groups) # Make sure all render layers are created - for group_id in layers_by_group_id.keys(): - if group_id not in valid_group_ids: - continue - render_layer_instance: Union[CreatedInstance, None] = ( - render_layers_by_group_id.get(group_id) - ) - - instance: Union[CreatedInstance, None] = self._prepare_render_layer( - project_name, - asset_doc, - task_name, - group_id, - scene_groups, - mark_layers_for_review, - render_layer_instance, + for group_id in groups_order: + instance: Union[CreatedInstance, None] = ( + self._prepare_render_layer( + project_name, + asset_doc, + task_name, + group_id, + scene_groups, + mark_layers_for_review, + render_layers_by_group_id.get(group_id), + ) ) if instance is not None: render_layers_by_group_id[group_id] = instance - for group_id, layers in layers_by_group_id.items(): + for group_id in groups_order: + layers: list[dict[str, Any]] = layers_by_group_id[group_id] render_layer_instance: Union[CreatedInstance, None] = ( render_layers_by_group_id.get(group_id) ) - if render_layer_instance is None: + if not layers or render_layer_instance is None: continue self._prepare_render_passes( @@ -930,11 +932,11 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): default=True ), BoolDef( - "rename_only_visible", + "only_visible_groups", label="Only visible color groups", tooltip=( - "Rename of groups will affect only groups with visible" - " layers." + "Render Layers and rename will happen only on color" + " groups with visible layers." ), default=True ), From abb3134bb4c8bb7413d415e29b5a950715ab23ab Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 Feb 2023 18:16:33 +0100 Subject: [PATCH 0765/1271] fix whitespace --- openpype/hosts/tvpaint/plugins/create/create_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 2e17995b7a..f16c3a437b 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -699,7 +699,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): group_id: int, groups: list[dict[str, Any]], mark_for_review: bool, - existing_instance: Optional[CreatedInstance]=None, + existing_instance: Optional[CreatedInstance] = None, ) -> Union[CreatedInstance, None]: match_group: Union[dict[str, Any], None] = next( ( From c334a5fc221fa6650d67c38338f9c71f96f11824 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 11:43:33 +0100 Subject: [PATCH 0766/1271] removed 'enabled' from 'auto_detect_render' settings --- openpype/settings/defaults/project_settings/tvpaint.json | 1 - .../schemas/projects_schema/schema_project_tvpaint.json | 5 ----- 2 files changed, 6 deletions(-) diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index 1cae94f590..40603ed874 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -42,7 +42,6 @@ "default_variants": [] }, "auto_detect_render": { - "enabled": false, "allow_group_rename": true, "group_name_template": "L{group_index}", "group_idx_offset": 10, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json index 05cfd99047..57016a8311 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json @@ -202,12 +202,7 @@ "key": "auto_detect_render", "label": "Auto-Detect Create Render", "is_group": true, - "checkbox_key": "enabled", "children": [ - { - "type": "boolean", - "key": "enabled" - }, { "type": "label", "label": "The creator tries to auto-detect Render Layers and Render Passes in scene. For Render Layers is used group name as a variant and for Render Passes is used TVPaint layer name.

Group names can be renamed by their used order in scene. The renaming template where can be used {group_index} formatting key which is filled by \"used position index of group\".
- Template: L{group_index}
- Group offset: 10
- Group padding: 3
Would create group names \"L010\", \"L020\", ..." From 0f20ed34a2a811599dccd2ebb46a2464cd66ebad Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 12:32:16 +0100 Subject: [PATCH 0767/1271] change label and add description to creator --- .../tvpaint/plugins/create/create_render.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index f16c3a437b..40386efe91 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -89,6 +89,25 @@ to match group color of Render Layer. ) +AUTODETECT_RENDER_DETAILED_DESCRIPTION = ( + """Semi-automated Render Layer and Render Pass creation. + +Based on information in TVPaint scene will be created Render Layers and Render +Passes. All color groups used in scene will be used for Render Layer creation. +Name of the group is used as a variant. + +All TVPaint layers under the color group will be created as Render Pass where +layer name is used as variant. + +The plugin will use all used color groups and layers, or can skip those that +are not visible. + +There is option to auto-rename color groups before Render Layer creation. That +is based on settings template where is filled index of used group from bottom +to top. +""" +) + class CreateRenderlayer(TVPaintCreator): """Mark layer group as Render layer instance. @@ -619,9 +638,13 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): """ family = "render" - label = "Auto detect renders" + label = "Render Layer/Passes" identifier = "render.auto.detect.creator" order = CreateRenderPass.order + 10 + description = ( + "Create Render Layers and Render Passes based on scene setup" + ) + detailed_description = AUTODETECT_RENDER_DETAILED_DESCRIPTION # Settings enabled = False From e0879fd67d642f7e34e4e70ba43979fc1cdb6dd9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 16 Feb 2023 17:03:04 +0100 Subject: [PATCH 0768/1271] Pass creation flags on windows in UI executables --- openpype/lib/execute.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index 39532b7aa5..e60bffbd7a 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -74,18 +74,23 @@ def execute(args, return popen.returncode -def run_subprocess(*args, **kwargs): +def run_subprocess(*args, creationflags=None, **kwargs): """Convenience method for getting output errors for subprocess. Output logged when process finish. Entered arguments and keyword arguments are passed to subprocess Popen. + Set 'creationflags' to '0' (int) if auto-fix for creation of new window + should be ignored. + Args: - *args: Variable length arument list passed to Popen. + *args: Variable length argument list passed to Popen. + creationflags (int): Creation flags for 'subprocess.Popen'. + Differentiate on OS. **kwargs : Arbitrary keyword arguments passed to Popen. Is possible to - pass `logging.Logger` object under "logger" if want to use - different than lib's logger. + pass `logging.Logger` object under "logger" to use custom logger + for output. Returns: str: Full output of subprocess concatenated stdout and stderr. @@ -95,6 +100,21 @@ def run_subprocess(*args, **kwargs): return code. """ + # Modify creation flags on windows to hide console window if in UI mode + if ( + sys.__stdout__ is None + and platform.system().lower() == "windows" + and creationflags is None + ): + creationflags = ( + subprocess.CREATE_NEW_PROCESS_GROUP + | subprocess.DETACHED_PROCESS + ) + + # Ignore if creation flags is set to '0' or 'None' + if creationflags: + kwargs["creationflags"] = creationflags + # Get environents from kwarg or use current process environments if were # not passed. env = kwargs.get("env") or os.environ From 567b6dfb140bafe31fa4c3daef052315e65efb67 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 17 Feb 2023 09:59:47 +0100 Subject: [PATCH 0769/1271] on windows always autofill creation flags --- openpype/lib/execute.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index e60bffbd7a..6a3d777427 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -102,8 +102,7 @@ def run_subprocess(*args, creationflags=None, **kwargs): # Modify creation flags on windows to hide console window if in UI mode if ( - sys.__stdout__ is None - and platform.system().lower() == "windows" + platform.system().lower() == "windows" and creationflags is None ): creationflags = ( From e69ba621d1063b9c2cd72448a3f49512e83b38b5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 17 Feb 2023 10:01:05 +0100 Subject: [PATCH 0770/1271] unify quotes --- openpype/lib/execute.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index 6a3d777427..47b4255e3b 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -126,10 +126,10 @@ def run_subprocess(*args, creationflags=None, **kwargs): logger = Logger.get_logger("run_subprocess") # set overrides - kwargs['stdout'] = kwargs.get('stdout', subprocess.PIPE) - kwargs['stderr'] = kwargs.get('stderr', subprocess.PIPE) - kwargs['stdin'] = kwargs.get('stdin', subprocess.PIPE) - kwargs['env'] = filtered_env + kwargs["stdout"] = kwargs.get("stdout", subprocess.PIPE) + kwargs["stderr"] = kwargs.get("stderr", subprocess.PIPE) + kwargs["stdin"] = kwargs.get("stdin", subprocess.PIPE) + kwargs["env"] = filtered_env proc = subprocess.Popen(*args, **kwargs) From 0f9e12955c1553e61eac5c9c43ddfa3b46e8cdeb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 10:23:24 +0100 Subject: [PATCH 0771/1271] look for creationflags in kwargs instead of explicit argument --- openpype/lib/execute.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index 47b4255e3b..6c1361d7c2 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -74,20 +74,18 @@ def execute(args, return popen.returncode -def run_subprocess(*args, creationflags=None, **kwargs): +def run_subprocess(*args, **kwargs): """Convenience method for getting output errors for subprocess. Output logged when process finish. Entered arguments and keyword arguments are passed to subprocess Popen. - Set 'creationflags' to '0' (int) if auto-fix for creation of new window - should be ignored. + On windows are 'creationflags' filled with flags that should cause ignore + creation of new window. Args: *args: Variable length argument list passed to Popen. - creationflags (int): Creation flags for 'subprocess.Popen'. - Differentiate on OS. **kwargs : Arbitrary keyword arguments passed to Popen. Is possible to pass `logging.Logger` object under "logger" to use custom logger for output. @@ -103,17 +101,13 @@ def run_subprocess(*args, creationflags=None, **kwargs): # Modify creation flags on windows to hide console window if in UI mode if ( platform.system().lower() == "windows" - and creationflags is None + and "creationflags" not in kwargs ): - creationflags = ( + kwargs["creationflags"] = ( subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS ) - # Ignore if creation flags is set to '0' or 'None' - if creationflags: - kwargs["creationflags"] = creationflags - # Get environents from kwarg or use current process environments if were # not passed. env = kwargs.get("env") or os.environ From ed909ca5278825b1d897aaa1d975090a13ecf5b2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 10:23:59 +0100 Subject: [PATCH 0772/1271] add 'CREATE_NO_WINDOW' to flags (if is available) --- openpype/lib/execute.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index 6c1361d7c2..f5745f9fdc 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -103,9 +103,11 @@ def run_subprocess(*args, **kwargs): platform.system().lower() == "windows" and "creationflags" not in kwargs ): + no_window = getattr(subprocess, "CREATE_NO_WINDOW", 0) kwargs["creationflags"] = ( subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS + | no_window ) # Get environents from kwarg or use current process environments if were From c177c26c863dbfee91cb67e78d5e54f8c5f44f5d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 19:02:59 +0100 Subject: [PATCH 0773/1271] safe access to constants --- openpype/lib/execute.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index f5745f9fdc..759a4db0cb 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -103,11 +103,10 @@ def run_subprocess(*args, **kwargs): platform.system().lower() == "windows" and "creationflags" not in kwargs ): - no_window = getattr(subprocess, "CREATE_NO_WINDOW", 0) kwargs["creationflags"] = ( subprocess.CREATE_NEW_PROCESS_GROUP - | subprocess.DETACHED_PROCESS - | no_window + | getattr(subprocess, "DETACHED_PROCESS", 0) + | getattr(subprocess, "CREATE_NO_WINDOW", 0) ) # Get environents from kwarg or use current process environments if were From cf17ff50b572b0f55b9c60b9981f872cd79ef34f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 10:41:01 +0100 Subject: [PATCH 0774/1271] use detached process in ffprobe --- openpype/lib/transcoding.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 57279d0380..039255d937 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -5,6 +5,7 @@ import json import collections import tempfile import subprocess +import platform import xml.etree.ElementTree @@ -745,11 +746,18 @@ def get_ffprobe_data(path_to_file, logger=None): logger.debug("FFprobe command: {}".format( subprocess.list2cmdline(args) )) - popen = subprocess.Popen( - args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + kwargs = { + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE, + } + if platform.system().lower() == "windows": + kwargs["creationflags"] = ( + subprocess.CREATE_NEW_PROCESS_GROUP + | getattr(subprocess, "DETACHED_PROCESS", 0) + | getattr(subprocess, "CREATE_NO_WINDOW", 0) + ) + + popen = subprocess.Popen(args, **kwargs) popen_stdout, popen_stderr = popen.communicate() if popen_stdout: From 79eb375c06d1dfeb6cb65c87f69c5f0476a05eb2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 10:43:21 +0100 Subject: [PATCH 0775/1271] hide console in extract burnin --- openpype/scripts/otio_burnin.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/openpype/scripts/otio_burnin.py b/openpype/scripts/otio_burnin.py index 3e40bf0c8b..cb4646c099 100644 --- a/openpype/scripts/otio_burnin.py +++ b/openpype/scripts/otio_burnin.py @@ -52,7 +52,16 @@ def _get_ffprobe_data(source): "-show_streams", source ] - proc = subprocess.Popen(command, stdout=subprocess.PIPE) + kwargs = { + "stdout": subprocess.PIPE, + } + if platform.system().lower() == "windows": + kwargs["creationflags"] = ( + subprocess.CREATE_NEW_PROCESS_GROUP + | getattr(subprocess, "DETACHED_PROCESS", 0) + | getattr(subprocess, "CREATE_NO_WINDOW", 0) + ) + proc = subprocess.Popen(command, **kwargs) out = proc.communicate()[0] if proc.returncode != 0: raise RuntimeError("Failed to run: %s" % command) @@ -331,12 +340,18 @@ class ModifiedBurnins(ffmpeg_burnins.Burnins): ) print("Launching command: {}".format(command)) - proc = subprocess.Popen( - command, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - shell=True - ) + kwargs = { + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE, + "shell": True, + } + if platform.system().lower() == "windows": + kwargs["creationflags"] = ( + subprocess.CREATE_NEW_PROCESS_GROUP + | getattr(subprocess, "DETACHED_PROCESS", 0) + | getattr(subprocess, "CREATE_NO_WINDOW", 0) + ) + proc = subprocess.Popen(command, **kwargs) _stdout, _stderr = proc.communicate() if _stdout: From 709d663870bb703d644ab0fdd8afc08da6c260c8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 22 Feb 2023 21:47:32 +0800 Subject: [PATCH 0776/1271] update docstring and style fix --- openpype/hosts/maya/api/lib.py | 58 ++++++++++++------- .../maya/plugins/publish/extract_look.py | 8 +-- .../publish/validate_look_color_space.py | 3 +- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 88d3701966..e369b46525 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -16,7 +16,15 @@ from six import string_types from maya import cmds, mel import maya.api.OpenMaya as om -from arnold import * # noqa +from arnold import ( + AiTextureGetBitDepth, + AiTextureGetFormat, + AiTextureInvalidate, + # types + AI_TYPE_BYTE, + AI_TYPE_INT, + AI_TYPE_UINT +) from openpype.client import ( get_project, @@ -3465,19 +3473,23 @@ def write_xgen_file(data, filepath): f.writelines(lines) -def imageInfo(filepath): - """Take reference from makeTx.py in Arnold - ImageInfo(filename): Get Image Information for colorspace - AiTextureGetFormat(filename): Get Texture Format - AiTextureGetBitDepth(filename): Get Texture Bit Depth +def image_info(file_path): + # type: (str) -> dict + """Based on tha texture path, get its bit depth and format information. + Take reference from makeTx.py in Arnold: + ImageInfo(filename): Get Image Information for colorspace + AiTextureGetFormat(filename): Get Texture Format + AiTextureGetBitDepth(filename): Get Texture bit depth + Args: + file_path (str): Path to the texture file. + Returns: + dict: Dictionary with the information about the texture file. """ - # Get Texture Information - img_info = {} - img_info['filename'] = filepath - if os.path.isfile(filepath): - img_info['bit_depth'] = AiTextureGetBitDepth(filepath) # noqa - img_info['format'] = AiTextureGetFormat(filepath) # noqa + img_info = {'filename': file_path} + if os.path.isfile(file_path): + img_info['bit_depth'] = AiTextureGetBitDepth(file_path) # noqa + img_info['format'] = AiTextureGetFormat(file_path) # noqa else: img_info['bit_depth'] = 8 img_info['format'] = "unknown" @@ -3485,21 +3497,25 @@ def imageInfo(filepath): def guess_colorspace(img_info): - ''' Take reference from makeTx.py - Guess the colorspace of the input image filename. - @return: a string suitable for the --colorconvert - option of maketx (linear, sRGB, Rec709) - ''' + # type: (dict) -> str + """Guess the colorspace of the input image filename. + Note: + Reference from makeTx.py + Args: + img_info (dict): Image info generated by :func:`image_info` + Returns: + str: color space name use in the `--colorconvert` + option of maketx. + """ try: if img_info['bit_depth'] <= 16: if img_info['format'] in (AI_TYPE_BYTE, AI_TYPE_INT, AI_TYPE_UINT): # noqa return 'sRGB' else: return 'linear' - # now discard the image file as AiTextureGetFormat has loaded it AiTextureInvalidate(img_info['filename']) # noqa except ValueError: - print('[maketx] Error: Could not guess' - 'colorspace for "%s"' % img_info['filename']) - return 'linear' + print(("[maketx] Error: Could not guess" + "colorspace for {}").format(img_info["filename"])) + return "linear" diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 46f7b0e03d..d9c19c9139 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -16,7 +16,7 @@ import pyblish.api from openpype.lib import source_hash, run_subprocess from openpype.pipeline import legacy_io, publish from openpype.hosts.maya.api import lib -from openpype.hosts.maya.api.lib import imageInfo, guess_colorspace +from openpype.hosts.maya.api.lib import image_info, guess_colorspace # Modes for transfer COPY = 1 @@ -369,7 +369,7 @@ class ExtractLook(publish.Extractor): linearize = False # if OCIO color management enabled - # it wont take the condition of the files_metadata + # it won't take the condition of the files_metadata ocio_maya = cmds.colorManagementPrefs(q=True, cmConfigFileEnabled=True, @@ -542,14 +542,14 @@ class ExtractLook(publish.Extractor): color_space = cmds.getAttr(color_space_attr) except ValueError: # node doesn't have color space attribute - img_info = imageInfo(filepath) + img_info = image_info(filepath) color_space = guess_colorspace(img_info) self.log.info("tx: converting {0} -> {1}".format(color_space, render_colorspace)) # noqa additional_args.extend(["--colorconvert", color_space, render_colorspace]) else: - img_info = imageInfo(filepath) + img_info = image_info(filepath) color_space = guess_colorspace(img_info) if color_space == "sRGB": self.log.info("tx: converting sRGB -> linear") diff --git a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py index b354d51fef..b1bdeb7541 100644 --- a/openpype/hosts/maya/plugins/publish/validate_look_color_space.py +++ b/openpype/hosts/maya/plugins/publish/validate_look_color_space.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api from openpype.pipeline.publish import ValidateContentsOrder +from openpype.pipeline import PublishValidationError class ValidateMayaColorSpace(pyblish.api.InstancePlugin): @@ -22,4 +23,4 @@ class ValidateMayaColorSpace(pyblish.api.InstancePlugin): maketx = instance.data["maketx"] if ocio_maya and maketx: - raise RuntimeError("Maya is color managed and maketx option is on. OpenPype doesn't support this combination yet.") # noqa + raise PublishValidationError("Maya is color managed and maketx option is on. OpenPype doesn't support this combination yet.") # noqa From 3cb530ce6048bf2e5cb85798b66d11893ba7acfb Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 22 Feb 2023 22:19:47 +0800 Subject: [PATCH 0777/1271] bug fix for not being able to remove item in scene inventory --- openpype/hosts/max/plugins/load/load_pointcache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/max/plugins/load/load_pointcache.py b/openpype/hosts/max/plugins/load/load_pointcache.py index 65d0662faa..f7a72ece25 100644 --- a/openpype/hosts/max/plugins/load/load_pointcache.py +++ b/openpype/hosts/max/plugins/load/load_pointcache.py @@ -80,7 +80,7 @@ importFile @"{file_path}" #noPrompt def remove(self, container): from pymxs import runtime as rt - node = container["node"] + node = rt.getNodeByName(container["instance_node"]) rt.delete(node) @staticmethod From 6860d6dcfeac07138887ea34ffbe2e56d102b098 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 22 Feb 2023 22:51:15 +0800 Subject: [PATCH 0778/1271] bug fix for not being able to remove item in scene inventory --- .../hosts/max/plugins/load/load_camera_fbx.py | 27 ++++++++++++++---- .../hosts/max/plugins/load/load_max_scene.py | 28 +++++++++++++------ 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/max/plugins/load/load_camera_fbx.py b/openpype/hosts/max/plugins/load/load_camera_fbx.py index 1b1df364c1..23933b29e6 100644 --- a/openpype/hosts/max/plugins/load/load_camera_fbx.py +++ b/openpype/hosts/max/plugins/load/load_camera_fbx.py @@ -1,7 +1,10 @@ import os from openpype.pipeline import ( - load + load, + get_representation_path ) +from openpype.hosts.max.api.pipeline import containerise +from openpype.hosts.max.api import lib class FbxLoader(load.LoaderPlugin): @@ -36,14 +39,26 @@ importFile @"{filepath}" #noPrompt using:FBXIMP container_name = f"{name}_CON" asset = rt.getNodeByName(f"{name}") - # rename the container with "_CON" - container = rt.container(name=container_name) - asset.Parent = container - return container + return containerise( + name, [asset], context, loader=self.__class__.__name__) + + def update(self, container, representation): + from pymxs import runtime as rt + + path = get_representation_path(representation) + node = rt.getNodeByName(container["instance_node"]) + + alembic_objects = self.get_container_children(node, "AlembicObject") + for alembic_object in alembic_objects: + alembic_object.source = path + + lib.imprint(container["instance_node"], { + "representation": str(representation["_id"]) + }) def remove(self, container): from pymxs import runtime as rt - node = container["node"] + node = rt.getNodeByName(container["instance_node"]) rt.delete(node) diff --git a/openpype/hosts/max/plugins/load/load_max_scene.py b/openpype/hosts/max/plugins/load/load_max_scene.py index 57f172cf6a..57a74c7ad7 100644 --- a/openpype/hosts/max/plugins/load/load_max_scene.py +++ b/openpype/hosts/max/plugins/load/load_max_scene.py @@ -1,7 +1,9 @@ import os from openpype.pipeline import ( - load + load, get_representation_path ) +from openpype.hosts.max.api.pipeline import containerise +from openpype.hosts.max.api import lib class MaxSceneLoader(load.LoaderPlugin): @@ -35,16 +37,26 @@ class MaxSceneLoader(load.LoaderPlugin): self.log.error("Something failed when loading.") max_container = max_containers.pop() - container_name = f"{name}_CON" - # rename the container with "_CON" - # get the original container - container = rt.container(name=container_name) - max_container.Parent = container - return container + return containerise( + name, [max_container], context, loader=self.__class__.__name__) + + def update(self, container, representation): + from pymxs import runtime as rt + + path = get_representation_path(representation) + node = rt.getNodeByName(container["instance_node"]) + + alembic_objects = self.get_container_children(node, "AlembicObject") + for alembic_object in alembic_objects: + alembic_object.source = path + + lib.imprint(container["instance_node"], { + "representation": str(representation["_id"]) + }) def remove(self, container): from pymxs import runtime as rt - node = container["node"] + node = rt.getNodeByName(container["instance_node"]) rt.delete(node) From 0ef87d0949105a386ba4e3200e421ee1af130176 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 22 Feb 2023 23:46:34 +0800 Subject: [PATCH 0779/1271] renaming some variables --- openpype/hosts/max/plugins/load/load_camera_fbx.py | 6 +++--- openpype/hosts/max/plugins/load/load_max_scene.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/max/plugins/load/load_camera_fbx.py b/openpype/hosts/max/plugins/load/load_camera_fbx.py index 23933b29e6..e6eac25cfc 100644 --- a/openpype/hosts/max/plugins/load/load_camera_fbx.py +++ b/openpype/hosts/max/plugins/load/load_camera_fbx.py @@ -49,9 +49,9 @@ importFile @"{filepath}" #noPrompt using:FBXIMP path = get_representation_path(representation) node = rt.getNodeByName(container["instance_node"]) - alembic_objects = self.get_container_children(node, "AlembicObject") - for alembic_object in alembic_objects: - alembic_object.source = path + fbx_objects = self.get_container_children(node, "AlembicObject") + for fbx_object in fbx_objects: + fbx_object.source = path lib.imprint(container["instance_node"], { "representation": str(representation["_id"]) diff --git a/openpype/hosts/max/plugins/load/load_max_scene.py b/openpype/hosts/max/plugins/load/load_max_scene.py index 57a74c7ad7..13cf1e6019 100644 --- a/openpype/hosts/max/plugins/load/load_max_scene.py +++ b/openpype/hosts/max/plugins/load/load_max_scene.py @@ -47,9 +47,9 @@ class MaxSceneLoader(load.LoaderPlugin): path = get_representation_path(representation) node = rt.getNodeByName(container["instance_node"]) - alembic_objects = self.get_container_children(node, "AlembicObject") - for alembic_object in alembic_objects: - alembic_object.source = path + max_objects = self.get_container_children(node, "AlembicObject") + for max_object in max_objects: + max_object.source = path lib.imprint(container["instance_node"], { "representation": str(representation["_id"]) From 810b57ffb190434aa433511c17aa38c792aac618 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 22 Feb 2023 23:58:02 +0800 Subject: [PATCH 0780/1271] renaming some variables --- openpype/hosts/max/plugins/load/load_camera_fbx.py | 2 +- openpype/hosts/max/plugins/load/load_max_scene.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/load/load_camera_fbx.py b/openpype/hosts/max/plugins/load/load_camera_fbx.py index e6eac25cfc..3a6947798e 100644 --- a/openpype/hosts/max/plugins/load/load_camera_fbx.py +++ b/openpype/hosts/max/plugins/load/load_camera_fbx.py @@ -49,7 +49,7 @@ importFile @"{filepath}" #noPrompt using:FBXIMP path = get_representation_path(representation) node = rt.getNodeByName(container["instance_node"]) - fbx_objects = self.get_container_children(node, "AlembicObject") + fbx_objects = self.get_container_children(node) for fbx_object in fbx_objects: fbx_object.source = path diff --git a/openpype/hosts/max/plugins/load/load_max_scene.py b/openpype/hosts/max/plugins/load/load_max_scene.py index 13cf1e6019..b863b9363f 100644 --- a/openpype/hosts/max/plugins/load/load_max_scene.py +++ b/openpype/hosts/max/plugins/load/load_max_scene.py @@ -47,7 +47,7 @@ class MaxSceneLoader(load.LoaderPlugin): path = get_representation_path(representation) node = rt.getNodeByName(container["instance_node"]) - max_objects = self.get_container_children(node, "AlembicObject") + max_objects = self.get_container_children(node) for max_object in max_objects: max_object.source = path From 9bf7246f52e085c8bd61816b0516c72f275536c3 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Tue, 21 Feb 2023 18:43:28 +0100 Subject: [PATCH 0781/1271] Fix typos --- .../nuke/plugins/publish/extract_review_data.py | 2 +- .../plugins/publish/submit_publish_job.py | 16 ++++++++-------- .../publish/integrate_shotgrid_publish.py | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data.py b/openpype/hosts/nuke/plugins/publish/extract_review_data.py index 3c85b21b08..dee8248295 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data.py @@ -23,7 +23,7 @@ class ExtractReviewData(publish.Extractor): representations = instance.data.get("representations", []) # review can be removed since `ProcessSubmittedJobOnFarm` will create - # reviable representation if needed + # reviewable representation if needed if ( "render.farm" in instance.data["families"] and "review" in instance.data["families"] diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index c7a559466c..b8edd0f161 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -194,7 +194,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): metadata_path = os.path.join(output_dir, metadata_filename) # Convert output dir to `{root}/rest/of/path/...` with Anatomy - success, roothless_mtdt_p = self.anatomy.find_root_template_from_path( + success, rootless_mtdt_p = self.anatomy.find_root_template_from_path( metadata_path) if not success: # `rootless_path` is not set to `output_dir` if none of roots match @@ -202,9 +202,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "Could not find root path for remapping \"{}\"." " This may cause issues on farm." ).format(output_dir)) - roothless_mtdt_p = metadata_path + rootless_mtdt_p = metadata_path - return metadata_path, roothless_mtdt_p + return metadata_path, rootless_mtdt_p def _submit_deadline_post_job(self, instance, job, instances): """Submit publish job to Deadline. @@ -237,7 +237,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # Transfer the environment from the original job to this dependent # job so they use the same environment - metadata_path, roothless_metadata_path = \ + metadata_path, rootless_metadata_path = \ self._create_metadata_path(instance) environment = { @@ -274,7 +274,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): args = [ "--headless", 'publish', - roothless_metadata_path, + rootless_metadata_path, "--targets", "deadline", "--targets", "farm" ] @@ -588,7 +588,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): host_name = os.environ.get("AVALON_APP", "") collections, remainders = clique.assemble(exp_files) - # create representation for every collected sequento ce + # create representation for every collected sequence for collection in collections: ext = collection.tail.lstrip(".") preview = False @@ -656,7 +656,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self._solve_families(instance, preview) - # add reminders as representations + # add remainders as representations for remainder in remainders: ext = remainder.split(".")[-1] @@ -1060,7 +1060,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): } publish_job.update({"ftrack": ftrack}) - metadata_path, roothless_metadata_path = self._create_metadata_path( + metadata_path, rootless_metadata_path = self._create_metadata_path( instance) self.log.info("Writing json file: {}".format(metadata_path)) diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py index fc15d5515f..a1eb2e188c 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py @@ -7,7 +7,7 @@ from openpype.pipeline.publish import get_publish_repre_path class IntegrateShotgridPublish(pyblish.api.InstancePlugin): """ Create published Files from representations and add it to version. If - representation is tagged add shotgrid review, it will add it in + representation is tagged as shotgrid review, it will add it in path to movie for a movie file or path to frame for an image sequence. """ From e5ebc8566376bf3be30e3b2be27b72ad2c237538 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Tue, 21 Feb 2023 18:44:15 +0100 Subject: [PATCH 0782/1271] Add OPENPYPE_SG_USER environment variable to deadline submission env --- .../modules/deadline/plugins/publish/submit_nuke_deadline.py | 3 ++- .../modules/deadline/plugins/publish/submit_publish_job.py | 3 ++- .../shotgrid/plugins/publish/collect_shotgrid_session.py | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py index cca2a4d896..faa66effbd 100644 --- a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -266,7 +266,8 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): "PYBLISHPLUGINPATH", "NUKE_PATH", "TOOL_ENV", - "FOUNDRY_LICENSE" + "FOUNDRY_LICENSE", + "OPENPYPE_SG_USER", ] # Add OpenPype version if we are running from build. diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index b8edd0f161..afab041b7d 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -139,7 +139,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "FTRACK_API_KEY", "FTRACK_SERVER", "AVALON_APP_NAME", - "OPENPYPE_USERNAME" + "OPENPYPE_USERNAME", + "OPENPYPE_SG_USER", ] # Add OpenPype version if we are running from build. diff --git a/openpype/modules/shotgrid/plugins/publish/collect_shotgrid_session.py b/openpype/modules/shotgrid/plugins/publish/collect_shotgrid_session.py index 9d5d2271bf..74f039ba22 100644 --- a/openpype/modules/shotgrid/plugins/publish/collect_shotgrid_session.py +++ b/openpype/modules/shotgrid/plugins/publish/collect_shotgrid_session.py @@ -83,6 +83,10 @@ class CollectShotgridSession(pyblish.api.ContextPlugin): "login to shotgrid withing openpype Tray" ) + # Set OPENPYPE_SG_USER with login so other deadline tasks can make use of it + self.log.info("Setting OPENPYPE_SG_USER to '%s'.", login) + os.environ["OPENPYPE_SG_USER"] = login + session = shotgun_api3.Shotgun( base_url=shotgrid_url, script_name=shotgrid_script_name, From 35e4313ddbd21161e2f14073caea63a1608be499 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Tue, 21 Feb 2023 18:44:34 +0100 Subject: [PATCH 0783/1271] Fix typo on function as 'fill_roots' doesn't exist --- openpype/modules/deadline/plugins/publish/submit_publish_job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index afab041b7d..84092a3bb2 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -412,7 +412,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): assert fn is not None, "padding string wasn't found" # list of tuples (source, destination) staging = representation.get("stagingDir") - staging = self.anatomy.fill_roots(staging) + staging = self.anatomy.fill_root(staging) resource_files.append( (frame, os.path.join(staging, From 03c64a7290a0159fcacea503ad85f5692041a922 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Tue, 21 Feb 2023 18:44:56 +0100 Subject: [PATCH 0784/1271] Set staging dir with rootless syntax like the other representations --- openpype/modules/deadline/plugins/publish/submit_publish_job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 84092a3bb2..8748da1b35 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -677,7 +677,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "name": ext, "ext": ext, "files": os.path.basename(remainder), - "stagingDir": os.path.dirname(remainder), + "stagingDir": staging, } preview = match_aov_pattern( From 271b19a90d6a4599c08917f6be78eea182d324fa Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Tue, 21 Feb 2023 18:45:18 +0100 Subject: [PATCH 0785/1271] Move variable initialization closer to its use --- .../shotgrid/plugins/publish/integrate_shotgrid_publish.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py index a1eb2e188c..ad400572c9 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_publish.py @@ -27,11 +27,11 @@ class IntegrateShotgridPublish(pyblish.api.InstancePlugin): local_path = get_publish_repre_path( instance, representation, False ) - code = os.path.basename(local_path) if representation.get("tags", []): continue + code = os.path.basename(local_path) published_file = self._find_existing_publish( code, context, shotgrid_version ) From ab5ef5469f355175094312c4815d018b1479a5a3 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Tue, 21 Feb 2023 18:46:13 +0100 Subject: [PATCH 0786/1271] Fix code so it doesn't error out when 'intent' attribute returns None or empty string --- .../shotgrid/plugins/publish/integrate_shotgrid_version.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py index adfdca718c..e1fa0c5174 100644 --- a/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py +++ b/openpype/modules/shotgrid/plugins/publish/integrate_shotgrid_version.py @@ -37,9 +37,9 @@ class IntegrateShotgridVersion(pyblish.api.InstancePlugin): self.log.info("Use existing Shotgrid version: {}".format(version)) data_to_update = {} - status = context.data.get("intent", {}).get("value") - if status: - data_to_update["sg_status_list"] = status + intent = context.data.get("intent") + if intent: + data_to_update["sg_status_list"] = intent["value"] for representation in instance.data.get("representations", []): local_path = get_publish_repre_path( From 7dd5c42ec9f46ebc83245fec9eb867290c8babde Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Tue, 21 Feb 2023 18:46:36 +0100 Subject: [PATCH 0787/1271] Load plugin modules with importlib so we can debug symbols --- openpype/pipeline/publish/lib.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index bbc511fc5a..313e80a02b 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -5,6 +5,7 @@ import inspect import copy import tempfile import xml.etree.ElementTree +import importlib import six import pyblish.plugin @@ -305,8 +306,15 @@ def publish_plugins_discover(paths=None): module.__file__ = abspath try: - with open(abspath, "rb") as f: - six.exec_(f.read(), module.__dict__) + if six.PY3: + # Use loader so module has full specs + module_loader = importlib.machinery.SourceFileLoader( + mod_name, abspath + ) + module_loader.exec_module(module) + else: + with open(abspath, "rb") as f: + six.exec_(f.read(), module.__dict__) # Store reference to original module, to avoid # garbage collection from collecting it's global From 0b811854ba67c23e6c8746d7776e5662a437fb57 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Tue, 21 Feb 2023 18:47:03 +0100 Subject: [PATCH 0788/1271] Expand stating dir paths on integrate plugin so it doesn't error out with rootless paths --- openpype/pipeline/publish/lib.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 313e80a02b..c563bc8207 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -691,6 +691,12 @@ def get_publish_repre_path(instance, repre, only_published=False): staging_dir = repre.get("stagingDir") if not staging_dir: staging_dir = get_instance_staging_dir(instance) + + # Expand the staging dir path in case it's been stored with the root + # template syntax + anatomy = instance.context.data.get("anatomy") + staging_dir = anatomy.fill_root(staging_dir) + src_path = os.path.normpath(os.path.join(staging_dir, filename)) if os.path.exists(src_path): return src_path From f9dc9f892076cb1ee8570ad04d6cd2c203dbc309 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Tue, 21 Feb 2023 18:47:19 +0100 Subject: [PATCH 0789/1271] Protect code for cases where 'Frames' key doesn't exist --- .../plugins/publish/validate_expected_and_rendered_files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py b/openpype/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py index f0a3ddd246..f34f71d213 100644 --- a/openpype/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py +++ b/openpype/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py @@ -91,7 +91,7 @@ class ValidateExpectedFiles(pyblish.api.InstancePlugin): for job_id in render_job_ids: job_info = self._get_job_info(job_id) - frame_list = job_info["Props"]["Frames"] + frame_list = job_info["Props"].get("Frames") if frame_list: all_frame_lists.extend(frame_list.split(',')) From f62e6c9c50327cc3f6869c635693900427ec0206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A0=20Serra=20Arrizabalaga?= Date: Tue, 21 Feb 2023 22:09:47 +0100 Subject: [PATCH 0790/1271] Break line so it fits under 79 chars --- .../shotgrid/plugins/publish/collect_shotgrid_session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/shotgrid/plugins/publish/collect_shotgrid_session.py b/openpype/modules/shotgrid/plugins/publish/collect_shotgrid_session.py index 74f039ba22..acfd6d1820 100644 --- a/openpype/modules/shotgrid/plugins/publish/collect_shotgrid_session.py +++ b/openpype/modules/shotgrid/plugins/publish/collect_shotgrid_session.py @@ -83,7 +83,8 @@ class CollectShotgridSession(pyblish.api.ContextPlugin): "login to shotgrid withing openpype Tray" ) - # Set OPENPYPE_SG_USER with login so other deadline tasks can make use of it + # Set OPENPYPE_SG_USER with login so other deadline tasks can make + # use of it self.log.info("Setting OPENPYPE_SG_USER to '%s'.", login) os.environ["OPENPYPE_SG_USER"] = login From bfbfdf7067703226c921916d2e4bbd1e0bd14854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A0=20Serra=20Arrizabalaga?= Date: Tue, 21 Feb 2023 23:33:28 +0100 Subject: [PATCH 0791/1271] Use dict indexing instead of .get to assert existence Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- 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 c563bc8207..5dce74156b 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -694,7 +694,7 @@ def get_publish_repre_path(instance, repre, only_published=False): # Expand the staging dir path in case it's been stored with the root # template syntax - anatomy = instance.context.data.get("anatomy") + anatomy = instance.context.data["anatomy"] staging_dir = anatomy.fill_root(staging_dir) src_path = os.path.normpath(os.path.join(staging_dir, filename)) From 574f8d2c6d5701b59b4d824310d88fb029e3f110 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Tue, 21 Feb 2023 23:43:21 +0100 Subject: [PATCH 0792/1271] Make use of openpype.lib.import_filepath function --- openpype/pipeline/publish/lib.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 5dce74156b..c51a96f2be 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -13,6 +13,7 @@ import pyblish.api from openpype.lib import ( Logger, + import_filepath, filter_profiles ) from openpype.settings import ( @@ -302,19 +303,8 @@ def publish_plugins_discover(paths=None): if not mod_ext == ".py": continue - module = types.ModuleType(mod_name) - module.__file__ = abspath - try: - if six.PY3: - # Use loader so module has full specs - module_loader = importlib.machinery.SourceFileLoader( - mod_name, abspath - ) - module_loader.exec_module(module) - else: - with open(abspath, "rb") as f: - six.exec_(f.read(), module.__dict__) + module = import_filepath(abspath) # Store reference to original module, to avoid # garbage collection from collecting it's global From c41a3b1cb851e3ce4e7c2954b8d20147a98d16e5 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Wed, 22 Feb 2023 00:43:26 +0100 Subject: [PATCH 0793/1271] Pass module name and set __file__ on module to avoid AttributeErrors saying module has no __file__ attribute --- openpype/lib/python_module_tools.py | 2 +- openpype/pipeline/publish/lib.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/lib/python_module_tools.py b/openpype/lib/python_module_tools.py index 6fad3b547f..9e8e94842c 100644 --- a/openpype/lib/python_module_tools.py +++ b/openpype/lib/python_module_tools.py @@ -28,6 +28,7 @@ def import_filepath(filepath, module_name=None): # Prepare module object where content of file will be parsed module = types.ModuleType(module_name) + module.__file__ = filepath if six.PY3: # Use loader so module has full specs @@ -41,7 +42,6 @@ def import_filepath(filepath, module_name=None): # Execute content and store it to module object six.exec_(_stream.read(), module.__dict__) - module.__file__ = filepath return module diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index c51a96f2be..7dcfec4ebc 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -304,7 +304,7 @@ def publish_plugins_discover(paths=None): continue try: - module = import_filepath(abspath) + module = import_filepath(abspath, mod_name) # Store reference to original module, to avoid # garbage collection from collecting it's global From a946eae6c413e64f50e96594e292906cc769f1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A0=20Serra=20Arrizabalaga?= Date: Wed, 22 Feb 2023 12:49:05 +0100 Subject: [PATCH 0794/1271] Remove unused module --- openpype/pipeline/publish/lib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 7dcfec4ebc..1ec641bac4 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -5,7 +5,6 @@ import inspect import copy import tempfile import xml.etree.ElementTree -import importlib import six import pyblish.plugin From 36b7fa32df20c9640ccda6a81ed61766017d607c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:33:20 +0100 Subject: [PATCH 0795/1271] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- openpype/lib/transcoding.py | 2 +- .../plugins/publish/extract_color_transcode.py | 15 +++++++++++---- .../schemas/schema_global_publish.json | 9 +++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 8a80e88d3a..42db374402 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1044,7 +1044,7 @@ def convert_colorspace( output_path, config_path, source_colorspace, - target_colorspace, + target_colorspace=None, view=None, display=None, additional_command_args=None, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..b0921688e9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,10 +118,17 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] - view = output_def["view"] or colorspace_data.get("view") - display = (output_def["display"] or - colorspace_data.get("display")) + transcoding_type = output_def["transcoding_type"] + + target_colorspace = view = display = None + if transcoding_type == "colorspace": + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) + else: + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, # but could be overwritten if view: diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3e9467af61..76574e8b9b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -271,6 +271,15 @@ "label": "Extension", "type": "text" }, + { + "type": "enum", + "key": "transcoding_type", + "label": "Transcoding type", + "enum_items": [ + { "colorspace": "Use Colorspace" }, + { "display": "Use Display&View" } + ] + }, { "key": "colorspace", "label": "Colorspace", From 90afe4185110f2571242007f88566be7dce78dff Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:34:03 +0100 Subject: [PATCH 0796/1271] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- .../assets/global_oiio_transcode.png | Bin 29010 -> 17936 bytes .../settings_project_global.md | 5 +++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png index 99396d5bb3f16d434a92c6079515128488b82489..d818ecfe19f93366e5f4664734d68c7b448d7b4f 100644 GIT binary patch literal 17936 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 DbV~re literal 29010 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 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 9e2ee187cc..6e78ee5d45 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,8 +53,9 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. +- **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. +- **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). +- **`Display & View`** - display and viewer colorspace. (If `Transcoding type` is `Use Display&View` values in configuration is used OR if empty values collected on instance from DCC). - **`Arguments`** - special additional command line arguments for `oiiotool`. From 1594d7753728820b4d84568c7c5fe80aaa0ebbab Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:42:07 +0100 Subject: [PATCH 0797/1271] OP-4643 - added use case for Maya to documentation --- .../assets/global_oiio_transcode2.png | Bin 0 -> 17960 bytes .../project_settings/settings_project_global.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode2.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode2.png b/website/docs/project_settings/assets/global_oiio_transcode2.png new file mode 100644 index 0000000000000000000000000000000000000000..906f780830a96b4bc6f26d98484dd3f9cc3885f2 GIT binary patch literal 17960 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 F{{cSxQDp!C literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 6e78ee5d45..f58d2c2bf2 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -62,6 +62,9 @@ Notable parameters: Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. ![global_oiio_transcode](assets/global_oiio_transcode.png) +Another use case is to transcode in Maya only `beauty` render layers and use collected `Display` and `View` colorspaces from DCC. +![global_oiio_transcode_in_Maya](assets/global_oiio_transcode.png) + ## Profile filters Many of the settings are using a concept of **Profile filters** From 9a44a8fd7d1d5cf4102066e12c56696769609f2b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 14:00:14 +0100 Subject: [PATCH 0798/1271] save default settings with newline character at the end of file --- openpype/settings/entities/root_entities.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/root_entities.py b/openpype/settings/entities/root_entities.py index ff76fa5180..f2e24fb522 100644 --- a/openpype/settings/entities/root_entities.py +++ b/openpype/settings/entities/root_entities.py @@ -440,8 +440,9 @@ class RootEntity(BaseItemEntity): os.makedirs(dirpath) self.log.debug("Saving data to: {}\n{}".format(subpath, value)) + data = json.dumps(value, indent=4) + "\n" with open(output_path, "w") as file_stream: - json.dump(value, file_stream, indent=4) + file_stream.write(data) dynamic_values_item = self.collect_dynamic_schema_entities() dynamic_values_item.save_values() From 974510044611ea05c58f6ef2f5eb70095ea122de Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 14:50:56 +0100 Subject: [PATCH 0799/1271] resave default settings --- openpype/settings/defaults/project_anatomy/attributes.json | 2 +- openpype/settings/defaults/project_anatomy/imageio.json | 2 +- openpype/settings/defaults/project_anatomy/roots.json | 2 +- openpype/settings/defaults/project_anatomy/tasks.json | 2 +- openpype/settings/defaults/project_anatomy/templates.json | 2 +- .../settings/defaults/project_settings/aftereffects.json | 2 +- openpype/settings/defaults/project_settings/blender.json | 2 +- openpype/settings/defaults/project_settings/celaction.json | 2 +- openpype/settings/defaults/project_settings/flame.json | 2 +- openpype/settings/defaults/project_settings/ftrack.json | 2 +- openpype/settings/defaults/project_settings/fusion.json | 2 +- openpype/settings/defaults/project_settings/global.json | 2 +- openpype/settings/defaults/project_settings/harmony.json | 2 +- openpype/settings/defaults/project_settings/hiero.json | 2 +- openpype/settings/defaults/project_settings/houdini.json | 2 +- openpype/settings/defaults/project_settings/kitsu.json | 2 +- openpype/settings/defaults/project_settings/max.json | 2 +- openpype/settings/defaults/project_settings/maya.json | 2 +- openpype/settings/defaults/project_settings/nuke.json | 2 +- openpype/settings/defaults/project_settings/photoshop.json | 2 +- openpype/settings/defaults/project_settings/resolve.json | 2 +- .../settings/defaults/project_settings/royalrender.json | 2 +- openpype/settings/defaults/project_settings/shotgrid.json | 2 +- openpype/settings/defaults/project_settings/slack.json | 2 +- .../defaults/project_settings/standalonepublisher.json | 2 +- .../settings/defaults/project_settings/traypublisher.json | 2 +- openpype/settings/defaults/project_settings/tvpaint.json | 2 +- openpype/settings/defaults/project_settings/unreal.json | 2 +- .../settings/defaults/project_settings/webpublisher.json | 2 +- .../settings/defaults/system_settings/applications.json | 6 ++++-- openpype/settings/defaults/system_settings/general.json | 2 +- openpype/settings/defaults/system_settings/modules.json | 2 +- openpype/settings/defaults/system_settings/tools.json | 2 +- 33 files changed, 36 insertions(+), 34 deletions(-) diff --git a/openpype/settings/defaults/project_anatomy/attributes.json b/openpype/settings/defaults/project_anatomy/attributes.json index bf8bbef8de..0cc414fb69 100644 --- a/openpype/settings/defaults/project_anatomy/attributes.json +++ b/openpype/settings/defaults/project_anatomy/attributes.json @@ -23,4 +23,4 @@ ], "tools_env": [], "active": true -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_anatomy/imageio.json b/openpype/settings/defaults/project_anatomy/imageio.json index caa2a8a206..d38d0a0774 100644 --- a/openpype/settings/defaults/project_anatomy/imageio.json +++ b/openpype/settings/defaults/project_anatomy/imageio.json @@ -255,4 +255,4 @@ ] } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_anatomy/roots.json b/openpype/settings/defaults/project_anatomy/roots.json index ce295e946f..8171d17d56 100644 --- a/openpype/settings/defaults/project_anatomy/roots.json +++ b/openpype/settings/defaults/project_anatomy/roots.json @@ -4,4 +4,4 @@ "darwin": "/Volumes/path", "linux": "/mnt/share/projects" } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_anatomy/tasks.json b/openpype/settings/defaults/project_anatomy/tasks.json index 74504cc4d7..135462839f 100644 --- a/openpype/settings/defaults/project_anatomy/tasks.json +++ b/openpype/settings/defaults/project_anatomy/tasks.json @@ -41,4 +41,4 @@ "Compositing": { "short_name": "comp" } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index 32230e0625..99a869963b 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -66,4 +66,4 @@ "source": "source" } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/aftereffects.json b/openpype/settings/defaults/project_settings/aftereffects.json index e4b957fb85..669e1db0b8 100644 --- a/openpype/settings/defaults/project_settings/aftereffects.json +++ b/openpype/settings/defaults/project_settings/aftereffects.json @@ -33,4 +33,4 @@ "create_first_version": false, "custom_templates": [] } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/blender.json b/openpype/settings/defaults/project_settings/blender.json index 3585d2ad0a..fe05f94590 100644 --- a/openpype/settings/defaults/project_settings/blender.json +++ b/openpype/settings/defaults/project_settings/blender.json @@ -82,4 +82,4 @@ "active": false } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/celaction.json b/openpype/settings/defaults/project_settings/celaction.json index ad01e62d95..bdba6d7322 100644 --- a/openpype/settings/defaults/project_settings/celaction.json +++ b/openpype/settings/defaults/project_settings/celaction.json @@ -16,4 +16,4 @@ "anatomy_template_key_metadata": "render" } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index cbd99c4560..3190bdb3bf 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -163,4 +163,4 @@ ] } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/ftrack.json b/openpype/settings/defaults/project_settings/ftrack.json index ec48ba52ea..4ca4a35d1f 100644 --- a/openpype/settings/defaults/project_settings/ftrack.json +++ b/openpype/settings/defaults/project_settings/ftrack.json @@ -496,4 +496,4 @@ "farm_status_profiles": [] } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/fusion.json b/openpype/settings/defaults/project_settings/fusion.json index 720178e17a..954606820a 100644 --- a/openpype/settings/defaults/project_settings/fusion.json +++ b/openpype/settings/defaults/project_settings/fusion.json @@ -17,4 +17,4 @@ } } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 0e078dc157..cedc2d6876 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -607,4 +607,4 @@ "linux": [] }, "project_environments": {} -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/harmony.json b/openpype/settings/defaults/project_settings/harmony.json index 1f4ea88272..3f51a9c28b 100644 --- a/openpype/settings/defaults/project_settings/harmony.json +++ b/openpype/settings/defaults/project_settings/harmony.json @@ -50,4 +50,4 @@ "skip_timelines_check": [] } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/hiero.json b/openpype/settings/defaults/project_settings/hiero.json index c6180d0a58..0412967eaa 100644 --- a/openpype/settings/defaults/project_settings/hiero.json +++ b/openpype/settings/defaults/project_settings/hiero.json @@ -97,4 +97,4 @@ } ] } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/houdini.json b/openpype/settings/defaults/project_settings/houdini.json index 68cc8945fe..1b7faf8526 100644 --- a/openpype/settings/defaults/project_settings/houdini.json +++ b/openpype/settings/defaults/project_settings/houdini.json @@ -76,4 +76,4 @@ "active": true } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/kitsu.json b/openpype/settings/defaults/project_settings/kitsu.json index 3a9723b9c0..95b3da04ae 100644 --- a/openpype/settings/defaults/project_settings/kitsu.json +++ b/openpype/settings/defaults/project_settings/kitsu.json @@ -10,4 +10,4 @@ "note_status_shortname": "wfa" } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json index 84e0c7dba7..667b42411d 100644 --- a/openpype/settings/defaults/project_settings/max.json +++ b/openpype/settings/defaults/project_settings/max.json @@ -5,4 +5,4 @@ "image_format": "exr", "multipass": true } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 03c2d325bb..b590a56da6 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1103,4 +1103,4 @@ "ValidateNoAnimation": false } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 2999d1427d..d475c337d9 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -533,4 +533,4 @@ "profiles": [] }, "filters": {} -} \ 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 cdfab0c439..bcf21f55dd 100644 --- a/openpype/settings/defaults/project_settings/photoshop.json +++ b/openpype/settings/defaults/project_settings/photoshop.json @@ -67,4 +67,4 @@ "create_first_version": false, "custom_templates": [] } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/resolve.json b/openpype/settings/defaults/project_settings/resolve.json index 66013c5ac7..264f3bd902 100644 --- a/openpype/settings/defaults/project_settings/resolve.json +++ b/openpype/settings/defaults/project_settings/resolve.json @@ -27,4 +27,4 @@ "handleEnd": 10 } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/royalrender.json b/openpype/settings/defaults/project_settings/royalrender.json index be267b11d8..b72fed8474 100644 --- a/openpype/settings/defaults/project_settings/royalrender.json +++ b/openpype/settings/defaults/project_settings/royalrender.json @@ -4,4 +4,4 @@ "review": true } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/shotgrid.json b/openpype/settings/defaults/project_settings/shotgrid.json index 774bce714b..83b6f69074 100644 --- a/openpype/settings/defaults/project_settings/shotgrid.json +++ b/openpype/settings/defaults/project_settings/shotgrid.json @@ -19,4 +19,4 @@ "step": "step" } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/slack.json b/openpype/settings/defaults/project_settings/slack.json index c156fed08e..910f099d04 100644 --- a/openpype/settings/defaults/project_settings/slack.json +++ b/openpype/settings/defaults/project_settings/slack.json @@ -17,4 +17,4 @@ ] } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/standalonepublisher.json b/openpype/settings/defaults/project_settings/standalonepublisher.json index b6e2e056a1..d923b4db43 100644 --- a/openpype/settings/defaults/project_settings/standalonepublisher.json +++ b/openpype/settings/defaults/project_settings/standalonepublisher.json @@ -304,4 +304,4 @@ } } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/traypublisher.json b/openpype/settings/defaults/project_settings/traypublisher.json index 8a222a6dd2..fdea4aeaba 100644 --- a/openpype/settings/defaults/project_settings/traypublisher.json +++ b/openpype/settings/defaults/project_settings/traypublisher.json @@ -321,4 +321,4 @@ "active": true } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index 40603ed874..0b6d3d7e81 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -109,4 +109,4 @@ "custom_templates": [] }, "filters": {} -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/unreal.json b/openpype/settings/defaults/project_settings/unreal.json index b06bf28714..75cee11bd9 100644 --- a/openpype/settings/defaults/project_settings/unreal.json +++ b/openpype/settings/defaults/project_settings/unreal.json @@ -14,4 +14,4 @@ "project_setup": { "dev_mode": true } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/project_settings/webpublisher.json b/openpype/settings/defaults/project_settings/webpublisher.json index 27eac131b7..e830ba6a40 100644 --- a/openpype/settings/defaults/project_settings/webpublisher.json +++ b/openpype/settings/defaults/project_settings/webpublisher.json @@ -141,4 +141,4 @@ "layer_name_regex": "(?PL[0-9]{3}_\\w+)_(?P.+)" } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index 936407a49b..f84d99e36b 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -1302,7 +1302,9 @@ "variant_label": "Current", "use_python_2": false, "executables": { - "windows": ["C:/Program Files/CelAction/CelAction2D Studio/CelAction2D.exe"], + "windows": [ + "C:/Program Files/CelAction/CelAction2D Studio/CelAction2D.exe" + ], "darwin": [], "linux": [] }, @@ -1365,4 +1367,4 @@ } }, "additional_apps": {} -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/system_settings/general.json b/openpype/settings/defaults/system_settings/general.json index 909ffc1ee4..d2994d1a62 100644 --- a/openpype/settings/defaults/system_settings/general.json +++ b/openpype/settings/defaults/system_settings/general.json @@ -18,4 +18,4 @@ "production_version": "", "staging_version": "", "version_check_interval": 5 -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/system_settings/modules.json b/openpype/settings/defaults/system_settings/modules.json index 703e72cb5d..1ddbfd2726 100644 --- a/openpype/settings/defaults/system_settings/modules.json +++ b/openpype/settings/defaults/system_settings/modules.json @@ -211,4 +211,4 @@ "linux": "" } } -} \ No newline at end of file +} diff --git a/openpype/settings/defaults/system_settings/tools.json b/openpype/settings/defaults/system_settings/tools.json index 243cde40cc..921e13af3a 100644 --- a/openpype/settings/defaults/system_settings/tools.json +++ b/openpype/settings/defaults/system_settings/tools.json @@ -87,4 +87,4 @@ "renderman": "Pixar Renderman" } } -} \ No newline at end of file +} From aa9817ae5272db128d66d009189811700b65b492 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Wed, 25 Jan 2023 11:09:19 +0000 Subject: [PATCH 0800/1271] Basic implementation of the new Creator --- openpype/hosts/unreal/api/__init__.py | 6 +- openpype/hosts/unreal/api/pipeline.py | 53 ++++++- openpype/hosts/unreal/api/plugin.py | 209 +++++++++++++++++++++++++- 3 files changed, 262 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/unreal/api/__init__.py b/openpype/hosts/unreal/api/__init__.py index ca9db259e6..2618a7677c 100644 --- a/openpype/hosts/unreal/api/__init__.py +++ b/openpype/hosts/unreal/api/__init__.py @@ -1,7 +1,11 @@ # -*- coding: utf-8 -*- """Unreal Editor OpenPype host API.""" -from .plugin import Loader +from .plugin import ( + UnrealActorCreator, + UnrealAssetCreator, + Loader +) from .pipeline import ( install, diff --git a/openpype/hosts/unreal/api/pipeline.py b/openpype/hosts/unreal/api/pipeline.py index 2081c8fd13..7a21effcbc 100644 --- a/openpype/hosts/unreal/api/pipeline.py +++ b/openpype/hosts/unreal/api/pipeline.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import os +import json import logging from typing import List from contextlib import contextmanager @@ -16,13 +17,14 @@ from openpype.pipeline import ( ) from openpype.tools.utils import host_tools import openpype.hosts.unreal -from openpype.host import HostBase, ILoadHost +from openpype.host import HostBase, ILoadHost, IPublishHost import unreal # noqa - logger = logging.getLogger("openpype.hosts.unreal") + OPENPYPE_CONTAINERS = "OpenPypeContainers" +CONTEXT_CONTAINER = "OpenPype/context.json" UNREAL_VERSION = semver.VersionInfo( *os.getenv("OPENPYPE_UNREAL_VERSION").split(".") ) @@ -35,7 +37,7 @@ CREATE_PATH = os.path.join(PLUGINS_DIR, "create") INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") -class UnrealHost(HostBase, ILoadHost): +class UnrealHost(HostBase, ILoadHost, IPublishHost): """Unreal host implementation. For some time this class will re-use functions from module based @@ -60,6 +62,26 @@ class UnrealHost(HostBase, ILoadHost): show_tools_dialog() + def update_context_data(self, data, changes): + unreal.log_warning("update_context_data") + unreal.log_warning(data) + content_path = unreal.Paths.project_content_dir() + op_ctx = content_path + CONTEXT_CONTAINER + with open(op_ctx, "w+") as f: + json.dump(data, f) + with open(op_ctx, "r") as fp: + test = eval(json.load(fp)) + unreal.log_warning(test) + + def get_context_data(self): + content_path = unreal.Paths.project_content_dir() + op_ctx = content_path + CONTEXT_CONTAINER + if not os.path.isfile(op_ctx): + return {} + with open(op_ctx, "r") as fp: + data = eval(json.load(fp)) + return data + def install(): """Install Unreal configuration for OpenPype.""" @@ -133,6 +155,31 @@ def ls(): yield data +def lsinst(): + ar = unreal.AssetRegistryHelpers.get_asset_registry() + # UE 5.1 changed how class name is specified + class_name = [ + "/Script/OpenPype", + "OpenPypePublishInstance" + ] if ( + UNREAL_VERSION.major == 5 + and UNREAL_VERSION.minor > 0 + ) else "OpenPypePublishInstance" # noqa + instances = ar.get_assets_by_class(class_name, True) + + # get_asset_by_class returns AssetData. To get all metadata we need to + # load asset. get_tag_values() work only on metadata registered in + # Asset Registry Project settings (and there is no way to set it with + # python short of editing ini configuration file). + for asset_data in instances: + asset = asset_data.get_asset() + data = unreal.EditorAssetLibrary.get_metadata_tag_values(asset) + data["objectName"] = asset_data.asset_name + data = cast_map_to_str_dict(data) + + yield data + + def parse_container(container): """To get data from container, AssetContainer must be loaded. diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index 6fc00cb71c..f89ff153b1 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -1,7 +1,212 @@ # -*- coding: utf-8 -*- -from abc import ABC +import sys +import six +from abc import ( + ABC, + ABCMeta, + abstractmethod +) -from openpype.pipeline import LoaderPlugin +import unreal + +from .pipeline import ( + create_publish_instance, + imprint, + lsinst +) +from openpype.lib import BoolDef +from openpype.pipeline import ( + Creator, + LoaderPlugin, + CreatorError, + CreatedInstance +) + + +class OpenPypeCreatorError(CreatorError): + pass + + +@six.add_metaclass(ABCMeta) +class UnrealBaseCreator(Creator): + """Base class for Unreal creator plugins.""" + root = "/Game/OpenPype/PublishInstances" + suffix = "_INS" + + @staticmethod + def cache_subsets(shared_data): + """Cache instances for Creators to shared data. + + Create `unreal_cached_subsets` key when needed in shared data and + fill it with all collected instances from the scene under its + respective creator identifiers. + + If legacy instances are detected in the scene, create + `unreal_cached_legacy_subsets` there and fill it with + all legacy subsets under family as a key. + + Args: + Dict[str, Any]: Shared data. + + Return: + Dict[str, Any]: Shared data dictionary. + + """ + if shared_data.get("unreal_cached_subsets") is None: + shared_data["unreal_cached_subsets"] = {} + if shared_data.get("unreal_cached_legacy_subsets") is None: + shared_data["unreal_cached_legacy_subsets"] = {} + cached_instances = lsinst() + for i in cached_instances: + if not i.get("creator_identifier"): + # we have legacy instance + family = i.get("family") + if (family not in + shared_data["unreal_cached_legacy_subsets"]): + shared_data[ + "unreal_cached_legacy_subsets"][family] = [i] + else: + shared_data[ + "unreal_cached_legacy_subsets"][family].append(i) + continue + + creator_id = i.get("creator_identifier") + if creator_id not in shared_data["unreal_cached_subsets"]: + shared_data["unreal_cached_subsets"][creator_id] = [i] + else: + shared_data["unreal_cached_subsets"][creator_id].append(i) + return shared_data + + @abstractmethod + def create(self, subset_name, instance_data, pre_create_data): + pass + + def collect_instances(self): + # cache instances if missing + self.cache_subsets(self.collection_shared_data) + for instance in self.collection_shared_data[ + "unreal_cached_subsets"].get(self.identifier, []): + created_instance = CreatedInstance.from_existing(instance, self) + self._add_instance_to_context(created_instance) + + def update_instances(self, update_list): + unreal.log_warning(f"Update instances: {update_list}") + for created_inst, _changes in update_list: + instance_node = created_inst.get("instance_path", "") + + if not instance_node: + unreal.log_warning( + f"Instance node not found for {created_inst}") + + new_values = { + key: new_value + for key, (_old_value, new_value) in _changes.items() + } + imprint( + instance_node, + new_values + ) + + def remove_instances(self, instances): + for instance in instances: + instance_node = instance.data.get("instance_path", "") + if instance_node: + unreal.EditorAssetLibrary.delete_asset(instance_node) + + self._remove_instance_from_context(instance) + + def get_pre_create_attr_defs(self): + return [ + BoolDef("use_selection", label="Use selection") + ] + + +@six.add_metaclass(ABCMeta) +class UnrealAssetCreator(UnrealBaseCreator): + """Base class for Unreal creator plugins based on assets.""" + + def create(self, subset_name, instance_data, pre_create_data): + """Create instance of the asset. + + Args: + subset_name (str): Name of the subset. + instance_data (dict): Data for the instance. + pre_create_data (dict): Data for the instance. + + Returns: + CreatedInstance: Created instance. + """ + try: + selection = [] + + if pre_create_data.get("use_selection"): + sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() + selection = [a.get_path_name() for a in sel_objects] + + instance_name = f"{subset_name}{self.suffix}" + create_publish_instance(instance_name, self.root) + instance_data["members"] = selection + instance_data["subset"] = subset_name + instance_data["instance_path"] = f"{self.root}/{instance_name}" + instance = CreatedInstance( + self.family, + subset_name, + instance_data, + self) + self._add_instance_to_context(instance) + + imprint(f"{self.root}/{instance_name}", instance_data) + + except Exception as er: + six.reraise( + OpenPypeCreatorError, + OpenPypeCreatorError(f"Creator error: {er}"), + sys.exc_info()[2]) + + +@six.add_metaclass(ABCMeta) +class UnrealActorCreator(UnrealBaseCreator): + """Base class for Unreal creator plugins based on actors.""" + + def create(self, subset_name, instance_data, pre_create_data): + """Create instance of the asset. + + Args: + subset_name (str): Name of the subset. + instance_data (dict): Data for the instance. + pre_create_data (dict): Data for the instance. + + Returns: + CreatedInstance: Created instance. + """ + try: + selection = [] + + if pre_create_data.get("use_selection"): + sel_objects = unreal.EditorUtilityLibrary.get_selected_actors() + selection = [a.get_path_name() for a in sel_objects] + + instance_name = f"{subset_name}{self.suffix}" + create_publish_instance(instance_name, self.root) + instance_data["members"] = selection + instance_data[ + "level"] = unreal.EditorLevelLibrary.get_editor_world() + instance_data["subset"] = subset_name + instance_data["instance_path"] = f"{self.root}/{instance_name}" + instance = CreatedInstance( + self.family, + subset_name, + instance_data, + self) + self._add_instance_to_context(instance) + + imprint(f"{self.root}/{instance_name}", instance_data) + + except Exception as er: + six.reraise( + OpenPypeCreatorError, + OpenPypeCreatorError(f"Creator error: {er}"), + sys.exc_info()[2]) class Loader(LoaderPlugin, ABC): From f0db455a09287223677c333b08a70a459120df6c Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 26 Jan 2023 17:35:11 +0000 Subject: [PATCH 0801/1271] Improved basic creator --- openpype/hosts/unreal/api/plugin.py | 95 ++++++++++++++++++----------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index f89ff153b1..6a561420fa 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -4,7 +4,6 @@ import six from abc import ( ABC, ABCMeta, - abstractmethod ) import unreal @@ -12,7 +11,8 @@ import unreal from .pipeline import ( create_publish_instance, imprint, - lsinst + lsinst, + UNREAL_VERSION ) from openpype.lib import BoolDef from openpype.pipeline import ( @@ -77,9 +77,28 @@ class UnrealBaseCreator(Creator): shared_data["unreal_cached_subsets"][creator_id].append(i) return shared_data - @abstractmethod def create(self, subset_name, instance_data, pre_create_data): - pass + try: + instance_name = f"{subset_name}{self.suffix}" + create_publish_instance(instance_name, self.root) + + instance_data["subset"] = subset_name + instance_data["instance_path"] = f"{self.root}/{instance_name}" + + instance = CreatedInstance( + self.family, + subset_name, + instance_data, + self) + self._add_instance_to_context(instance) + + imprint(f"{self.root}/{instance_name}", instance_data) + + except Exception as er: + six.reraise( + OpenPypeCreatorError, + OpenPypeCreatorError(f"Creator error: {er}"), + sys.exc_info()[2]) def collect_instances(self): # cache instances if missing @@ -117,7 +136,7 @@ class UnrealBaseCreator(Creator): def get_pre_create_attr_defs(self): return [ - BoolDef("use_selection", label="Use selection") + BoolDef("use_selection", label="Use selection", default=True) ] @@ -137,25 +156,21 @@ class UnrealAssetCreator(UnrealBaseCreator): CreatedInstance: Created instance. """ try: - selection = [] + # Check if instance data has members, filled by the plugin. + # If not, use selection. + if not instance_data.get("members"): + selection = [] - if pre_create_data.get("use_selection"): - sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() - selection = [a.get_path_name() for a in sel_objects] + if pre_create_data.get("use_selection"): + sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() + selection = [a.get_path_name() for a in sel_objects] - instance_name = f"{subset_name}{self.suffix}" - create_publish_instance(instance_name, self.root) - instance_data["members"] = selection - instance_data["subset"] = subset_name - instance_data["instance_path"] = f"{self.root}/{instance_name}" - instance = CreatedInstance( - self.family, + instance_data["members"] = selection + + super(UnrealAssetCreator, self).create( subset_name, instance_data, - self) - self._add_instance_to_context(instance) - - imprint(f"{self.root}/{instance_name}", instance_data) + pre_create_data) except Exception as er: six.reraise( @@ -180,27 +195,33 @@ class UnrealActorCreator(UnrealBaseCreator): CreatedInstance: Created instance. """ try: - selection = [] + if UNREAL_VERSION.major == 5: + world = unreal.UnrealEditorSubsystem().get_editor_world() + else: + world = unreal.EditorLevelLibrary.get_editor_world() - if pre_create_data.get("use_selection"): - sel_objects = unreal.EditorUtilityLibrary.get_selected_actors() - selection = [a.get_path_name() for a in sel_objects] + # Check if the level is saved + if world.get_path_name().startswith("/Temp/"): + raise OpenPypeCreatorError( + "Level must be saved before creating instances.") - instance_name = f"{subset_name}{self.suffix}" - create_publish_instance(instance_name, self.root) - instance_data["members"] = selection - instance_data[ - "level"] = unreal.EditorLevelLibrary.get_editor_world() - instance_data["subset"] = subset_name - instance_data["instance_path"] = f"{self.root}/{instance_name}" - instance = CreatedInstance( - self.family, + # Check if instance data has members, filled by the plugin. + # If not, use selection. + if not instance_data.get("members"): + selection = [] + + if pre_create_data.get("use_selection"): + sel_objects = unreal.EditorUtilityLibrary.get_selected_actors() + selection = [a.get_path_name() for a in sel_objects] + + instance_data["members"] = selection + + instance_data["level"] = world.get_path_name() + + super(UnrealActorCreator, self).create( subset_name, instance_data, - self) - self._add_instance_to_context(instance) - - imprint(f"{self.root}/{instance_name}", instance_data) + pre_create_data) except Exception as er: six.reraise( From 7284601d62c2304ce6fe07b538a858745e7d8869 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 26 Jan 2023 17:35:42 +0000 Subject: [PATCH 0802/1271] Updated creators to be compatible with new publisher --- .../unreal/plugins/create/create_camera.py | 44 +++---------- .../unreal/plugins/create/create_layout.py | 39 ++--------- .../unreal/plugins/create/create_look.py | 64 +++++++++---------- .../plugins/create/create_staticmeshfbx.py | 34 ++-------- .../unreal/plugins/create/create_uasset.py | 44 ++++--------- 5 files changed, 65 insertions(+), 160 deletions(-) diff --git a/openpype/hosts/unreal/plugins/create/create_camera.py b/openpype/hosts/unreal/plugins/create/create_camera.py index bf1489d688..239dc87db5 100644 --- a/openpype/hosts/unreal/plugins/create/create_camera.py +++ b/openpype/hosts/unreal/plugins/create/create_camera.py @@ -1,41 +1,13 @@ -import unreal -from unreal import EditorAssetLibrary as eal -from unreal import EditorLevelLibrary as ell - -from openpype.hosts.unreal.api.pipeline import instantiate -from openpype.pipeline import LegacyCreator +# -*- coding: utf-8 -*- +from openpype.hosts.unreal.api.plugin import ( + UnrealActorCreator, +) -class CreateCamera(LegacyCreator): - """Layout output for character rigs""" +class CreateCamera(UnrealActorCreator): + """Create Camera.""" - name = "layoutMain" + identifier = "io.openpype.creators.unreal.camera" label = "Camera" family = "camera" - icon = "cubes" - - root = "/Game/OpenPype/Instances" - suffix = "_INS" - - def __init__(self, *args, **kwargs): - super(CreateCamera, self).__init__(*args, **kwargs) - - def process(self): - data = self.data - - name = data["subset"] - - data["level"] = ell.get_editor_world().get_path_name() - - if not eal.does_directory_exist(self.root): - eal.make_directory(self.root) - - factory = unreal.LevelSequenceFactoryNew() - tools = unreal.AssetToolsHelpers().get_asset_tools() - tools.create_asset(name, f"{self.root}/{name}", None, factory) - - asset_name = f"{self.root}/{name}/{name}.{name}" - - data["members"] = [asset_name] - - instantiate(f"{self.root}", name, data, None, self.suffix) + icon = "camera" diff --git a/openpype/hosts/unreal/plugins/create/create_layout.py b/openpype/hosts/unreal/plugins/create/create_layout.py index c1067b00d9..1d2e800a13 100644 --- a/openpype/hosts/unreal/plugins/create/create_layout.py +++ b/openpype/hosts/unreal/plugins/create/create_layout.py @@ -1,42 +1,13 @@ # -*- coding: utf-8 -*- -from unreal import EditorLevelLibrary - -from openpype.pipeline import LegacyCreator -from openpype.hosts.unreal.api.pipeline import instantiate +from openpype.hosts.unreal.api.plugin import ( + UnrealActorCreator, +) -class CreateLayout(LegacyCreator): +class CreateLayout(UnrealActorCreator): """Layout output for character rigs.""" - name = "layoutMain" + identifier = "io.openpype.creators.unreal.layout" label = "Layout" family = "layout" icon = "cubes" - - root = "/Game" - suffix = "_INS" - - def __init__(self, *args, **kwargs): - super(CreateLayout, self).__init__(*args, **kwargs) - - def process(self): - data = self.data - - name = data["subset"] - - selection = [] - # if (self.options or {}).get("useSelection"): - # sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() - # selection = [a.get_path_name() for a in sel_objects] - - data["level"] = EditorLevelLibrary.get_editor_world().get_path_name() - - data["members"] = [] - - if (self.options or {}).get("useSelection"): - # Set as members the selected actors - for actor in EditorLevelLibrary.get_selected_level_actors(): - data["members"].append("{}.{}".format( - actor.get_outer().get_name(), actor.get_name())) - - instantiate(self.root, name, data, selection, self.suffix) diff --git a/openpype/hosts/unreal/plugins/create/create_look.py b/openpype/hosts/unreal/plugins/create/create_look.py index 4abf3f6095..08d61ab9f8 100644 --- a/openpype/hosts/unreal/plugins/create/create_look.py +++ b/openpype/hosts/unreal/plugins/create/create_look.py @@ -1,56 +1,53 @@ # -*- coding: utf-8 -*- -"""Create look in Unreal.""" -import unreal # noqa -from openpype.hosts.unreal.api import pipeline, plugin -from openpype.pipeline import LegacyCreator +import unreal + +from openpype.hosts.unreal.api.pipeline import ( + create_folder +) +from openpype.hosts.unreal.api.plugin import ( + UnrealAssetCreator +) -class CreateLook(LegacyCreator): +class CreateLook(UnrealAssetCreator): """Shader connections defining shape look.""" - name = "unrealLook" - label = "Unreal - Look" + identifier = "io.openpype.creators.unreal.look" + label = "Look" family = "look" icon = "paint-brush" - root = "/Game/Avalon/Assets" - suffix = "_INS" - - def __init__(self, *args, **kwargs): - super(CreateLook, self).__init__(*args, **kwargs) - - def process(self): - name = self.data["subset"] - + def create(self, subset_name, instance_data, pre_create_data): selection = [] - if (self.options or {}).get("useSelection"): + if pre_create_data.get("use_selection"): sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() selection = [a.get_path_name() for a in sel_objects] + if len(selection) != 1: + raise RuntimeError("Please select only one asset.") + + selected_asset = selection[0] + + look_directory = "/Game/OpenPype/Looks" + # Create the folder - path = f"{self.root}/{self.data['asset']}" - new_name = pipeline.create_folder(path, name) - full_path = f"{path}/{new_name}" + folder_name = create_folder(look_directory, subset_name) + path = f"{look_directory}/{folder_name}" # Create a new cube static mesh ar = unreal.AssetRegistryHelpers.get_asset_registry() cube = ar.get_asset_by_object_path("/Engine/BasicShapes/Cube.Cube") - # Create the avalon publish instance object - container_name = f"{name}{self.suffix}" - pipeline.create_publish_instance( - instance=container_name, path=full_path) - # Get the mesh of the selected object - original_mesh = ar.get_asset_by_object_path(selection[0]).get_asset() - materials = original_mesh.get_editor_property('materials') + original_mesh = ar.get_asset_by_object_path(selected_asset).get_asset() + materials = original_mesh.get_editor_property('static_materials') - self.data["members"] = [] + instance_data["members"] = [] # Add the materials to the cube for material in materials: - name = material.get_editor_property('material_slot_name') - object_path = f"{full_path}/{name}.{name}" + mat_name = material.get_editor_property('material_slot_name') + object_path = f"{path}/{mat_name}.{mat_name}" unreal_object = unreal.EditorAssetLibrary.duplicate_loaded_asset( cube.get_asset(), object_path ) @@ -61,8 +58,11 @@ class CreateLook(LegacyCreator): unreal_object.add_material( material.get_editor_property('material_interface')) - self.data["members"].append(object_path) + instance_data["members"].append(object_path) unreal.EditorAssetLibrary.save_asset(object_path) - pipeline.imprint(f"{full_path}/{container_name}", self.data) + super(CreateLook, self).create( + subset_name, + instance_data, + pre_create_data) diff --git a/openpype/hosts/unreal/plugins/create/create_staticmeshfbx.py b/openpype/hosts/unreal/plugins/create/create_staticmeshfbx.py index 45d517d27d..1acf7084d1 100644 --- a/openpype/hosts/unreal/plugins/create/create_staticmeshfbx.py +++ b/openpype/hosts/unreal/plugins/create/create_staticmeshfbx.py @@ -1,35 +1,13 @@ # -*- coding: utf-8 -*- -"""Create Static Meshes as FBX geometry.""" -import unreal # noqa -from openpype.hosts.unreal.api.pipeline import ( - instantiate, +from openpype.hosts.unreal.api.plugin import ( + UnrealAssetCreator, ) -from openpype.pipeline import LegacyCreator -class CreateStaticMeshFBX(LegacyCreator): - """Static FBX geometry.""" +class CreateStaticMeshFBX(UnrealAssetCreator): + """Create Static Meshes as FBX geometry.""" - name = "unrealStaticMeshMain" - label = "Unreal - Static Mesh" + identifier = "io.openpype.creators.unreal.staticmeshfbx" + label = "Static Mesh (FBX)" family = "unrealStaticMesh" icon = "cube" - asset_types = ["StaticMesh"] - - root = "/Game" - suffix = "_INS" - - def __init__(self, *args, **kwargs): - super(CreateStaticMeshFBX, self).__init__(*args, **kwargs) - - def process(self): - - name = self.data["subset"] - - selection = [] - if (self.options or {}).get("useSelection"): - sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() - selection = [a.get_path_name() for a in sel_objects] - - unreal.log("selection: {}".format(selection)) - instantiate(self.root, name, self.data, selection, self.suffix) diff --git a/openpype/hosts/unreal/plugins/create/create_uasset.py b/openpype/hosts/unreal/plugins/create/create_uasset.py index ee584ac00c..2d6fcc1d59 100644 --- a/openpype/hosts/unreal/plugins/create/create_uasset.py +++ b/openpype/hosts/unreal/plugins/create/create_uasset.py @@ -1,36 +1,25 @@ -"""Create UAsset.""" +# -*- coding: utf-8 -*- from pathlib import Path import unreal -from openpype.hosts.unreal.api import pipeline -from openpype.pipeline import LegacyCreator +from openpype.hosts.unreal.api.plugin import ( + UnrealAssetCreator, +) -class CreateUAsset(LegacyCreator): - """UAsset.""" +class CreateUAsset(UnrealAssetCreator): + """Create UAsset.""" - name = "UAsset" + identifier = "io.openpype.creators.unreal.uasset" label = "UAsset" family = "uasset" icon = "cube" - root = "/Game/OpenPype" - suffix = "_INS" + def create(self, subset_name, instance_data, pre_create_data): + if pre_create_data.get("use_selection"): + ar = unreal.AssetRegistryHelpers.get_asset_registry() - def __init__(self, *args, **kwargs): - super(CreateUAsset, self).__init__(*args, **kwargs) - - def process(self): - ar = unreal.AssetRegistryHelpers.get_asset_registry() - - subset = self.data["subset"] - path = f"{self.root}/PublishInstances/" - - unreal.EditorAssetLibrary.make_directory(path) - - selection = [] - if (self.options or {}).get("useSelection"): sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() selection = [a.get_path_name() for a in sel_objects] @@ -50,12 +39,7 @@ class CreateUAsset(LegacyCreator): if Path(sys_path).suffix != ".uasset": raise RuntimeError(f"{Path(sys_path).name} is not a UAsset.") - unreal.log("selection: {}".format(selection)) - container_name = f"{subset}{self.suffix}" - pipeline.create_publish_instance( - instance=container_name, path=path) - - data = self.data.copy() - data["members"] = selection - - pipeline.imprint(f"{path}/{container_name}", data) + super(CreateUAsset, self).create( + subset_name, + instance_data, + pre_create_data) From ca7ae2a306218ab729e51af94e70b842aabfd671 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Fri, 27 Jan 2023 16:53:39 +0000 Subject: [PATCH 0803/1271] Updated render creator --- .../unreal/plugins/create/create_render.py | 174 ++++++++++-------- 1 file changed, 94 insertions(+), 80 deletions(-) diff --git a/openpype/hosts/unreal/plugins/create/create_render.py b/openpype/hosts/unreal/plugins/create/create_render.py index a85d17421b..de3efdad74 100644 --- a/openpype/hosts/unreal/plugins/create/create_render.py +++ b/openpype/hosts/unreal/plugins/create/create_render.py @@ -1,117 +1,131 @@ +# -*- coding: utf-8 -*- import unreal -from openpype.hosts.unreal.api import pipeline -from openpype.pipeline import LegacyCreator +from openpype.hosts.unreal.api.pipeline import ( + get_subsequences +) +from openpype.hosts.unreal.api.plugin import ( + UnrealAssetCreator, +) -class CreateRender(LegacyCreator): +class CreateRender(UnrealAssetCreator): """Create instance for sequence for rendering""" - name = "unrealRender" - label = "Unreal - Render" + identifier = "io.openpype.creators.unreal.render" + label = "Render" family = "render" - icon = "cube" - asset_types = ["LevelSequence"] - - root = "/Game/OpenPype/PublishInstances" - suffix = "_INS" - - def process(self): - subset = self.data["subset"] + icon = "eye" + def create(self, subset_name, instance_data, pre_create_data): ar = unreal.AssetRegistryHelpers.get_asset_registry() - # The asset name is the the third element of the path which contains - # the map. - # The index of the split path is 3 because the first element is an - # empty string, as the path begins with "/Content". - a = unreal.EditorUtilityLibrary.get_selected_assets()[0] - asset_name = a.get_path_name().split("/")[3] - - # Get the master sequence and the master level. - # There should be only one sequence and one level in the directory. - filter = unreal.ARFilter( - class_names=["LevelSequence"], - package_paths=[f"/Game/OpenPype/{asset_name}"], - recursive_paths=False) - sequences = ar.get_assets(filter) - ms = sequences[0].get_editor_property('object_path') - filter = unreal.ARFilter( - class_names=["World"], - package_paths=[f"/Game/OpenPype/{asset_name}"], - recursive_paths=False) - levels = ar.get_assets(filter) - ml = levels[0].get_editor_property('object_path') - - selection = [] - if (self.options or {}).get("useSelection"): + if pre_create_data.get("use_selection"): sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() selection = [ a.get_path_name() for a in sel_objects - if a.get_class().get_name() in self.asset_types] + if a.get_class().get_name() == "LevelSequence"] else: - selection.append(self.data['sequence']) + selection = [instance_data['sequence']] - unreal.log(f"selection: {selection}") + seq_data = None - path = f"{self.root}" - unreal.EditorAssetLibrary.make_directory(path) + for sel in selection: + selected_asset = ar.get_asset_by_object_path(sel).get_asset() + selected_asset_path = selected_asset.get_path_name() - ar = unreal.AssetRegistryHelpers.get_asset_registry() + # Check if the selected asset is a level sequence asset. + if selected_asset.get_class().get_name() != "LevelSequence": + unreal.log_warning( + f"Skipping {selected_asset.get_name()}. It isn't a Level " + "Sequence.") - for a in selection: - ms_obj = ar.get_asset_by_object_path(ms).get_asset() + # The asset name is the the third element of the path which + # contains the map. + # To take the asset name, we remove from the path the prefix + # "/Game/OpenPype/" and then we split the path by "/". + sel_path = selected_asset_path + asset_name = sel_path.replace("/Game/OpenPype/", "").split("/")[0] - seq_data = None + # Get the master sequence and the master level. + # There should be only one sequence and one level in the directory. + ar_filter = unreal.ARFilter( + class_names=["LevelSequence"], + package_paths=[f"/Game/OpenPype/{asset_name}"], + recursive_paths=False) + sequences = ar.get_assets(ar_filter) + master_seq = sequences[0].get_asset().get_path_name() + master_seq_obj = sequences[0].get_asset() + ar_filter = unreal.ARFilter( + class_names=["World"], + package_paths=[f"/Game/OpenPype/{asset_name}"], + recursive_paths=False) + levels = ar.get_assets(ar_filter) + master_lvl = levels[0].get_asset().get_path_name() - if a == ms: - seq_data = { - "sequence": ms_obj, - "output": f"{ms_obj.get_name()}", - "frame_range": ( - ms_obj.get_playback_start(), ms_obj.get_playback_end()) - } + # If the selected asset is the master sequence, we get its data + # and then we create the instance for the master sequence. + # Otherwise, we cycle from the master sequence to find the selected + # sequence and we get its data. This data will be used to create + # the instance for the selected sequence. In particular, + # we get the frame range of the selected sequence and its final + # output path. + master_seq_data = { + "sequence": master_seq_obj, + "output": f"{master_seq_obj.get_name()}", + "frame_range": ( + master_seq_obj.get_playback_start(), + master_seq_obj.get_playback_end())} + + if selected_asset_path == master_seq: + seq_data = master_seq_data else: - seq_data_list = [{ - "sequence": ms_obj, - "output": f"{ms_obj.get_name()}", - "frame_range": ( - ms_obj.get_playback_start(), ms_obj.get_playback_end()) - }] + seq_data_list = [master_seq_data] - for s in seq_data_list: - subscenes = pipeline.get_subsequences(s.get('sequence')) + for seq in seq_data_list: + subscenes = get_subsequences(seq.get('sequence')) - for ss in subscenes: + for sub_seq in subscenes: + sub_seq_obj = sub_seq.get_sequence() curr_data = { - "sequence": ss.get_sequence(), - "output": (f"{s.get('output')}/" - f"{ss.get_sequence().get_name()}"), + "sequence": sub_seq_obj, + "output": (f"{seq.get('output')}/" + f"{sub_seq_obj.get_name()}"), "frame_range": ( - ss.get_start_frame(), ss.get_end_frame() - 1) - } + sub_seq.get_start_frame(), + sub_seq.get_end_frame() - 1)} - if ss.get_sequence().get_path_name() == a: + # If the selected asset is the current sub-sequence, + # we get its data and we break the loop. + # Otherwise, we add the current sub-sequence data to + # the list of sequences to check. + if sub_seq_obj.get_path_name() == selected_asset_path: seq_data = curr_data break + seq_data_list.append(curr_data) + # If we found the selected asset, we break the loop. if seq_data is not None: break + # If we didn't find the selected asset, we don't create the + # instance. if not seq_data: + unreal.log_warning( + f"Skipping {selected_asset.get_name()}. It isn't a " + "sub-sequence of the master sequence.") continue - d = self.data.copy() - d["members"] = [a] - d["sequence"] = a - d["master_sequence"] = ms - d["master_level"] = ml - d["output"] = seq_data.get('output') - d["frameStart"] = seq_data.get('frame_range')[0] - d["frameEnd"] = seq_data.get('frame_range')[1] + instance_data["members"] = [selected_asset_path] + instance_data["sequence"] = selected_asset_path + instance_data["master_sequence"] = master_seq + instance_data["master_level"] = master_lvl + instance_data["output"] = seq_data.get('output') + instance_data["frameStart"] = seq_data.get('frame_range')[0] + instance_data["frameEnd"] = seq_data.get('frame_range')[1] - container_name = f"{subset}{self.suffix}" - pipeline.create_publish_instance( - instance=container_name, path=path) - pipeline.imprint(f"{path}/{container_name}", d) + super(CreateRender, self).create( + subset_name, + instance_data, + pre_create_data) From a4c751b49ad43dfd4226fd3a99348877b8e5c084 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Mon, 30 Jan 2023 11:17:21 +0000 Subject: [PATCH 0804/1271] Hound fixes --- openpype/hosts/unreal/api/plugin.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index 6a561420fa..71ce0c18a7 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -104,7 +104,7 @@ class UnrealBaseCreator(Creator): # cache instances if missing self.cache_subsets(self.collection_shared_data) for instance in self.collection_shared_data[ - "unreal_cached_subsets"].get(self.identifier, []): + "unreal_cached_subsets"].get(self.identifier, []): created_instance = CreatedInstance.from_existing(instance, self) self._add_instance_to_context(created_instance) @@ -162,7 +162,8 @@ class UnrealAssetCreator(UnrealBaseCreator): selection = [] if pre_create_data.get("use_selection"): - sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() + utility_lib = unreal.EditorUtilityLibrary + sel_objects = utility_lib.get_selected_assets() selection = [a.get_path_name() for a in sel_objects] instance_data["members"] = selection @@ -211,7 +212,8 @@ class UnrealActorCreator(UnrealBaseCreator): selection = [] if pre_create_data.get("use_selection"): - sel_objects = unreal.EditorUtilityLibrary.get_selected_actors() + utility_lib = unreal.EditorUtilityLibrary + sel_objects = utility_lib.get_selected_assets() selection = [a.get_path_name() for a in sel_objects] instance_data["members"] = selection From 833860468c90395601679941fdb0d69b05a7a472 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 31 Jan 2023 16:05:01 +0000 Subject: [PATCH 0805/1271] Collect instances is no longer needed with the new publisher --- .../plugins/publish/collect_instances.py | 67 ------------------- 1 file changed, 67 deletions(-) delete mode 100644 openpype/hosts/unreal/plugins/publish/collect_instances.py diff --git a/openpype/hosts/unreal/plugins/publish/collect_instances.py b/openpype/hosts/unreal/plugins/publish/collect_instances.py deleted file mode 100644 index 27b711cad6..0000000000 --- a/openpype/hosts/unreal/plugins/publish/collect_instances.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- -"""Collect publishable instances in Unreal.""" -import ast -import unreal # noqa -import pyblish.api -from openpype.hosts.unreal.api.pipeline import UNREAL_VERSION -from openpype.pipeline.publish import KnownPublishError - - -class CollectInstances(pyblish.api.ContextPlugin): - """Gather instances by OpenPypePublishInstance class - - This collector finds all paths containing `OpenPypePublishInstance` class - asset - - Identifier: - id (str): "pyblish.avalon.instance" - - """ - - label = "Collect Instances" - order = pyblish.api.CollectorOrder - 0.1 - hosts = ["unreal"] - - def process(self, context): - - ar = unreal.AssetRegistryHelpers.get_asset_registry() - class_name = [ - "/Script/OpenPype", - "OpenPypePublishInstance" - ] if ( - UNREAL_VERSION.major == 5 - and UNREAL_VERSION.minor > 0 - ) else "OpenPypePublishInstance" # noqa - instance_containers = ar.get_assets_by_class(class_name, True) - - for container_data in instance_containers: - asset = container_data.get_asset() - data = unreal.EditorAssetLibrary.get_metadata_tag_values(asset) - data["objectName"] = container_data.asset_name - # convert to strings - data = {str(key): str(value) for (key, value) in data.items()} - if not data.get("family"): - raise KnownPublishError("instance has no family") - - # content of container - members = ast.literal_eval(data.get("members")) - self.log.debug(members) - self.log.debug(asset.get_path_name()) - # remove instance container - self.log.info("Creating instance for {}".format(asset.get_name())) - - instance = context.create_instance(asset.get_name()) - instance[:] = members - - # Store the exact members of the object set - instance.data["setMembers"] = members - instance.data["families"] = [data.get("family")] - instance.data["level"] = data.get("level") - instance.data["parent"] = data.get("parent") - - label = "{0} ({1})".format(asset.get_name()[:-4], - data["asset"]) - - instance.data["label"] = label - - instance.data.update(data) From ba6d77b88ba5f808c9c6e2a33dfacdb5388ddf91 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 2 Feb 2023 11:22:36 +0000 Subject: [PATCH 0806/1271] Use External Data in the Unreal Publish Instance to store members Not possible with all the families. Some families require to store actors in a scenes, and we cannot store them in the External Data. --- openpype/hosts/unreal/api/plugin.py | 24 ++++++--- .../unreal/plugins/create/create_look.py | 6 ++- .../publish/collect_instance_members.py | 49 +++++++++++++++++++ .../unreal/plugins/publish/extract_look.py | 4 +- .../unreal/plugins/publish/extract_uasset.py | 8 ++- 5 files changed, 78 insertions(+), 13 deletions(-) create mode 100644 openpype/hosts/unreal/plugins/publish/collect_instance_members.py diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index 71ce0c18a7..da571af9be 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -80,7 +80,7 @@ class UnrealBaseCreator(Creator): def create(self, subset_name, instance_data, pre_create_data): try: instance_name = f"{subset_name}{self.suffix}" - create_publish_instance(instance_name, self.root) + pub_instance = create_publish_instance(instance_name, self.root) instance_data["subset"] = subset_name instance_data["instance_path"] = f"{self.root}/{instance_name}" @@ -92,6 +92,15 @@ class UnrealBaseCreator(Creator): self) self._add_instance_to_context(instance) + pub_instance.set_editor_property('add_external_assets', True) + assets = pub_instance.get_editor_property('asset_data_external') + + ar = unreal.AssetRegistryHelpers.get_asset_registry() + + for member in pre_create_data.get("members", []): + obj = ar.get_asset_by_object_path(member).get_asset() + assets.add(obj) + imprint(f"{self.root}/{instance_name}", instance_data) except Exception as er: @@ -158,15 +167,14 @@ class UnrealAssetCreator(UnrealBaseCreator): try: # Check if instance data has members, filled by the plugin. # If not, use selection. - if not instance_data.get("members"): - selection = [] + if not pre_create_data.get("members"): + pre_create_data["members"] = [] if pre_create_data.get("use_selection"): - utility_lib = unreal.EditorUtilityLibrary - sel_objects = utility_lib.get_selected_assets() - selection = [a.get_path_name() for a in sel_objects] - - instance_data["members"] = selection + utilib = unreal.EditorUtilityLibrary + sel_objects = utilib.get_selected_assets() + pre_create_data["members"] = [ + a.get_path_name() for a in sel_objects] super(UnrealAssetCreator, self).create( subset_name, diff --git a/openpype/hosts/unreal/plugins/create/create_look.py b/openpype/hosts/unreal/plugins/create/create_look.py index 08d61ab9f8..047764ef2a 100644 --- a/openpype/hosts/unreal/plugins/create/create_look.py +++ b/openpype/hosts/unreal/plugins/create/create_look.py @@ -34,6 +34,8 @@ class CreateLook(UnrealAssetCreator): folder_name = create_folder(look_directory, subset_name) path = f"{look_directory}/{folder_name}" + instance_data["look"] = path + # Create a new cube static mesh ar = unreal.AssetRegistryHelpers.get_asset_registry() cube = ar.get_asset_by_object_path("/Engine/BasicShapes/Cube.Cube") @@ -42,7 +44,7 @@ class CreateLook(UnrealAssetCreator): original_mesh = ar.get_asset_by_object_path(selected_asset).get_asset() materials = original_mesh.get_editor_property('static_materials') - instance_data["members"] = [] + pre_create_data["members"] = [] # Add the materials to the cube for material in materials: @@ -58,7 +60,7 @@ class CreateLook(UnrealAssetCreator): unreal_object.add_material( material.get_editor_property('material_interface')) - instance_data["members"].append(object_path) + pre_create_data["members"].append(object_path) unreal.EditorAssetLibrary.save_asset(object_path) diff --git a/openpype/hosts/unreal/plugins/publish/collect_instance_members.py b/openpype/hosts/unreal/plugins/publish/collect_instance_members.py new file mode 100644 index 0000000000..74969f5033 --- /dev/null +++ b/openpype/hosts/unreal/plugins/publish/collect_instance_members.py @@ -0,0 +1,49 @@ +import unreal + +import pyblish.api + + +class CollectInstanceMembers(pyblish.api.InstancePlugin): + """ + Collect members of instance. + + This collector will collect the assets for the families that support to + have them included as External Data, and will add them to the instance + as members. + """ + + order = pyblish.api.CollectorOrder + 0.1 + hosts = ["unreal"] + families = ["look", "unrealStaticMesh", "uasset"] + label = "Collect Instance Members" + + def process(self, instance): + """Collect members of instance.""" + self.log.info("Collecting instance members") + + ar = unreal.AssetRegistryHelpers.get_asset_registry() + + inst_path = instance.data.get('instance_path') + inst_name = instance.data.get('objectName') + + pub_instance = ar.get_asset_by_object_path( + f"{inst_path}.{inst_name}").get_asset() + + if not pub_instance: + self.log.error(f"{inst_path}.{inst_name}") + raise RuntimeError(f"Instance {instance} not found.") + + if not pub_instance.get_editor_property("add_external_assets"): + # No external assets in the instance + return + + assets = pub_instance.get_editor_property('asset_data_external') + + members = [] + + for asset in assets: + members.append(asset.get_path_name()) + + self.log.debug(f"Members: {members}") + + instance.data["members"] = members diff --git a/openpype/hosts/unreal/plugins/publish/extract_look.py b/openpype/hosts/unreal/plugins/publish/extract_look.py index f999ad8651..4b32b4eb95 100644 --- a/openpype/hosts/unreal/plugins/publish/extract_look.py +++ b/openpype/hosts/unreal/plugins/publish/extract_look.py @@ -29,13 +29,13 @@ class ExtractLook(publish.Extractor): for member in instance: asset = ar.get_asset_by_object_path(member) - object = asset.get_asset() + obj = asset.get_asset() name = asset.get_editor_property('asset_name') json_element = {'material': str(name)} - material_obj = object.get_editor_property('static_materials')[0] + material_obj = obj.get_editor_property('static_materials')[0] material = material_obj.material_interface base_color = mat_lib.get_material_property_input_node( diff --git a/openpype/hosts/unreal/plugins/publish/extract_uasset.py b/openpype/hosts/unreal/plugins/publish/extract_uasset.py index 89d779d368..f719df2a82 100644 --- a/openpype/hosts/unreal/plugins/publish/extract_uasset.py +++ b/openpype/hosts/unreal/plugins/publish/extract_uasset.py @@ -22,7 +22,13 @@ class ExtractUAsset(publish.Extractor): staging_dir = self.staging_dir(instance) filename = "{}.uasset".format(instance.name) - obj = instance[0] + members = instance.data.get("members", []) + + if not members: + raise RuntimeError("No members found in instance.") + + # UAsset publishing supports only one member + obj = members[0] asset = ar.get_asset_by_object_path(obj).get_asset() sys_path = unreal.SystemLibrary.get_system_path(asset) From 6f15f39e4ffcbc5f4d8f09627901b5cc78daa13f Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 2 Feb 2023 12:18:03 +0000 Subject: [PATCH 0807/1271] Improved attributes for the creators --- openpype/hosts/unreal/api/plugin.py | 20 +++++++++++++------ .../unreal/plugins/create/create_render.py | 6 ++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index da571af9be..7121aea20b 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -14,7 +14,10 @@ from .pipeline import ( lsinst, UNREAL_VERSION ) -from openpype.lib import BoolDef +from openpype.lib import ( + BoolDef, + UILabelDef +) from openpype.pipeline import ( Creator, LoaderPlugin, @@ -143,11 +146,6 @@ class UnrealBaseCreator(Creator): self._remove_instance_from_context(instance) - def get_pre_create_attr_defs(self): - return [ - BoolDef("use_selection", label="Use selection", default=True) - ] - @six.add_metaclass(ABCMeta) class UnrealAssetCreator(UnrealBaseCreator): @@ -187,6 +185,11 @@ class UnrealAssetCreator(UnrealBaseCreator): OpenPypeCreatorError(f"Creator error: {er}"), sys.exc_info()[2]) + def get_pre_create_attr_defs(self): + return [ + BoolDef("use_selection", label="Use selection", default=True) + ] + @six.add_metaclass(ABCMeta) class UnrealActorCreator(UnrealBaseCreator): @@ -239,6 +242,11 @@ class UnrealActorCreator(UnrealBaseCreator): OpenPypeCreatorError(f"Creator error: {er}"), sys.exc_info()[2]) + def get_pre_create_attr_defs(self): + return [ + UILabelDef("Select actors to create instance from them.") + ] + class Loader(LoaderPlugin, ABC): """This serves as skeleton for future OpenPype specific functionality""" diff --git a/openpype/hosts/unreal/plugins/create/create_render.py b/openpype/hosts/unreal/plugins/create/create_render.py index de3efdad74..8100a5016c 100644 --- a/openpype/hosts/unreal/plugins/create/create_render.py +++ b/openpype/hosts/unreal/plugins/create/create_render.py @@ -7,6 +7,7 @@ from openpype.hosts.unreal.api.pipeline import ( from openpype.hosts.unreal.api.plugin import ( UnrealAssetCreator, ) +from openpype.lib import UILabelDef class CreateRender(UnrealAssetCreator): @@ -129,3 +130,8 @@ class CreateRender(UnrealAssetCreator): subset_name, instance_data, pre_create_data) + + def get_pre_create_attr_defs(self): + return [ + UILabelDef("Select the sequence to render.") + ] From f3834db1ae65566e91e4ba5532befbab637a333e Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 2 Feb 2023 16:15:03 +0000 Subject: [PATCH 0808/1271] Fix render creator problem with selection --- .../hosts/unreal/plugins/create/create_render.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/unreal/plugins/create/create_render.py b/openpype/hosts/unreal/plugins/create/create_render.py index 8100a5016c..a1e3e43a78 100644 --- a/openpype/hosts/unreal/plugins/create/create_render.py +++ b/openpype/hosts/unreal/plugins/create/create_render.py @@ -6,6 +6,7 @@ from openpype.hosts.unreal.api.pipeline import ( ) from openpype.hosts.unreal.api.plugin import ( UnrealAssetCreator, + OpenPypeCreatorError ) from openpype.lib import UILabelDef @@ -21,13 +22,13 @@ class CreateRender(UnrealAssetCreator): def create(self, subset_name, instance_data, pre_create_data): ar = unreal.AssetRegistryHelpers.get_asset_registry() - if pre_create_data.get("use_selection"): - sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() - selection = [ - a.get_path_name() for a in sel_objects - if a.get_class().get_name() == "LevelSequence"] - else: - selection = [instance_data['sequence']] + sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() + selection = [ + a.get_path_name() for a in sel_objects + if a.get_class().get_name() == "LevelSequence"] + + if len(selection) == 0: + raise RuntimeError("Please select at least one Level Sequence.") seq_data = None From cae2186d402e6546e14b66c9a99d4bd772311cd3 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 2 Feb 2023 16:17:23 +0000 Subject: [PATCH 0809/1271] Hound fixes --- openpype/hosts/unreal/plugins/create/create_render.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/unreal/plugins/create/create_render.py b/openpype/hosts/unreal/plugins/create/create_render.py index a1e3e43a78..c957e50e29 100644 --- a/openpype/hosts/unreal/plugins/create/create_render.py +++ b/openpype/hosts/unreal/plugins/create/create_render.py @@ -5,8 +5,7 @@ from openpype.hosts.unreal.api.pipeline import ( get_subsequences ) from openpype.hosts.unreal.api.plugin import ( - UnrealAssetCreator, - OpenPypeCreatorError + UnrealAssetCreator ) from openpype.lib import UILabelDef From b54d83247800c601f5e120cd02ed69e37bd347ef Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Wed, 15 Feb 2023 11:41:57 +0000 Subject: [PATCH 0810/1271] Implemented suggestions from review --- openpype/hosts/unreal/api/pipeline.py | 3 - openpype/hosts/unreal/api/plugin.py | 69 +++++++------------ .../unreal/plugins/create/create_camera.py | 2 +- .../unreal/plugins/create/create_look.py | 14 ++-- 4 files changed, 37 insertions(+), 51 deletions(-) diff --git a/openpype/hosts/unreal/api/pipeline.py b/openpype/hosts/unreal/api/pipeline.py index 7a21effcbc..0fe8c02ec5 100644 --- a/openpype/hosts/unreal/api/pipeline.py +++ b/openpype/hosts/unreal/api/pipeline.py @@ -69,9 +69,6 @@ class UnrealHost(HostBase, ILoadHost, IPublishHost): op_ctx = content_path + CONTEXT_CONTAINER with open(op_ctx, "w+") as f: json.dump(data, f) - with open(op_ctx, "r") as fp: - test = eval(json.load(fp)) - unreal.log_warning(test) def get_context_data(self): content_path = unreal.Paths.project_content_dir() diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index 7121aea20b..fc724105b6 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- +import collections import sys import six -from abc import ( - ABC, - ABCMeta, -) +from abc import ABC import unreal @@ -26,11 +24,6 @@ from openpype.pipeline import ( ) -class OpenPypeCreatorError(CreatorError): - pass - - -@six.add_metaclass(ABCMeta) class UnrealBaseCreator(Creator): """Base class for Unreal creator plugins.""" root = "/Game/OpenPype/PublishInstances" @@ -56,28 +49,20 @@ class UnrealBaseCreator(Creator): """ if shared_data.get("unreal_cached_subsets") is None: - shared_data["unreal_cached_subsets"] = {} - if shared_data.get("unreal_cached_legacy_subsets") is None: - shared_data["unreal_cached_legacy_subsets"] = {} - cached_instances = lsinst() - for i in cached_instances: - if not i.get("creator_identifier"): - # we have legacy instance - family = i.get("family") - if (family not in - shared_data["unreal_cached_legacy_subsets"]): - shared_data[ - "unreal_cached_legacy_subsets"][family] = [i] - else: - shared_data[ - "unreal_cached_legacy_subsets"][family].append(i) - continue - - creator_id = i.get("creator_identifier") - if creator_id not in shared_data["unreal_cached_subsets"]: - shared_data["unreal_cached_subsets"][creator_id] = [i] + unreal_cached_subsets = collections.defaultdict(list) + unreal_cached_legacy_subsets = collections.defaultdict(list) + for instance in lsinst(): + creator_id = instance.get("creator_identifier") + if creator_id: + unreal_cached_subsets[creator_id].append(instance) else: - shared_data["unreal_cached_subsets"][creator_id].append(i) + family = instance.get("family") + unreal_cached_legacy_subsets[family].append(instance) + + shared_data["unreal_cached_subsets"] = unreal_cached_subsets + shared_data["unreal_cached_legacy_subsets"] = ( + unreal_cached_legacy_subsets + ) return shared_data def create(self, subset_name, instance_data, pre_create_data): @@ -108,8 +93,8 @@ class UnrealBaseCreator(Creator): except Exception as er: six.reraise( - OpenPypeCreatorError, - OpenPypeCreatorError(f"Creator error: {er}"), + CreatorError, + CreatorError(f"Creator error: {er}"), sys.exc_info()[2]) def collect_instances(self): @@ -121,17 +106,17 @@ class UnrealBaseCreator(Creator): self._add_instance_to_context(created_instance) def update_instances(self, update_list): - unreal.log_warning(f"Update instances: {update_list}") - for created_inst, _changes in update_list: + for created_inst, changes in update_list: instance_node = created_inst.get("instance_path", "") if not instance_node: unreal.log_warning( f"Instance node not found for {created_inst}") + continue new_values = { - key: new_value - for key, (_old_value, new_value) in _changes.items() + key: changes[key].new_value + for key in changes.changed_keys } imprint( instance_node, @@ -147,7 +132,6 @@ class UnrealBaseCreator(Creator): self._remove_instance_from_context(instance) -@six.add_metaclass(ABCMeta) class UnrealAssetCreator(UnrealBaseCreator): """Base class for Unreal creator plugins based on assets.""" @@ -181,8 +165,8 @@ class UnrealAssetCreator(UnrealBaseCreator): except Exception as er: six.reraise( - OpenPypeCreatorError, - OpenPypeCreatorError(f"Creator error: {er}"), + CreatorError, + CreatorError(f"Creator error: {er}"), sys.exc_info()[2]) def get_pre_create_attr_defs(self): @@ -191,7 +175,6 @@ class UnrealAssetCreator(UnrealBaseCreator): ] -@six.add_metaclass(ABCMeta) class UnrealActorCreator(UnrealBaseCreator): """Base class for Unreal creator plugins based on actors.""" @@ -214,7 +197,7 @@ class UnrealActorCreator(UnrealBaseCreator): # Check if the level is saved if world.get_path_name().startswith("/Temp/"): - raise OpenPypeCreatorError( + raise CreatorError( "Level must be saved before creating instances.") # Check if instance data has members, filled by the plugin. @@ -238,8 +221,8 @@ class UnrealActorCreator(UnrealBaseCreator): except Exception as er: six.reraise( - OpenPypeCreatorError, - OpenPypeCreatorError(f"Creator error: {er}"), + CreatorError, + CreatorError(f"Creator error: {er}"), sys.exc_info()[2]) def get_pre_create_attr_defs(self): diff --git a/openpype/hosts/unreal/plugins/create/create_camera.py b/openpype/hosts/unreal/plugins/create/create_camera.py index 239dc87db5..00815e1ed4 100644 --- a/openpype/hosts/unreal/plugins/create/create_camera.py +++ b/openpype/hosts/unreal/plugins/create/create_camera.py @@ -10,4 +10,4 @@ class CreateCamera(UnrealActorCreator): identifier = "io.openpype.creators.unreal.camera" label = "Camera" family = "camera" - icon = "camera" + icon = "fa.camera" diff --git a/openpype/hosts/unreal/plugins/create/create_look.py b/openpype/hosts/unreal/plugins/create/create_look.py index 047764ef2a..cecb88bca3 100644 --- a/openpype/hosts/unreal/plugins/create/create_look.py +++ b/openpype/hosts/unreal/plugins/create/create_look.py @@ -7,6 +7,7 @@ from openpype.hosts.unreal.api.pipeline import ( from openpype.hosts.unreal.api.plugin import ( UnrealAssetCreator ) +from openpype.lib import UILabelDef class CreateLook(UnrealAssetCreator): @@ -18,10 +19,10 @@ class CreateLook(UnrealAssetCreator): icon = "paint-brush" def create(self, subset_name, instance_data, pre_create_data): - selection = [] - if pre_create_data.get("use_selection"): - sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() - selection = [a.get_path_name() for a in sel_objects] + # We need to set this to True for the parent class to work + pre_create_data["use_selection"] = True + sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() + selection = [a.get_path_name() for a in sel_objects] if len(selection) != 1: raise RuntimeError("Please select only one asset.") @@ -68,3 +69,8 @@ class CreateLook(UnrealAssetCreator): subset_name, instance_data, pre_create_data) + + def get_pre_create_attr_defs(self): + return [ + UILabelDef("Select the asset from which to create the look.") + ] From 4f882b9b1989edaa53464ce3375ca85124d65489 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Wed, 15 Feb 2023 11:45:30 +0000 Subject: [PATCH 0811/1271] Fixed problem with the instance metadata --- openpype/hosts/unreal/api/pipeline.py | 2 +- openpype/hosts/unreal/api/plugin.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/unreal/api/pipeline.py b/openpype/hosts/unreal/api/pipeline.py index 0fe8c02ec5..0810ec7c07 100644 --- a/openpype/hosts/unreal/api/pipeline.py +++ b/openpype/hosts/unreal/api/pipeline.py @@ -76,7 +76,7 @@ class UnrealHost(HostBase, ILoadHost, IPublishHost): if not os.path.isfile(op_ctx): return {} with open(op_ctx, "r") as fp: - data = eval(json.load(fp)) + data = json.load(fp) return data diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index fc724105b6..a852ed9bb1 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import ast import collections import sys import six @@ -89,7 +90,9 @@ class UnrealBaseCreator(Creator): obj = ar.get_asset_by_object_path(member).get_asset() assets.add(obj) - imprint(f"{self.root}/{instance_name}", instance_data) + imprint(f"{self.root}/{instance_name}", instance.data_to_store()) + + return instance except Exception as er: six.reraise( @@ -102,6 +105,11 @@ class UnrealBaseCreator(Creator): self.cache_subsets(self.collection_shared_data) for instance in self.collection_shared_data[ "unreal_cached_subsets"].get(self.identifier, []): + # Unreal saves metadata as string, so we need to convert it back + instance['creator_attributes'] = ast.literal_eval( + instance.get('creator_attributes', '{}')) + instance['publish_attributes'] = ast.literal_eval( + instance.get('publish_attributes', '{}')) created_instance = CreatedInstance.from_existing(instance, self) self._add_instance_to_context(created_instance) From 606b3e3449e5f9ad0493ec3e93358bdd3a65f4c0 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Mon, 20 Feb 2023 12:31:13 +0000 Subject: [PATCH 0812/1271] More changes from suggestions --- openpype/hosts/unreal/api/pipeline.py | 2 +- openpype/hosts/unreal/api/plugin.py | 12 +++++++++--- openpype/hosts/unreal/plugins/create/create_look.py | 3 ++- .../hosts/unreal/plugins/create/create_render.py | 7 ++++--- .../hosts/unreal/plugins/create/create_uasset.py | 7 ++++--- .../plugins/publish/collect_instance_members.py | 5 +---- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/unreal/api/pipeline.py b/openpype/hosts/unreal/api/pipeline.py index 0810ec7c07..4a22189c14 100644 --- a/openpype/hosts/unreal/api/pipeline.py +++ b/openpype/hosts/unreal/api/pipeline.py @@ -152,7 +152,7 @@ def ls(): yield data -def lsinst(): +def ls_inst(): ar = unreal.AssetRegistryHelpers.get_asset_registry() # UE 5.1 changed how class name is specified class_name = [ diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index a852ed9bb1..2498a249e8 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -3,14 +3,17 @@ import ast import collections import sys import six -from abc import ABC +from abc import ( + ABC, + ABCMeta, +) import unreal from .pipeline import ( create_publish_instance, imprint, - lsinst, + ls_inst, UNREAL_VERSION ) from openpype.lib import ( @@ -25,6 +28,7 @@ from openpype.pipeline import ( ) +@six.add_metaclass(ABCMeta) class UnrealBaseCreator(Creator): """Base class for Unreal creator plugins.""" root = "/Game/OpenPype/PublishInstances" @@ -52,7 +56,7 @@ class UnrealBaseCreator(Creator): if shared_data.get("unreal_cached_subsets") is None: unreal_cached_subsets = collections.defaultdict(list) unreal_cached_legacy_subsets = collections.defaultdict(list) - for instance in lsinst(): + for instance in ls_inst(): creator_id = instance.get("creator_identifier") if creator_id: unreal_cached_subsets[creator_id].append(instance) @@ -140,6 +144,7 @@ class UnrealBaseCreator(Creator): self._remove_instance_from_context(instance) +@six.add_metaclass(ABCMeta) class UnrealAssetCreator(UnrealBaseCreator): """Base class for Unreal creator plugins based on assets.""" @@ -183,6 +188,7 @@ class UnrealAssetCreator(UnrealBaseCreator): ] +@six.add_metaclass(ABCMeta) class UnrealActorCreator(UnrealBaseCreator): """Base class for Unreal creator plugins based on actors.""" diff --git a/openpype/hosts/unreal/plugins/create/create_look.py b/openpype/hosts/unreal/plugins/create/create_look.py index cecb88bca3..f6c73e47e6 100644 --- a/openpype/hosts/unreal/plugins/create/create_look.py +++ b/openpype/hosts/unreal/plugins/create/create_look.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import unreal +from openpype.pipeline import CreatorError from openpype.hosts.unreal.api.pipeline import ( create_folder ) @@ -25,7 +26,7 @@ class CreateLook(UnrealAssetCreator): selection = [a.get_path_name() for a in sel_objects] if len(selection) != 1: - raise RuntimeError("Please select only one asset.") + raise CreatorError("Please select only one asset.") selected_asset = selection[0] diff --git a/openpype/hosts/unreal/plugins/create/create_render.py b/openpype/hosts/unreal/plugins/create/create_render.py index c957e50e29..5834d2e7a7 100644 --- a/openpype/hosts/unreal/plugins/create/create_render.py +++ b/openpype/hosts/unreal/plugins/create/create_render.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import unreal +from openpype.pipeline import CreatorError from openpype.hosts.unreal.api.pipeline import ( get_subsequences ) @@ -26,8 +27,8 @@ class CreateRender(UnrealAssetCreator): a.get_path_name() for a in sel_objects if a.get_class().get_name() == "LevelSequence"] - if len(selection) == 0: - raise RuntimeError("Please select at least one Level Sequence.") + if not selection: + raise CreatorError("Please select at least one Level Sequence.") seq_data = None @@ -41,7 +42,7 @@ class CreateRender(UnrealAssetCreator): f"Skipping {selected_asset.get_name()}. It isn't a Level " "Sequence.") - # The asset name is the the third element of the path which + # The asset name is the third element of the path which # contains the map. # To take the asset name, we remove from the path the prefix # "/Game/OpenPype/" and then we split the path by "/". diff --git a/openpype/hosts/unreal/plugins/create/create_uasset.py b/openpype/hosts/unreal/plugins/create/create_uasset.py index 2d6fcc1d59..70f17d478b 100644 --- a/openpype/hosts/unreal/plugins/create/create_uasset.py +++ b/openpype/hosts/unreal/plugins/create/create_uasset.py @@ -3,6 +3,7 @@ from pathlib import Path import unreal +from openpype.pipeline import CreatorError from openpype.hosts.unreal.api.plugin import ( UnrealAssetCreator, ) @@ -24,7 +25,7 @@ class CreateUAsset(UnrealAssetCreator): selection = [a.get_path_name() for a in sel_objects] if len(selection) != 1: - raise RuntimeError("Please select only one object.") + raise CreatorError("Please select only one object.") obj = selection[0] @@ -32,12 +33,12 @@ class CreateUAsset(UnrealAssetCreator): sys_path = unreal.SystemLibrary.get_system_path(asset) if not sys_path: - raise RuntimeError( + raise CreatorError( f"{Path(obj).name} is not on the disk. Likely it needs to" "be saved first.") if Path(sys_path).suffix != ".uasset": - raise RuntimeError(f"{Path(sys_path).name} is not a UAsset.") + raise CreatorError(f"{Path(sys_path).name} is not a UAsset.") super(CreateUAsset, self).create( subset_name, diff --git a/openpype/hosts/unreal/plugins/publish/collect_instance_members.py b/openpype/hosts/unreal/plugins/publish/collect_instance_members.py index 74969f5033..bd467a98fb 100644 --- a/openpype/hosts/unreal/plugins/publish/collect_instance_members.py +++ b/openpype/hosts/unreal/plugins/publish/collect_instance_members.py @@ -39,10 +39,7 @@ class CollectInstanceMembers(pyblish.api.InstancePlugin): assets = pub_instance.get_editor_property('asset_data_external') - members = [] - - for asset in assets: - members.append(asset.get_path_name()) + members = [asset.get_path_name() for asset in assets] self.log.debug(f"Members: {members}") From 637c1c08068a57a98972f8a0c29838f5628e6645 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 21 Feb 2023 12:28:33 +0000 Subject: [PATCH 0813/1271] Addressing a concurrency issue when trying to access context --- openpype/hosts/unreal/api/pipeline.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/unreal/api/pipeline.py b/openpype/hosts/unreal/api/pipeline.py index 4a22189c14..8a5a459194 100644 --- a/openpype/hosts/unreal/api/pipeline.py +++ b/openpype/hosts/unreal/api/pipeline.py @@ -5,6 +5,7 @@ import logging from typing import List from contextlib import contextmanager import semver +import time import pyblish.api @@ -63,12 +64,21 @@ class UnrealHost(HostBase, ILoadHost, IPublishHost): show_tools_dialog() def update_context_data(self, data, changes): - unreal.log_warning("update_context_data") - unreal.log_warning(data) content_path = unreal.Paths.project_content_dir() op_ctx = content_path + CONTEXT_CONTAINER - with open(op_ctx, "w+") as f: - json.dump(data, f) + attempts = 3 + for i in range(attempts): + try: + with open(op_ctx, "w+") as f: + json.dump(data, f) + break + except IOError: + if i == attempts - 1: + raise Exception("Failed to write context data. Aborting.") + unreal.log_warning("Failed to write context data. Retrying...") + i += 1 + time.sleep(3) + continue def get_context_data(self): content_path = unreal.Paths.project_content_dir() From e958a692896027fe351e1b74266546eef5eb3fb3 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 21 Feb 2023 17:30:23 +0000 Subject: [PATCH 0814/1271] Fix UnrealActorCreator when getting actors from the scene --- openpype/hosts/unreal/api/plugin.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index 2498a249e8..d60050a696 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -217,12 +217,9 @@ class UnrealActorCreator(UnrealBaseCreator): # Check if instance data has members, filled by the plugin. # If not, use selection. if not instance_data.get("members"): - selection = [] - - if pre_create_data.get("use_selection"): - utility_lib = unreal.EditorUtilityLibrary - sel_objects = utility_lib.get_selected_assets() - selection = [a.get_path_name() for a in sel_objects] + actor_subsystem = unreal.EditorActorSubsystem() + sel_actors = actor_subsystem.get_selected_level_actors() + selection = [a.get_path_name() for a in sel_actors] instance_data["members"] = selection From 8fb21d9a1444326ed7b43aa4efe7f4e7f7aeaa2b Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 21 Feb 2023 17:32:17 +0000 Subject: [PATCH 0815/1271] Fix Camera Publishing --- .../unreal/plugins/create/create_camera.py | 29 ++++++++- .../publish/collect_instance_members.py | 2 +- .../unreal/plugins/publish/extract_camera.py | 64 ++++++++++++++----- 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/openpype/hosts/unreal/plugins/create/create_camera.py b/openpype/hosts/unreal/plugins/create/create_camera.py index 00815e1ed4..642924e2d6 100644 --- a/openpype/hosts/unreal/plugins/create/create_camera.py +++ b/openpype/hosts/unreal/plugins/create/create_camera.py @@ -1,13 +1,38 @@ # -*- coding: utf-8 -*- +import unreal + +from openpype.pipeline import CreatorError +from openpype.hosts.unreal.api.pipeline import UNREAL_VERSION from openpype.hosts.unreal.api.plugin import ( - UnrealActorCreator, + UnrealAssetCreator, ) -class CreateCamera(UnrealActorCreator): +class CreateCamera(UnrealAssetCreator): """Create Camera.""" identifier = "io.openpype.creators.unreal.camera" label = "Camera" family = "camera" icon = "fa.camera" + + def create(self, subset_name, instance_data, pre_create_data): + if pre_create_data.get("use_selection"): + sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() + selection = [a.get_path_name() for a in sel_objects] + + if len(selection) != 1: + raise CreatorError("Please select only one object.") + + # Add the current level path to the metadata + if UNREAL_VERSION.major == 5: + world = unreal.UnrealEditorSubsystem().get_editor_world() + else: + world = unreal.EditorLevelLibrary.get_editor_world() + + instance_data["level"] = world.get_path_name() + + super(CreateCamera, self).create( + subset_name, + instance_data, + pre_create_data) diff --git a/openpype/hosts/unreal/plugins/publish/collect_instance_members.py b/openpype/hosts/unreal/plugins/publish/collect_instance_members.py index bd467a98fb..46ca51ab7e 100644 --- a/openpype/hosts/unreal/plugins/publish/collect_instance_members.py +++ b/openpype/hosts/unreal/plugins/publish/collect_instance_members.py @@ -14,7 +14,7 @@ class CollectInstanceMembers(pyblish.api.InstancePlugin): order = pyblish.api.CollectorOrder + 0.1 hosts = ["unreal"] - families = ["look", "unrealStaticMesh", "uasset"] + families = ["camera", "look", "unrealStaticMesh", "uasset"] label = "Collect Instance Members" def process(self, instance): diff --git a/openpype/hosts/unreal/plugins/publish/extract_camera.py b/openpype/hosts/unreal/plugins/publish/extract_camera.py index 4e37cc6a86..70a835aca2 100644 --- a/openpype/hosts/unreal/plugins/publish/extract_camera.py +++ b/openpype/hosts/unreal/plugins/publish/extract_camera.py @@ -3,10 +3,9 @@ import os import unreal -from unreal import EditorAssetLibrary as eal -from unreal import EditorLevelLibrary as ell from openpype.pipeline import publish +from openpype.hosts.unreal.api.pipeline import UNREAL_VERSION class ExtractCamera(publish.Extractor): @@ -18,6 +17,8 @@ class ExtractCamera(publish.Extractor): optional = True def process(self, instance): + ar = unreal.AssetRegistryHelpers.get_asset_registry() + # Define extract output file path staging_dir = self.staging_dir(instance) fbx_filename = "{}.fbx".format(instance.name) @@ -26,23 +27,54 @@ class ExtractCamera(publish.Extractor): self.log.info("Performing extraction..") # Check if the loaded level is the same of the instance - current_level = ell.get_editor_world().get_path_name() + if UNREAL_VERSION.major == 5: + world = unreal.UnrealEditorSubsystem().get_editor_world() + else: + world = unreal.EditorLevelLibrary.get_editor_world() + current_level = world.get_path_name() assert current_level == instance.data.get("level"), \ "Wrong level loaded" - for member in instance[:]: - data = eal.find_asset_data(member) - if data.asset_class == "LevelSequence": - ar = unreal.AssetRegistryHelpers.get_asset_registry() - sequence = ar.get_asset_by_object_path(member).get_asset() - unreal.SequencerTools.export_fbx( - ell.get_editor_world(), - sequence, - sequence.get_bindings(), - unreal.FbxExportOption(), - os.path.join(staging_dir, fbx_filename) - ) - break + for member in instance.data.get('members'): + data = ar.get_asset_by_object_path(member) + if UNREAL_VERSION.major == 5: + is_level_sequence = ( + data.asset_class_path.asset_name == "LevelSequence") + else: + is_level_sequence = (data.asset_class == "LevelSequence") + + if is_level_sequence: + sequence = data.get_asset() + if UNREAL_VERSION.major == 5 and UNREAL_VERSION.minor >= 1: + params = unreal.SequencerExportFBXParams( + world=world, + root_sequence=sequence, + sequence=sequence, + bindings=sequence.get_bindings(), + master_tracks=sequence.get_master_tracks(), + fbx_file_name=os.path.join(staging_dir, fbx_filename) + ) + unreal.SequencerTools.export_level_sequence_fbx(params) + elif UNREAL_VERSION.major == 4 and UNREAL_VERSION.minor == 26: + unreal.SequencerTools.export_fbx( + world, + sequence, + sequence.get_bindings(), + unreal.FbxExportOption(), + os.path.join(staging_dir, fbx_filename) + ) + else: + # Unreal 5.0 or 4.27 + unreal.SequencerTools.export_level_sequence_fbx( + world, + sequence, + sequence.get_bindings(), + unreal.FbxExportOption(), + os.path.join(staging_dir, fbx_filename) + ) + + if not os.path.isfile(os.path.join(staging_dir, fbx_filename)): + raise RuntimeError("Failed to extract camera") if "representations" not in instance.data: instance.data["representations"] = [] From 1dd41d072d5b038e03f20b0d19262ea34933a841 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 21 Feb 2023 17:33:29 +0000 Subject: [PATCH 0816/1271] Hound fixes --- openpype/hosts/unreal/plugins/publish/extract_camera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/plugins/publish/extract_camera.py b/openpype/hosts/unreal/plugins/publish/extract_camera.py index 70a835aca2..16e365ca96 100644 --- a/openpype/hosts/unreal/plugins/publish/extract_camera.py +++ b/openpype/hosts/unreal/plugins/publish/extract_camera.py @@ -39,7 +39,7 @@ class ExtractCamera(publish.Extractor): data = ar.get_asset_by_object_path(member) if UNREAL_VERSION.major == 5: is_level_sequence = ( - data.asset_class_path.asset_name == "LevelSequence") + data.asset_class_path.asset_name == "LevelSequence") else: is_level_sequence = (data.asset_class == "LevelSequence") From 9f44aff3ed946e7158c8ba3be67ae084384fbd7c Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 21 Feb 2023 17:40:10 +0000 Subject: [PATCH 0817/1271] Changed menu to remove Create and link Publish to new publisher --- openpype/hosts/unreal/api/tools_ui.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/unreal/api/tools_ui.py b/openpype/hosts/unreal/api/tools_ui.py index 708e167a65..8531472142 100644 --- a/openpype/hosts/unreal/api/tools_ui.py +++ b/openpype/hosts/unreal/api/tools_ui.py @@ -17,9 +17,8 @@ class ToolsBtnsWidget(QtWidgets.QWidget): 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) + publish_btn = QtWidgets.QPushButton("Publisher...", self) manage_btn = QtWidgets.QPushButton("Manage...", self) render_btn = QtWidgets.QPushButton("Render...", self) experimental_tools_btn = QtWidgets.QPushButton( @@ -28,7 +27,6 @@ class ToolsBtnsWidget(QtWidgets.QWidget): 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) @@ -36,7 +34,6 @@ class ToolsBtnsWidget(QtWidgets.QWidget): 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) @@ -50,7 +47,7 @@ class ToolsBtnsWidget(QtWidgets.QWidget): self.tool_required.emit("loader") def _on_publish(self): - self.tool_required.emit("publish") + self.tool_required.emit("publisher") def _on_manage(self): self.tool_required.emit("sceneinventory") From a07e76ebb56d9dc325f11b40a5b336f4125725e0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 23 Feb 2023 11:05:26 +0100 Subject: [PATCH 0818/1271] OP-4643 - updates to documentation Co-authored-by: Roy Nieterau --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index f58d2c2bf2..d904080ad1 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -51,7 +51,7 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: -- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation loses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. - **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). From 2a912abbcebccdf16e21e4ac597b17f8b282f932 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 10:53:44 +0100 Subject: [PATCH 0819/1271] extract sequence can ignore layer's transparency --- .../plugins/publish/extract_sequence.py | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py b/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py index f2856c72a9..1a21715aa2 100644 --- a/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py +++ b/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py @@ -59,6 +59,10 @@ class ExtractSequence(pyblish.api.Extractor): ) ) + ignore_layers_transparency = instance.data.get( + "ignoreLayersTransparency", False + ) + family_lowered = instance.data["family"].lower() mark_in = instance.context.data["sceneMarkIn"] mark_out = instance.context.data["sceneMarkOut"] @@ -114,7 +118,11 @@ class ExtractSequence(pyblish.api.Extractor): else: # Render output result = self.render( - output_dir, mark_in, mark_out, filtered_layers + output_dir, + mark_in, + mark_out, + filtered_layers, + ignore_layers_transparency ) output_filepaths_by_frame_idx, thumbnail_fullpath = result @@ -274,7 +282,9 @@ class ExtractSequence(pyblish.api.Extractor): return output_filepaths_by_frame_idx, thumbnail_filepath - def render(self, output_dir, mark_in, mark_out, layers): + def render( + self, output_dir, mark_in, mark_out, layers, ignore_layer_opacity + ): """ Export images from TVPaint. Args: @@ -282,6 +292,7 @@ class ExtractSequence(pyblish.api.Extractor): mark_in (int): Starting frame index from which export will begin. mark_out (int): On which frame index export will end. layers (list): List of layers to be exported. + ignore_layer_opacity (bool): Layer's opacity will be ignored. Returns: tuple: With 2 items first is list of filenames second is path to @@ -323,7 +334,7 @@ class ExtractSequence(pyblish.api.Extractor): for layer_id, render_data in extraction_data_by_layer_id.items(): layer = layers_by_id[layer_id] filepaths_by_layer_id[layer_id] = self._render_layer( - render_data, layer, output_dir + render_data, layer, output_dir, ignore_layer_opacity ) # Prepare final filepaths where compositing should store result @@ -380,7 +391,9 @@ class ExtractSequence(pyblish.api.Extractor): red, green, blue = self.review_bg return (red, green, blue) - def _render_layer(self, render_data, layer, output_dir): + def _render_layer( + self, render_data, layer, output_dir, ignore_layer_opacity + ): frame_references = render_data["frame_references"] filenames_by_frame_index = render_data["filenames_by_frame_index"] @@ -389,6 +402,12 @@ class ExtractSequence(pyblish.api.Extractor): "tv_layerset {}".format(layer_id), "tv_SaveMode \"PNG\"" ] + # Set density to 100 and store previous opacity + if ignore_layer_opacity: + george_script_lines.extend([ + "tv_layerdensity 100", + "orig_opacity = result", + ]) filepaths_by_frame = {} frames_to_render = [] @@ -409,6 +428,10 @@ class ExtractSequence(pyblish.api.Extractor): # Store image to output george_script_lines.append("tv_saveimage \"{}\"".format(dst_path)) + # Set density back to origin opacity + if ignore_layer_opacity: + george_script_lines.append("tv_layerdensity orig_opacity") + self.log.debug("Rendering Exposure frames {} of layer {} ({})".format( ",".join(frames_to_render), layer_id, layer["name"] )) From fd086776102236e9137a8d6d80d56b5d8def2a3a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 10:58:49 +0100 Subject: [PATCH 0820/1271] collect render instances can set 'ignoreLayersTransparency' --- .../tvpaint/plugins/publish/collect_render_instances.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py b/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py index ba89deac5d..e89fbf7882 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py @@ -9,6 +9,8 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): hosts = ["tvpaint"] families = ["render", "review"] + ignore_render_pass_transparency = False + def process(self, instance): context = instance.context creator_identifier = instance.data["creator_identifier"] @@ -63,6 +65,9 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): for layer in layers_data if layer["name"] in layer_names ] + instance.data["ignoreLayersTransparency"] = ( + self.ignore_render_pass_transparency + ) render_layer_data = None render_layer_id = creator_attributes["render_layer_instance_id"] From 9c8a9412006a2c6a598cbb5d9253ca8dfb837126 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 10:59:03 +0100 Subject: [PATCH 0821/1271] added settings for Collect Render Instances --- .../defaults/project_settings/tvpaint.json | 3 +++ .../projects_schema/schema_project_tvpaint.json | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index 0b6d3d7e81..87d3601ae4 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -49,6 +49,9 @@ } }, "publish": { + "CollectRenderInstances": { + "ignore_render_pass_transparency": true + }, "ExtractSequence": { "review_bg": [ 255, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json index 57016a8311..708b688ba5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json @@ -241,6 +241,20 @@ "key": "publish", "label": "Publish plugins", "children": [ + { + "type": "dict", + "collapsible": true, + "key": "CollectRenderInstances", + "label": "Collect Render Instances", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "ignore_render_pass_transparency", + "label": "Ignore Render Pass opacity" + } + ] + }, { "type": "dict", "collapsible": true, From 0cfc46f4140531e5646df353c81d8ec6895b1b44 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 11:19:49 +0100 Subject: [PATCH 0822/1271] disable ignore transparency by default --- openpype/settings/defaults/project_settings/tvpaint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index 87d3601ae4..e06a67a254 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -50,7 +50,7 @@ }, "publish": { "CollectRenderInstances": { - "ignore_render_pass_transparency": true + "ignore_render_pass_transparency": false }, "ExtractSequence": { "review_bg": [ From ddf333fcdb502ecb8ce10a9b2d2152de198c686c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 10:39:10 +0100 Subject: [PATCH 0823/1271] fix access to not existing settings key --- openpype/hosts/tvpaint/plugins/create/create_render.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 40386efe91..7e85977b11 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -660,7 +660,6 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): ["create"] ["auto_detect_render"] ) - self.enabled = plugin_settings["enabled"] self.allow_group_rename = plugin_settings["allow_group_rename"] self.group_name_template = plugin_settings["group_name_template"] self.group_idx_offset = plugin_settings["group_idx_offset"] From bae2ded2f711d68291d6a6d14e60cb19f856d1c5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 23 Feb 2023 11:30:15 +0100 Subject: [PATCH 0824/1271] Fix - check existence only if not None review_path might be None --- openpype/modules/slack/plugins/publish/integrate_slack_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index 4e2557ccc7..86c97586d2 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -187,7 +187,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): repre_review_path = get_publish_repre_path( instance, repre, False ) - if os.path.exists(repre_review_path): + if repre_review_path and os.path.exists(repre_review_path): review_path = repre_review_path if "burnin" in tags: # burnin has precedence if exists break From d74e17a0e112c48714a87afeddf7750494fb6fca Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 21 Feb 2023 17:40:29 +0000 Subject: [PATCH 0825/1271] Implement get_multipart --- openpype/hosts/maya/api/lib_renderproducts.py | 82 ++++++++++++------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 60090e9f6d..e635414029 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -196,12 +196,18 @@ class ARenderProducts: """Constructor.""" self.layer = layer self.render_instance = render_instance - self.multipart = False + self.multipart = self.get_multipart() # Initialize self.layer_data = self._get_layer_data() self.layer_data.products = self.get_render_products() + def get_multipart(self): + raise NotImplementedError( + "The render product implementation does not have a " + "\"get_multipart\" method." + ) + def has_camera_token(self): # type: () -> bool """Check if camera token is in image prefix. @@ -344,7 +350,6 @@ class ARenderProducts: separator = file_prefix[matches[0].end(1):matches[1].start(1)] return separator - def _get_layer_data(self): # type: () -> LayerMetadata # ______________________________________________ @@ -531,16 +536,20 @@ class RenderProductsArnold(ARenderProducts): return prefix - def _get_aov_render_products(self, aov, cameras=None): - """Return all render products for the AOV""" - - products = [] - aov_name = self._get_attr(aov, "name") + def get_multipart(self): multipart = False multilayer = bool(self._get_attr("defaultArnoldDriver.multipart")) merge_AOVs = bool(self._get_attr("defaultArnoldDriver.mergeAOVs")) if multilayer or merge_AOVs: multipart = True + + return multipart + + def _get_aov_render_products(self, aov, cameras=None): + """Return all render products for the AOV""" + + products = [] + aov_name = self._get_attr(aov, "name") ai_drivers = cmds.listConnections("{}.outputs".format(aov), source=True, destination=False, @@ -594,7 +603,7 @@ class RenderProductsArnold(ARenderProducts): ext=ext, aov=aov_name, driver=ai_driver, - multipart=multipart, + multipart=self.multipart, camera=camera) products.append(product) @@ -731,6 +740,14 @@ class RenderProductsVray(ARenderProducts): renderer = "vray" + def get_multipart(self): + multipart = False + image_format = self._get_attr("vraySettings.imageFormatStr") + if image_format == "exr (multichannel)": + multipart = True + + return multipart + def get_renderer_prefix(self): # type: () -> str """Get image prefix for V-Ray. @@ -797,11 +814,6 @@ class RenderProductsVray(ARenderProducts): if default_ext in {"exr (multichannel)", "exr (deep)"}: default_ext = "exr" - # Define multipart. - multipart = False - if image_format_str == "exr (multichannel)": - multipart = True - products = [] # add beauty as default when not disabled @@ -813,7 +825,7 @@ class RenderProductsVray(ARenderProducts): productName="", ext=default_ext, camera=camera, - multipart=multipart + multipart=self.multipart ) ) @@ -826,10 +838,10 @@ class RenderProductsVray(ARenderProducts): productName="Alpha", ext=default_ext, camera=camera, - multipart=multipart + multipart=self.multipart ) ) - if multipart: + if self.multipart: # AOVs are merged in m-channel file, only main layer is rendered return products @@ -989,6 +1001,19 @@ class RenderProductsRedshift(ARenderProducts): renderer = "redshift" unmerged_aovs = {"Cryptomatte"} + def get_multipart(self): + # For Redshift we don't directly return upon forcing multilayer + # due to some AOVs still being written into separate files, + # like Cryptomatte. + # AOVs are merged in multi-channel file + multipart = False + force_layer = bool(self._get_attr("redshiftOptions.exrForceMultilayer")) # noqa + exMultipart = bool(self._get_attr("redshiftOptions.exrMultipart")) + if exMultipart or force_layer: + multipart = True + + return multipart + def get_renderer_prefix(self): """Get image prefix for Redshift. @@ -1028,16 +1053,6 @@ class RenderProductsRedshift(ARenderProducts): for c in self.get_renderable_cameras() ] - # For Redshift we don't directly return upon forcing multilayer - # due to some AOVs still being written into separate files, - # like Cryptomatte. - # AOVs are merged in multi-channel file - multipart = False - force_layer = bool(self._get_attr("redshiftOptions.exrForceMultilayer")) # noqa - exMultipart = bool(self._get_attr("redshiftOptions.exrMultipart")) - if exMultipart or force_layer: - multipart = True - # Get Redshift Extension from image format image_format = self._get_attr("redshiftOptions.imageFormat") # integer ext = mel.eval("redshiftGetImageExtension(%i)" % image_format) @@ -1059,7 +1074,7 @@ class RenderProductsRedshift(ARenderProducts): continue aov_type = self._get_attr(aov, "aovType") - if multipart and aov_type not in self.unmerged_aovs: + if self.multipart and aov_type not in self.unmerged_aovs: continue # Any AOVs that still get processed, like Cryptomatte @@ -1094,7 +1109,7 @@ class RenderProductsRedshift(ARenderProducts): productName=aov_light_group_name, aov=aov_name, ext=ext, - multipart=multipart, + multipart=self.multipart, camera=camera) products.append(product) @@ -1108,7 +1123,7 @@ class RenderProductsRedshift(ARenderProducts): product = RenderProduct(productName=aov_name, aov=aov_name, ext=ext, - multipart=multipart, + multipart=self.multipart, camera=camera) products.append(product) @@ -1124,7 +1139,7 @@ class RenderProductsRedshift(ARenderProducts): products.insert(0, RenderProduct(productName=beauty_name, ext=ext, - multipart=multipart, + multipart=self.multipart, camera=camera)) return products @@ -1144,6 +1159,10 @@ class RenderProductsRenderman(ARenderProducts): renderer = "renderman" unmerged_aovs = {"PxrCryptomatte"} + def get_multipart(self): + # Implemented as display specific in "get_render_products". + return False + def get_render_products(self): """Get all AOVs. @@ -1283,6 +1302,9 @@ class RenderProductsMayaHardware(ARenderProducts): {"label": "EXR(exr)", "index": 40, "extension": "exr"} ] + def get_multipart(self): + return False + def _get_extension(self, value): result = None if isinstance(value, int): From 069e1eed21a360d8a01effdacd3b3684954ea83c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 22 Feb 2023 18:00:11 +0000 Subject: [PATCH 0826/1271] Fix Redshift expected files. --- openpype/hosts/maya/api/lib_renderproducts.py | 26 +++++++++++++++---- .../maya/plugins/publish/collect_render.py | 7 ++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index e635414029..4e9e13d2a3 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -1001,6 +1001,20 @@ class RenderProductsRedshift(ARenderProducts): renderer = "redshift" unmerged_aovs = {"Cryptomatte"} + def get_files(self, product): + # When outputting AOVs we need to replace Redshift specific AOV tokens + # with Maya render tokens for generating file sequences. We validate to + # a specific AOV fileprefix so we only need to accout for one + # replacement. + if not product.multipart and product.driver: + file_prefix = self._get_attr(product.driver + ".filePrefix") + self.layer_data.filePrefix = file_prefix.replace( + "/", + "//" + ) + + return super(RenderProductsRedshift, self).get_files(product) + def get_multipart(self): # For Redshift we don't directly return upon forcing multilayer # due to some AOVs still being written into separate files, @@ -1009,7 +1023,7 @@ class RenderProductsRedshift(ARenderProducts): multipart = False force_layer = bool(self._get_attr("redshiftOptions.exrForceMultilayer")) # noqa exMultipart = bool(self._get_attr("redshiftOptions.exrMultipart")) - if exMultipart or force_layer: + if exMultipart and force_layer: multipart = True return multipart @@ -1109,8 +1123,9 @@ class RenderProductsRedshift(ARenderProducts): productName=aov_light_group_name, aov=aov_name, ext=ext, - multipart=self.multipart, - camera=camera) + multipart=False, + camera=camera, + driver=aov) products.append(product) if light_groups: @@ -1123,8 +1138,9 @@ class RenderProductsRedshift(ARenderProducts): product = RenderProduct(productName=aov_name, aov=aov_name, ext=ext, - multipart=self.multipart, - camera=camera) + multipart=False, + camera=camera, + driver=aov) products.append(product) # When a Beauty AOV is added manually, it will be rendered as diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index f2b5262187..338f148f85 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -42,6 +42,7 @@ Provides: import re import os import platform +import json from maya import cmds import maya.app.renderSetup.model.renderSetup as renderSetup @@ -183,7 +184,11 @@ class CollectMayaRender(pyblish.api.ContextPlugin): self.log.info("multipart: {}".format( multipart)) assert exp_files, "no file names were generated, this is bug" - self.log.info(exp_files) + self.log.info( + "expected files: {}".format( + json.dumps(exp_files, indent=4, sort_keys=True) + ) + ) # if we want to attach render to subset, check if we have AOV's # in expectedFiles. If so, raise error as we cannot attach AOV From 43020a6c9d6a8366048a294969deb5d6e6a999e8 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 Feb 2023 09:20:25 +0000 Subject: [PATCH 0827/1271] Only use force options as multipart identifier. --- openpype/hosts/maya/api/lib_renderproducts.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 4e9e13d2a3..02e55601b9 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -1021,9 +1021,10 @@ class RenderProductsRedshift(ARenderProducts): # like Cryptomatte. # AOVs are merged in multi-channel file multipart = False - force_layer = bool(self._get_attr("redshiftOptions.exrForceMultilayer")) # noqa - exMultipart = bool(self._get_attr("redshiftOptions.exrMultipart")) - if exMultipart and force_layer: + force_layer = bool( + self._get_attr("redshiftOptions.exrForceMultilayer") + ) + if force_layer: multipart = True return multipart From 9c688309a9883654c0fb66fab992e6f28323670c Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 23 Feb 2023 11:53:55 +0000 Subject: [PATCH 0828/1271] Update openpype/hosts/maya/api/lib_renderproducts.py --- openpype/hosts/maya/api/lib_renderproducts.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 02e55601b9..463324284b 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -1320,6 +1320,7 @@ class RenderProductsMayaHardware(ARenderProducts): ] def get_multipart(self): + # MayaHardware does not support multipart EXRs. return False def _get_extension(self, value): From d760cbab77c39f3a4ae9b72924ad222304a8aa65 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 31 Jan 2023 06:55:36 +0000 Subject: [PATCH 0829/1271] Batch script for running Openpype on Deadline. --- tools/openpype_console.bat | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tools/openpype_console.bat diff --git a/tools/openpype_console.bat b/tools/openpype_console.bat new file mode 100644 index 0000000000..414b5fdf66 --- /dev/null +++ b/tools/openpype_console.bat @@ -0,0 +1,3 @@ +cd "%~dp0\.." +echo %OPENPYPE_MONGO% +.poetry\bin\poetry.exe run python start.py %* From 5d9fe60013148054b3cbd70d3701febc7f2fe3a4 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Feb 2023 16:25:35 +0000 Subject: [PATCH 0830/1271] Commenting for documentation --- tools/openpype_console.bat | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/openpype_console.bat b/tools/openpype_console.bat index 414b5fdf66..04b28c389f 100644 --- a/tools/openpype_console.bat +++ b/tools/openpype_console.bat @@ -1,3 +1,15 @@ +goto comment +SYNOPSIS + Helper script running scripts through the OpenPype environment. + +DESCRIPTION + This script is usually used as a replacement for building when tested farm integration like Deadline. + +EXAMPLE + +cmd> .\openpype_console.bat path/to/python_script.py +:comment + cd "%~dp0\.." echo %OPENPYPE_MONGO% .poetry\bin\poetry.exe run python start.py %* From 7c73995971a39ef5f77bfe277cd86cddc76b78cd Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Thu, 23 Feb 2023 00:52:40 +0100 Subject: [PATCH 0831/1271] Move get_workfile_build_placeholder_plugins to NukeHost class as workfile template builder expects --- openpype/hosts/nuke/api/__init__.py | 3 --- openpype/hosts/nuke/api/pipeline.py | 13 ++++++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/nuke/api/__init__.py b/openpype/hosts/nuke/api/__init__.py index 3b00ca9f6f..1af5ff365d 100644 --- a/openpype/hosts/nuke/api/__init__.py +++ b/openpype/hosts/nuke/api/__init__.py @@ -30,7 +30,6 @@ from .pipeline import ( parse_container, update_container, - get_workfile_build_placeholder_plugins, ) from .lib import ( INSTANCE_DATA_KNOB, @@ -79,8 +78,6 @@ __all__ = ( "parse_container", "update_container", - "get_workfile_build_placeholder_plugins", - "INSTANCE_DATA_KNOB", "ROOT_DATA_KNOB", "maintained_selection", diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 6dec60d81a..d5289010cb 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -101,6 +101,12 @@ class NukeHost( def get_workfile_extensions(self): return file_extensions() + def get_workfile_build_placeholder_plugins(self): + return [ + NukePlaceholderLoadPlugin, + NukePlaceholderCreatePlugin + ] + def get_containers(self): return ls() @@ -200,13 +206,6 @@ def _show_workfiles(): host_tools.show_workfiles(parent=None, on_top=False) -def get_workfile_build_placeholder_plugins(): - return [ - NukePlaceholderLoadPlugin, - NukePlaceholderCreatePlugin - ] - - def _install_menu(): # uninstall original avalon menu main_window = get_main_window() From 2921579164112453343a580f8f358c124eb9c1d7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:57:51 +0100 Subject: [PATCH 0832/1271] OP-4643 - added Settings for ExtractColorTranscode --- .../defaults/project_settings/global.json | 4 + .../schemas/schema_global_publish.json | 73 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index cedc2d6876..8485bec67b 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -68,6 +68,10 @@ "output": [] } }, + "ExtractColorTranscode": { + "enabled": true, + "profiles": [] + }, "ExtractReview": { "enabled": true, "profiles": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5388d04bc9..46ae6ba554 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -197,6 +197,79 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractColorTranscode", + "label": "ExtractColorTranscode", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "key": "hosts", + "label": "Host names", + "type": "hosts-enum", + "multiselection": true + }, + { + "key": "task_types", + "label": "Task types", + "type": "task-types-enum" + }, + { + "key": "task_names", + "label": "Task names", + "type": "list", + "object_type": "text" + }, + { + "key": "subsets", + "label": "Subset names", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "ext", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } + } + ] + }, { "type": "dict", "collapsible": true, From 8a67065fce1bd39de5870bb768365dffafdda8b2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:58:51 +0100 Subject: [PATCH 0833/1271] OP-4643 - added ExtractColorTranscode Added method to convert from one colorspace to another to transcoding lib --- openpype/lib/transcoding.py | 53 ++++++++ .../publish/extract_color_transcode.py | 124 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 openpype/plugins/publish/extract_color_transcode.py diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 039255d937..2fc662f2a4 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1045,3 +1045,56 @@ def convert_ffprobe_fps_to_float(value): if divisor == 0.0: return 0.0 return dividend / divisor + + +def convert_colorspace_for_input_paths( + input_paths, + output_dir, + source_color_space, + target_color_space, + logger=None +): + """Convert source files from one color space to another. + + Filenames of input files are kept so make sure that output directory + is not the same directory as input files have. + - This way it can handle gaps and can keep input filenames without handling + frame template + + Args: + input_paths (str): Paths that should be converted. It is expected that + contains single file or image sequence of samy type. + output_dir (str): Path to directory where output will be rendered. + Must not be same as input's directory. + source_color_space (str): ocio valid color space of source files + target_color_space (str): ocio valid target color space + logger (logging.Logger): Logger used for logging. + + """ + if logger is None: + logger = logging.getLogger(__name__) + + input_arg = "-i" + oiio_cmd = [ + get_oiio_tools_path(), + + # Don't add any additional attributes + "--nosoftwareattrib", + "--colorconvert", source_color_space, target_color_space + ] + for input_path in input_paths: + # Prepare subprocess arguments + + oiio_cmd.extend([ + input_arg, input_path, + ]) + + # Add last argument - path to output + base_filename = os.path.basename(input_path) + output_path = os.path.join(output_dir, base_filename) + oiio_cmd.extend([ + "-o", output_path + ]) + + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py new file mode 100644 index 0000000000..58508ab18f --- /dev/null +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -0,0 +1,124 @@ +import pyblish.api + +from openpype.pipeline import publish +from openpype.lib import ( + + is_oiio_supported, +) + +from openpype.lib.transcoding import ( + convert_colorspace_for_input_paths, + get_transcode_temp_directory, +) + +from openpype.lib.profiles_filtering import filter_profiles + + +class ExtractColorTranscode(publish.Extractor): + """ + Extractor to convert colors from one colorspace to different. + """ + + label = "Transcode color spaces" + order = pyblish.api.ExtractorOrder + 0.01 + + optional = True + + # Configurable by Settings + profiles = None + options = None + + def process(self, instance): + if not self.profiles: + self.log.warning("No profiles present for create burnin") + return + + if "representations" not in instance.data: + self.log.warning("No representations, skipping.") + return + + if not is_oiio_supported(): + self.log.warning("OIIO not supported, no transcoding possible.") + return + + colorspace_data = instance.data.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Instance has not colorspace data, skipping") + return + source_color_space = colorspace_data["colorspace"] + + host_name = instance.context.data["hostName"] + family = instance.data["family"] + task_data = instance.data["anatomyData"].get("task", {}) + task_name = task_data.get("name") + task_type = task_data.get("type") + subset = instance.data["subset"] + + filtering_criteria = { + "hosts": host_name, + "families": family, + "task_names": task_name, + "task_types": task_type, + "subset": subset + } + profile = filter_profiles(self.profiles, filtering_criteria, + logger=self.log) + + if not profile: + self.log.info(( + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) + return + + self.log.debug("profile: {}".format(profile)) + + target_colorspace = profile["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(repres): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self.repre_is_valid(repre): + continue + + new_staging_dir = get_transcode_temp_directory() + repre["stagingDir"] = new_staging_dir + files_to_remove = repre["files"] + if not isinstance(files_to_remove, list): + files_to_remove = [files_to_remove] + instance.context.data["cleanupFullPaths"].extend(files_to_remove) + + convert_colorspace_for_input_paths( + repre["files"], + new_staging_dir, + source_color_space, + target_colorspace, + self.log + ) + + def repre_is_valid(self, repre): + """Validation if representation should be processed. + + Args: + repre (dict): Representation which should be checked. + + Returns: + bool: False if can't be processed else True. + """ + + if "review" not in (repre.get("tags") or []): + self.log.info(( + "Representation \"{}\" don't have \"review\" tag. Skipped." + ).format(repre["name"])) + return False + + if not repre.get("files"): + self.log.warning(( + "Representation \"{}\" have empty files. Skipped." + ).format(repre["name"])) + return False + return True From 76c00f9fa8888e991c61074ed894b09c211b55f0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 12:05:57 +0100 Subject: [PATCH 0834/1271] OP-4643 - extractor must run just before ExtractReview Nuke render local is set to 0.01 --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 58508ab18f..5163cd4045 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -20,7 +20,7 @@ class ExtractColorTranscode(publish.Extractor): """ label = "Transcode color spaces" - order = pyblish.api.ExtractorOrder + 0.01 + order = pyblish.api.ExtractorOrder + 0.019 optional = True From 455eb379c26ac8494d1aff4fb257cd01e80dbdd5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:03:22 +0100 Subject: [PATCH 0835/1271] OP-4643 - fix for full file paths --- .../publish/extract_color_transcode.py | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 5163cd4045..6ad7599f2c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,3 +1,4 @@ +import os import pyblish.api from openpype.pipeline import publish @@ -41,13 +42,6 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return - colorspace_data = instance.data.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Instance has not colorspace data, skipping") - return - source_color_space = colorspace_data["colorspace"] - host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) @@ -82,18 +76,32 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self.repre_is_valid(repre): + # if not self.repre_is_valid(repre): + # continue + + colorspace_data = repre.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Repre has not colorspace data, skipping") + continue + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") continue new_staging_dir = get_transcode_temp_directory() + original_staging_dir = repre["stagingDir"] repre["stagingDir"] = new_staging_dir - files_to_remove = repre["files"] - if not isinstance(files_to_remove, list): - files_to_remove = [files_to_remove] - instance.context.data["cleanupFullPaths"].extend(files_to_remove) + files_to_convert = repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + instance.context.data["cleanupFullPaths"].extend(files_to_convert) convert_colorspace_for_input_paths( - repre["files"], + files_to_convert, new_staging_dir, source_color_space, target_colorspace, From 52a5865341039d03da72dee00d4eb996334a4dbd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:04:06 +0100 Subject: [PATCH 0836/1271] OP-4643 - pass path for ocio config --- openpype/lib/transcoding.py | 3 +++ openpype/plugins/publish/extract_color_transcode.py | 1 + 2 files changed, 4 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 2fc662f2a4..ab86e44304 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1050,6 +1050,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace_for_input_paths( input_paths, output_dir, + config_path, source_color_space, target_color_space, logger=None @@ -1066,6 +1067,7 @@ def convert_colorspace_for_input_paths( contains single file or image sequence of samy type. output_dir (str): Path to directory where output will be rendered. Must not be same as input's directory. + config_path (str): path to OCIO config file source_color_space (str): ocio valid color space of source files target_color_space (str): ocio valid target color space logger (logging.Logger): Logger used for logging. @@ -1080,6 +1082,7 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", + "--colorconfig", config_path, "--colorconvert", source_color_space, target_color_space ] for input_path in input_paths: diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 6ad7599f2c..fdb13a47e8 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -103,6 +103,7 @@ class ExtractColorTranscode(publish.Extractor): convert_colorspace_for_input_paths( files_to_convert, new_staging_dir, + config_path, source_color_space, target_colorspace, self.log From 0f1dcb64eb5c98ca1d78b48b4e9e4a9dcfc86ffa Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:15:33 +0100 Subject: [PATCH 0837/1271] OP-4643 - add custom_tags --- openpype/plugins/publish/extract_color_transcode.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index fdb13a47e8..ab932b2476 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -72,6 +72,7 @@ class ExtractColorTranscode(publish.Extractor): target_colorspace = profile["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") + custom_tags = profile["custom_tags"] repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): @@ -109,6 +110,11 @@ class ExtractColorTranscode(publish.Extractor): self.log ) + if custom_tags: + if not repre.get("custom_tags"): + repre["custom_tags"] = [] + repre["custom_tags"].extend(custom_tags) + def repre_is_valid(self, repre): """Validation if representation should be processed. From 4d29e43a41a49dd805390fd03d3d7602d15ad38f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:18:38 +0100 Subject: [PATCH 0838/1271] OP-4643 - added docstring --- openpype/plugins/publish/extract_color_transcode.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index ab932b2476..88e2eed90f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -18,6 +18,17 @@ from openpype.lib.profiles_filtering import filter_profiles class ExtractColorTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. + + Expects "colorspaceData" on representation. This dictionary is collected + previously and denotes that representation files should be converted. + This dict contains source colorspace information, collected by hosts. + + Target colorspace is selected by profiles in the Settings, based on: + - families + - host + - task types + - task names + - subset names """ label = "Transcode color spaces" From 454f65dc50253afdb7c74c7c8b851fcbe54c372d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:15:44 +0100 Subject: [PATCH 0839/1271] OP-4643 - updated Settings schema --- .../schemas/schema_global_publish.json | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 46ae6ba554..c2c911d7d6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -246,24 +246,44 @@ "type": "list", "object_type": "text" }, + { + "type": "boolean", + "key": "delete_original", + "label": "Delete Original Representation" + }, { "type": "splitter" }, { - "key": "ext", - "label": "Output extension", - "type": "text" - }, - { - "key": "output_colorspace", - "label": "Output colorspace", - "type": "text" - }, - { - "key": "custom_tags", - "label": "Custom Tags", - "type": "list", - "object_type": "text" + "key": "outputs", + "label": "Output Definitions", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": { + "type": "dict", + "children": [ + { + "key": "output_extension", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "type": "schema", + "name": "schema_representation_tags" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } } ] } From a197c6820209db57dc3182ca392e51239be57bb5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:17:25 +0100 Subject: [PATCH 0840/1271] OP-4643 - skip video files Only frames currently supported. --- .../plugins/publish/extract_color_transcode.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 88e2eed90f..a0714c9a33 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -36,6 +36,9 @@ class ExtractColorTranscode(publish.Extractor): optional = True + # Supported extensions + supported_exts = ["exr", "jpg", "jpeg", "png", "dpx"] + # Configurable by Settings profiles = None options = None @@ -88,13 +91,7 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - # if not self.repre_is_valid(repre): - # continue - - colorspace_data = repre.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Repre has not colorspace data, skipping") + if not self._repre_is_valid(repre): continue source_color_space = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") @@ -136,9 +133,9 @@ class ExtractColorTranscode(publish.Extractor): bool: False if can't be processed else True. """ - if "review" not in (repre.get("tags") or []): - self.log.info(( - "Representation \"{}\" don't have \"review\" tag. Skipped." + if repre.get("ext") not in self.supported_exts: + self.log.warning(( + "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False From bd1a9c7342098e05e095f422200090ce918a56b5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:19:08 +0100 Subject: [PATCH 0841/1271] OP-4643 - refactored profile, delete of original Implemented multiple outputs from single input representation --- .../publish/extract_color_transcode.py | 156 ++++++++++++------ 1 file changed, 109 insertions(+), 47 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index a0714c9a33..b0c851d5f4 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,4 +1,6 @@ import os +import copy + import pyblish.api from openpype.pipeline import publish @@ -56,13 +58,94 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return + profile = self._get_profile(instance) + if not profile: + return + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(list(repres)): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self._repre_is_valid(repre): + continue + + colorspace_data = repre["colorspaceData"] + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") + continue + + repre = self._handle_original_repre(repre, profile) + + for _, output_def in profile.get("outputs", {}).items(): + new_repre = copy.deepcopy(repre) + + new_staging_dir = get_transcode_temp_directory() + original_staging_dir = new_repre["stagingDir"] + new_repre["stagingDir"] = new_staging_dir + files_to_convert = new_repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + + files_to_delete = copy.deepcopy(files_to_convert) + + output_extension = output_def["output_extension"] + files_to_convert = self._rename_output_files(files_to_convert, + output_extension) + + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + + target_colorspace = output_def["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + convert_colorspace_for_input_paths( + files_to_convert, + new_staging_dir, + config_path, + source_color_space, + target_colorspace, + self.log + ) + + instance.context.data["cleanupFullPaths"].extend( + files_to_delete) + + custom_tags = output_def.get("custom_tags") + if custom_tags: + if not new_repre.get("custom_tags"): + new_repre["custom_tags"] = [] + new_repre["custom_tags"].extend(custom_tags) + + # Add additional tags from output definition to representation + for tag in output_def["tags"]: + if tag not in new_repre["tags"]: + new_repre["tags"].append(tag) + + instance.data["representations"].append(new_repre) + + def _rename_output_files(self, files_to_convert, output_extension): + """Change extension of converted files.""" + if output_extension: + output_extension = output_extension.replace('.', '') + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files + return files_to_convert + + def _get_profile(self, instance): + """Returns profile if and how repre should be color transcoded.""" host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) task_name = task_data.get("name") task_type = task_data.get("type") subset = instance.data["subset"] - filtering_criteria = { "hosts": host_name, "families": family, @@ -75,55 +158,15 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) - return + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) + return profile - target_colorspace = profile["output_colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") - custom_tags = profile["custom_tags"] - - repres = instance.data.get("representations") or [] - for idx, repre in enumerate(repres): - self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self._repre_is_valid(repre): - continue - source_color_space = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): - self.log.warning("Config file doesn't exist, skipping") - continue - - new_staging_dir = get_transcode_temp_directory() - original_staging_dir = repre["stagingDir"] - repre["stagingDir"] = new_staging_dir - files_to_convert = repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - instance.context.data["cleanupFullPaths"].extend(files_to_convert) - - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) - - if custom_tags: - if not repre.get("custom_tags"): - repre["custom_tags"] = [] - repre["custom_tags"].extend(custom_tags) - - def repre_is_valid(self, repre): + def _repre_is_valid(self, repre): """Validation if representation should be processed. Args: @@ -144,4 +187,23 @@ class ExtractColorTranscode(publish.Extractor): "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False + + if not repre.get("colorspaceData"): + self.log.warning("Repre has not colorspace data, skipping") + return False + return True + + def _handle_original_repre(self, repre, profile): + delete_original = profile["delete_original"] + + if delete_original: + if not repre.get("tags"): + repre["tags"] = [] + + if "review" in repre["tags"]: + repre["tags"].remove("review") + if "delete" not in repre["tags"]: + repre["tags"].append("delete") + + return repre From bb85bc330a514f088fd2fad6037f60976e0e993b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:23:01 +0100 Subject: [PATCH 0842/1271] OP-4643 - switched logging levels Do not use warning unnecessary. --- openpype/plugins/publish/extract_color_transcode.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index b0c851d5f4..4d38514b8b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -47,11 +47,11 @@ class ExtractColorTranscode(publish.Extractor): def process(self, instance): if not self.profiles: - self.log.warning("No profiles present for create burnin") + self.log.debug("No profiles present for color transcode") return if "representations" not in instance.data: - self.log.warning("No representations, skipping.") + self.log.debug("No representations, skipping.") return if not is_oiio_supported(): @@ -177,19 +177,19 @@ class ExtractColorTranscode(publish.Extractor): """ if repre.get("ext") not in self.supported_exts: - self.log.warning(( + self.log.debug(( "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): - self.log.warning(( + self.log.debug(( "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.warning("Repre has not colorspace data, skipping") + self.log.debug("Repre has no colorspace data. Skipped.") return False return True From 1fd17b9f597dfef41852572deae8edbec65c3280 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:14 +0100 Subject: [PATCH 0843/1271] OP-4643 - propagate new extension to representation --- .../publish/extract_color_transcode.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4d38514b8b..62cf8f0dee 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -90,8 +90,13 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) output_extension = output_def["output_extension"] - files_to_convert = self._rename_output_files(files_to_convert, - output_extension) + output_extension = output_extension.replace('.', '') + if output_extension: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + files_to_convert = self._rename_output_files( + files_to_convert, output_extension) files_to_convert = [os.path.join(original_staging_dir, path) for path in files_to_convert] @@ -127,15 +132,13 @@ class ExtractColorTranscode(publish.Extractor): def _rename_output_files(self, files_to_convert, output_extension): """Change extension of converted files.""" - if output_extension: - output_extension = output_extension.replace('.', '') - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files return files_to_convert def _get_profile(self, instance): From ffec1179ad1c9c07fae71db44ff2dc96b6fd7ec2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:35 +0100 Subject: [PATCH 0844/1271] OP-4643 - added label to Settings --- .../projects_schema/schemas/schema_global_publish.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index c2c911d7d6..7155510fef 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -201,10 +201,14 @@ "type": "dict", "collapsible": true, "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode", + "label": "ExtractColorTranscode (ImageIO)", "checkbox_key": "enabled", "is_group": true, "children": [ + { + "type": "label", + "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + }, { "type": "boolean", "key": "enabled", From 5a562dc821b2cbefc780509b44c084bba786e80c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jan 2023 18:22:08 +0100 Subject: [PATCH 0845/1271] OP-4643 - refactored according to review Function turned into single filepath input. --- openpype/lib/transcoding.py | 43 ++++++----- .../publish/extract_color_transcode.py | 72 ++++++++++--------- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index ab86e44304..e1bd22d109 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1047,12 +1047,12 @@ def convert_ffprobe_fps_to_float(value): return dividend / divisor -def convert_colorspace_for_input_paths( - input_paths, - output_dir, +def convert_colorspace( + input_path, + out_filepath, config_path, - source_color_space, - target_color_space, + source_colorspace, + target_colorspace, logger=None ): """Convert source files from one color space to another. @@ -1063,13 +1063,13 @@ def convert_colorspace_for_input_paths( frame template Args: - input_paths (str): Paths that should be converted. It is expected that + input_path (str): Paths that should be converted. It is expected that contains single file or image sequence of samy type. - output_dir (str): Path to directory where output will be rendered. + out_filepath (str): Path to directory where output will be rendered. Must not be same as input's directory. config_path (str): path to OCIO config file - source_color_space (str): ocio valid color space of source files - target_color_space (str): ocio valid target color space + source_colorspace (str): ocio valid color space of source files + target_colorspace (str): ocio valid target color space logger (logging.Logger): Logger used for logging. """ @@ -1083,21 +1083,18 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_color_space, target_color_space + "--colorconvert", source_colorspace, target_colorspace ] - for input_path in input_paths: - # Prepare subprocess arguments + # Prepare subprocess arguments - oiio_cmd.extend([ - input_arg, input_path, - ]) + oiio_cmd.extend([ + input_arg, input_path, + ]) - # Add last argument - path to output - base_filename = os.path.basename(input_path) - output_path = os.path.join(output_dir, base_filename) - oiio_cmd.extend([ - "-o", output_path - ]) + # Add last argument - path to output + oiio_cmd.extend([ + "-o", out_filepath + ]) - logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) - run_subprocess(oiio_cmd, logger=logger) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 62cf8f0dee..3a05426432 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -10,7 +10,7 @@ from openpype.lib import ( ) from openpype.lib.transcoding import ( - convert_colorspace_for_input_paths, + convert_colorspace, get_transcode_temp_directory, ) @@ -69,7 +69,7 @@ class ExtractColorTranscode(publish.Extractor): continue colorspace_data = repre["colorspaceData"] - source_color_space = colorspace_data["colorspace"] + source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") if not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") @@ -80,8 +80,8 @@ class ExtractColorTranscode(publish.Extractor): for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) - new_staging_dir = get_transcode_temp_directory() original_staging_dir = new_repre["stagingDir"] + new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir files_to_convert = new_repre["files"] if not isinstance(files_to_convert, list): @@ -92,27 +92,28 @@ class ExtractColorTranscode(publish.Extractor): output_extension = output_def["output_extension"] output_extension = output_extension.replace('.', '') if output_extension: - new_repre["name"] = output_extension + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension new_repre["ext"] = output_extension - files_to_convert = self._rename_output_files( - files_to_convert, output_extension) - - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - target_colorspace = output_def["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) + for file_name in files_to_convert: + input_filepath = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_filepath, + new_staging_dir, + output_extension) + convert_colorspace( + input_filepath, + output_path, + config_path, + source_colorspace, + target_colorspace, + self.log + ) instance.context.data["cleanupFullPaths"].extend( files_to_delete) @@ -130,16 +131,16 @@ class ExtractColorTranscode(publish.Extractor): instance.data["representations"].append(new_repre) - def _rename_output_files(self, files_to_convert, output_extension): - """Change extension of converted files.""" - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files - return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, + output_extension): + """Create output file name path.""" + file_name = os.path.basename(input_filepath) + file_name, input_extension = os.path.splitext(file_name) + if not output_extension: + output_extension = input_extension + new_file_name = '{}.{}'.format(file_name, + output_extension) + return os.path.join(output_dir, new_file_name) def _get_profile(self, instance): """Returns profile if and how repre should be color transcoded.""" @@ -161,10 +162,10 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) return profile @@ -181,18 +182,19 @@ class ExtractColorTranscode(publish.Extractor): if repre.get("ext") not in self.supported_exts: self.log.debug(( - "Representation \"{}\" of unsupported extension. Skipped." + "Representation '{}' of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): self.log.debug(( - "Representation \"{}\" have empty files. Skipped." + "Representation '{}' have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.debug("Repre has no colorspace data. Skipped.") + self.log.debug("Representation '{}' has no colorspace data. " + "Skipped.") return False return True From e0a163bc2e836c9292e52757abab43d26bc44c07 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:53:02 +0100 Subject: [PATCH 0846/1271] OP-4643 - updated schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 7155510fef..80c18ce118 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -267,8 +267,8 @@ "type": "dict", "children": [ { - "key": "output_extension", - "label": "Output extension", + "key": "extension", + "label": "Extension", "type": "text" }, { From 657c3156dfc046405cee116c6ccb141e60901d23 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:54:46 +0100 Subject: [PATCH 0847/1271] OP-4643 - updated plugin name in schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 80c18ce118..357cbfb287 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -200,8 +200,8 @@ { "type": "dict", "collapsible": true, - "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode (ImageIO)", + "key": "ExtractOIIOTranscode", + "label": "Extract OIIO Transcode", "checkbox_key": "enabled", "is_group": true, "children": [ From 4b5c14e46686e471f20bef6909d6287b2eae6859 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:55:57 +0100 Subject: [PATCH 0848/1271] OP-4643 - updated key in schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 357cbfb287..0281b0ded6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -272,8 +272,8 @@ "type": "text" }, { - "key": "output_colorspace", - "label": "Output colorspace", + "key": "colorspace", + "label": "Colorspace", "type": "text" }, { From 756661f71b762fb738bd793984787941f2051e4d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:57:03 +0100 Subject: [PATCH 0849/1271] OP-4643 - changed oiio_cmd creation Co-authored-by: Toke Jepsen --- openpype/lib/transcoding.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index e1bd22d109..f22628dd28 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1076,25 +1076,15 @@ def convert_colorspace( if logger is None: logger = logging.getLogger(__name__) - input_arg = "-i" oiio_cmd = [ get_oiio_tools_path(), - + input_path, # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace - ] - # Prepare subprocess arguments - - oiio_cmd.extend([ - input_arg, input_path, - ]) - - # Add last argument - path to output - oiio_cmd.extend([ + "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath - ]) + ] logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From c3bf4734fdabc019c966bae93e131235f2560f34 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:44:45 +0100 Subject: [PATCH 0850/1271] OP-4643 - updated new keys into settings --- .../settings/defaults/project_settings/global.json | 2 +- .../projects_schema/schemas/schema_global_publish.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 8485bec67b..a5e2d25a88 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -68,7 +68,7 @@ "output": [] } }, - "ExtractColorTranscode": { + "ExtractOIIOTranscode": { "enabled": true, "profiles": [] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 0281b0ded6..74b81b13af 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -276,6 +276,16 @@ "label": "Colorspace", "type": "text" }, + { + "key": "display", + "label": "Display", + "type": "text" + }, + { + "key": "view", + "label": "View", + "type": "text" + }, { "type": "schema", "name": "schema_representation_tags" From 2b20ede6d86924a19e5bf5ad9697f451a85a757e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:45:42 +0100 Subject: [PATCH 0851/1271] OP-4643 - renanmed plugin, added new keys into outputs --- openpype/plugins/publish/extract_color_transcode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3a05426432..cc63b35988 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -17,7 +17,7 @@ from openpype.lib.transcoding import ( from openpype.lib.profiles_filtering import filter_profiles -class ExtractColorTranscode(publish.Extractor): +class ExtractOIIOTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. @@ -89,14 +89,14 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) - output_extension = output_def["output_extension"] + output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: if new_repre["name"] == new_repre["ext"]: new_repre["name"] = output_extension new_repre["ext"] = output_extension - target_colorspace = output_def["output_colorspace"] + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From 8b47a44d04aace508cc05608a18c4f7cce23cb9b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:03:13 +0100 Subject: [PATCH 0852/1271] OP-4643 - fixed config path key --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cc63b35988..245faeb306 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -70,8 +70,8 @@ class ExtractOIIOTranscode(publish.Extractor): colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): + config_path = colorspace_data.get("config", {}).get("path") + if not config_path or not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") continue From ed7faeef8f0a7678e99f70645d57a72864cc9461 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:03:42 +0100 Subject: [PATCH 0853/1271] OP-4643 - fixed renaming files --- openpype/plugins/publish/extract_color_transcode.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 245faeb306..c079dcf70e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -96,6 +96,14 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["name"] = output_extension new_repre["ext"] = output_extension + renamed_files = [] + _, orig_ext = os.path.splitext(files_to_convert[0]) + for file_name in files_to_convert: + file_name = file_name.replace(orig_ext, + "."+output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From 4b1418a79242eb63cfd4295d70d474b992c276ea Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:04:44 +0100 Subject: [PATCH 0854/1271] OP-4643 - updated to calculate sequence format --- .../publish/extract_color_transcode.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c079dcf70e..09c86909cb 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,5 +1,6 @@ import os import copy +import clique import pyblish.api @@ -108,6 +109,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not target_colorspace: raise RuntimeError("Target colorspace must be set") + files_to_convert = self._translate_to_sequence( + files_to_convert) for file_name in files_to_convert: input_filepath = os.path.join(original_staging_dir, file_name) @@ -139,6 +142,40 @@ class ExtractOIIOTranscode(publish.Extractor): instance.data["representations"].append(new_repre) + def _translate_to_sequence(self, files_to_convert): + """Returns original list of files or single sequence format filename. + + Uses clique to find frame sequence, in this case it merges all frames + into sequence format (%0X) and returns it. + If sequence not found, it returns original list + + Args: + files_to_convert (list): list of file names + Returns: + (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + """ + pattern = [clique.PATTERNS["frames"]] + collections, remainder = clique.assemble( + files_to_convert, patterns=pattern, + assume_padded_when_ambiguous=True) + + if collections: + if len(collections) > 1: + raise ValueError( + "Too many collections {}".format(collections)) + + collection = collections[0] + padding = collection.padding + padding_str = "%0{}".format(padding) + frames = list(collection.indexes) + frame_str = "{}-{}#".format(frames[0], frames[-1]) + file_name = "{}{}{}".format(collection.head, frame_str, + collection.tail) + + files_to_convert = [file_name] + + return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, output_extension): """Create output file name path.""" From 382074b54cc0eb7a7c6d403853f4350d485fa00a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:54:02 +0100 Subject: [PATCH 0855/1271] OP-4643 - implemented display and viewer color space --- openpype/lib/transcoding.py | 23 +++++++++++++++++-- .../publish/extract_color_transcode.py | 13 +++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index f22628dd28..cc9cd4e1eb 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1053,6 +1053,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, + view, + display, logger=None ): """Convert source files from one color space to another. @@ -1070,8 +1072,11 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + view (str): name for viewer space (ocio valid) + display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. - + Raises: + ValueError: if misconfigured """ if logger is None: logger = logging.getLogger(__name__) @@ -1082,9 +1087,23 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath ] + if all([target_colorspace, view, display]): + raise ValueError("Colorspace and both screen and display" + " cannot be set together." + "Choose colorspace or screen and display") + if not target_colorspace and not all([view, display]): + raise ValueError("Both screen and display must be set.") + + if target_colorspace: + oiio_cmd.extend(["--colorconvert", + source_colorspace, + target_colorspace]) + if view and display: + oiio_cmd.extend(["--iscolorspace", source_colorspace]) + oiio_cmd.extend(["--ociodisplay", display, view]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 09c86909cb..cd8421c0cd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -106,8 +106,15 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["files"] = renamed_files target_colorspace = output_def["colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, + # but could be overwritten + if view: + new_repre["colorspaceData"]["view"] = view + if display: + new_repre["colorspaceData"]["view"] = display files_to_convert = self._translate_to_sequence( files_to_convert) @@ -123,6 +130,8 @@ class ExtractOIIOTranscode(publish.Extractor): config_path, source_colorspace, target_colorspace, + view, + display, self.log ) From 8873c9f9056b9b672fe95be0707c9ab8753726b8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 19:03:10 +0100 Subject: [PATCH 0856/1271] OP-4643 - fix wrong order of deletion of representation --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cd8421c0cd..9cca5cc969 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -69,6 +69,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not self._repre_is_valid(repre): continue + added_representations = False + colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("config", {}).get("path") @@ -76,8 +78,6 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - repre = self._handle_original_repre(repre, profile) - for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) @@ -150,6 +150,10 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["tags"].append(tag) instance.data["representations"].append(new_repre) + added_representations = True + + if added_representations: + self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): """Returns original list of files or single sequence format filename. @@ -253,7 +257,8 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _handle_original_repre(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile): + """If new transcoded representation created, delete old.""" delete_original = profile["delete_original"] if delete_original: @@ -264,5 +269,3 @@ class ExtractOIIOTranscode(publish.Extractor): repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") - - return repre From 176f53117fe49410135121dc0b858eadb8041270 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:26:27 +0100 Subject: [PATCH 0857/1271] OP-4643 - updated docstring, standardized arguments --- openpype/lib/transcoding.py | 19 +++++++---------- .../publish/extract_color_transcode.py | 21 +++++++++---------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index cc9cd4e1eb..0f6d35affe 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1049,7 +1049,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace( input_path, - out_filepath, + output_path, config_path, source_colorspace, target_colorspace, @@ -1057,18 +1057,13 @@ def convert_colorspace( display, logger=None ): - """Convert source files from one color space to another. - - Filenames of input files are kept so make sure that output directory - is not the same directory as input files have. - - This way it can handle gaps and can keep input filenames without handling - frame template + """Convert source file from one color space to another. Args: - input_path (str): Paths that should be converted. It is expected that - contains single file or image sequence of samy type. - out_filepath (str): Path to directory where output will be rendered. - Must not be same as input's directory. + input_path (str): Path that should be converted. It is expected that + contains single file or image sequence of same type + (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + output_path (str): Path to output filename. config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space @@ -1087,7 +1082,7 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "-o", out_filepath + "-o", output_path ] if all([target_colorspace, view, display]): diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 9cca5cc969..c4cef15ea6 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -119,13 +119,13 @@ class ExtractOIIOTranscode(publish.Extractor): files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: - input_filepath = os.path.join(original_staging_dir, - file_name) - output_path = self._get_output_file_path(input_filepath, + input_path = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_path, new_staging_dir, output_extension) convert_colorspace( - input_filepath, + input_path, output_path, config_path, source_colorspace, @@ -156,16 +156,17 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): - """Returns original list of files or single sequence format filename. + """Returns original list or list with filename formatted in single + sequence format. Uses clique to find frame sequence, in this case it merges all frames - into sequence format (%0X) and returns it. + into sequence format (FRAMESTART-FRAMEEND#) and returns it. If sequence not found, it returns original list Args: files_to_convert (list): list of file names Returns: - (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + (list) of [file.1001-1010#.exr] or [fileA.exr, fileB.exr] """ pattern = [clique.PATTERNS["frames"]] collections, remainder = clique.assemble( @@ -178,8 +179,6 @@ class ExtractOIIOTranscode(publish.Extractor): "Too many collections {}".format(collections)) collection = collections[0] - padding = collection.padding - padding_str = "%0{}".format(padding) frames = list(collection.indexes) frame_str = "{}-{}#".format(frames[0], frames[-1]) file_name = "{}{}{}".format(collection.head, frame_str, @@ -189,10 +188,10 @@ class ExtractOIIOTranscode(publish.Extractor): return files_to_convert - def _get_output_file_path(self, input_filepath, output_dir, + def _get_output_file_path(self, input_path, output_dir, output_extension): """Create output file name path.""" - file_name = os.path.basename(input_filepath) + file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: output_extension = input_extension From 04ae5a28b415197f36d198e409e8343bc4bc6022 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:27:06 +0100 Subject: [PATCH 0858/1271] OP-4643 - fix wrong assignment --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c4cef15ea6..4e899a519c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -114,7 +114,7 @@ class ExtractOIIOTranscode(publish.Extractor): if view: new_repre["colorspaceData"]["view"] = view if display: - new_repre["colorspaceData"]["view"] = display + new_repre["colorspaceData"]["display"] = display files_to_convert = self._translate_to_sequence( files_to_convert) From c6571b9dfd520a77877c84a86c60999ab5257a3a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:59:13 +0100 Subject: [PATCH 0859/1271] OP-4643 - fix files to delete --- .../publish/extract_color_transcode.py | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4e899a519c..99e684ba21 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -84,26 +84,18 @@ class ExtractOIIOTranscode(publish.Extractor): original_staging_dir = new_repre["stagingDir"] new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir - files_to_convert = new_repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_delete = copy.deepcopy(files_to_convert) + if isinstance(new_repre["files"], list): + files_to_convert = copy.deepcopy(new_repre["files"]) + else: + files_to_convert = [new_repre["files"]] output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension - new_repre["ext"] = output_extension - - renamed_files = [] - _, orig_ext = os.path.splitext(files_to_convert[0]) - for file_name in files_to_convert: - file_name = file_name.replace(orig_ext, - "."+output_extension) - renamed_files.append(file_name) - new_repre["files"] = renamed_files + self._rename_in_representation(new_repre, + files_to_convert, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -135,8 +127,12 @@ class ExtractOIIOTranscode(publish.Extractor): self.log ) - instance.context.data["cleanupFullPaths"].extend( - files_to_delete) + # cleanup temporary transcoded files + for file_name in new_repre["files"]: + transcoded_file_path = os.path.join(new_staging_dir, + file_name) + instance.context.data["cleanupFullPaths"].append( + transcoded_file_path) custom_tags = output_def.get("custom_tags") if custom_tags: @@ -155,6 +151,21 @@ class ExtractOIIOTranscode(publish.Extractor): if added_representations: self._mark_original_repre_for_deletion(repre, profile) + def _rename_in_representation(self, new_repre, files_to_convert, + output_extension): + """Replace old extension with new one everywhere in representation.""" + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + def _translate_to_sequence(self, files_to_convert): """Returns original list or list with filename formatted in single sequence format. From 8598c1ec393a1c8c0f05ad12a2a66bbf7eb89136 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:17:59 +0100 Subject: [PATCH 0860/1271] OP-4643 - moved output argument to the end --- openpype/lib/transcoding.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 0f6d35affe..e74dab4ccc 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1081,8 +1081,7 @@ def convert_colorspace( input_path, # Don't add any additional attributes "--nosoftwareattrib", - "--colorconfig", config_path, - "-o", output_path + "--colorconfig", config_path ] if all([target_colorspace, view, display]): @@ -1100,5 +1099,7 @@ def convert_colorspace( oiio_cmd.extend(["--iscolorspace", source_colorspace]) oiio_cmd.extend(["--ociodisplay", display, view]) + oiio_cmd.extend(["-o", output_path]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From afe0a97bc5ace60a5f7740d22b3d1937e1b69fdf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:18:33 +0100 Subject: [PATCH 0861/1271] OP-4643 - fix no tags in repre --- openpype/plugins/publish/extract_color_transcode.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 99e684ba21..3d897c6d9f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -142,6 +142,8 @@ class ExtractOIIOTranscode(publish.Extractor): # Add additional tags from output definition to representation for tag in output_def["tags"]: + if not new_repre.get("tags"): + new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) From 190a79a836d6e981e57f9f23be58d146193c4d6c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:26:07 +0100 Subject: [PATCH 0862/1271] OP-4643 - changed docstring Elaborated more that 'target_colorspace' and ('view', 'display') are disjunctive. --- openpype/lib/transcoding.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index e74dab4ccc..f7d5e222c8 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1053,8 +1053,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, - view, - display, + view=None, + display=None, logger=None ): """Convert source file from one color space to another. @@ -1067,7 +1067,9 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + if filled, 'view' and 'display' must be empty view (str): name for viewer space (ocio valid) + both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. Raises: From 4967d91010dc03df5640364350095f0fae9aa52c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 11:14:07 +0100 Subject: [PATCH 0863/1271] OP-4663 - fix double dots in extension Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3d897c6d9f..bfed69c300 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -207,7 +207,7 @@ class ExtractOIIOTranscode(publish.Extractor): file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: - output_extension = input_extension + output_extension = input_extension.replace(".", "") new_file_name = '{}.{}'.format(file_name, output_extension) return os.path.join(output_dir, new_file_name) From 43df616692568352b9734715e3a48b71a60e2221 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:11:45 +0100 Subject: [PATCH 0864/1271] OP-4643 - update documentation in Settings schema --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 74b81b13af..3956f403f4 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -207,7 +207,7 @@ "children": [ { "type": "label", - "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + "label": "Configure Output Definition(s) for new representation(s). \nEmpty 'Extension' denotes keeping source extension. \nName(key) of output definition will be used as new representation name \nunless 'passthrough' value is used to keep existing name. \nFill either 'Colorspace' (for target colorspace) or \nboth 'Display' and 'View' (for display and viewer colorspaces)." }, { "type": "boolean", From 5b72bafcfc9a33302f588e29d314d91b98186f87 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:13:59 +0100 Subject: [PATCH 0865/1271] OP-4643 - name of new representation from output definition key --- .../publish/extract_color_transcode.py | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index bfed69c300..e39ea3add9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -32,6 +32,25 @@ class ExtractOIIOTranscode(publish.Extractor): - task types - task names - subset names + + Can produce one or more representations (with different extensions) based + on output definition in format: + "output_name: { + "extension": "png", + "colorspace": "ACES - ACEScg", + "display": "", + "view": "", + "tags": [], + "custom_tags": [] + } + + If 'extension' is empty original representation extension is used. + 'output_name' will be used as name of new representation. In case of value + 'passthrough' name of original representation will be used. + + 'colorspace' denotes target colorspace to be transcoded into. Could be + empty if transcoding should be only into display and viewer colorspace. + (In that case both 'display' and 'view' must be filled.) """ label = "Transcode color spaces" @@ -78,7 +97,7 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - for _, output_def in profile.get("outputs", {}).items(): + for output_name, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) original_staging_dir = new_repre["stagingDir"] @@ -92,10 +111,10 @@ class ExtractOIIOTranscode(publish.Extractor): output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') - if output_extension: - self._rename_in_representation(new_repre, - files_to_convert, - output_extension) + self._rename_in_representation(new_repre, + files_to_convert, + output_name, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -154,10 +173,22 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _rename_in_representation(self, new_repre, files_to_convert, - output_extension): - """Replace old extension with new one everywhere in representation.""" - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension + output_name, output_extension): + """Replace old extension with new one everywhere in representation. + + Args: + new_repre (dict) + files_to_convert (list): of filenames from repre["files"], + standardized to always list + output_name (str): key of output definition from Settings, + if "" token used, keep original repre name + output_extension (str): extension from output definition + """ + if output_name != "passthrough": + new_repre["name"] = output_name + if not output_extension: + return + new_repre["ext"] = output_extension renamed_files = [] From 7eacd1f30f2ce92adcec6ce8e48fb62bfb92a148 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:42:48 +0100 Subject: [PATCH 0866/1271] OP-4643 - updated docstring for convert_colorspace --- openpype/lib/transcoding.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index f7d5e222c8..b6edd863f8 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1062,8 +1062,11 @@ def convert_colorspace( Args: input_path (str): Path that should be converted. It is expected that contains single file or image sequence of same type - (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + (sequence in format 'file.FRAMESTART-FRAMEEND#.ext', see oiio docs, + eg `big.1-3#.tif`) output_path (str): Path to output filename. + (must follow format of 'input_path', eg. single file or + sequence in 'file.FRAMESTART-FRAMEEND#.ext', `output.1-3#.tif`) config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space From 440c4e0c100a413f62aaaf582201384124ba916b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:22:10 +0100 Subject: [PATCH 0867/1271] OP-4643 - remove review from old representation If new representation gets created and adds 'review' tag it becomes new reviewable representation. --- .../publish/extract_color_transcode.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index e39ea3add9..d10b887a0b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -89,6 +89,7 @@ class ExtractOIIOTranscode(publish.Extractor): continue added_representations = False + added_review = False colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] @@ -166,11 +167,15 @@ class ExtractOIIOTranscode(publish.Extractor): if tag not in new_repre["tags"]: new_repre["tags"].append(tag) + if tag == "review": + added_review = True + instance.data["representations"].append(new_repre) added_representations = True if added_representations: - self._mark_original_repre_for_deletion(repre, profile) + self._mark_original_repre_for_deletion(repre, profile, + added_review) def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): @@ -300,15 +305,16 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _mark_original_repre_for_deletion(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile, added_review): """If new transcoded representation created, delete old.""" + if not repre.get("tags"): + repre["tags"] = [] + delete_original = profile["delete_original"] if delete_original: - if not repre.get("tags"): - repre["tags"] = [] - - if "review" in repre["tags"]: - repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") + + if added_review and "review" in repre["tags"]: + repre["tags"].remove("review") From 212cbe79a2eb05f3b426a01d836d42964838bbbb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:23:42 +0100 Subject: [PATCH 0868/1271] OP-4643 - remove representation that should be deleted Or old revieable representation would be reviewed too. --- openpype/plugins/publish/extract_color_transcode.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index d10b887a0b..93ee1ec44d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -177,6 +177,11 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile, added_review) + for repre in tuple(instance.data["representations"]): + tags = repre.get("tags") or [] + if "delete" in tags and "thumbnail" not in tags: + instance.data["representations"].remove(repre) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From 6295d0f34a9efc8e86ebbbf1dbe4b40a391dbf7b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 14:54:25 +0100 Subject: [PATCH 0869/1271] OP-4643 - fix logging Wrong variable used --- openpype/plugins/publish/extract_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index dcb43d7fa2..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -169,7 +169,7 @@ class ExtractReview(pyblish.api.InstancePlugin): "Skipped representation. All output definitions from" " selected profile does not match to representation's" " custom tags. \"{}\"" - ).format(str(tags))) + ).format(str(custom_tags))) continue outputs_per_representations.append((repre, outputs)) From 03e8661323622c39d91c647d2f6cdd615a4ca99c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 15:14:14 +0100 Subject: [PATCH 0870/1271] OP-4643 - allow new repre to stay One might want to delete outputs with 'delete' tag, but repre must stay there at least until extract_review. More universal new tag might be created for this. --- openpype/plugins/publish/extract_color_transcode.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 93ee1ec44d..4a03e623fd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,15 +161,17 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation + if not new_repre.get("tags"): + new_repre["tags"] = [] for tag in output_def["tags"]: - if not new_repre.get("tags"): - new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) if tag == "review": added_review = True + new_repre["tags"].append("newly_added") + instance.data["representations"].append(new_repre) added_representations = True @@ -179,6 +181,12 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] + # TODO implement better way, for now do not delete new repre + # new repre might have 'delete' tag to removed, but it first must + # be there for review to be created + if "newly_added" in tags: + tags.remove("newly_added") + continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) From f4140d7664cb3a36c032f4ae4b6d0a240fc01d3a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:04:59 +0100 Subject: [PATCH 0871/1271] OP-4642 - added additional command arguments to Settings --- .../schemas/schema_global_publish.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3956f403f4..5333d514b5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -286,6 +286,20 @@ "label": "View", "type": "text" }, + { + "key": "oiiotool_args", + "label": "OIIOtool arguments", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "additional_command_args", + "label": "Additional command line arguments", + "type": "list", + "object_type": "text" + } + ] + }, { "type": "schema", "name": "schema_representation_tags" From b1d30058b0cdb26e46618d37807b01d400c89e33 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:08:06 +0100 Subject: [PATCH 0872/1271] OP-4642 - added additional command arguments for oiiotool Some extension requires special command line arguments (.dpx and binary depth). --- openpype/lib/transcoding.py | 6 ++++++ openpype/plugins/publish/extract_color_transcode.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index b6edd863f8..982cee7a46 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1055,6 +1055,7 @@ def convert_colorspace( target_colorspace, view=None, display=None, + additional_command_args=None, logger=None ): """Convert source file from one color space to another. @@ -1074,6 +1075,8 @@ def convert_colorspace( view (str): name for viewer space (ocio valid) both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) + additional_command_args (list): arguments for oiiotool (like binary + depth for .dpx) logger (logging.Logger): Logger used for logging. Raises: ValueError: if misconfigured @@ -1096,6 +1099,9 @@ def convert_colorspace( if not target_colorspace and not all([view, display]): raise ValueError("Both screen and display must be set.") + if additional_command_args: + oiio_cmd.extend(additional_command_args) + if target_colorspace: oiio_cmd.extend(["--colorconvert", source_colorspace, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4a03e623fd..3de404125d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -128,6 +128,9 @@ class ExtractOIIOTranscode(publish.Extractor): if display: new_repre["colorspaceData"]["display"] = display + additional_command_args = (output_def["oiiotool_args"] + ["additional_command_args"]) + files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: @@ -144,6 +147,7 @@ class ExtractOIIOTranscode(publish.Extractor): target_colorspace, view, display, + additional_command_args, self.log ) From e8a79b4f7673a37657b54a62f74c81985a2b5636 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:21:25 +0100 Subject: [PATCH 0873/1271] OP-4642 - refactored newly added representations --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3de404125d..8c4ef59de9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -82,6 +82,7 @@ class ExtractOIIOTranscode(publish.Extractor): if not profile: return + new_representations = [] repres = instance.data.get("representations") or [] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) @@ -174,9 +175,7 @@ class ExtractOIIOTranscode(publish.Extractor): if tag == "review": added_review = True - new_repre["tags"].append("newly_added") - - instance.data["representations"].append(new_repre) + new_representations.append(new_repre) added_representations = True if added_representations: @@ -185,15 +184,11 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] - # TODO implement better way, for now do not delete new repre - # new repre might have 'delete' tag to removed, but it first must - # be there for review to be created - if "newly_added" in tags: - tags.remove("newly_added") - continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) + instance.data["representations"].extend(new_representations) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From e5ec6c4812aa5d5c59a04d834b631afb0917bd2e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:24:53 +0100 Subject: [PATCH 0874/1271] OP-4642 - refactored query of representations line 73 returns if no representations. --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 8c4ef59de9..de36ea7d5f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -83,7 +83,7 @@ class ExtractOIIOTranscode(publish.Extractor): return new_representations = [] - repres = instance.data.get("representations") or [] + repres = instance.data["representations"] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) if not self._repre_is_valid(repre): From 6235faab82af4ac7d3a50315b4c1dc9223a73c0a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 10:44:10 +0100 Subject: [PATCH 0875/1271] OP-4643 - fixed subset filtering Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index de36ea7d5f..71124b527a 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -273,7 +273,7 @@ class ExtractOIIOTranscode(publish.Extractor): "families": family, "task_names": task_name, "task_types": task_type, - "subset": subset + "subsets": subset } profile = filter_profiles(self.profiles, filtering_criteria, logger=self.log) From b304d63461704f7c2e0709bd5165683a0d890a10 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 12:12:35 +0100 Subject: [PATCH 0876/1271] OP-4643 - split command line arguments to separate items Reuse existing method from ExtractReview, put it into transcoding.py --- openpype/lib/transcoding.py | 29 +++++++++++++++++++++- openpype/plugins/publish/extract_review.py | 27 +++----------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 982cee7a46..4d2f72fc41 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,3 +1114,30 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 0f6dacba18..e80141fc4a 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,6 +22,7 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, + split_cmd_args ) @@ -670,7 +671,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) + ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -723,28 +724,6 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) - def split_ffmpeg_args(self, in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args - def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -764,7 +743,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = self.split_ffmpeg_args(output_args) + output_args = split_cmd_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 5d0dc43494452813d19f7ffa511b841e59b64209 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:02:41 +0100 Subject: [PATCH 0877/1271] OP-4643 - refactor - changed existence check --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 71124b527a..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,12 +161,12 @@ class ExtractOIIOTranscode(publish.Extractor): custom_tags = output_def.get("custom_tags") if custom_tags: - if not new_repre.get("custom_tags"): + if new_repre.get("custom_tags") is None: new_repre["custom_tags"] = [] new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation - if not new_repre.get("tags"): + if new_repre.get("tags") is None: new_repre["tags"] = [] for tag in output_def["tags"]: if tag not in new_repre["tags"]: From 8651a693f990d52e43c14845e82efc3033b5a054 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:11:11 +0100 Subject: [PATCH 0878/1271] Revert "Fix - added missed scopes for Slack bot" This reverts commit 5e0c4a3ab1432e120b8f0c324f899070f1a5f831. --- openpype/modules/slack/manifest.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 233c39fbaf..7a65cc5915 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,8 +19,6 @@ oauth_config: - chat:write.public - files:write - channels:read - - users:read - - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From 68313f9215a54d94f4c18119a56679b7c277f937 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:10:11 +0100 Subject: [PATCH 0879/1271] OP-4643 - changed label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5333d514b5..3e9467af61 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -294,7 +294,7 @@ "children": [ { "key": "additional_command_args", - "label": "Additional command line arguments", + "label": "Arguments", "type": "list", "object_type": "text" } From 68a0892a1d20a17f2504382a3c16586de5039108 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 15:06:16 +0100 Subject: [PATCH 0880/1271] OP-4643 - added documentation --- .../assets/global_oiio_transcode.png | Bin 0 -> 29010 bytes .../project_settings/settings_project_global.md | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png new file mode 100644 index 0000000000000000000000000000000000000000..99396d5bb3f16d434a92c6079515128488b82489 GIT binary patch literal 29010 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 literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 37fed93e69..52671d2db6 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -45,6 +45,21 @@ The _input pattern_ matching uses REGEX expression syntax (try [regexr.com](http The **colorspace name** value is a raw string input and no validation is run after saving project settings. We recommend to open the specified `config.ocio` file and copy pasting the exact colorspace names. ::: +### Extract OIIO Transcode +There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +`oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. + +Notable parameters: +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Extension`** - target extension, could be empty - original extension is used +- **`Colorspace`** - target colorspace - must be available in used color config +- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Arguments`** - special additional command line arguments for `oiiotool` + + +Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. +![global_oiio_transcode](assets/global_oiio_transcode.png) ## Profile filters From 265a08abcdf88d3180fc3a1da362351c28083b9b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:41:42 +0100 Subject: [PATCH 0881/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 52671d2db6..cc661a21fa 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,7 +46,7 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. From b2d40c1cc39fbc42b0365a3edc9fb638db5c3584 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:06 +0100 Subject: [PATCH 0882/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index cc661a21fa..8e557a381c 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -52,7 +52,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. -- **`Extension`** - target extension, could be empty - original extension is used +- **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace - must be available in used color config - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From 2d601023f7db52033736e770b6ec3879fda04de5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:29 +0100 Subject: [PATCH 0883/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 8e557a381c..166400cb7f 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,7 +53,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace - must be available in used color config +- **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From 6260ea0a91cce8f1a67707aa55553e6ea6afbcab Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:48 +0100 Subject: [PATCH 0884/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 166400cb7f..908191f122 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -54,7 +54,7 @@ Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. - **`Arguments`** - special additional command line arguments for `oiiotool` From c1c8ca234f97c6b3f64bd91c52f91969656077e1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:43:06 +0100 Subject: [PATCH 0885/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 908191f122..0a73868d2d 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -55,7 +55,7 @@ Notable parameters: - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. -- **`Arguments`** - special additional command line arguments for `oiiotool` +- **`Arguments`** - special additional command line arguments for `oiiotool`. Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. From 92768e004993311fae5e74ccc9e552ba8f171a2c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:21:20 +0100 Subject: [PATCH 0886/1271] OP-4643 - updates to documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- website/docs/project_settings/settings_project_global.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 0a73868d2d..9e2ee187cc 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,8 +46,8 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. -Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +OIIOTools transcoder plugin with configurable output presets. Any incoming representation with `colorspaceData` is convertable to single or multiple representations with different target colorspaces or display and viewer names found in linked **config.ocio** file. + `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: From 94ee02879286ef75ce60001f8c94d818a24aea57 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:56:21 +0100 Subject: [PATCH 0887/1271] Revert "OP-4643 - split command line arguments to separate items" This reverts commit deaad39437501f18fc3ba4be8b1fc5f0ee3be65d. --- openpype/lib/transcoding.py | 29 +--------------------- openpype/plugins/publish/extract_review.py | 27 +++++++++++++++++--- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 4d2f72fc41..982cee7a46 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(split_cmd_args(additional_command_args)) + oiio_cmd.extend(additional_command_args) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,30 +1114,3 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) - - -def split_cmd_args(in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - Args: - in_args (list): of arguments ['-n', '-d uint10'] - Returns - (list): ['-n', '-d', 'unint10'] - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index e80141fc4a..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,7 +22,6 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, - split_cmd_args ) @@ -671,7 +670,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) + ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -724,6 +723,28 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) + def split_ffmpeg_args(self, in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args + def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -743,7 +764,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = split_cmd_args(output_args) + output_args = self.split_ffmpeg_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 840f6811345241dd4e058d2d5940847c5b31c69f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:02:17 +0100 Subject: [PATCH 0888/1271] OP-4643 - different splitting for oiio It seems that logic in ExtractReview does different thing. --- openpype/lib/transcoding.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 982cee7a46..376297ff32 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,3 +1114,21 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + if not arg.strip(): + continue + splitted_args.extend(arg.split(" ")) + return splitted_args From 5a7e84ab90042c3565e5ba13f11cf5674f02e4f8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:14:57 +0100 Subject: [PATCH 0889/1271] OP-4643 - allow colorspace to be empty and collected from DCC --- openpype/plugins/publish/extract_color_transcode.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..82b92ec93e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,7 +118,8 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From 4abac5ec783c29465d4eb6347ffbd87b6315c2df Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 12:22:53 +0100 Subject: [PATCH 0890/1271] OP-4643 - fix colorspace from DCC representation["colorspaceData"]["colorspace"] is only input colorspace --- openpype/plugins/publish/extract_color_transcode.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 82b92ec93e..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,8 +118,7 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = (output_def["colorspace"] or - colorspace_data.get("colorspace")) + target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From 6cb8cbd6fc03b2d63f09bac0b25634cb1ef3f827 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:33:20 +0100 Subject: [PATCH 0891/1271] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- openpype/lib/transcoding.py | 2 +- .../plugins/publish/extract_color_transcode.py | 15 +++++++++++---- .../schemas/schema_global_publish.json | 9 +++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 376297ff32..c0bda2aa37 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1052,7 +1052,7 @@ def convert_colorspace( output_path, config_path, source_colorspace, - target_colorspace, + target_colorspace=None, view=None, display=None, additional_command_args=None, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..b0921688e9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,10 +118,17 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] - view = output_def["view"] or colorspace_data.get("view") - display = (output_def["display"] or - colorspace_data.get("display")) + transcoding_type = output_def["transcoding_type"] + + target_colorspace = view = display = None + if transcoding_type == "colorspace": + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) + else: + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, # but could be overwritten if view: diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3e9467af61..76574e8b9b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -271,6 +271,15 @@ "label": "Extension", "type": "text" }, + { + "type": "enum", + "key": "transcoding_type", + "label": "Transcoding type", + "enum_items": [ + { "colorspace": "Use Colorspace" }, + { "display": "Use Display&View" } + ] + }, { "key": "colorspace", "label": "Colorspace", From 58ae146027ebe31b9fe2d7fcc6d4323efb93c883 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:34:03 +0100 Subject: [PATCH 0892/1271] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- .../assets/global_oiio_transcode.png | Bin 29010 -> 17936 bytes .../settings_project_global.md | 5 +++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png index 99396d5bb3f16d434a92c6079515128488b82489..d818ecfe19f93366e5f4664734d68c7b448d7b4f 100644 GIT binary patch literal 17936 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 DbV~re literal 29010 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 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 9e2ee187cc..6e78ee5d45 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,8 +53,9 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. +- **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. +- **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). +- **`Display & View`** - display and viewer colorspace. (If `Transcoding type` is `Use Display&View` values in configuration is used OR if empty values collected on instance from DCC). - **`Arguments`** - special additional command line arguments for `oiiotool`. From aae1430904962212e3c31345eb317d74cbd2385b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:42:07 +0100 Subject: [PATCH 0893/1271] OP-4643 - added use case for Maya to documentation --- .../assets/global_oiio_transcode2.png | Bin 0 -> 17960 bytes .../project_settings/settings_project_global.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode2.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode2.png b/website/docs/project_settings/assets/global_oiio_transcode2.png new file mode 100644 index 0000000000000000000000000000000000000000..906f780830a96b4bc6f26d98484dd3f9cc3885f2 GIT binary patch literal 17960 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 F{{cSxQDp!C literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 6e78ee5d45..f58d2c2bf2 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -62,6 +62,9 @@ Notable parameters: Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. ![global_oiio_transcode](assets/global_oiio_transcode.png) +Another use case is to transcode in Maya only `beauty` render layers and use collected `Display` and `View` colorspaces from DCC. +![global_oiio_transcode_in_Maya](assets/global_oiio_transcode.png) + ## Profile filters Many of the settings are using a concept of **Profile filters** From 82e4e3e5b76d195be63bfce7017e3cc2ede23704 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 23 Feb 2023 11:05:26 +0100 Subject: [PATCH 0894/1271] OP-4643 - updates to documentation Co-authored-by: Roy Nieterau --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index f58d2c2bf2..d904080ad1 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -51,7 +51,7 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: -- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation loses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. - **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). From 04109103303c436873a1898de53a54735c524f10 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:57:51 +0100 Subject: [PATCH 0895/1271] OP-4643 - added Settings for ExtractColorTranscode --- .../defaults/project_settings/global.json | 4 + .../schemas/schema_global_publish.json | 73 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index cedc2d6876..8485bec67b 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -68,6 +68,10 @@ "output": [] } }, + "ExtractColorTranscode": { + "enabled": true, + "profiles": [] + }, "ExtractReview": { "enabled": true, "profiles": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5388d04bc9..46ae6ba554 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -197,6 +197,79 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractColorTranscode", + "label": "ExtractColorTranscode", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "key": "hosts", + "label": "Host names", + "type": "hosts-enum", + "multiselection": true + }, + { + "key": "task_types", + "label": "Task types", + "type": "task-types-enum" + }, + { + "key": "task_names", + "label": "Task names", + "type": "list", + "object_type": "text" + }, + { + "key": "subsets", + "label": "Subset names", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "ext", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } + } + ] + }, { "type": "dict", "collapsible": true, From 2f1888bbfbd8dbabcd50ed4d48ab2230d810ba53 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:58:51 +0100 Subject: [PATCH 0896/1271] OP-4643 - added ExtractColorTranscode Added method to convert from one colorspace to another to transcoding lib --- openpype/lib/transcoding.py | 53 ++++++++ .../publish/extract_color_transcode.py | 124 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 openpype/plugins/publish/extract_color_transcode.py diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 039255d937..2fc662f2a4 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1045,3 +1045,56 @@ def convert_ffprobe_fps_to_float(value): if divisor == 0.0: return 0.0 return dividend / divisor + + +def convert_colorspace_for_input_paths( + input_paths, + output_dir, + source_color_space, + target_color_space, + logger=None +): + """Convert source files from one color space to another. + + Filenames of input files are kept so make sure that output directory + is not the same directory as input files have. + - This way it can handle gaps and can keep input filenames without handling + frame template + + Args: + input_paths (str): Paths that should be converted. It is expected that + contains single file or image sequence of samy type. + output_dir (str): Path to directory where output will be rendered. + Must not be same as input's directory. + source_color_space (str): ocio valid color space of source files + target_color_space (str): ocio valid target color space + logger (logging.Logger): Logger used for logging. + + """ + if logger is None: + logger = logging.getLogger(__name__) + + input_arg = "-i" + oiio_cmd = [ + get_oiio_tools_path(), + + # Don't add any additional attributes + "--nosoftwareattrib", + "--colorconvert", source_color_space, target_color_space + ] + for input_path in input_paths: + # Prepare subprocess arguments + + oiio_cmd.extend([ + input_arg, input_path, + ]) + + # Add last argument - path to output + base_filename = os.path.basename(input_path) + output_path = os.path.join(output_dir, base_filename) + oiio_cmd.extend([ + "-o", output_path + ]) + + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py new file mode 100644 index 0000000000..58508ab18f --- /dev/null +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -0,0 +1,124 @@ +import pyblish.api + +from openpype.pipeline import publish +from openpype.lib import ( + + is_oiio_supported, +) + +from openpype.lib.transcoding import ( + convert_colorspace_for_input_paths, + get_transcode_temp_directory, +) + +from openpype.lib.profiles_filtering import filter_profiles + + +class ExtractColorTranscode(publish.Extractor): + """ + Extractor to convert colors from one colorspace to different. + """ + + label = "Transcode color spaces" + order = pyblish.api.ExtractorOrder + 0.01 + + optional = True + + # Configurable by Settings + profiles = None + options = None + + def process(self, instance): + if not self.profiles: + self.log.warning("No profiles present for create burnin") + return + + if "representations" not in instance.data: + self.log.warning("No representations, skipping.") + return + + if not is_oiio_supported(): + self.log.warning("OIIO not supported, no transcoding possible.") + return + + colorspace_data = instance.data.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Instance has not colorspace data, skipping") + return + source_color_space = colorspace_data["colorspace"] + + host_name = instance.context.data["hostName"] + family = instance.data["family"] + task_data = instance.data["anatomyData"].get("task", {}) + task_name = task_data.get("name") + task_type = task_data.get("type") + subset = instance.data["subset"] + + filtering_criteria = { + "hosts": host_name, + "families": family, + "task_names": task_name, + "task_types": task_type, + "subset": subset + } + profile = filter_profiles(self.profiles, filtering_criteria, + logger=self.log) + + if not profile: + self.log.info(( + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) + return + + self.log.debug("profile: {}".format(profile)) + + target_colorspace = profile["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(repres): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self.repre_is_valid(repre): + continue + + new_staging_dir = get_transcode_temp_directory() + repre["stagingDir"] = new_staging_dir + files_to_remove = repre["files"] + if not isinstance(files_to_remove, list): + files_to_remove = [files_to_remove] + instance.context.data["cleanupFullPaths"].extend(files_to_remove) + + convert_colorspace_for_input_paths( + repre["files"], + new_staging_dir, + source_color_space, + target_colorspace, + self.log + ) + + def repre_is_valid(self, repre): + """Validation if representation should be processed. + + Args: + repre (dict): Representation which should be checked. + + Returns: + bool: False if can't be processed else True. + """ + + if "review" not in (repre.get("tags") or []): + self.log.info(( + "Representation \"{}\" don't have \"review\" tag. Skipped." + ).format(repre["name"])) + return False + + if not repre.get("files"): + self.log.warning(( + "Representation \"{}\" have empty files. Skipped." + ).format(repre["name"])) + return False + return True From b932994e15ab43e5df93bcf3e81e71622594c6a2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 12:05:57 +0100 Subject: [PATCH 0897/1271] OP-4643 - extractor must run just before ExtractReview Nuke render local is set to 0.01 --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 58508ab18f..5163cd4045 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -20,7 +20,7 @@ class ExtractColorTranscode(publish.Extractor): """ label = "Transcode color spaces" - order = pyblish.api.ExtractorOrder + 0.01 + order = pyblish.api.ExtractorOrder + 0.019 optional = True From 48f24ef17d8929e84ac16868ad2d6a733d47b1f1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:03:22 +0100 Subject: [PATCH 0898/1271] OP-4643 - fix for full file paths --- .../publish/extract_color_transcode.py | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 5163cd4045..6ad7599f2c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,3 +1,4 @@ +import os import pyblish.api from openpype.pipeline import publish @@ -41,13 +42,6 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return - colorspace_data = instance.data.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Instance has not colorspace data, skipping") - return - source_color_space = colorspace_data["colorspace"] - host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) @@ -82,18 +76,32 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self.repre_is_valid(repre): + # if not self.repre_is_valid(repre): + # continue + + colorspace_data = repre.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Repre has not colorspace data, skipping") + continue + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") continue new_staging_dir = get_transcode_temp_directory() + original_staging_dir = repre["stagingDir"] repre["stagingDir"] = new_staging_dir - files_to_remove = repre["files"] - if not isinstance(files_to_remove, list): - files_to_remove = [files_to_remove] - instance.context.data["cleanupFullPaths"].extend(files_to_remove) + files_to_convert = repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + instance.context.data["cleanupFullPaths"].extend(files_to_convert) convert_colorspace_for_input_paths( - repre["files"], + files_to_convert, new_staging_dir, source_color_space, target_colorspace, From ec299f0d3ca379f73e6f06949506148e78ca5fa1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:04:06 +0100 Subject: [PATCH 0899/1271] OP-4643 - pass path for ocio config --- openpype/lib/transcoding.py | 3 +++ openpype/plugins/publish/extract_color_transcode.py | 1 + 2 files changed, 4 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 2fc662f2a4..ab86e44304 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1050,6 +1050,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace_for_input_paths( input_paths, output_dir, + config_path, source_color_space, target_color_space, logger=None @@ -1066,6 +1067,7 @@ def convert_colorspace_for_input_paths( contains single file or image sequence of samy type. output_dir (str): Path to directory where output will be rendered. Must not be same as input's directory. + config_path (str): path to OCIO config file source_color_space (str): ocio valid color space of source files target_color_space (str): ocio valid target color space logger (logging.Logger): Logger used for logging. @@ -1080,6 +1082,7 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", + "--colorconfig", config_path, "--colorconvert", source_color_space, target_color_space ] for input_path in input_paths: diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 6ad7599f2c..fdb13a47e8 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -103,6 +103,7 @@ class ExtractColorTranscode(publish.Extractor): convert_colorspace_for_input_paths( files_to_convert, new_staging_dir, + config_path, source_color_space, target_colorspace, self.log From 2bc8377dbcf856012489a49051da9952ab75546b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:15:33 +0100 Subject: [PATCH 0900/1271] OP-4643 - add custom_tags --- openpype/plugins/publish/extract_color_transcode.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index fdb13a47e8..ab932b2476 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -72,6 +72,7 @@ class ExtractColorTranscode(publish.Extractor): target_colorspace = profile["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") + custom_tags = profile["custom_tags"] repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): @@ -109,6 +110,11 @@ class ExtractColorTranscode(publish.Extractor): self.log ) + if custom_tags: + if not repre.get("custom_tags"): + repre["custom_tags"] = [] + repre["custom_tags"].extend(custom_tags) + def repre_is_valid(self, repre): """Validation if representation should be processed. From 4a80b7bb34efdf1dbd7c8f554d42f1caff035385 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:18:38 +0100 Subject: [PATCH 0901/1271] OP-4643 - added docstring --- openpype/plugins/publish/extract_color_transcode.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index ab932b2476..88e2eed90f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -18,6 +18,17 @@ from openpype.lib.profiles_filtering import filter_profiles class ExtractColorTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. + + Expects "colorspaceData" on representation. This dictionary is collected + previously and denotes that representation files should be converted. + This dict contains source colorspace information, collected by hosts. + + Target colorspace is selected by profiles in the Settings, based on: + - families + - host + - task types + - task names + - subset names """ label = "Transcode color spaces" From f92c74605b793db31bbee90ccbed656571e69c39 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:15:44 +0100 Subject: [PATCH 0902/1271] OP-4643 - updated Settings schema --- .../schemas/schema_global_publish.json | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 46ae6ba554..c2c911d7d6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -246,24 +246,44 @@ "type": "list", "object_type": "text" }, + { + "type": "boolean", + "key": "delete_original", + "label": "Delete Original Representation" + }, { "type": "splitter" }, { - "key": "ext", - "label": "Output extension", - "type": "text" - }, - { - "key": "output_colorspace", - "label": "Output colorspace", - "type": "text" - }, - { - "key": "custom_tags", - "label": "Custom Tags", - "type": "list", - "object_type": "text" + "key": "outputs", + "label": "Output Definitions", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": { + "type": "dict", + "children": [ + { + "key": "output_extension", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "type": "schema", + "name": "schema_representation_tags" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } } ] } From 2f79021aca117bf3ebea7a2bd1103a6c7e958525 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:17:25 +0100 Subject: [PATCH 0903/1271] OP-4643 - skip video files Only frames currently supported. --- .../plugins/publish/extract_color_transcode.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 88e2eed90f..a0714c9a33 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -36,6 +36,9 @@ class ExtractColorTranscode(publish.Extractor): optional = True + # Supported extensions + supported_exts = ["exr", "jpg", "jpeg", "png", "dpx"] + # Configurable by Settings profiles = None options = None @@ -88,13 +91,7 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - # if not self.repre_is_valid(repre): - # continue - - colorspace_data = repre.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Repre has not colorspace data, skipping") + if not self._repre_is_valid(repre): continue source_color_space = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") @@ -136,9 +133,9 @@ class ExtractColorTranscode(publish.Extractor): bool: False if can't be processed else True. """ - if "review" not in (repre.get("tags") or []): - self.log.info(( - "Representation \"{}\" don't have \"review\" tag. Skipped." + if repre.get("ext") not in self.supported_exts: + self.log.warning(( + "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False From e63dc4075629efe7499a510742c61a0f5e9c46fd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:19:08 +0100 Subject: [PATCH 0904/1271] OP-4643 - refactored profile, delete of original Implemented multiple outputs from single input representation --- .../publish/extract_color_transcode.py | 156 ++++++++++++------ 1 file changed, 109 insertions(+), 47 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index a0714c9a33..b0c851d5f4 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,4 +1,6 @@ import os +import copy + import pyblish.api from openpype.pipeline import publish @@ -56,13 +58,94 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return + profile = self._get_profile(instance) + if not profile: + return + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(list(repres)): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self._repre_is_valid(repre): + continue + + colorspace_data = repre["colorspaceData"] + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") + continue + + repre = self._handle_original_repre(repre, profile) + + for _, output_def in profile.get("outputs", {}).items(): + new_repre = copy.deepcopy(repre) + + new_staging_dir = get_transcode_temp_directory() + original_staging_dir = new_repre["stagingDir"] + new_repre["stagingDir"] = new_staging_dir + files_to_convert = new_repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + + files_to_delete = copy.deepcopy(files_to_convert) + + output_extension = output_def["output_extension"] + files_to_convert = self._rename_output_files(files_to_convert, + output_extension) + + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + + target_colorspace = output_def["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + convert_colorspace_for_input_paths( + files_to_convert, + new_staging_dir, + config_path, + source_color_space, + target_colorspace, + self.log + ) + + instance.context.data["cleanupFullPaths"].extend( + files_to_delete) + + custom_tags = output_def.get("custom_tags") + if custom_tags: + if not new_repre.get("custom_tags"): + new_repre["custom_tags"] = [] + new_repre["custom_tags"].extend(custom_tags) + + # Add additional tags from output definition to representation + for tag in output_def["tags"]: + if tag not in new_repre["tags"]: + new_repre["tags"].append(tag) + + instance.data["representations"].append(new_repre) + + def _rename_output_files(self, files_to_convert, output_extension): + """Change extension of converted files.""" + if output_extension: + output_extension = output_extension.replace('.', '') + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files + return files_to_convert + + def _get_profile(self, instance): + """Returns profile if and how repre should be color transcoded.""" host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) task_name = task_data.get("name") task_type = task_data.get("type") subset = instance.data["subset"] - filtering_criteria = { "hosts": host_name, "families": family, @@ -75,55 +158,15 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) - return + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) + return profile - target_colorspace = profile["output_colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") - custom_tags = profile["custom_tags"] - - repres = instance.data.get("representations") or [] - for idx, repre in enumerate(repres): - self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self._repre_is_valid(repre): - continue - source_color_space = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): - self.log.warning("Config file doesn't exist, skipping") - continue - - new_staging_dir = get_transcode_temp_directory() - original_staging_dir = repre["stagingDir"] - repre["stagingDir"] = new_staging_dir - files_to_convert = repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - instance.context.data["cleanupFullPaths"].extend(files_to_convert) - - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) - - if custom_tags: - if not repre.get("custom_tags"): - repre["custom_tags"] = [] - repre["custom_tags"].extend(custom_tags) - - def repre_is_valid(self, repre): + def _repre_is_valid(self, repre): """Validation if representation should be processed. Args: @@ -144,4 +187,23 @@ class ExtractColorTranscode(publish.Extractor): "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False + + if not repre.get("colorspaceData"): + self.log.warning("Repre has not colorspace data, skipping") + return False + return True + + def _handle_original_repre(self, repre, profile): + delete_original = profile["delete_original"] + + if delete_original: + if not repre.get("tags"): + repre["tags"] = [] + + if "review" in repre["tags"]: + repre["tags"].remove("review") + if "delete" not in repre["tags"]: + repre["tags"].append("delete") + + return repre From 7341c618274ccdc8f469473ff05698499f6d5b72 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:23:01 +0100 Subject: [PATCH 0905/1271] OP-4643 - switched logging levels Do not use warning unnecessary. --- openpype/plugins/publish/extract_color_transcode.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index b0c851d5f4..4d38514b8b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -47,11 +47,11 @@ class ExtractColorTranscode(publish.Extractor): def process(self, instance): if not self.profiles: - self.log.warning("No profiles present for create burnin") + self.log.debug("No profiles present for color transcode") return if "representations" not in instance.data: - self.log.warning("No representations, skipping.") + self.log.debug("No representations, skipping.") return if not is_oiio_supported(): @@ -177,19 +177,19 @@ class ExtractColorTranscode(publish.Extractor): """ if repre.get("ext") not in self.supported_exts: - self.log.warning(( + self.log.debug(( "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): - self.log.warning(( + self.log.debug(( "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.warning("Repre has not colorspace data, skipping") + self.log.debug("Repre has no colorspace data. Skipped.") return False return True From d5cc450e9cd18f7360d65140e48ea5eab810c8e2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:14 +0100 Subject: [PATCH 0906/1271] OP-4643 - propagate new extension to representation --- .../publish/extract_color_transcode.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4d38514b8b..62cf8f0dee 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -90,8 +90,13 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) output_extension = output_def["output_extension"] - files_to_convert = self._rename_output_files(files_to_convert, - output_extension) + output_extension = output_extension.replace('.', '') + if output_extension: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + files_to_convert = self._rename_output_files( + files_to_convert, output_extension) files_to_convert = [os.path.join(original_staging_dir, path) for path in files_to_convert] @@ -127,15 +132,13 @@ class ExtractColorTranscode(publish.Extractor): def _rename_output_files(self, files_to_convert, output_extension): """Change extension of converted files.""" - if output_extension: - output_extension = output_extension.replace('.', '') - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files return files_to_convert def _get_profile(self, instance): From 65b454c42c77fb75afc06dd34cf36c40ea52a751 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:35 +0100 Subject: [PATCH 0907/1271] OP-4643 - added label to Settings --- .../projects_schema/schemas/schema_global_publish.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index c2c911d7d6..7155510fef 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -201,10 +201,14 @@ "type": "dict", "collapsible": true, "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode", + "label": "ExtractColorTranscode (ImageIO)", "checkbox_key": "enabled", "is_group": true, "children": [ + { + "type": "label", + "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + }, { "type": "boolean", "key": "enabled", From 9fc4070e066499a89898c450263119dbf8e2f99a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jan 2023 18:22:08 +0100 Subject: [PATCH 0908/1271] OP-4643 - refactored according to review Function turned into single filepath input. --- openpype/lib/transcoding.py | 43 ++++++----- .../publish/extract_color_transcode.py | 72 ++++++++++--------- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index ab86e44304..e1bd22d109 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1047,12 +1047,12 @@ def convert_ffprobe_fps_to_float(value): return dividend / divisor -def convert_colorspace_for_input_paths( - input_paths, - output_dir, +def convert_colorspace( + input_path, + out_filepath, config_path, - source_color_space, - target_color_space, + source_colorspace, + target_colorspace, logger=None ): """Convert source files from one color space to another. @@ -1063,13 +1063,13 @@ def convert_colorspace_for_input_paths( frame template Args: - input_paths (str): Paths that should be converted. It is expected that + input_path (str): Paths that should be converted. It is expected that contains single file or image sequence of samy type. - output_dir (str): Path to directory where output will be rendered. + out_filepath (str): Path to directory where output will be rendered. Must not be same as input's directory. config_path (str): path to OCIO config file - source_color_space (str): ocio valid color space of source files - target_color_space (str): ocio valid target color space + source_colorspace (str): ocio valid color space of source files + target_colorspace (str): ocio valid target color space logger (logging.Logger): Logger used for logging. """ @@ -1083,21 +1083,18 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_color_space, target_color_space + "--colorconvert", source_colorspace, target_colorspace ] - for input_path in input_paths: - # Prepare subprocess arguments + # Prepare subprocess arguments - oiio_cmd.extend([ - input_arg, input_path, - ]) + oiio_cmd.extend([ + input_arg, input_path, + ]) - # Add last argument - path to output - base_filename = os.path.basename(input_path) - output_path = os.path.join(output_dir, base_filename) - oiio_cmd.extend([ - "-o", output_path - ]) + # Add last argument - path to output + oiio_cmd.extend([ + "-o", out_filepath + ]) - logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) - run_subprocess(oiio_cmd, logger=logger) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 62cf8f0dee..3a05426432 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -10,7 +10,7 @@ from openpype.lib import ( ) from openpype.lib.transcoding import ( - convert_colorspace_for_input_paths, + convert_colorspace, get_transcode_temp_directory, ) @@ -69,7 +69,7 @@ class ExtractColorTranscode(publish.Extractor): continue colorspace_data = repre["colorspaceData"] - source_color_space = colorspace_data["colorspace"] + source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") if not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") @@ -80,8 +80,8 @@ class ExtractColorTranscode(publish.Extractor): for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) - new_staging_dir = get_transcode_temp_directory() original_staging_dir = new_repre["stagingDir"] + new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir files_to_convert = new_repre["files"] if not isinstance(files_to_convert, list): @@ -92,27 +92,28 @@ class ExtractColorTranscode(publish.Extractor): output_extension = output_def["output_extension"] output_extension = output_extension.replace('.', '') if output_extension: - new_repre["name"] = output_extension + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension new_repre["ext"] = output_extension - files_to_convert = self._rename_output_files( - files_to_convert, output_extension) - - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - target_colorspace = output_def["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) + for file_name in files_to_convert: + input_filepath = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_filepath, + new_staging_dir, + output_extension) + convert_colorspace( + input_filepath, + output_path, + config_path, + source_colorspace, + target_colorspace, + self.log + ) instance.context.data["cleanupFullPaths"].extend( files_to_delete) @@ -130,16 +131,16 @@ class ExtractColorTranscode(publish.Extractor): instance.data["representations"].append(new_repre) - def _rename_output_files(self, files_to_convert, output_extension): - """Change extension of converted files.""" - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files - return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, + output_extension): + """Create output file name path.""" + file_name = os.path.basename(input_filepath) + file_name, input_extension = os.path.splitext(file_name) + if not output_extension: + output_extension = input_extension + new_file_name = '{}.{}'.format(file_name, + output_extension) + return os.path.join(output_dir, new_file_name) def _get_profile(self, instance): """Returns profile if and how repre should be color transcoded.""" @@ -161,10 +162,10 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) return profile @@ -181,18 +182,19 @@ class ExtractColorTranscode(publish.Extractor): if repre.get("ext") not in self.supported_exts: self.log.debug(( - "Representation \"{}\" of unsupported extension. Skipped." + "Representation '{}' of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): self.log.debug(( - "Representation \"{}\" have empty files. Skipped." + "Representation '{}' have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.debug("Repre has no colorspace data. Skipped.") + self.log.debug("Representation '{}' has no colorspace data. " + "Skipped.") return False return True From 5eb771333b9d0b0a7b2abf5a2487284f5043f478 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:53:02 +0100 Subject: [PATCH 0909/1271] OP-4643 - updated schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 7155510fef..80c18ce118 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -267,8 +267,8 @@ "type": "dict", "children": [ { - "key": "output_extension", - "label": "Output extension", + "key": "extension", + "label": "Extension", "type": "text" }, { From 49a06f873bc1a77e1dde35a9e6c7f1603508e6e4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:54:46 +0100 Subject: [PATCH 0910/1271] OP-4643 - updated plugin name in schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 80c18ce118..357cbfb287 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -200,8 +200,8 @@ { "type": "dict", "collapsible": true, - "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode (ImageIO)", + "key": "ExtractOIIOTranscode", + "label": "Extract OIIO Transcode", "checkbox_key": "enabled", "is_group": true, "children": [ From 2ec5221b282b4222f5ac179f63f5650050f3b331 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:55:57 +0100 Subject: [PATCH 0911/1271] OP-4643 - updated key in schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 357cbfb287..0281b0ded6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -272,8 +272,8 @@ "type": "text" }, { - "key": "output_colorspace", - "label": "Output colorspace", + "key": "colorspace", + "label": "Colorspace", "type": "text" }, { From 83d21d9d7793350d6374c2c240f4f70e688c2e88 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:57:03 +0100 Subject: [PATCH 0912/1271] OP-4643 - changed oiio_cmd creation Co-authored-by: Toke Jepsen --- openpype/lib/transcoding.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index e1bd22d109..f22628dd28 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1076,25 +1076,15 @@ def convert_colorspace( if logger is None: logger = logging.getLogger(__name__) - input_arg = "-i" oiio_cmd = [ get_oiio_tools_path(), - + input_path, # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace - ] - # Prepare subprocess arguments - - oiio_cmd.extend([ - input_arg, input_path, - ]) - - # Add last argument - path to output - oiio_cmd.extend([ + "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath - ]) + ] logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From 40f8cd4a93bfe76dcb91ee683c78033417f40525 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:44:45 +0100 Subject: [PATCH 0913/1271] OP-4643 - updated new keys into settings --- .../settings/defaults/project_settings/global.json | 2 +- .../projects_schema/schemas/schema_global_publish.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 8485bec67b..a5e2d25a88 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -68,7 +68,7 @@ "output": [] } }, - "ExtractColorTranscode": { + "ExtractOIIOTranscode": { "enabled": true, "profiles": [] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 0281b0ded6..74b81b13af 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -276,6 +276,16 @@ "label": "Colorspace", "type": "text" }, + { + "key": "display", + "label": "Display", + "type": "text" + }, + { + "key": "view", + "label": "View", + "type": "text" + }, { "type": "schema", "name": "schema_representation_tags" From e64389f11be2d072e9d8d59dce5334eebb727776 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:45:42 +0100 Subject: [PATCH 0914/1271] OP-4643 - renanmed plugin, added new keys into outputs --- openpype/plugins/publish/extract_color_transcode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3a05426432..cc63b35988 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -17,7 +17,7 @@ from openpype.lib.transcoding import ( from openpype.lib.profiles_filtering import filter_profiles -class ExtractColorTranscode(publish.Extractor): +class ExtractOIIOTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. @@ -89,14 +89,14 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) - output_extension = output_def["output_extension"] + output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: if new_repre["name"] == new_repre["ext"]: new_repre["name"] = output_extension new_repre["ext"] = output_extension - target_colorspace = output_def["output_colorspace"] + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From 99d687c9a1366984116b5ef43d65cab465886b12 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:03:13 +0100 Subject: [PATCH 0915/1271] OP-4643 - fixed config path key --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cc63b35988..245faeb306 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -70,8 +70,8 @@ class ExtractOIIOTranscode(publish.Extractor): colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): + config_path = colorspace_data.get("config", {}).get("path") + if not config_path or not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") continue From 2a7fd01aada28eebf603e6bd8cc6d6b1a8a560a6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:03:42 +0100 Subject: [PATCH 0916/1271] OP-4643 - fixed renaming files --- openpype/plugins/publish/extract_color_transcode.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 245faeb306..c079dcf70e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -96,6 +96,14 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["name"] = output_extension new_repre["ext"] = output_extension + renamed_files = [] + _, orig_ext = os.path.splitext(files_to_convert[0]) + for file_name in files_to_convert: + file_name = file_name.replace(orig_ext, + "."+output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From 34d519524e9998c5436baf5b53f8740bf2eceff3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:04:44 +0100 Subject: [PATCH 0917/1271] OP-4643 - updated to calculate sequence format --- .../publish/extract_color_transcode.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c079dcf70e..09c86909cb 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,5 +1,6 @@ import os import copy +import clique import pyblish.api @@ -108,6 +109,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not target_colorspace: raise RuntimeError("Target colorspace must be set") + files_to_convert = self._translate_to_sequence( + files_to_convert) for file_name in files_to_convert: input_filepath = os.path.join(original_staging_dir, file_name) @@ -139,6 +142,40 @@ class ExtractOIIOTranscode(publish.Extractor): instance.data["representations"].append(new_repre) + def _translate_to_sequence(self, files_to_convert): + """Returns original list of files or single sequence format filename. + + Uses clique to find frame sequence, in this case it merges all frames + into sequence format (%0X) and returns it. + If sequence not found, it returns original list + + Args: + files_to_convert (list): list of file names + Returns: + (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + """ + pattern = [clique.PATTERNS["frames"]] + collections, remainder = clique.assemble( + files_to_convert, patterns=pattern, + assume_padded_when_ambiguous=True) + + if collections: + if len(collections) > 1: + raise ValueError( + "Too many collections {}".format(collections)) + + collection = collections[0] + padding = collection.padding + padding_str = "%0{}".format(padding) + frames = list(collection.indexes) + frame_str = "{}-{}#".format(frames[0], frames[-1]) + file_name = "{}{}{}".format(collection.head, frame_str, + collection.tail) + + files_to_convert = [file_name] + + return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, output_extension): """Create output file name path.""" From be176bbeb2feb40751be9c208fb4b0dd236f66ae Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:54:02 +0100 Subject: [PATCH 0918/1271] OP-4643 - implemented display and viewer color space --- openpype/lib/transcoding.py | 23 +++++++++++++++++-- .../publish/extract_color_transcode.py | 13 +++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index f22628dd28..cc9cd4e1eb 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1053,6 +1053,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, + view, + display, logger=None ): """Convert source files from one color space to another. @@ -1070,8 +1072,11 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + view (str): name for viewer space (ocio valid) + display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. - + Raises: + ValueError: if misconfigured """ if logger is None: logger = logging.getLogger(__name__) @@ -1082,9 +1087,23 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath ] + if all([target_colorspace, view, display]): + raise ValueError("Colorspace and both screen and display" + " cannot be set together." + "Choose colorspace or screen and display") + if not target_colorspace and not all([view, display]): + raise ValueError("Both screen and display must be set.") + + if target_colorspace: + oiio_cmd.extend(["--colorconvert", + source_colorspace, + target_colorspace]) + if view and display: + oiio_cmd.extend(["--iscolorspace", source_colorspace]) + oiio_cmd.extend(["--ociodisplay", display, view]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 09c86909cb..cd8421c0cd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -106,8 +106,15 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["files"] = renamed_files target_colorspace = output_def["colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, + # but could be overwritten + if view: + new_repre["colorspaceData"]["view"] = view + if display: + new_repre["colorspaceData"]["view"] = display files_to_convert = self._translate_to_sequence( files_to_convert) @@ -123,6 +130,8 @@ class ExtractOIIOTranscode(publish.Extractor): config_path, source_colorspace, target_colorspace, + view, + display, self.log ) From 3dba4f3eb14c545038bb340614023f974c2c405a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 19:03:10 +0100 Subject: [PATCH 0919/1271] OP-4643 - fix wrong order of deletion of representation --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cd8421c0cd..9cca5cc969 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -69,6 +69,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not self._repre_is_valid(repre): continue + added_representations = False + colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("config", {}).get("path") @@ -76,8 +78,6 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - repre = self._handle_original_repre(repre, profile) - for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) @@ -150,6 +150,10 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["tags"].append(tag) instance.data["representations"].append(new_repre) + added_representations = True + + if added_representations: + self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): """Returns original list of files or single sequence format filename. @@ -253,7 +257,8 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _handle_original_repre(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile): + """If new transcoded representation created, delete old.""" delete_original = profile["delete_original"] if delete_original: @@ -264,5 +269,3 @@ class ExtractOIIOTranscode(publish.Extractor): repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") - - return repre From 7e9d707226dd1956e457e1d29a0d2df58334e26d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:26:27 +0100 Subject: [PATCH 0920/1271] OP-4643 - updated docstring, standardized arguments --- openpype/lib/transcoding.py | 19 +++++++---------- .../publish/extract_color_transcode.py | 21 +++++++++---------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index cc9cd4e1eb..0f6d35affe 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1049,7 +1049,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace( input_path, - out_filepath, + output_path, config_path, source_colorspace, target_colorspace, @@ -1057,18 +1057,13 @@ def convert_colorspace( display, logger=None ): - """Convert source files from one color space to another. - - Filenames of input files are kept so make sure that output directory - is not the same directory as input files have. - - This way it can handle gaps and can keep input filenames without handling - frame template + """Convert source file from one color space to another. Args: - input_path (str): Paths that should be converted. It is expected that - contains single file or image sequence of samy type. - out_filepath (str): Path to directory where output will be rendered. - Must not be same as input's directory. + input_path (str): Path that should be converted. It is expected that + contains single file or image sequence of same type + (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + output_path (str): Path to output filename. config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space @@ -1087,7 +1082,7 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "-o", out_filepath + "-o", output_path ] if all([target_colorspace, view, display]): diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 9cca5cc969..c4cef15ea6 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -119,13 +119,13 @@ class ExtractOIIOTranscode(publish.Extractor): files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: - input_filepath = os.path.join(original_staging_dir, - file_name) - output_path = self._get_output_file_path(input_filepath, + input_path = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_path, new_staging_dir, output_extension) convert_colorspace( - input_filepath, + input_path, output_path, config_path, source_colorspace, @@ -156,16 +156,17 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): - """Returns original list of files or single sequence format filename. + """Returns original list or list with filename formatted in single + sequence format. Uses clique to find frame sequence, in this case it merges all frames - into sequence format (%0X) and returns it. + into sequence format (FRAMESTART-FRAMEEND#) and returns it. If sequence not found, it returns original list Args: files_to_convert (list): list of file names Returns: - (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + (list) of [file.1001-1010#.exr] or [fileA.exr, fileB.exr] """ pattern = [clique.PATTERNS["frames"]] collections, remainder = clique.assemble( @@ -178,8 +179,6 @@ class ExtractOIIOTranscode(publish.Extractor): "Too many collections {}".format(collections)) collection = collections[0] - padding = collection.padding - padding_str = "%0{}".format(padding) frames = list(collection.indexes) frame_str = "{}-{}#".format(frames[0], frames[-1]) file_name = "{}{}{}".format(collection.head, frame_str, @@ -189,10 +188,10 @@ class ExtractOIIOTranscode(publish.Extractor): return files_to_convert - def _get_output_file_path(self, input_filepath, output_dir, + def _get_output_file_path(self, input_path, output_dir, output_extension): """Create output file name path.""" - file_name = os.path.basename(input_filepath) + file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: output_extension = input_extension From c50d9917a4428270db3cc0797da1c4f941d2022e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:27:06 +0100 Subject: [PATCH 0921/1271] OP-4643 - fix wrong assignment --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c4cef15ea6..4e899a519c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -114,7 +114,7 @@ class ExtractOIIOTranscode(publish.Extractor): if view: new_repre["colorspaceData"]["view"] = view if display: - new_repre["colorspaceData"]["view"] = display + new_repre["colorspaceData"]["display"] = display files_to_convert = self._translate_to_sequence( files_to_convert) From f226dc60cf055d836614b540b0eca31611f568ce Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:59:13 +0100 Subject: [PATCH 0922/1271] OP-4643 - fix files to delete --- .../publish/extract_color_transcode.py | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4e899a519c..99e684ba21 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -84,26 +84,18 @@ class ExtractOIIOTranscode(publish.Extractor): original_staging_dir = new_repre["stagingDir"] new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir - files_to_convert = new_repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_delete = copy.deepcopy(files_to_convert) + if isinstance(new_repre["files"], list): + files_to_convert = copy.deepcopy(new_repre["files"]) + else: + files_to_convert = [new_repre["files"]] output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension - new_repre["ext"] = output_extension - - renamed_files = [] - _, orig_ext = os.path.splitext(files_to_convert[0]) - for file_name in files_to_convert: - file_name = file_name.replace(orig_ext, - "."+output_extension) - renamed_files.append(file_name) - new_repre["files"] = renamed_files + self._rename_in_representation(new_repre, + files_to_convert, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -135,8 +127,12 @@ class ExtractOIIOTranscode(publish.Extractor): self.log ) - instance.context.data["cleanupFullPaths"].extend( - files_to_delete) + # cleanup temporary transcoded files + for file_name in new_repre["files"]: + transcoded_file_path = os.path.join(new_staging_dir, + file_name) + instance.context.data["cleanupFullPaths"].append( + transcoded_file_path) custom_tags = output_def.get("custom_tags") if custom_tags: @@ -155,6 +151,21 @@ class ExtractOIIOTranscode(publish.Extractor): if added_representations: self._mark_original_repre_for_deletion(repre, profile) + def _rename_in_representation(self, new_repre, files_to_convert, + output_extension): + """Replace old extension with new one everywhere in representation.""" + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + def _translate_to_sequence(self, files_to_convert): """Returns original list or list with filename formatted in single sequence format. From 97a2014c125d7b8f766754e14ac4d74889a5f469 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:17:59 +0100 Subject: [PATCH 0923/1271] OP-4643 - moved output argument to the end --- openpype/lib/transcoding.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 0f6d35affe..e74dab4ccc 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1081,8 +1081,7 @@ def convert_colorspace( input_path, # Don't add any additional attributes "--nosoftwareattrib", - "--colorconfig", config_path, - "-o", output_path + "--colorconfig", config_path ] if all([target_colorspace, view, display]): @@ -1100,5 +1099,7 @@ def convert_colorspace( oiio_cmd.extend(["--iscolorspace", source_colorspace]) oiio_cmd.extend(["--ociodisplay", display, view]) + oiio_cmd.extend(["-o", output_path]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From 3d2f4319369d6459263cd79e69bec3898ee86efa Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:18:33 +0100 Subject: [PATCH 0924/1271] OP-4643 - fix no tags in repre --- openpype/plugins/publish/extract_color_transcode.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 99e684ba21..3d897c6d9f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -142,6 +142,8 @@ class ExtractOIIOTranscode(publish.Extractor): # Add additional tags from output definition to representation for tag in output_def["tags"]: + if not new_repre.get("tags"): + new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) From 016111ab3381e2ba6c9b5b47fe361a25208c1a6b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:26:07 +0100 Subject: [PATCH 0925/1271] OP-4643 - changed docstring Elaborated more that 'target_colorspace' and ('view', 'display') are disjunctive. --- openpype/lib/transcoding.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index e74dab4ccc..f7d5e222c8 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1053,8 +1053,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, - view, - display, + view=None, + display=None, logger=None ): """Convert source file from one color space to another. @@ -1067,7 +1067,9 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + if filled, 'view' and 'display' must be empty view (str): name for viewer space (ocio valid) + both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. Raises: From e1d68ec387572f180844ca8677110cc32d8cf9df Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 11:14:07 +0100 Subject: [PATCH 0926/1271] OP-4663 - fix double dots in extension Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3d897c6d9f..bfed69c300 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -207,7 +207,7 @@ class ExtractOIIOTranscode(publish.Extractor): file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: - output_extension = input_extension + output_extension = input_extension.replace(".", "") new_file_name = '{}.{}'.format(file_name, output_extension) return os.path.join(output_dir, new_file_name) From b5246cdf6587975cec5638666e82196e84854de3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:11:45 +0100 Subject: [PATCH 0927/1271] OP-4643 - update documentation in Settings schema --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 74b81b13af..3956f403f4 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -207,7 +207,7 @@ "children": [ { "type": "label", - "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + "label": "Configure Output Definition(s) for new representation(s). \nEmpty 'Extension' denotes keeping source extension. \nName(key) of output definition will be used as new representation name \nunless 'passthrough' value is used to keep existing name. \nFill either 'Colorspace' (for target colorspace) or \nboth 'Display' and 'View' (for display and viewer colorspaces)." }, { "type": "boolean", From b4085288c34f0a035f5be5a536bb6cfdcc4f1a2c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:13:59 +0100 Subject: [PATCH 0928/1271] OP-4643 - name of new representation from output definition key --- .../publish/extract_color_transcode.py | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index bfed69c300..e39ea3add9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -32,6 +32,25 @@ class ExtractOIIOTranscode(publish.Extractor): - task types - task names - subset names + + Can produce one or more representations (with different extensions) based + on output definition in format: + "output_name: { + "extension": "png", + "colorspace": "ACES - ACEScg", + "display": "", + "view": "", + "tags": [], + "custom_tags": [] + } + + If 'extension' is empty original representation extension is used. + 'output_name' will be used as name of new representation. In case of value + 'passthrough' name of original representation will be used. + + 'colorspace' denotes target colorspace to be transcoded into. Could be + empty if transcoding should be only into display and viewer colorspace. + (In that case both 'display' and 'view' must be filled.) """ label = "Transcode color spaces" @@ -78,7 +97,7 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - for _, output_def in profile.get("outputs", {}).items(): + for output_name, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) original_staging_dir = new_repre["stagingDir"] @@ -92,10 +111,10 @@ class ExtractOIIOTranscode(publish.Extractor): output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') - if output_extension: - self._rename_in_representation(new_repre, - files_to_convert, - output_extension) + self._rename_in_representation(new_repre, + files_to_convert, + output_name, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -154,10 +173,22 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _rename_in_representation(self, new_repre, files_to_convert, - output_extension): - """Replace old extension with new one everywhere in representation.""" - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension + output_name, output_extension): + """Replace old extension with new one everywhere in representation. + + Args: + new_repre (dict) + files_to_convert (list): of filenames from repre["files"], + standardized to always list + output_name (str): key of output definition from Settings, + if "" token used, keep original repre name + output_extension (str): extension from output definition + """ + if output_name != "passthrough": + new_repre["name"] = output_name + if not output_extension: + return + new_repre["ext"] = output_extension renamed_files = [] From 925c7a9564fa1e2c8cc61d025de35d75af155939 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:42:48 +0100 Subject: [PATCH 0929/1271] OP-4643 - updated docstring for convert_colorspace --- openpype/lib/transcoding.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index f7d5e222c8..b6edd863f8 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1062,8 +1062,11 @@ def convert_colorspace( Args: input_path (str): Path that should be converted. It is expected that contains single file or image sequence of same type - (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + (sequence in format 'file.FRAMESTART-FRAMEEND#.ext', see oiio docs, + eg `big.1-3#.tif`) output_path (str): Path to output filename. + (must follow format of 'input_path', eg. single file or + sequence in 'file.FRAMESTART-FRAMEEND#.ext', `output.1-3#.tif`) config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space From d96775867a333566ff0681dbdb86688c3bda7f3d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:22:10 +0100 Subject: [PATCH 0930/1271] OP-4643 - remove review from old representation If new representation gets created and adds 'review' tag it becomes new reviewable representation. --- .../publish/extract_color_transcode.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index e39ea3add9..d10b887a0b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -89,6 +89,7 @@ class ExtractOIIOTranscode(publish.Extractor): continue added_representations = False + added_review = False colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] @@ -166,11 +167,15 @@ class ExtractOIIOTranscode(publish.Extractor): if tag not in new_repre["tags"]: new_repre["tags"].append(tag) + if tag == "review": + added_review = True + instance.data["representations"].append(new_repre) added_representations = True if added_representations: - self._mark_original_repre_for_deletion(repre, profile) + self._mark_original_repre_for_deletion(repre, profile, + added_review) def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): @@ -300,15 +305,16 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _mark_original_repre_for_deletion(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile, added_review): """If new transcoded representation created, delete old.""" + if not repre.get("tags"): + repre["tags"] = [] + delete_original = profile["delete_original"] if delete_original: - if not repre.get("tags"): - repre["tags"] = [] - - if "review" in repre["tags"]: - repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") + + if added_review and "review" in repre["tags"]: + repre["tags"].remove("review") From 7540f61791958b6043ad1a466900a41fc8b27e4c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:23:42 +0100 Subject: [PATCH 0931/1271] OP-4643 - remove representation that should be deleted Or old revieable representation would be reviewed too. --- openpype/plugins/publish/extract_color_transcode.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index d10b887a0b..93ee1ec44d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -177,6 +177,11 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile, added_review) + for repre in tuple(instance.data["representations"]): + tags = repre.get("tags") or [] + if "delete" in tags and "thumbnail" not in tags: + instance.data["representations"].remove(repre) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From 82b44da739625242d4e2a0ffddec317cad25e806 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 14:54:25 +0100 Subject: [PATCH 0932/1271] OP-4643 - fix logging Wrong variable used --- openpype/plugins/publish/extract_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index dcb43d7fa2..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -169,7 +169,7 @@ class ExtractReview(pyblish.api.InstancePlugin): "Skipped representation. All output definitions from" " selected profile does not match to representation's" " custom tags. \"{}\"" - ).format(str(tags))) + ).format(str(custom_tags))) continue outputs_per_representations.append((repre, outputs)) From 1d12316ee18889a2b18e02db06edf082e6a61d70 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 15:14:14 +0100 Subject: [PATCH 0933/1271] OP-4643 - allow new repre to stay One might want to delete outputs with 'delete' tag, but repre must stay there at least until extract_review. More universal new tag might be created for this. --- openpype/plugins/publish/extract_color_transcode.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 93ee1ec44d..4a03e623fd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,15 +161,17 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation + if not new_repre.get("tags"): + new_repre["tags"] = [] for tag in output_def["tags"]: - if not new_repre.get("tags"): - new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) if tag == "review": added_review = True + new_repre["tags"].append("newly_added") + instance.data["representations"].append(new_repre) added_representations = True @@ -179,6 +181,12 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] + # TODO implement better way, for now do not delete new repre + # new repre might have 'delete' tag to removed, but it first must + # be there for review to be created + if "newly_added" in tags: + tags.remove("newly_added") + continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) From b5c3e0931e0dc1d13d09cd2b8579e1fc86b0169e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:04:59 +0100 Subject: [PATCH 0934/1271] OP-4642 - added additional command arguments to Settings --- .../schemas/schema_global_publish.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3956f403f4..5333d514b5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -286,6 +286,20 @@ "label": "View", "type": "text" }, + { + "key": "oiiotool_args", + "label": "OIIOtool arguments", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "additional_command_args", + "label": "Additional command line arguments", + "type": "list", + "object_type": "text" + } + ] + }, { "type": "schema", "name": "schema_representation_tags" From 3921982365792bb92912777c0cc130462c0e6e14 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:08:06 +0100 Subject: [PATCH 0935/1271] OP-4642 - added additional command arguments for oiiotool Some extension requires special command line arguments (.dpx and binary depth). --- openpype/lib/transcoding.py | 6 ++++++ openpype/plugins/publish/extract_color_transcode.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index b6edd863f8..982cee7a46 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1055,6 +1055,7 @@ def convert_colorspace( target_colorspace, view=None, display=None, + additional_command_args=None, logger=None ): """Convert source file from one color space to another. @@ -1074,6 +1075,8 @@ def convert_colorspace( view (str): name for viewer space (ocio valid) both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) + additional_command_args (list): arguments for oiiotool (like binary + depth for .dpx) logger (logging.Logger): Logger used for logging. Raises: ValueError: if misconfigured @@ -1096,6 +1099,9 @@ def convert_colorspace( if not target_colorspace and not all([view, display]): raise ValueError("Both screen and display must be set.") + if additional_command_args: + oiio_cmd.extend(additional_command_args) + if target_colorspace: oiio_cmd.extend(["--colorconvert", source_colorspace, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4a03e623fd..3de404125d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -128,6 +128,9 @@ class ExtractOIIOTranscode(publish.Extractor): if display: new_repre["colorspaceData"]["display"] = display + additional_command_args = (output_def["oiiotool_args"] + ["additional_command_args"]) + files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: @@ -144,6 +147,7 @@ class ExtractOIIOTranscode(publish.Extractor): target_colorspace, view, display, + additional_command_args, self.log ) From 0834b7564b842832cba47a80a2b8c933d8bce918 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:21:25 +0100 Subject: [PATCH 0936/1271] OP-4642 - refactored newly added representations --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3de404125d..8c4ef59de9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -82,6 +82,7 @@ class ExtractOIIOTranscode(publish.Extractor): if not profile: return + new_representations = [] repres = instance.data.get("representations") or [] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) @@ -174,9 +175,7 @@ class ExtractOIIOTranscode(publish.Extractor): if tag == "review": added_review = True - new_repre["tags"].append("newly_added") - - instance.data["representations"].append(new_repre) + new_representations.append(new_repre) added_representations = True if added_representations: @@ -185,15 +184,11 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] - # TODO implement better way, for now do not delete new repre - # new repre might have 'delete' tag to removed, but it first must - # be there for review to be created - if "newly_added" in tags: - tags.remove("newly_added") - continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) + instance.data["representations"].extend(new_representations) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From 263d3dccc2bb2f2ddc3d18d725a9d16101404cbb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:24:53 +0100 Subject: [PATCH 0937/1271] OP-4642 - refactored query of representations line 73 returns if no representations. --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 8c4ef59de9..de36ea7d5f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -83,7 +83,7 @@ class ExtractOIIOTranscode(publish.Extractor): return new_representations = [] - repres = instance.data.get("representations") or [] + repres = instance.data["representations"] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) if not self._repre_is_valid(repre): From cf066d1441d5d356e4be59591aa8fadfc24bcd0b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 10:44:10 +0100 Subject: [PATCH 0938/1271] OP-4643 - fixed subset filtering Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index de36ea7d5f..71124b527a 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -273,7 +273,7 @@ class ExtractOIIOTranscode(publish.Extractor): "families": family, "task_names": task_name, "task_types": task_type, - "subset": subset + "subsets": subset } profile = filter_profiles(self.profiles, filtering_criteria, logger=self.log) From 984974d7e01a48ebcd704e167c1e3fef42418d82 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 12:12:35 +0100 Subject: [PATCH 0939/1271] OP-4643 - split command line arguments to separate items Reuse existing method from ExtractReview, put it into transcoding.py --- openpype/lib/transcoding.py | 29 +++++++++++++++++++++- openpype/plugins/publish/extract_review.py | 27 +++----------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 982cee7a46..4d2f72fc41 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,3 +1114,30 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 0f6dacba18..e80141fc4a 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,6 +22,7 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, + split_cmd_args ) @@ -670,7 +671,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) + ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -723,28 +724,6 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) - def split_ffmpeg_args(self, in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args - def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -764,7 +743,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = self.split_ffmpeg_args(output_args) + output_args = split_cmd_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 51c54e1aa1bf602fe4cc2ba0ff1110dcfe5f5539 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:02:41 +0100 Subject: [PATCH 0940/1271] OP-4643 - refactor - changed existence check --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 71124b527a..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,12 +161,12 @@ class ExtractOIIOTranscode(publish.Extractor): custom_tags = output_def.get("custom_tags") if custom_tags: - if not new_repre.get("custom_tags"): + if new_repre.get("custom_tags") is None: new_repre["custom_tags"] = [] new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation - if not new_repre.get("tags"): + if new_repre.get("tags") is None: new_repre["tags"] = [] for tag in output_def["tags"]: if tag not in new_repre["tags"]: From cb551fe83acde2ea43e61706273bf33fec1c37d3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:11:11 +0100 Subject: [PATCH 0941/1271] Revert "Fix - added missed scopes for Slack bot" This reverts commit 5e0c4a3ab1432e120b8f0c324f899070f1a5f831. --- openpype/modules/slack/manifest.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 233c39fbaf..7a65cc5915 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,8 +19,6 @@ oauth_config: - chat:write.public - files:write - channels:read - - users:read - - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From e2ebbae14772b4f24b169c104cdabfcabfab6b66 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:10:11 +0100 Subject: [PATCH 0942/1271] OP-4643 - changed label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5333d514b5..3e9467af61 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -294,7 +294,7 @@ "children": [ { "key": "additional_command_args", - "label": "Additional command line arguments", + "label": "Arguments", "type": "list", "object_type": "text" } From 4e755e193a6c1e6f2d074d98d2684b3e18cf5282 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 15:06:16 +0100 Subject: [PATCH 0943/1271] OP-4643 - added documentation --- .../assets/global_oiio_transcode.png | Bin 0 -> 29010 bytes .../project_settings/settings_project_global.md | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png new file mode 100644 index 0000000000000000000000000000000000000000..99396d5bb3f16d434a92c6079515128488b82489 GIT binary patch literal 29010 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 literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 37fed93e69..52671d2db6 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -45,6 +45,21 @@ The _input pattern_ matching uses REGEX expression syntax (try [regexr.com](http The **colorspace name** value is a raw string input and no validation is run after saving project settings. We recommend to open the specified `config.ocio` file and copy pasting the exact colorspace names. ::: +### Extract OIIO Transcode +There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +`oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. + +Notable parameters: +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Extension`** - target extension, could be empty - original extension is used +- **`Colorspace`** - target colorspace - must be available in used color config +- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Arguments`** - special additional command line arguments for `oiiotool` + + +Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. +![global_oiio_transcode](assets/global_oiio_transcode.png) ## Profile filters From 54e92f02b9f1a31fd9b05005864b6f1e8e5b99ff Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:41:42 +0100 Subject: [PATCH 0944/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 52671d2db6..cc661a21fa 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,7 +46,7 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. From 665132d9feb19a6d566e2541ccb1207bef1dcc53 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:06 +0100 Subject: [PATCH 0945/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index cc661a21fa..8e557a381c 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -52,7 +52,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. -- **`Extension`** - target extension, could be empty - original extension is used +- **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace - must be available in used color config - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From ceca9a5bb89c8da7915d7c4772cbd23448b49714 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:29 +0100 Subject: [PATCH 0946/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 8e557a381c..166400cb7f 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,7 +53,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace - must be available in used color config +- **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From 0a4cae9db2eaf3beea496e57065240a7b4eec1c1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:48 +0100 Subject: [PATCH 0947/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 166400cb7f..908191f122 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -54,7 +54,7 @@ Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. - **`Arguments`** - special additional command line arguments for `oiiotool` From 3a0e9dc78ce48bf9d964eed90b5afea24853e8a7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:43:06 +0100 Subject: [PATCH 0948/1271] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 908191f122..0a73868d2d 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -55,7 +55,7 @@ Notable parameters: - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. -- **`Arguments`** - special additional command line arguments for `oiiotool` +- **`Arguments`** - special additional command line arguments for `oiiotool`. Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. From 7f8a766c6a294991c379a356e63ab3b2fd9e53a7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:21:20 +0100 Subject: [PATCH 0949/1271] OP-4643 - updates to documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- website/docs/project_settings/settings_project_global.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 0a73868d2d..9e2ee187cc 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,8 +46,8 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. -Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +OIIOTools transcoder plugin with configurable output presets. Any incoming representation with `colorspaceData` is convertable to single or multiple representations with different target colorspaces or display and viewer names found in linked **config.ocio** file. + `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: From 559d54c3a1b061f3234b0491f6abbb8b22aee6c8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:56:21 +0100 Subject: [PATCH 0950/1271] Revert "OP-4643 - split command line arguments to separate items" This reverts commit deaad39437501f18fc3ba4be8b1fc5f0ee3be65d. --- openpype/lib/transcoding.py | 29 +--------------------- openpype/plugins/publish/extract_review.py | 27 +++++++++++++++++--- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 4d2f72fc41..982cee7a46 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(split_cmd_args(additional_command_args)) + oiio_cmd.extend(additional_command_args) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,30 +1114,3 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) - - -def split_cmd_args(in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - Args: - in_args (list): of arguments ['-n', '-d uint10'] - Returns - (list): ['-n', '-d', 'unint10'] - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index e80141fc4a..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,7 +22,6 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, - split_cmd_args ) @@ -671,7 +670,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) + ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -724,6 +723,28 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) + def split_ffmpeg_args(self, in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args + def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -743,7 +764,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = split_cmd_args(output_args) + output_args = self.split_ffmpeg_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 5678bdbf065922d1e065782ce419149bdd29cbae Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:02:17 +0100 Subject: [PATCH 0951/1271] OP-4643 - different splitting for oiio It seems that logic in ExtractReview does different thing. --- openpype/lib/transcoding.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 982cee7a46..376297ff32 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,3 +1114,21 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + if not arg.strip(): + continue + splitted_args.extend(arg.split(" ")) + return splitted_args From f30b3c52307e4db5e6715a56ba9532c89dcfceb8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:14:57 +0100 Subject: [PATCH 0952/1271] OP-4643 - allow colorspace to be empty and collected from DCC --- openpype/plugins/publish/extract_color_transcode.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..82b92ec93e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,7 +118,8 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From 7ecf6fde48aebc705f13ac965e2a9819239b2c87 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 12:22:53 +0100 Subject: [PATCH 0953/1271] OP-4643 - fix colorspace from DCC representation["colorspaceData"]["colorspace"] is only input colorspace --- openpype/plugins/publish/extract_color_transcode.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 82b92ec93e..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,8 +118,7 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = (output_def["colorspace"] or - colorspace_data.get("colorspace")) + target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From 945f1dfe55ad1f150cf0f71baacaea80857a0e4e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:33:20 +0100 Subject: [PATCH 0954/1271] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- openpype/lib/transcoding.py | 2 +- .../plugins/publish/extract_color_transcode.py | 15 +++++++++++---- .../schemas/schema_global_publish.json | 9 +++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 376297ff32..c0bda2aa37 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1052,7 +1052,7 @@ def convert_colorspace( output_path, config_path, source_colorspace, - target_colorspace, + target_colorspace=None, view=None, display=None, additional_command_args=None, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..b0921688e9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,10 +118,17 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] - view = output_def["view"] or colorspace_data.get("view") - display = (output_def["display"] or - colorspace_data.get("display")) + transcoding_type = output_def["transcoding_type"] + + target_colorspace = view = display = None + if transcoding_type == "colorspace": + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) + else: + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, # but could be overwritten if view: diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3e9467af61..76574e8b9b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -271,6 +271,15 @@ "label": "Extension", "type": "text" }, + { + "type": "enum", + "key": "transcoding_type", + "label": "Transcoding type", + "enum_items": [ + { "colorspace": "Use Colorspace" }, + { "display": "Use Display&View" } + ] + }, { "key": "colorspace", "label": "Colorspace", From 25fb38bd4c210770c711d725ed1b88f0c1b8731b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:34:03 +0100 Subject: [PATCH 0955/1271] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- .../assets/global_oiio_transcode.png | Bin 29010 -> 17936 bytes .../settings_project_global.md | 5 +++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png index 99396d5bb3f16d434a92c6079515128488b82489..d818ecfe19f93366e5f4664734d68c7b448d7b4f 100644 GIT binary patch literal 17936 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 DbV~re literal 29010 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 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 9e2ee187cc..6e78ee5d45 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,8 +53,9 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. +- **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. +- **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). +- **`Display & View`** - display and viewer colorspace. (If `Transcoding type` is `Use Display&View` values in configuration is used OR if empty values collected on instance from DCC). - **`Arguments`** - special additional command line arguments for `oiiotool`. From b8f8fd9a5a5342376b28b18f4a9d394ddf3d6b42 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:42:07 +0100 Subject: [PATCH 0956/1271] OP-4643 - added use case for Maya to documentation --- .../assets/global_oiio_transcode2.png | Bin 0 -> 17960 bytes .../project_settings/settings_project_global.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode2.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode2.png b/website/docs/project_settings/assets/global_oiio_transcode2.png new file mode 100644 index 0000000000000000000000000000000000000000..906f780830a96b4bc6f26d98484dd3f9cc3885f2 GIT binary patch literal 17960 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 F{{cSxQDp!C literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 6e78ee5d45..f58d2c2bf2 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -62,6 +62,9 @@ Notable parameters: Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. ![global_oiio_transcode](assets/global_oiio_transcode.png) +Another use case is to transcode in Maya only `beauty` render layers and use collected `Display` and `View` colorspaces from DCC. +![global_oiio_transcode_in_Maya](assets/global_oiio_transcode.png) + ## Profile filters Many of the settings are using a concept of **Profile filters** From 539ba60eb4c21e310e716a806683acbc7a0284a5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 23 Feb 2023 11:05:26 +0100 Subject: [PATCH 0957/1271] OP-4643 - updates to documentation Co-authored-by: Roy Nieterau --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index f58d2c2bf2..d904080ad1 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -51,7 +51,7 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: -- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation loses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. - **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). From 6ad3421f7f4c6aa7d3cd21d7e63dca19df3d8e41 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 15:40:55 +0100 Subject: [PATCH 0958/1271] improving deprecation --- openpype/hosts/nuke/api/lib.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 0325838e78..b13c592fbf 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -214,8 +214,9 @@ def update_node_data(node, knobname, data): knob.setValue(knob_value) +@deprecated class Knobby(object): - """[DEPRICATED] For creating knob which it's type isn't + """[DEPRECATED] For creating knob which it's type isn't mapped in `create_knobs` Args: @@ -248,8 +249,9 @@ class Knobby(object): return " ".join(words) +@deprecated def create_knobs(data, tab=None): - """[DEPRICATED] Create knobs by data + """[DEPRECATED] Create knobs by data Depending on the type of each dict value and creates the correct Knob. @@ -342,8 +344,9 @@ def create_knobs(data, tab=None): return knobs +@deprecated def imprint(node, data, tab=None): - """[DEPRICATED] Store attributes with value on node + """[DEPRECATED] Store attributes with value on node Parse user data into Node knobs. Use `collections.OrderedDict` to ensure knob order. @@ -398,8 +401,9 @@ def imprint(node, data, tab=None): node.addKnob(knob) +@deprecated def add_publish_knob(node): - """[DEPRICATED] Add Publish knob to node + """[DEPRECATED] Add Publish knob to node Arguments: node (nuke.Node): nuke node to be processed @@ -416,8 +420,9 @@ def add_publish_knob(node): return node +@deprecated def set_avalon_knob_data(node, data=None, prefix="avalon:"): - """[DEPRICATED] Sets data into nodes's avalon knob + """[DEPRECATED] Sets data into nodes's avalon knob Arguments: node (nuke.Node): Nuke node to imprint with data, @@ -478,8 +483,9 @@ def set_avalon_knob_data(node, data=None, prefix="avalon:"): return node +@deprecated def get_avalon_knob_data(node, prefix="avalon:", create=True): - """[DEPRICATED] Gets a data from nodes's avalon knob + """[DEPRECATED] Gets a data from nodes's avalon knob Arguments: node (obj): Nuke node to search for data, @@ -521,8 +527,9 @@ def get_avalon_knob_data(node, prefix="avalon:", create=True): return data +@deprecated def fix_data_for_node_create(data): - """[DEPRICATED] Fixing data to be used for nuke knobs + """[DEPRECATED] Fixing data to be used for nuke knobs """ for k, v in data.items(): if isinstance(v, six.text_type): @@ -532,8 +539,9 @@ def fix_data_for_node_create(data): return data +@deprecated def add_write_node_legacy(name, **kwarg): - """[DEPRICATED] Adding nuke write node + """[DEPRECATED] Adding nuke write node Arguments: name (str): nuke node name kwarg (attrs): data for nuke knobs @@ -697,7 +705,7 @@ def get_nuke_imageio_settings(): @deprecated("openpype.hosts.nuke.api.lib.get_nuke_imageio_settings") def get_created_node_imageio_setting_legacy(nodeclass, creator, subset): - '''[DEPRICATED] Get preset data for dataflow (fileType, compression, bitDepth) + '''[DEPRECATED] Get preset data for dataflow (fileType, compression, bitDepth) ''' assert any([creator, nodeclass]), nuke.message( From 2e07aa33fa398148dd51ed103cf4da553c4f2379 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 17:21:17 +0100 Subject: [PATCH 0959/1271] Nuke: baking with multiple reposition nodes also with settings and defaults --- openpype/hosts/nuke/api/plugin.py | 96 ++++++++++++------- .../defaults/project_settings/nuke.json | 35 +++++++ .../schemas/schema_nuke_publish.json | 47 +++++++++ 3 files changed, 144 insertions(+), 34 deletions(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index d3f8357f7d..5521db99c0 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -558,9 +558,7 @@ class ExporterReview(object): self.path_in = self.instance.data.get("path", None) self.staging_dir = self.instance.data["stagingDir"] self.collection = self.instance.data.get("collection", None) - self.data = dict({ - "representations": list() - }) + self.data = {"representations": []} def get_file_info(self): if self.collection: @@ -626,7 +624,7 @@ class ExporterReview(object): nuke_imageio = opnlib.get_nuke_imageio_settings() # TODO: this is only securing backward compatibility lets remove - # this once all projects's anotomy are updated to newer config + # this once all projects's anatomy are updated to newer config if "baking" in nuke_imageio.keys(): return nuke_imageio["baking"]["viewerProcess"] else: @@ -823,8 +821,41 @@ class ExporterReviewMov(ExporterReview): add_tags = [] self.publish_on_farm = farm read_raw = kwargs["read_raw"] + + # TODO: remove this when `reformat_nodes_config` + # is changed in settings reformat_node_add = kwargs["reformat_node_add"] reformat_node_config = kwargs["reformat_node_config"] + + # TODO: make this required in future + reformat_nodes_config = kwargs.get("reformat_nodes_config", {}) + + # TODO: remove this once deprecated is removed + # make sure only reformat_nodes_config is used in future + if reformat_node_add and reformat_nodes_config.get("enabled"): + self.log.warning( + "`reformat_node_add` is deprecated. " + "Please use only `reformat_nodes_config` instead.") + reformat_nodes_config = None + + # TODO: reformat code when backward compatibility is not needed + # warning if reformat_nodes_config is not set + if not reformat_nodes_config: + self.log.warning( + "Please set `reformat_nodes_config` in settings.") + self.log.warning( + "Using `reformat_node_config` instead.") + reformat_nodes_config = { + "enabled": reformat_node_add, + "reposition_nodes": [ + { + "node_class": "Reformat", + "knobs": reformat_node_config + } + ] + } + + bake_viewer_process = kwargs["bake_viewer_process"] bake_viewer_input_process_node = kwargs[ "bake_viewer_input_process"] @@ -846,7 +877,6 @@ class ExporterReviewMov(ExporterReview): subset = self.instance.data["subset"] self._temp_nodes[subset] = [] - # ---------- start nodes creation # Read node r_node = nuke.createNode("Read") @@ -860,44 +890,39 @@ class ExporterReviewMov(ExporterReview): if read_raw: r_node["raw"].setValue(1) - # connect - self._temp_nodes[subset].append(r_node) - self.previous_node = r_node - self.log.debug("Read... `{}`".format(self._temp_nodes[subset])) + # connect to Read node + self._shift_to_previous_node_and_temp(subset, r_node, "Read... `{}`") # add reformat node - if reformat_node_add: + if reformat_nodes_config["enabled"]: + reposition_nodes = reformat_nodes_config["reposition_nodes"] + for reposition_node in reposition_nodes: + node_class = reposition_node["node_class"] + knobs = reposition_node["knobs"] + node = nuke.createNode(node_class) + set_node_knobs_from_settings(node, knobs) + + # connect in order + self._connect_to_above_nodes( + node, subset, "Reposition node... `{}`" + ) # append reformated tag add_tags.append("reformated") - rf_node = nuke.createNode("Reformat") - set_node_knobs_from_settings(rf_node, reformat_node_config) - - # connect - rf_node.setInput(0, self.previous_node) - self._temp_nodes[subset].append(rf_node) - self.previous_node = rf_node - self.log.debug( - "Reformat... `{}`".format(self._temp_nodes[subset])) - # only create colorspace baking if toggled on if bake_viewer_process: if bake_viewer_input_process_node: # View Process node ipn = get_view_process_node() if ipn is not None: - # connect - ipn.setInput(0, self.previous_node) - self._temp_nodes[subset].append(ipn) - self.previous_node = ipn - self.log.debug( - "ViewProcess... `{}`".format( - self._temp_nodes[subset])) + # connect to ViewProcess node + self._connect_to_above_nodes(ipn, subset, "ViewProcess... `{}`") if not self.viewer_lut_raw: # OCIODisplay dag_node = nuke.createNode("OCIODisplay") + # assign display display, viewer = get_viewer_config_from_string( str(baking_view_profile) ) @@ -907,13 +932,7 @@ class ExporterReviewMov(ExporterReview): # assign viewer dag_node["view"].setValue(viewer) - # connect - dag_node.setInput(0, self.previous_node) - self._temp_nodes[subset].append(dag_node) - self.previous_node = dag_node - self.log.debug("OCIODisplay... `{}`".format( - self._temp_nodes[subset])) - + self._connect_to_above_nodes(dag_node, subset, "OCIODisplay... `{}`") # Write node write_node = nuke.createNode("Write") self.log.debug("Path: {}".format(self.path)) @@ -967,6 +986,15 @@ class ExporterReviewMov(ExporterReview): return self.data + def _shift_to_previous_node_and_temp(self, subset, node, message): + self._temp_nodes[subset].append(node) + self.previous_node = node + self.log.debug(message.format(self._temp_nodes[subset])) + + def _connect_to_above_nodes(self, node, subset, message): + node.setInput(0, self.previous_node) + self._shift_to_previous_node_and_temp(subset, node, message) + @deprecated("openpype.hosts.nuke.api.plugin.NukeWriteCreator") class AbstractWriteRender(OpenPypeCreator): diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index d475c337d9..2545411e0a 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -446,6 +446,41 @@ "value": false } ], + "reformat_nodes_config": { + "enabled": false, + "reposition_nodes": [ + { + "node_class": "Reformat", + "knobs": [ + { + "type": "text", + "name": "type", + "value": "to format" + }, + { + "type": "text", + "name": "format", + "value": "HD_1080" + }, + { + "type": "text", + "name": "filter", + "value": "Lanczos6" + }, + { + "type": "bool", + "name": "black_outside", + "value": true + }, + { + "type": "bool", + "name": "pbb", + "value": false + } + ] + } + ] + }, "extension": "mov", "add_custom_tags": [] } diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index 5b9145e7d9..1c542279fc 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -271,6 +271,10 @@ { "type": "separator" }, + { + "type": "label", + "label": "Currently we are supporting also multiple reposition nodes.
Older single reformat node is still supported
and if it is activated then preference will
be on it. If you want to use multiple reformat
nodes then you need to disable single reformat
node and enable multiple Reformat nodes
here." + }, { "type": "boolean", "key": "reformat_node_add", @@ -287,6 +291,49 @@ } ] }, + { + "key": "reformat_nodes_config", + "type": "dict", + "label": "Reformat Nodes", + "collapsible": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Reposition knobs supported only.
You can add multiple reformat nodes
and set their knobs. Order of reformat
nodes is important. First reformat node
will be applied first and last reformat
node will be applied last." + }, + { + "key": "reposition_nodes", + "type": "list", + "label": "Reposition nodes", + "object_type": { + "type": "dict", + "children": [ + { + "key": "node_class", + "label": "Node class", + "type": "text" + }, + { + "type": "schema_template", + "name": "template_nuke_knob_inputs", + "template_data": [ + { + "label": "Node knobs", + "key": "knobs" + } + ] + } + ] + } + } + ] + }, { "type": "separator" }, From fb3bda7c80c908dc052f50092e6d94a62947a3ad Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 Feb 2023 10:57:00 +0100 Subject: [PATCH 0960/1271] little fixes --- openpype/hosts/nuke/api/lib.py | 9 +++------ openpype/hosts/nuke/api/plugin.py | 6 +++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index b13c592fbf..73d4986b64 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -214,7 +214,6 @@ def update_node_data(node, knobname, data): knob.setValue(knob_value) -@deprecated class Knobby(object): """[DEPRECATED] For creating knob which it's type isn't mapped in `create_knobs` @@ -249,9 +248,8 @@ class Knobby(object): return " ".join(words) -@deprecated def create_knobs(data, tab=None): - """[DEPRECATED] Create knobs by data + """Create knobs by data Depending on the type of each dict value and creates the correct Knob. @@ -344,9 +342,8 @@ def create_knobs(data, tab=None): return knobs -@deprecated def imprint(node, data, tab=None): - """[DEPRECATED] Store attributes with value on node + """Store attributes with value on node Parse user data into Node knobs. Use `collections.OrderedDict` to ensure knob order. @@ -1249,7 +1246,7 @@ def create_write_node( nodes to be created before write with dependency review (bool)[optional]: adding review knob farm (bool)[optional]: rendering workflow target - kwargs (dict)[optional]: additional key arguments for formating + kwargs (dict)[optional]: additional key arguments for formatting Example: prenodes = { diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 5521db99c0..160ca820a4 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -842,9 +842,9 @@ class ExporterReviewMov(ExporterReview): # warning if reformat_nodes_config is not set if not reformat_nodes_config: self.log.warning( - "Please set `reformat_nodes_config` in settings.") - self.log.warning( - "Using `reformat_node_config` instead.") + "Please set `reformat_nodes_config` in settings. " + "Using `reformat_node_config` instead." + ) reformat_nodes_config = { "enabled": reformat_node_add, "reposition_nodes": [ From b4541d29fe823416c27b9a07424d431a738d3e8a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 11:47:32 +0100 Subject: [PATCH 0961/1271] nuke assist kickoff --- .../system_settings/applications.json | 128 ++++++++++++++++++ .../system_schema/schema_applications.json | 8 ++ 2 files changed, 136 insertions(+) diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index f84d99e36b..5fd9b926fb 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -337,6 +337,134 @@ } } }, + "nukeassist": { + "enabled": true, + "label": "Nuke Assist", + "icon": "{}/app_icons/nuke.png", + "host_name": "nuke", + "environment": { + "NUKE_PATH": [ + "{NUKE_PATH}", + "{OPENPYPE_STUDIO_PLUGINS}/nuke" + ] + }, + "variants": { + "13-2": { + "use_python_2": false, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke13.2v1\\Nuke13.2.exe" + ], + "darwin": [], + "linux": [ + "/usr/local/Nuke13.2v1/Nuke13.2" + ] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "13-0": { + "use_python_2": false, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke13.0v1\\Nuke13.0.exe" + ], + "darwin": [], + "linux": [ + "/usr/local/Nuke13.0v1/Nuke13.0" + ] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "12-2": { + "use_python_2": true, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke12.2v3\\Nuke12.2.exe" + ], + "darwin": [], + "linux": [ + "/usr/local/Nuke12.2v3Nuke12.2" + ] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "12-0": { + "use_python_2": true, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe" + ], + "darwin": [], + "linux": [ + "/usr/local/Nuke12.0v1/Nuke12.0" + ] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "11-3": { + "use_python_2": true, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe" + ], + "darwin": [], + "linux": [ + "/usr/local/Nuke11.3v5/Nuke11.3" + ] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "11-2": { + "use_python_2": true, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe" + ], + "darwin": [], + "linux": [] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "__dynamic_keys_labels__": { + "13-2": "13.2", + "13-0": "13.0", + "12-2": "12.2", + "12-0": "12.0", + "11-3": "11.3", + "11-2": "11.2" + } + } + }, "nukex": { "enabled": true, "label": "Nuke X", diff --git a/openpype/settings/entities/schemas/system_schema/schema_applications.json b/openpype/settings/entities/schemas/system_schema/schema_applications.json index 36c5811496..b17687cf71 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_applications.json +++ b/openpype/settings/entities/schemas/system_schema/schema_applications.json @@ -25,6 +25,14 @@ "nuke_label": "Nuke" } }, + { + "type": "schema_template", + "name": "template_nuke", + "template_data": { + "nuke_type": "nukeassist", + "nuke_label": "Nuke Assist" + } + }, { "type": "schema_template", "name": "template_nuke", From e57c9e8dd6dc9b08d914f43805b3583e5c3bbe1c Mon Sep 17 00:00:00 2001 From: ynput Date: Tue, 21 Feb 2023 14:23:26 +0200 Subject: [PATCH 0962/1271] adding appgroup to prelaunch hook --- 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 85f68c6b60..2092d5025d 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", "nukeassist", "nukex", "hiero", "nukestudio"] platforms = ["windows"] def execute(self): From fc6e5ca854fad80a6687befba10422a187724bfd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 13:39:02 +0100 Subject: [PATCH 0963/1271] adding nukeassist hook --- openpype/hosts/nuke/hooks/__init__.py | 0 openpype/hosts/nuke/hooks/pre_nukeassist_setup.py | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 openpype/hosts/nuke/hooks/__init__.py create mode 100644 openpype/hosts/nuke/hooks/pre_nukeassist_setup.py diff --git a/openpype/hosts/nuke/hooks/__init__.py b/openpype/hosts/nuke/hooks/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py new file mode 100644 index 0000000000..80696c34e5 --- /dev/null +++ b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py @@ -0,0 +1,10 @@ +from openpype.lib import PreLaunchHook + +class PrelaunchNukeAssistHook(PreLaunchHook): + """ + Adding flag when nukeassist + """ + app_groups = ["nukeassist"] + + def execute(self): + self.launch_context.env["NUKEASSIST"] = True From a1c9e396640e9d6691af68e21ac1fa3ff78e9bf3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 13:56:17 +0100 Subject: [PATCH 0964/1271] adding hook to host --- openpype/hosts/nuke/addon.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/hosts/nuke/addon.py b/openpype/hosts/nuke/addon.py index 9d25afe2b6..6a4b91a76d 100644 --- a/openpype/hosts/nuke/addon.py +++ b/openpype/hosts/nuke/addon.py @@ -63,5 +63,12 @@ class NukeAddon(OpenPypeModule, IHostAddon): path_paths.append(quick_time_path) env["PATH"] = os.pathsep.join(path_paths) + def get_launch_hook_paths(self, app): + if app.host_name != self.host_name: + return [] + return [ + os.path.join(NUKE_ROOT_DIR, "hooks") + ] + def get_workfile_extensions(self): return [".nk"] From 71745e185cae75de715a0962fcf34262d4d7df9d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 14:54:51 +0100 Subject: [PATCH 0965/1271] adding menu assist variant conditions --- openpype/hosts/nuke/api/pipeline.py | 50 +++++++++++-------- openpype/hosts/nuke/hooks/__init__.py | 0 .../hosts/nuke/hooks/pre_nukeassist_setup.py | 2 +- 3 files changed, 30 insertions(+), 22 deletions(-) delete mode 100644 openpype/hosts/nuke/hooks/__init__.py diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index d5289010cb..306fa50de9 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -71,7 +71,7 @@ CREATE_PATH = os.path.join(PLUGINS_DIR, "create") INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") MENU_LABEL = os.environ["AVALON_LABEL"] - +ASSIST = bool(os.getenv("NUKEASSIST")) # registering pyblish gui regarding settings in presets if os.getenv("PYBLISH_GUI", None): @@ -207,6 +207,7 @@ def _show_workfiles(): def _install_menu(): + # uninstall original avalon menu main_window = get_main_window() menubar = nuke.menu("Nuke") @@ -217,7 +218,9 @@ def _install_menu(): ) Context.context_label = label context_action = menu.addCommand(label) - context_action.setEnabled(False) + + if not ASSIST: + context_action.setEnabled(False) menu.addSeparator() menu.addCommand( @@ -226,18 +229,20 @@ def _install_menu(): ) menu.addSeparator() - menu.addCommand( - "Create...", - lambda: host_tools.show_publisher( - tab="create" + if not ASSIST: + menu.addCommand( + "Create...", + lambda: host_tools.show_publisher( + tab="create" + ) ) - ) - menu.addCommand( - "Publish...", - lambda: host_tools.show_publisher( - tab="publish" + menu.addCommand( + "Publish...", + lambda: host_tools.show_publisher( + tab="publish" + ) ) - ) + menu.addCommand( "Load...", lambda: host_tools.show_loader( @@ -285,15 +290,18 @@ def _install_menu(): "Build Workfile from template", lambda: build_workfile_template() ) - menu_template.addSeparator() - menu_template.addCommand( - "Create Place Holder", - lambda: create_placeholder() - ) - menu_template.addCommand( - "Update Place Holder", - lambda: update_placeholder() - ) + + if not ASSIST: + menu_template.addSeparator() + menu_template.addCommand( + "Create Place Holder", + lambda: create_placeholder() + ) + menu_template.addCommand( + "Update Place Holder", + lambda: update_placeholder() + ) + menu.addSeparator() menu.addCommand( "Experimental tools...", diff --git a/openpype/hosts/nuke/hooks/__init__.py b/openpype/hosts/nuke/hooks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py index 80696c34e5..054bd677a7 100644 --- a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py +++ b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py @@ -7,4 +7,4 @@ class PrelaunchNukeAssistHook(PreLaunchHook): app_groups = ["nukeassist"] def execute(self): - self.launch_context.env["NUKEASSIST"] = True + self.launch_context.env["NUKEASSIST"] = "1" From f3431c62792cea3a65c95366d105bd782fe46376 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 15:26:07 +0100 Subject: [PATCH 0966/1271] moving Assist switch to api level condition for updating nodes --- openpype/hosts/nuke/api/__init__.py | 7 ++++++- openpype/hosts/nuke/api/lib.py | 21 ++++++++++++--------- openpype/hosts/nuke/api/pipeline.py | 2 +- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/nuke/api/__init__.py b/openpype/hosts/nuke/api/__init__.py index 1af5ff365d..7766a94140 100644 --- a/openpype/hosts/nuke/api/__init__.py +++ b/openpype/hosts/nuke/api/__init__.py @@ -1,3 +1,4 @@ +import os from .workio import ( file_extensions, has_unsaved_changes, @@ -50,6 +51,8 @@ from .utils import ( get_colorspace_list ) +ASSIST = bool(os.getenv("NUKEASSIST")) + __all__ = ( "file_extensions", "has_unsaved_changes", @@ -92,5 +95,7 @@ __all__ = ( "create_write_node", "colorspace_exists_on_node", - "get_colorspace_list" + "get_colorspace_list", + + "ASSIST" ) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 73d4986b64..ec5bc58f9f 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -49,7 +49,7 @@ from openpype.pipeline.colorspace import ( ) from openpype.pipeline.workfile import BuildWorkfile -from . import gizmo_menu +from . import gizmo_menu, ASSIST from .workio import ( save_file, @@ -2263,14 +2263,17 @@ class WorkfileSettings(object): node['frame_range'].setValue(range) node['frame_range_lock'].setValue(True) - set_node_data( - self._root_node, - INSTANCE_DATA_KNOB, - { - "handleStart": int(handle_start), - "handleEnd": int(handle_end) - } - ) + if not ASSIST: + set_node_data( + self._root_node, + INSTANCE_DATA_KNOB, + { + "handleStart": int(handle_start), + "handleEnd": int(handle_end) + } + ) + else: + log.warning("NukeAssist mode is not allowing updating custom knobs...") def reset_resolution(self): """Set resolution to project resolution.""" diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 306fa50de9..94c4518664 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -60,6 +60,7 @@ from .workio import ( work_root, current_file ) +from . import ASSIST log = Logger.get_logger(__name__) @@ -71,7 +72,6 @@ CREATE_PATH = os.path.join(PLUGINS_DIR, "create") INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") MENU_LABEL = os.environ["AVALON_LABEL"] -ASSIST = bool(os.getenv("NUKEASSIST")) # registering pyblish gui regarding settings in presets if os.getenv("PYBLISH_GUI", None): From fe163ab19b43bbde9c9ee297cf02ca2d42d998b5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 16:06:39 +0100 Subject: [PATCH 0967/1271] moving ASSIST switch to utils --- openpype/hosts/nuke/api/__init__.py | 7 +------ openpype/hosts/nuke/api/lib.py | 3 ++- openpype/hosts/nuke/api/pipeline.py | 2 +- openpype/hosts/nuke/api/utils.py | 1 + 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/nuke/api/__init__.py b/openpype/hosts/nuke/api/__init__.py index 7766a94140..1af5ff365d 100644 --- a/openpype/hosts/nuke/api/__init__.py +++ b/openpype/hosts/nuke/api/__init__.py @@ -1,4 +1,3 @@ -import os from .workio import ( file_extensions, has_unsaved_changes, @@ -51,8 +50,6 @@ from .utils import ( get_colorspace_list ) -ASSIST = bool(os.getenv("NUKEASSIST")) - __all__ = ( "file_extensions", "has_unsaved_changes", @@ -95,7 +92,5 @@ __all__ = ( "create_write_node", "colorspace_exists_on_node", - "get_colorspace_list", - - "ASSIST" + "get_colorspace_list" ) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index ec5bc58f9f..dfc647872b 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -49,7 +49,8 @@ from openpype.pipeline.colorspace import ( ) from openpype.pipeline.workfile import BuildWorkfile -from . import gizmo_menu, ASSIST +from . import gizmo_menu +from .utils import ASSIST from .workio import ( save_file, diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 94c4518664..55cb77bafe 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -60,7 +60,7 @@ from .workio import ( work_root, current_file ) -from . import ASSIST +from .utils import ASSIST log = Logger.get_logger(__name__) diff --git a/openpype/hosts/nuke/api/utils.py b/openpype/hosts/nuke/api/utils.py index 6bcb752dd1..261eba8401 100644 --- a/openpype/hosts/nuke/api/utils.py +++ b/openpype/hosts/nuke/api/utils.py @@ -4,6 +4,7 @@ import nuke from openpype import resources from .lib import maintained_selection +ASSIST = bool(os.getenv("NUKEASSIST")) def set_context_favorites(favorites=None): """ Adding favorite folders to nuke's browser From 360a5b3b68de7e3f735078173c897e21196adec4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 16:38:54 +0100 Subject: [PATCH 0968/1271] move ASSIST to constant --- openpype/hosts/nuke/api/constants.py | 4 ++++ openpype/hosts/nuke/api/lib.py | 2 +- openpype/hosts/nuke/api/pipeline.py | 2 +- openpype/hosts/nuke/api/utils.py | 1 - 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 openpype/hosts/nuke/api/constants.py diff --git a/openpype/hosts/nuke/api/constants.py b/openpype/hosts/nuke/api/constants.py new file mode 100644 index 0000000000..110199720f --- /dev/null +++ b/openpype/hosts/nuke/api/constants.py @@ -0,0 +1,4 @@ +import os + + +ASSIST = bool(os.getenv("NUKEASSIST")) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index dfc647872b..9e36fb147b 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -50,7 +50,7 @@ from openpype.pipeline.colorspace import ( from openpype.pipeline.workfile import BuildWorkfile from . import gizmo_menu -from .utils import ASSIST +from .constants import ASSIST from .workio import ( save_file, diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 55cb77bafe..f07d150ba5 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -60,7 +60,7 @@ from .workio import ( work_root, current_file ) -from .utils import ASSIST +from .constants import ASSIST log = Logger.get_logger(__name__) diff --git a/openpype/hosts/nuke/api/utils.py b/openpype/hosts/nuke/api/utils.py index 261eba8401..6bcb752dd1 100644 --- a/openpype/hosts/nuke/api/utils.py +++ b/openpype/hosts/nuke/api/utils.py @@ -4,7 +4,6 @@ import nuke from openpype import resources from .lib import maintained_selection -ASSIST = bool(os.getenv("NUKEASSIST")) def set_context_favorites(favorites=None): """ Adding favorite folders to nuke's browser From 132b616f1f9b4c802f7c985b5eb73948d3ae22a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Wed, 22 Feb 2023 14:41:36 +0100 Subject: [PATCH 0969/1271] Update openpype/hosts/nuke/hooks/pre_nukeassist_setup.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/nuke/hooks/pre_nukeassist_setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py index 054bd677a7..3a0f00413a 100644 --- a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py +++ b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py @@ -8,3 +8,4 @@ class PrelaunchNukeAssistHook(PreLaunchHook): def execute(self): self.launch_context.env["NUKEASSIST"] = "1" + From b3b488c2a6235d71d1e15eb2d058d22c439f5303 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 14:44:22 +0100 Subject: [PATCH 0970/1271] removing line --- openpype/hosts/nuke/hooks/pre_nukeassist_setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py index 3a0f00413a..054bd677a7 100644 --- a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py +++ b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py @@ -8,4 +8,3 @@ class PrelaunchNukeAssistHook(PreLaunchHook): def execute(self): self.launch_context.env["NUKEASSIST"] = "1" - From 363551af73a5703a08af4a5881448e9bc61885d3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 14:52:57 +0100 Subject: [PATCH 0971/1271] context label is not needed in nukeassist --- openpype/hosts/nuke/api/pipeline.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index f07d150ba5..4c0f169ade 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -207,22 +207,25 @@ def _show_workfiles(): def _install_menu(): + """Install Avalon menu into Nuke's main menu bar.""" # uninstall original avalon menu main_window = get_main_window() menubar = nuke.menu("Nuke") menu = menubar.addMenu(MENU_LABEL) - label = "{0}, {1}".format( - os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"] - ) - Context.context_label = label - context_action = menu.addCommand(label) if not ASSIST: + label = "{0}, {1}".format( + os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"] + ) + Context.context_label = label + context_action = menu.addCommand(label) context_action.setEnabled(False) - menu.addSeparator() + # add separator after context label + menu.addSeparator() + menu.addCommand( "Work Files...", _show_workfiles From cb87097f74af6d6c616486867e89a9c0afada6c0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 14:56:27 +0100 Subject: [PATCH 0972/1271] adding empty lines --- openpype/hosts/nuke/api/pipeline.py | 1 - openpype/hosts/nuke/hooks/pre_nukeassist_setup.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 4c0f169ade..2496d66c1d 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -214,7 +214,6 @@ def _install_menu(): menubar = nuke.menu("Nuke") menu = menubar.addMenu(MENU_LABEL) - if not ASSIST: label = "{0}, {1}".format( os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"] diff --git a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py index 054bd677a7..3948a665c6 100644 --- a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py +++ b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py @@ -1,5 +1,6 @@ from openpype.lib import PreLaunchHook + class PrelaunchNukeAssistHook(PreLaunchHook): """ Adding flag when nukeassist From 3218dd021ad1f471215fd9e7b1f2ce1972e42d74 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 14:59:30 +0100 Subject: [PATCH 0973/1271] hound comments --- openpype/hosts/nuke/api/lib.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 9e36fb147b..c08db978d3 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -2274,7 +2274,10 @@ class WorkfileSettings(object): } ) else: - log.warning("NukeAssist mode is not allowing updating custom knobs...") + log.warning( + "NukeAssist mode is not allowing " + "updating custom knobs..." + ) def reset_resolution(self): """Set resolution to project resolution.""" From 7ec1cb77a46675a2e986a78f6081594d67f996a5 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Thu, 23 Feb 2023 23:00:16 +0800 Subject: [PATCH 0974/1271] maya gltf texture convertor and validator (#4261) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- .../maya/plugins/publish/extract_gltf.py | 3 + .../plugins/publish/validate_glsl_material.py | 207 ++++++++++++++++++ .../plugins/publish/validate_glsl_plugin.py | 31 +++ .../defaults/project_settings/maya.json | 15 ++ .../schemas/schema_maya_publish.json | 32 +++ 5 files changed, 288 insertions(+) create mode 100644 openpype/hosts/maya/plugins/publish/validate_glsl_material.py create mode 100644 openpype/hosts/maya/plugins/publish/validate_glsl_plugin.py diff --git a/openpype/hosts/maya/plugins/publish/extract_gltf.py b/openpype/hosts/maya/plugins/publish/extract_gltf.py index f5ceed5f33..ac258ffb3d 100644 --- a/openpype/hosts/maya/plugins/publish/extract_gltf.py +++ b/openpype/hosts/maya/plugins/publish/extract_gltf.py @@ -22,6 +22,8 @@ class ExtractGLB(publish.Extractor): self.log.info("Extracting GLB to: {}".format(path)) + cmds.loadPlugin("maya2glTF", quiet=True) + nodes = instance[:] self.log.info("Instance: {0}".format(nodes)) @@ -45,6 +47,7 @@ class ExtractGLB(publish.Extractor): "glb": True, "vno": True # visibleNodeOnly } + with lib.maintained_selection(): cmds.select(nodes, hi=True, noExpand=True) extract_gltf(staging_dir, diff --git a/openpype/hosts/maya/plugins/publish/validate_glsl_material.py b/openpype/hosts/maya/plugins/publish/validate_glsl_material.py new file mode 100644 index 0000000000..10c48da404 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_glsl_material.py @@ -0,0 +1,207 @@ +import os +from maya import cmds + +import pyblish.api +from openpype.pipeline.publish import ( + RepairAction, + ValidateContentsOrder +) +from openpype.pipeline import PublishValidationError + + +class ValidateGLSLMaterial(pyblish.api.InstancePlugin): + """ + Validate if the asset uses GLSL Shader + """ + + order = ValidateContentsOrder + 0.1 + families = ['gltf'] + hosts = ['maya'] + label = 'GLSL Shader for GLTF' + actions = [RepairAction] + optional = True + active = True + + def process(self, instance): + shading_grp = self.get_material_from_shapes(instance) + if not shading_grp: + raise PublishValidationError("No shading group found") + invalid = self.get_texture_shader_invalid(instance) + if invalid: + raise PublishValidationError("Non GLSL Shader found: " + "{0}".format(invalid)) + + def get_material_from_shapes(self, instance): + shapes = cmds.ls(instance, type="mesh", long=True) + for shape in shapes: + shading_grp = cmds.listConnections(shape, + destination=True, + type="shadingEngine") + + return shading_grp or [] + + def get_texture_shader_invalid(self, instance): + + invalid = set() + shading_grp = self.get_material_from_shapes(instance) + for shading_group in shading_grp: + material_name = "{}.surfaceShader".format(shading_group) + material = cmds.listConnections(material_name, + source=True, + destination=False, + type="GLSLShader") + + if not material: + # add material name + material = cmds.listConnections(material_name)[0] + invalid.add(material) + + return list(invalid) + + @classmethod + def repair(cls, instance): + """ + Repair instance by assigning GLSL Shader + to the material + """ + cls.assign_glsl_shader(instance) + return + + @classmethod + def assign_glsl_shader(cls, instance): + """ + Converting StingrayPBS material to GLSL Shaders + for the glb export through Maya2GLTF plugin + """ + + meshes = cmds.ls(instance, type="mesh", long=True) + cls.log.info("meshes: {}".format(meshes)) + # load the glsl shader plugin + cmds.loadPlugin("glslShader", quiet=True) + + for mesh in meshes: + # create glsl shader + glsl = cmds.createNode('GLSLShader') + glsl_shading_grp = cmds.sets(name=glsl + "SG", empty=True, + renderable=True, noSurfaceShader=True) + cmds.connectAttr(glsl + ".outColor", + glsl_shading_grp + ".surfaceShader") + + # load the maya2gltf shader + ogsfx_path = instance.context.data["project_settings"]["maya"]["publish"]["ExtractGLB"]["ogsfx_path"] # noqa + if not os.path.exists(ogsfx_path): + if ogsfx_path: + # if custom ogsfx path is not specified + # the log below is the warning for the user + cls.log.warning("ogsfx shader file " + "not found in {}".format(ogsfx_path)) + + cls.log.info("Find the ogsfx shader file in " + "default maya directory...") + # re-direct to search the ogsfx path in maya_dir + ogsfx_path = os.getenv("MAYA_APP_DIR") + ogsfx_path + if not os.path.exists(ogsfx_path): + raise PublishValidationError("The ogsfx shader file does not " # noqa + "exist: {}".format(ogsfx_path)) # noqa + + cmds.setAttr(glsl + ".shader", ogsfx_path, typ="string") + # list the materials used for the assets + shading_grp = cmds.listConnections(mesh, + destination=True, + type="shadingEngine") + + # get the materials related to the selected assets + for material in shading_grp: + pbs_shader = cmds.listConnections(material, + destination=True, + type="StingrayPBS") + if pbs_shader: + cls.pbs_shader_conversion(pbs_shader, glsl) + # setting up to relink the texture if + # the mesh is with aiStandardSurface + arnold_shader = cmds.listConnections(material, + destination=True, + type="aiStandardSurface") + if arnold_shader: + cls.arnold_shader_conversion(arnold_shader, glsl) + + cmds.sets(mesh, forceElement=str(glsl_shading_grp)) + + @classmethod + def pbs_shader_conversion(cls, main_shader, glsl): + + cls.log.info("StringrayPBS detected " + "-> Can do texture conversion") + + for shader in main_shader: + # get the file textures related to the PBS Shader + albedo = cmds.listConnections(shader + + ".TEX_color_map") + if albedo: + dif_output = albedo[0] + ".outColor" + # get the glsl_shader input + # reconnect the file nodes to maya2gltf shader + glsl_dif = glsl + ".u_BaseColorTexture" + cmds.connectAttr(dif_output, glsl_dif) + + # connect orm map if there is one + orm_packed = cmds.listConnections(shader + + ".TEX_ao_map") + if orm_packed: + orm_output = orm_packed[0] + ".outColor" + + mtl = glsl + ".u_MetallicTexture" + ao = glsl + ".u_OcclusionTexture" + rough = glsl + ".u_RoughnessTexture" + + cmds.connectAttr(orm_output, mtl) + cmds.connectAttr(orm_output, ao) + cmds.connectAttr(orm_output, rough) + + # connect nrm map if there is one + nrm = cmds.listConnections(shader + + ".TEX_normal_map") + if nrm: + nrm_output = nrm[0] + ".outColor" + glsl_nrm = glsl + ".u_NormalTexture" + cmds.connectAttr(nrm_output, glsl_nrm) + + @classmethod + def arnold_shader_conversion(cls, main_shader, glsl): + cls.log.info("aiStandardSurface detected " + "-> Can do texture conversion") + + for shader in main_shader: + # get the file textures related to the PBS Shader + albedo = cmds.listConnections(shader + ".baseColor") + if albedo: + dif_output = albedo[0] + ".outColor" + # get the glsl_shader input + # reconnect the file nodes to maya2gltf shader + glsl_dif = glsl + ".u_BaseColorTexture" + cmds.connectAttr(dif_output, glsl_dif) + + orm_packed = cmds.listConnections(shader + + ".specularRoughness") + if orm_packed: + orm_output = orm_packed[0] + ".outColor" + + mtl = glsl + ".u_MetallicTexture" + ao = glsl + ".u_OcclusionTexture" + rough = glsl + ".u_RoughnessTexture" + + cmds.connectAttr(orm_output, mtl) + cmds.connectAttr(orm_output, ao) + cmds.connectAttr(orm_output, rough) + + # connect nrm map if there is one + bump_node = cmds.listConnections(shader + + ".normalCamera") + if bump_node: + for bump in bump_node: + nrm = cmds.listConnections(bump + + ".bumpValue") + if nrm: + nrm_output = nrm[0] + ".outColor" + glsl_nrm = glsl + ".u_NormalTexture" + cmds.connectAttr(nrm_output, glsl_nrm) diff --git a/openpype/hosts/maya/plugins/publish/validate_glsl_plugin.py b/openpype/hosts/maya/plugins/publish/validate_glsl_plugin.py new file mode 100644 index 0000000000..53c2cf548a --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_glsl_plugin.py @@ -0,0 +1,31 @@ + +from maya import cmds + +import pyblish.api +from openpype.pipeline.publish import ( + RepairAction, + ValidateContentsOrder +) + + +class ValidateGLSLPlugin(pyblish.api.InstancePlugin): + """ + Validate if the asset uses GLSL Shader + """ + + order = ValidateContentsOrder + 0.15 + families = ['gltf'] + hosts = ['maya'] + label = 'maya2glTF plugin' + actions = [RepairAction] + + def process(self, instance): + if not cmds.pluginInfo("maya2glTF", query=True, loaded=True): + raise RuntimeError("maya2glTF is not loaded") + + @classmethod + def repair(cls, instance): + """ + Repair instance by enabling the plugin + """ + return cmds.loadPlugin("maya2glTF", quiet=True) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index b590a56da6..32b141566b 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -407,6 +407,16 @@ "optional": false, "active": true }, + "ValidateGLSLMaterial": { + "enabled": true, + "optional": false, + "active": true + }, + "ValidateGLSLPlugin": { + "enabled": true, + "optional": false, + "active": true + }, "ValidateRenderImageRule": { "enabled": true, "optional": false, @@ -898,6 +908,11 @@ "optional": true, "active": true, "bake_attributes": [] + }, + "ExtractGLB": { + "enabled": true, + "active": true, + "ogsfx_path": "/maya2glTF/PBR/shaders/glTF_PBR.ogsfx" } }, "load": { 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 873bb79c95..994e2d0032 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 @@ -408,6 +408,14 @@ "key": "ValidateCurrentRenderLayerIsRenderable", "label": "Validate Current Render Layer Has Renderable Camera" }, + { + "key": "ValidateGLSLMaterial", + "label": "Validate GLSL Material" + }, + { + "key": "ValidateGLSLPlugin", + "label": "Validate GLSL Plugin" + }, { "key": "ValidateRenderImageRule", "label": "Validate Images File Rule (Workspace)" @@ -956,6 +964,30 @@ "is_list": true } ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractGLB", + "label": "Extract GLB", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + }, + { + "type": "text", + "key": "ogsfx_path", + "label": "GLSL Shader Directory" + } + ] } ] } From 64a142ef6482b61eb0967806882ccb6c70ecd91c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:03:22 +0100 Subject: [PATCH 0975/1271] OP-4643 - fix for full file paths --- openpype/plugins/publish/extract_color_transcode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index b0921688e9..99c8c87e51 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,7 +1,6 @@ import os import copy import clique - import pyblish.api from openpype.pipeline import publish From fad36c75a9e485b765a83a24a459540a9d9e7aed Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 Feb 2023 16:11:33 +0100 Subject: [PATCH 0976/1271] nuke: rendersettins lib with farm rendering class --- openpype/hosts/nuke/api/lib_rendersettings.py | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 openpype/hosts/nuke/api/lib_rendersettings.py diff --git a/openpype/hosts/nuke/api/lib_rendersettings.py b/openpype/hosts/nuke/api/lib_rendersettings.py new file mode 100644 index 0000000000..959b461c29 --- /dev/null +++ b/openpype/hosts/nuke/api/lib_rendersettings.py @@ -0,0 +1,86 @@ + +from openpype.lib import Logger +from openpype.settings import ( + get_current_project_settings, + get_system_settings +) + + +class RenderFarmSettings: + """ Class for getting farm settings from project settings + """ + log = Logger.get_logger("RenderFarmSettings") + + _active_farm_module: str = None + _farm_modules: list = [ + "deadline", "muster", "royalrender"] + _farm_plugins: dict = { + "deadline": "NukeSubmitDeadline" + } + _creator_farm_keys: list = [ + "chunk_size", "priority", "concurrent_tasks"] + + def __init__(self, project_settings=None): + """ Get project settings and active farm module + """ + self._project_settings = ( + project_settings or get_current_project_settings() + ) + # Get active farm module from system settings + self._get_active_farm_module_from_system_settings() + + def _get_active_farm_module_from_system_settings(self): + """ Get active farm module from system settings + """ + active_modules = [ + module_ + for module_ in self._farm_modules + if get_system_settings()["modules"][module_]["enabled"] + ] + if not active_modules: + raise ValueError(( + "No active farm module found in system settings." + )) + if len(active_modules) > 1: + raise ValueError(( + "Multiple active farm modules " + "found in system settings. {}".format(active_modules) + )) + + self._active_farm_module = active_modules.pop() + + @property + def active_farm_module(self): + return self._active_farm_module + + def get_rendering_attributes(self): + ''' Get rendering attributes from project settings + + Returns: + dict: rendering attributes + ''' + return_dict = {} + farm_plugin = self._farm_plugins.get(self.active_farm_module) + + if farm_plugin: + raise ValueError(( + "Farm plugin \"{}\" not found in farm plugins." + ).format(farm_plugin)) + + # Get farm module settings + module_settings = self._project_settings[self.active_farm_module] + + # Get farm plugin settings + farm_plugin_settings = ( + module_settings["publish"][farm_plugin]) + + # Get all keys from farm_plugin_settings + for key in self._creator_farm_keys: + if key not in farm_plugin_settings: + self.log.warning(( + "Key \"{}\" not found in farm plugin \"{}\" settings." + ).format(key, farm_plugin)) + continue + return_dict[key] = farm_plugin_settings[key] + + return return_dict From 073f0be7f706fd6ae222c3900cb1c02c523d1c04 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 Feb 2023 16:12:49 +0100 Subject: [PATCH 0977/1271] nuke: creators with new RenderFarmSettings class --- openpype/hosts/nuke/api/plugin.py | 2 +- .../nuke/plugins/create/create_write_prerender.py | 13 ++++++++----- .../nuke/plugins/create/create_write_render.py | 13 ++++++++----- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index d3f8357f7d..ef77e029ad 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -1236,7 +1236,7 @@ def convert_to_valid_instaces(): creator_attr["farm_chunk"] = ( node["deadlineChunkSize"].value()) if "deadlineConcurrentTasks" in node.knobs(): - creator_attr["farm_concurency"] = ( + creator_attr["farm_concurrency"] = ( node["deadlineConcurrentTasks"].value()) _remove_old_knobs(node) diff --git a/openpype/hosts/nuke/plugins/create/create_write_prerender.py b/openpype/hosts/nuke/plugins/create/create_write_prerender.py index a15f362dd1..a99bd0c4ab 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_prerender.py +++ b/openpype/hosts/nuke/plugins/create/create_write_prerender.py @@ -12,6 +12,7 @@ from openpype.lib import ( UILabelDef ) from openpype.hosts.nuke import api as napi +from openpype.hosts.nuke.api.lib_rendersettings import RenderFarmSettings class CreateWritePrerender(napi.NukeWriteCreator): @@ -50,6 +51,8 @@ class CreateWritePrerender(napi.NukeWriteCreator): self._get_reviewable_bool() ] if "farm_rendering" in self.instance_attributes: + render_farm_settings = RenderFarmSettings().get_rendering_attributes() + attr_defs.extend([ UISeparatorDef(), UILabelDef("Farm rendering attributes"), @@ -59,21 +62,21 @@ class CreateWritePrerender(napi.NukeWriteCreator): label="Priority", minimum=1, maximum=99, - default=50 + default=render_farm_settings.get("priority", 50) ), NumberDef( "farm_chunk", label="Chunk size", minimum=1, maximum=99, - default=10 + default=render_farm_settings.get("chunk_size", 10) ), NumberDef( - "farm_concurency", - label="Concurent tasks", + "farm_concurrency", + label="Concurrent tasks", minimum=1, maximum=10, - default=1 + default=render_farm_settings.get("concurrent_tasks", 1) ) ]) return attr_defs diff --git a/openpype/hosts/nuke/plugins/create/create_write_render.py b/openpype/hosts/nuke/plugins/create/create_write_render.py index 481d1d2201..bbaba212c2 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_render.py +++ b/openpype/hosts/nuke/plugins/create/create_write_render.py @@ -12,6 +12,7 @@ from openpype.lib import ( UILabelDef ) from openpype.hosts.nuke import api as napi +from openpype.hosts.nuke.api.lib_rendersettings import RenderFarmSettings class CreateWriteRender(napi.NukeWriteCreator): @@ -47,6 +48,8 @@ class CreateWriteRender(napi.NukeWriteCreator): self._get_reviewable_bool() ] if "farm_rendering" in self.instance_attributes: + render_farm_settings = RenderFarmSettings().get_rendering_attributes() + attr_defs.extend([ UISeparatorDef(), UILabelDef("Farm rendering attributes"), @@ -56,21 +59,21 @@ class CreateWriteRender(napi.NukeWriteCreator): label="Priority", minimum=1, maximum=99, - default=50 + default=render_farm_settings.get("priority", 50) ), NumberDef( "farm_chunk", label="Chunk size", minimum=1, maximum=99, - default=10 + default=render_farm_settings.get("chunk_size", 10) ), NumberDef( - "farm_concurency", - label="Concurent tasks", + "farm_concurrency", + label="Concurrent tasks", minimum=1, maximum=10, - default=1 + default=render_farm_settings.get("concurrent_tasks", 1) ) ]) return attr_defs From 5aaf8eb18a021b76249c561d499e3e664c983886 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 Feb 2023 16:13:10 +0100 Subject: [PATCH 0978/1271] nuke: publishing with new settings --- .../hosts/nuke/plugins/publish/collect_writes.py | 16 ++++++++-------- .../plugins/publish/submit_nuke_deadline.py | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/collect_writes.py b/openpype/hosts/nuke/plugins/publish/collect_writes.py index 3054e5a30c..0aa044f06d 100644 --- a/openpype/hosts/nuke/plugins/publish/collect_writes.py +++ b/openpype/hosts/nuke/plugins/publish/collect_writes.py @@ -132,14 +132,14 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): self.log.info("Publishing rendered frames ...") elif render_target == "farm": - farm_priority = creator_attributes.get("farm_priority") - farm_chunk = creator_attributes.get("farm_chunk") - farm_concurency = creator_attributes.get("farm_concurency") - instance.data.update({ - "deadlineChunkSize": farm_chunk or 1, - "deadlinePriority": farm_priority or 50, - "deadlineConcurrentTasks": farm_concurency or 0 - }) + farm_keys = ["farm_chunk", "farm_priority", "farm_concurrency"] + for key in farm_keys: + # Skip if key is not in creator attributes + if key not in creator_attributes: + continue + # Add farm attributes to instance + instance.data[key] = creator_attributes[key] + # Farm rendering instance.data["transfer"] = False instance.data["farm"] = True diff --git a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py index faa66effbd..0f8c69629e 100644 --- a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -162,16 +162,16 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): pass # define chunk and priority - chunk_size = instance.data["deadlineChunkSize"] - if chunk_size == 0 and self.chunk_size: + chunk_size = instance.data.get("farm_chunk") + if not chunk_size: chunk_size = self.chunk_size # define chunk and priority - concurrent_tasks = instance.data["deadlineConcurrentTasks"] - if concurrent_tasks == 0 and self.concurrent_tasks: + concurrent_tasks = instance.data.get("farm_concurrency") + if not concurrent_tasks: concurrent_tasks = self.concurrent_tasks - priority = instance.data["deadlinePriority"] + priority = instance.data.get("farm_priority") if not priority: priority = self.priority From d2f8407111905b621e29b27202ef3e59afa63983 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:59:13 +0100 Subject: [PATCH 0979/1271] OP-4643 - fix files to delete --- .../plugins/publish/extract_color_transcode.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 99c8c87e51..61e29697d6 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -222,6 +222,21 @@ class ExtractOIIOTranscode(publish.Extractor): renamed_files.append(file_name) new_repre["files"] = renamed_files + def _rename_in_representation(self, new_repre, files_to_convert, + output_extension): + """Replace old extension with new one everywhere in representation.""" + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + def _translate_to_sequence(self, files_to_convert): """Returns original list or list with filename formatted in single sequence format. From c038fbf884f872ec717588290b9ba9273f219d36 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:18:33 +0100 Subject: [PATCH 0980/1271] OP-4643 - fix no tags in repre --- openpype/plugins/publish/extract_color_transcode.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 61e29697d6..aca4adc40c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -175,6 +175,8 @@ class ExtractOIIOTranscode(publish.Extractor): if new_repre.get("tags") is None: new_repre["tags"] = [] for tag in output_def["tags"]: + if not new_repre.get("tags"): + new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) From 195e9b436047a797bd9c99ac37ba5080690e3943 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:13:59 +0100 Subject: [PATCH 0981/1271] OP-4643 - name of new representation from output definition key --- .../publish/extract_color_transcode.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index aca4adc40c..bd81dd6087 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -225,10 +225,22 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["files"] = renamed_files def _rename_in_representation(self, new_repre, files_to_convert, - output_extension): - """Replace old extension with new one everywhere in representation.""" - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension + output_name, output_extension): + """Replace old extension with new one everywhere in representation. + + Args: + new_repre (dict) + files_to_convert (list): of filenames from repre["files"], + standardized to always list + output_name (str): key of output definition from Settings, + if "" token used, keep original repre name + output_extension (str): extension from output definition + """ + if output_name != "passthrough": + new_repre["name"] = output_name + if not output_extension: + return + new_repre["ext"] = output_extension renamed_files = [] From 4a70ec9c54fbf7caec8ac8bd1f17adecbd9dd6b0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 12:07:00 +0100 Subject: [PATCH 0982/1271] Fix - added missed scopes for Slack bot --- openpype/modules/slack/manifest.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 7a65cc5915..233c39fbaf 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,6 +19,8 @@ oauth_config: - chat:write.public - files:write - channels:read + - users:read + - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From 7f94f7ef7183a51bdfa1bd40078a9183ee50e1bd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 15:14:14 +0100 Subject: [PATCH 0983/1271] OP-4643 - allow new repre to stay One might want to delete outputs with 'delete' tag, but repre must stay there at least until extract_review. More universal new tag might be created for this. --- openpype/plugins/publish/extract_color_transcode.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index bd81dd6087..4892a00fbe 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -175,8 +175,6 @@ class ExtractOIIOTranscode(publish.Extractor): if new_repre.get("tags") is None: new_repre["tags"] = [] for tag in output_def["tags"]: - if not new_repre.get("tags"): - new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) @@ -192,6 +190,12 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] + # TODO implement better way, for now do not delete new repre + # new repre might have 'delete' tag to removed, but it first must + # be there for review to be created + if "newly_added" in tags: + tags.remove("newly_added") + continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) From cce048fd3e0f50441fa9f893dfb9efebe43d95a8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:21:25 +0100 Subject: [PATCH 0984/1271] OP-4642 - refactored newly added representations --- openpype/plugins/publish/extract_color_transcode.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4892a00fbe..a6fa710425 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -190,12 +190,6 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] - # TODO implement better way, for now do not delete new repre - # new repre might have 'delete' tag to removed, but it first must - # be there for review to be created - if "newly_added" in tags: - tags.remove("newly_added") - continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) From 9eaa0d1ff8e0882a2778fce44f09fba3f2cccecd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 12:12:35 +0100 Subject: [PATCH 0985/1271] OP-4643 - split command line arguments to separate items Reuse existing method from ExtractReview, put it into transcoding.py --- openpype/plugins/publish/extract_review.py | 27 +++------------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 0f6dacba18..e80141fc4a 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,6 +22,7 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, + split_cmd_args ) @@ -670,7 +671,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) + ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -723,28 +724,6 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) - def split_ffmpeg_args(self, in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args - def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -764,7 +743,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = self.split_ffmpeg_args(output_args) + output_args = split_cmd_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 751586fd415ea6aa10327cca8fb57f2f1657b9d7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:11:11 +0100 Subject: [PATCH 0986/1271] Revert "Fix - added missed scopes for Slack bot" This reverts commit 5e0c4a3ab1432e120b8f0c324f899070f1a5f831. --- openpype/modules/slack/manifest.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 233c39fbaf..7a65cc5915 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,8 +19,6 @@ oauth_config: - chat:write.public - files:write - channels:read - - users:read - - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From 84ccfa60f43c1fa5a575b62c79779d66dfc2951d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 15:06:16 +0100 Subject: [PATCH 0987/1271] OP-4643 - added documentation --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index d904080ad1..b320b5502f 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -63,7 +63,7 @@ Example here describes use case for creation of new color coded review of png im ![global_oiio_transcode](assets/global_oiio_transcode.png) Another use case is to transcode in Maya only `beauty` render layers and use collected `Display` and `View` colorspaces from DCC. -![global_oiio_transcode_in_Maya](assets/global_oiio_transcode.png) +![global_oiio_transcode_in_Maya](assets/global_oiio_transcode.png)n ## Profile filters From 72a3572d9527093290ebd600b538e2375e690861 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:56:21 +0100 Subject: [PATCH 0988/1271] Revert "OP-4643 - split command line arguments to separate items" This reverts commit deaad39437501f18fc3ba4be8b1fc5f0ee3be65d. --- openpype/lib/transcoding.py | 3 ++- openpype/plugins/publish/extract_review.py | 27 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index c0bda2aa37..e5e21195e5 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(split_cmd_args(additional_command_args)) + oiio_cmd.extend(additional_command_args) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1132,3 +1132,4 @@ def split_cmd_args(in_args): continue splitted_args.extend(arg.split(" ")) return splitted_args + diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index e80141fc4a..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,7 +22,6 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, - split_cmd_args ) @@ -671,7 +670,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) + ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -724,6 +723,28 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) + def split_ffmpeg_args(self, in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args + def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -743,7 +764,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = split_cmd_args(output_args) + output_args = self.split_ffmpeg_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 656318f122fe4cdc167e780b7737c8780890582b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 Feb 2023 16:59:40 +0100 Subject: [PATCH 0989/1271] adding log to render farm settings class init --- openpype/hosts/nuke/api/lib_rendersettings.py | 10 ++++++++-- .../nuke/plugins/create/create_write_prerender.py | 4 +++- .../hosts/nuke/plugins/create/create_write_render.py | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/nuke/api/lib_rendersettings.py b/openpype/hosts/nuke/api/lib_rendersettings.py index 959b461c29..4d5440fe48 100644 --- a/openpype/hosts/nuke/api/lib_rendersettings.py +++ b/openpype/hosts/nuke/api/lib_rendersettings.py @@ -20,9 +20,12 @@ class RenderFarmSettings: _creator_farm_keys: list = [ "chunk_size", "priority", "concurrent_tasks"] - def __init__(self, project_settings=None): + def __init__(self, project_settings=None, log=None): """ Get project settings and active farm module """ + if log: + self.log = log + self._project_settings = ( project_settings or get_current_project_settings() ) @@ -61,8 +64,9 @@ class RenderFarmSettings: ''' return_dict = {} farm_plugin = self._farm_plugins.get(self.active_farm_module) + self.log.debug("Farm plugin: \"{}\"".format(farm_plugin)) - if farm_plugin: + if not farm_plugin: raise ValueError(( "Farm plugin \"{}\" not found in farm plugins." ).format(farm_plugin)) @@ -73,6 +77,8 @@ class RenderFarmSettings: # Get farm plugin settings farm_plugin_settings = ( module_settings["publish"][farm_plugin]) + self.log.debug( + "Farm plugin settings: \"{}\"".format(farm_plugin_settings)) # Get all keys from farm_plugin_settings for key in self._creator_farm_keys: diff --git a/openpype/hosts/nuke/plugins/create/create_write_prerender.py b/openpype/hosts/nuke/plugins/create/create_write_prerender.py index a99bd0c4ab..411a79dbf4 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_prerender.py +++ b/openpype/hosts/nuke/plugins/create/create_write_prerender.py @@ -51,7 +51,9 @@ class CreateWritePrerender(napi.NukeWriteCreator): self._get_reviewable_bool() ] if "farm_rendering" in self.instance_attributes: - render_farm_settings = RenderFarmSettings().get_rendering_attributes() + render_farm_settings = RenderFarmSettings( + log=self.log).get_rendering_attributes() + attr_defs.extend([ UISeparatorDef(), diff --git a/openpype/hosts/nuke/plugins/create/create_write_render.py b/openpype/hosts/nuke/plugins/create/create_write_render.py index bbaba212c2..a51661425f 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_render.py +++ b/openpype/hosts/nuke/plugins/create/create_write_render.py @@ -48,7 +48,8 @@ class CreateWriteRender(napi.NukeWriteCreator): self._get_reviewable_bool() ] if "farm_rendering" in self.instance_attributes: - render_farm_settings = RenderFarmSettings().get_rendering_attributes() + render_farm_settings = RenderFarmSettings( + log=self.log).get_rendering_attributes() attr_defs.extend([ UISeparatorDef(), From b09bc8d4a3e85eb3187b08b1672ca8bd0528dd99 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 Feb 2023 17:00:19 +0100 Subject: [PATCH 0990/1271] removing unused items from settings --- openpype/settings/defaults/project_settings/deadline.json | 1 - .../schemas/projects_schema/schema_project_deadline.json | 5 ----- 2 files changed, 6 deletions(-) diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index 7183603c4b..6b6f2d465b 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -52,7 +52,6 @@ "enabled": true, "optional": false, "active": true, - "use_published": true, "priority": 50, "chunk_size": 10, "concurrent_tasks": 1, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index a320dfca4f..bb5a65e1b7 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -285,11 +285,6 @@ "key": "active", "label": "Active" }, - { - "type": "boolean", - "key": "use_published", - "label": "Use Published scene" - }, { "type": "splitter" }, From f2274ec2e963e6eb5e10233ff593804e02ab1f5c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 Feb 2023 17:00:51 +0100 Subject: [PATCH 0991/1271] removing none existent host also fix typo --- .../plugins/publish/submit_nuke_deadline.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py index 0f8c69629e..b4b59c4c77 100644 --- a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -23,7 +23,7 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): label = "Submit to Deadline" order = pyblish.api.IntegratorOrder + 0.1 - hosts = ["nuke", "nukestudio"] + hosts = ["nuke"] families = ["render.farm", "prerender.farm"] optional = True targets = ["local"] @@ -141,7 +141,7 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): exe_node_name, start_frame, end_frame, - responce_data=None + response_data=None ): render_dir = os.path.normpath(os.path.dirname(render_path)) batch_name = os.path.basename(script_path) @@ -152,8 +152,8 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): output_filename_0 = self.preview_fname(render_path) - if not responce_data: - responce_data = {} + if not response_data: + response_data = {} try: # Ensure render folder exists @@ -244,11 +244,11 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): "AuxFiles": [] } - if responce_data.get("_id"): + if response_data.get("_id"): payload["JobInfo"].update({ "JobType": "Normal", - "BatchName": responce_data["Props"]["Batch"], - "JobDependency0": responce_data["_id"], + "BatchName": response_data["Props"]["Batch"], + "JobDependency0": response_data["_id"], "ChunkSize": 99999999 }) From 59c3510a0219fe726e52816e4d1ff20015455794 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 11:26:06 +0100 Subject: [PATCH 0992/1271] don't add opacity to layers info --- openpype/hosts/tvpaint/api/lib.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/api/lib.py b/openpype/hosts/tvpaint/api/lib.py index 312a211d49..a0cf761870 100644 --- a/openpype/hosts/tvpaint/api/lib.py +++ b/openpype/hosts/tvpaint/api/lib.py @@ -50,7 +50,8 @@ def parse_layers_data(data): "group_id": int(group_id), "visible": visible == "ON", "position": int(position), - "opacity": int(opacity), + # Opacity from 'tv_layerinfo' is always set to '0' so it's unusable + # "opacity": int(opacity), "name": name, "type": layer_type, "frame_start": int(frame_start), From 0f8a9c58a758fda407d777232112b7723d0e4332 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 11:26:22 +0100 Subject: [PATCH 0993/1271] added is_current information to layers info --- openpype/hosts/tvpaint/api/lib.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/tvpaint/api/lib.py b/openpype/hosts/tvpaint/api/lib.py index a0cf761870..49846d7f29 100644 --- a/openpype/hosts/tvpaint/api/lib.py +++ b/openpype/hosts/tvpaint/api/lib.py @@ -43,7 +43,7 @@ def parse_layers_data(data): layer_id, group_id, visible, position, opacity, name, layer_type, frame_start, frame_end, prelighttable, postlighttable, - selected, editable, sencil_state + selected, editable, sencil_state, is_current ) = layer_raw.split("|") layer = { "layer_id": int(layer_id), @@ -60,7 +60,8 @@ def parse_layers_data(data): "postlighttable": postlighttable == "1", "selected": selected == "1", "editable": editable == "1", - "sencil_state": sencil_state + "sencil_state": sencil_state, + "is_current": is_current == "1" } layers.append(layer) return layers @@ -88,15 +89,17 @@ def get_layers_data_george_script(output_filepath, layer_ids=None): " selected editable sencilState" ), # Check if layer ID match `tv_LayerCurrentID` + "is_current=0", "IF CMP(current_layer_id, layer_id)==1", # - mark layer as selected if layer id match to current layer id + "is_current=1", "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" + "postlighttable'|'selected'|'editable'|'sencilState'|'is_current" ), # Write data to output file "tv_writetextfile \"strict\" \"append\" '\"'output_path'\"' line", From 80151ca8800ca3fdcbcdebc092f27f6277201aff Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 11:39:50 +0100 Subject: [PATCH 0994/1271] disable review on render layer/pass by default --- openpype/settings/defaults/project_settings/tvpaint.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index e06a67a254..9173a8c3d5 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -31,13 +31,13 @@ "default_variants": [] }, "create_render_layer": { - "mark_for_review": true, + "mark_for_review": false, "default_pass_name": "beauty", "default_variant": "Main", "default_variants": [] }, "create_render_pass": { - "mark_for_review": true, + "mark_for_review": false, "default_variant": "Main", "default_variants": [] }, From abc36c8357e0773e35b5c312663d80ac3afbbfb1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 12:08:08 +0100 Subject: [PATCH 0995/1271] fix detailed descriptions --- openpype/hosts/tvpaint/plugins/create/create_render.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 7e85977b11..d996c06818 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -66,7 +66,7 @@ RENDER_LAYER_DETAILED_DESCRIPTIONS = ( Be aware Render Layer is not TVPaint layer. All TVPaint layers in the scene with the color group id are rendered in the -beauty pass. To create sub passes use Render Layer creator which is +beauty pass. To create sub passes use Render Pass creator which is dependent on existence of render layer instance. The group can represent an asset (tree) or different part of scene that consist @@ -82,8 +82,8 @@ could be Render Layer which has 'Arm', 'Head' and 'Body' as Render Passes. RENDER_PASS_DETAILED_DESCRIPTIONS = ( """Render Pass is sub part of Render Layer. -Render Pass can consist of one or more TVPaint layers. Render Layers must -belong to a Render Layer. Marker TVPaint layers will change it's group color +Render Pass can consist of one or more TVPaint layers. Render Pass must +belong to a Render Layer. Marked TVPaint layers will change it's group color to match group color of Render Layer. """ ) From 79a40902e0a16518097240dd8bffd19c90fecbdd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 14:49:02 +0100 Subject: [PATCH 0996/1271] Better error message --- openpype/hosts/tvpaint/plugins/create/create_render.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index d996c06818..563d16f847 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -461,7 +461,10 @@ class CreateRenderPass(TVPaintCreator): "render_layer_instance_id" ) if not render_layer_instance_id: - raise CreatorError("Missing RenderLayer instance") + raise CreatorError(( + "You cannot create a Render Pass without a Render Layer." + " Please select one first" + )) render_layer_instance = self.create_context.instances_by_id.get( render_layer_instance_id From ba792f648b6aeea6299a5137afd23de4dc677a3f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 14:52:30 +0100 Subject: [PATCH 0997/1271] use 'list_instances' instead of existing instances --- .../hosts/tvpaint/plugins/create/create_render.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 563d16f847..6a3788cc08 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -601,13 +601,16 @@ class CreateRenderPass(TVPaintCreator): ] def get_pre_create_attr_defs(self): + # Find available Render Layers + # - instances are created after creators reset + current_instances = self.host.list_instances() render_layers = [ { - "value": instance.id, - "label": instance.label + "value": instance["instance_id"], + "label": instance["subset"] } - for instance in self.create_context.instances - if instance.creator_identifier == CreateRenderlayer.identifier + for instance in current_instances + if instance["creator_identifier"] == CreateRenderlayer.identifier ] if not render_layers: render_layers.append({"value": None, "label": "N/A"}) From 8218e18edf352d377c901197ecf9408988e974e2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 14:52:53 +0100 Subject: [PATCH 0998/1271] added label with hint for artist --- openpype/hosts/tvpaint/plugins/create/create_render.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 6a3788cc08..d356a07687 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -41,6 +41,7 @@ from openpype.client import get_asset_by_name from openpype.lib import ( prepare_template_data, AbstractAttrDef, + UILabelDef, UISeparatorDef, EnumDef, TextDef, @@ -621,6 +622,9 @@ class CreateRenderPass(TVPaintCreator): label="Render Layer", items=render_layers ), + UILabelDef( + "NOTE: Try to hit refresh if you don't see a Render Layer" + ), BoolDef( "mark_for_review", label="Review", From 227f1402fe03590b4350124db0a3894335967c04 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 14:57:34 +0100 Subject: [PATCH 0999/1271] change how instance attributes are filled --- .../tvpaint/plugins/create/create_render.py | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index d356a07687..9711024c79 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -633,7 +633,34 @@ class CreateRenderPass(TVPaintCreator): ] def get_instance_attr_defs(self): - return self.get_pre_create_attr_defs() + # Find available Render Layers + current_instances = self.create_context.instances + render_layers = [ + { + "value": instance.id, + "label": instance.label + } + for instance in current_instances + if instance.creator_identifier == CreateRenderlayer.identifier + ] + if not render_layers: + render_layers.append({"value": None, "label": "N/A"}) + + return [ + EnumDef( + "render_layer_instance_id", + label="Render Layer", + items=render_layers + ), + UILabelDef( + "NOTE: Try to hit refresh if you don't see a Render Layer" + ), + BoolDef( + "mark_for_review", + label="Review", + default=self.mark_for_review + ) + ] class TVPaintAutoDetectRenderCreator(TVPaintCreator): From de15f1bc3a135738f218fe27db928aef5cdb1af9 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 23 Feb 2023 18:50:21 +0100 Subject: [PATCH 1000/1271] Updated render_local.py to not only process the first instance Moved the __hasRun to render_once() so the check only happens with the rendering. Currently only the first render node gets the representations added --- .../fusion/plugins/publish/render_local.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 53d8eb64e1..49aaf63a61 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -18,15 +18,9 @@ class Fusionlocal(pyblish.api.InstancePlugin): def process(self, instance): - # This plug-in runs only once and thus assumes all instances - # currently will render the same frame range context = instance.context - key = f"__hasRun{self.__class__.__name__}" - if context.data.get(key, False): - return - - context.data[key] = True - + + # Start render self.render_once(context) frame_start = context.data["frameStartHandle"] @@ -60,6 +54,14 @@ class Fusionlocal(pyblish.api.InstancePlugin): def render_once(self, context): """Render context comp only once, even with more render instances""" + + # This plug-in assumes all render nodes get rendered at the same time + # to speed up the rendering. The check below makes sure that we only + # execute the rendering once and not for each instance. + key = f"__hasRun{self.__class__.__name__}" + if context.data.get(key, False): + return + context.data[key] = True current_comp = context.data["currentComp"] frame_start = context.data["frameStartHandle"] From d8041562d0e08afd7dfc34158591142158d51e0e Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 23 Feb 2023 18:52:50 +0100 Subject: [PATCH 1001/1271] Fixed Hounds notes --- openpype/hosts/fusion/plugins/publish/render_local.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 49aaf63a61..c22074d6c6 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -19,7 +19,7 @@ class Fusionlocal(pyblish.api.InstancePlugin): def process(self, instance): context = instance.context - + # Start render self.render_once(context) @@ -54,7 +54,7 @@ class Fusionlocal(pyblish.api.InstancePlugin): def render_once(self, context): """Render context comp only once, even with more render instances""" - + # This plug-in assumes all render nodes get rendered at the same time # to speed up the rendering. The check below makes sure that we only # execute the rendering once and not for each instance. From 31d7b18e9ff6d5561ddc9d7ea2d003ae3a5c86bd Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 23 Feb 2023 19:18:41 +0100 Subject: [PATCH 1002/1271] Changed to f-string --- openpype/hosts/fusion/plugins/create/create_saver.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index 99aa7583f1..59fc198243 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -39,8 +39,9 @@ class CreateSaver(Creator): # Check file format settings are available if saver[file_format] is None: - raise RuntimeError("File format is not set to {}, " - "this is a bug".format(file_format)) + raise RuntimeError( + f"File format is not set to {file_format}, this is a bug" + ) # Set file format attributes saver[file_format]["Depth"] = 1 # int8 | int16 | float32 | other @@ -110,7 +111,7 @@ class CreateSaver(Creator): def _imprint(self, tool, data): # Save all data in a "openpype.{key}" = value data for key, value in data.items(): - tool.SetData("openpype.{}".format(key), value) + tool.SetData(f"openpype.{key}", value) def _update_tool_with_data(self, tool, data): """Update tool node name and output path based on subset data""" @@ -123,7 +124,7 @@ class CreateSaver(Creator): # Subset change detected # Update output filepath workdir = os.path.normpath(legacy_io.Session["AVALON_WORKDIR"]) - filename = "{}..exr".format(subset) + filename = f"{subset}..exr" filepath = os.path.join(workdir, "render", subset, filename) tool["Clip"] = filepath From 4d3442db70c025a67e5503fb8a728e2c3acd7b96 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 23 Feb 2023 19:19:24 +0100 Subject: [PATCH 1003/1271] Add subset to instance_data --- openpype/hosts/fusion/plugins/create/create_saver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index 59fc198243..5725b5d0d1 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -33,6 +33,7 @@ class CreateSaver(Creator): args = (-32768, -32768) # Magical position numbers saver = comp.AddTool("Saver", *args) + instance_data["subset"] = subset_name self._update_tool_with_data(saver, data=instance_data) saver["OutputFormat"] = file_format From 8afd6f4f359cf1bdea8f08386a983a1782e35871 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 23 Feb 2023 19:19:52 +0100 Subject: [PATCH 1004/1271] Change render depth to Auto instead of forcing int16 as it was --- openpype/hosts/fusion/plugins/create/create_saver.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index 5725b5d0d1..439064770e 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -45,8 +45,9 @@ class CreateSaver(Creator): ) # Set file format attributes - saver[file_format]["Depth"] = 1 # int8 | int16 | float32 | other - saver[file_format]["SaveAlpha"] = 0 + saver[file_format]["Depth"] = 0 # Auto | float16 | float32 + # TODO Is this needed? + saver[file_format]["SaveAlpha"] = 1 self._imprint(saver, instance_data) From f840347f4e27a3f7474fe7e29facf9fa02f4e7ef Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 23 Feb 2023 19:20:20 +0100 Subject: [PATCH 1005/1271] Import get_current_comp from API directly --- openpype/hosts/fusion/plugins/create/create_workfile.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_workfile.py b/openpype/hosts/fusion/plugins/create/create_workfile.py index 783d3a147a..05dbcddd52 100644 --- a/openpype/hosts/fusion/plugins/create/create_workfile.py +++ b/openpype/hosts/fusion/plugins/create/create_workfile.py @@ -2,7 +2,9 @@ import collections import qtawesome -import openpype.hosts.fusion.api as api +from openpype.hosts.fusion.api import ( + get_current_comp +) from openpype.client import get_asset_by_name from openpype.pipeline import ( AutoCreator, @@ -35,7 +37,7 @@ class FusionWorkfileCreator(AutoCreator): def collect_instances(self): - comp = api.get_current_comp() + comp = get_current_comp() data = comp.GetData(self.data_key) if not data: return @@ -69,7 +71,7 @@ class FusionWorkfileCreator(AutoCreator): def create(self, options=None): - comp = api.get_current_comp() + comp = get_current_comp() if not comp: self.log.error("Unable to find current comp") return From f5a40056ab3e8cf18f5562d8961174fe422ce3e1 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 23 Feb 2023 19:21:00 +0100 Subject: [PATCH 1006/1271] Pass "data" to get_dynamic_data() was missing --- openpype/hosts/fusion/plugins/create/create_workfile.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_workfile.py b/openpype/hosts/fusion/plugins/create/create_workfile.py index 05dbcddd52..917780c56e 100644 --- a/openpype/hosts/fusion/plugins/create/create_workfile.py +++ b/openpype/hosts/fusion/plugins/create/create_workfile.py @@ -99,8 +99,12 @@ class FusionWorkfileCreator(AutoCreator): "variant": self.default_variant } data.update(self.get_dynamic_data( - self.default_variant, task_name, asset_doc, - project_name, host_name + self.default_variant, + task_name, + asset_doc, + project_name, + host_name, + data )) instance = CreatedInstance( From f0cd353301c6192ec0203a83de2ec7b31e4979c6 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 23 Feb 2023 19:29:03 +0100 Subject: [PATCH 1007/1271] Fixed Hound's notes --- openpype/hosts/fusion/api/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/api/pipeline.py b/openpype/hosts/fusion/api/pipeline.py index eb097e5c5b..2d0a1da8fa 100644 --- a/openpype/hosts/fusion/api/pipeline.py +++ b/openpype/hosts/fusion/api/pipeline.py @@ -463,4 +463,4 @@ class FusionEventHandler(QtCore.QObject): # Comp Opened elif what in {"Comp_Opened"}: - emit_event("open", data=event) \ No newline at end of file + emit_event("open", data=event) From 79b73efa28fd6edfd49f7ee8aa3750a9bc910c86 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 24 Feb 2023 12:41:26 +0800 Subject: [PATCH 1008/1271] color convert only enabled when there is arnold plugin --- openpype/hosts/maya/api/lib.py | 20 +++++---- .../maya/plugins/publish/extract_look.py | 43 ++++++++++++------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 614b04903f..a4d823b7a1 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -16,15 +16,6 @@ from six import string_types from maya import cmds, mel import maya.api.OpenMaya as om -from arnold import ( - AiTextureGetBitDepth, - AiTextureGetFormat, - AiTextureInvalidate, - # types - AI_TYPE_BYTE, - AI_TYPE_INT, - AI_TYPE_UINT -) from openpype.client import ( get_project, @@ -3532,6 +3523,10 @@ def image_info(file_path): Returns: dict: Dictionary with the information about the texture file. """ + from arnold import ( + AiTextureGetBitDepth, + AiTextureGetFormat +) # Get Texture Information img_info = {'filename': file_path} if os.path.isfile(file_path): @@ -3554,6 +3549,13 @@ def guess_colorspace(img_info): str: color space name use in the `--colorconvert` option of maketx. """ + from arnold import ( + AiTextureInvalidate, + # types + AI_TYPE_BYTE, + AI_TYPE_INT, + AI_TYPE_UINT +) try: if img_info['bit_depth'] <= 16: if img_info['format'] in (AI_TYPE_BYTE, AI_TYPE_INT, AI_TYPE_UINT): # noqa diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index d9c19c9139..ca110ceadd 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -537,33 +537,44 @@ class ExtractLook(publish.Extractor): if linearize: if cmds.colorManagementPrefs(query=True, cmEnabled=True): render_colorspace = cmds.colorManagementPrefs(query=True, renderingSpaceName=True) # noqa + config_path = cmds.colorManagementPrefs(query=True, + configFilePath=True) + if not os.path.exists(config_path): + raise RuntimeError("No OCIO config path found!") + color_space_attr = resource["node"] + ".colorSpace" try: color_space = cmds.getAttr(color_space_attr) except ValueError: # node doesn't have color space attribute - img_info = image_info(filepath) - color_space = guess_colorspace(img_info) + if cmds.loadPlugin("mtoa", quiet=True): + img_info = image_info(filepath) + color_space = guess_colorspace(img_info) + else: + color_space = "Raw" self.log.info("tx: converting {0} -> {1}".format(color_space, render_colorspace)) # noqa + additional_args.extend(["--colorconvert", color_space, render_colorspace]) else: - img_info = image_info(filepath) - color_space = guess_colorspace(img_info) - if color_space == "sRGB": - self.log.info("tx: converting sRGB -> linear") - additional_args.extend(["--colorconvert", - "sRGB", - "Raw"]) - else: - self.log.info("tx: texture's colorspace " - "is already linear") - config_path = cmds.colorManagementPrefs(query=True, - configFilePath=True) - if not os.path.exists(config_path): - raise RuntimeError("No OCIO config path found!") + if cmds.loadPlugin("mtoa", quiet=True): + img_info = image_info(filepath) + color_space = guess_colorspace(img_info) + if color_space == "sRGB": + self.log.info("tx: converting sRGB -> linear") + additional_args.extend(["--colorconvert", + "sRGB", + "Raw"]) + else: + self.log.info("tx: texture's colorspace " + "is already linear") + else: + self.log.warning("cannot guess the colorspace" + "color conversion won't be available!") + + additional_args.extend(["--colorconfig", config_path]) # Ensure folder exists if not os.path.exists(os.path.dirname(converted)): From 5aba18e089e4947ce7b19d3875ee284d164e2b35 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 10:53:44 +0100 Subject: [PATCH 1009/1271] extract sequence can ignore layer's transparency --- .../plugins/publish/extract_sequence.py | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py b/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py index f2856c72a9..1a21715aa2 100644 --- a/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py +++ b/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py @@ -59,6 +59,10 @@ class ExtractSequence(pyblish.api.Extractor): ) ) + ignore_layers_transparency = instance.data.get( + "ignoreLayersTransparency", False + ) + family_lowered = instance.data["family"].lower() mark_in = instance.context.data["sceneMarkIn"] mark_out = instance.context.data["sceneMarkOut"] @@ -114,7 +118,11 @@ class ExtractSequence(pyblish.api.Extractor): else: # Render output result = self.render( - output_dir, mark_in, mark_out, filtered_layers + output_dir, + mark_in, + mark_out, + filtered_layers, + ignore_layers_transparency ) output_filepaths_by_frame_idx, thumbnail_fullpath = result @@ -274,7 +282,9 @@ class ExtractSequence(pyblish.api.Extractor): return output_filepaths_by_frame_idx, thumbnail_filepath - def render(self, output_dir, mark_in, mark_out, layers): + def render( + self, output_dir, mark_in, mark_out, layers, ignore_layer_opacity + ): """ Export images from TVPaint. Args: @@ -282,6 +292,7 @@ class ExtractSequence(pyblish.api.Extractor): mark_in (int): Starting frame index from which export will begin. mark_out (int): On which frame index export will end. layers (list): List of layers to be exported. + ignore_layer_opacity (bool): Layer's opacity will be ignored. Returns: tuple: With 2 items first is list of filenames second is path to @@ -323,7 +334,7 @@ class ExtractSequence(pyblish.api.Extractor): for layer_id, render_data in extraction_data_by_layer_id.items(): layer = layers_by_id[layer_id] filepaths_by_layer_id[layer_id] = self._render_layer( - render_data, layer, output_dir + render_data, layer, output_dir, ignore_layer_opacity ) # Prepare final filepaths where compositing should store result @@ -380,7 +391,9 @@ class ExtractSequence(pyblish.api.Extractor): red, green, blue = self.review_bg return (red, green, blue) - def _render_layer(self, render_data, layer, output_dir): + def _render_layer( + self, render_data, layer, output_dir, ignore_layer_opacity + ): frame_references = render_data["frame_references"] filenames_by_frame_index = render_data["filenames_by_frame_index"] @@ -389,6 +402,12 @@ class ExtractSequence(pyblish.api.Extractor): "tv_layerset {}".format(layer_id), "tv_SaveMode \"PNG\"" ] + # Set density to 100 and store previous opacity + if ignore_layer_opacity: + george_script_lines.extend([ + "tv_layerdensity 100", + "orig_opacity = result", + ]) filepaths_by_frame = {} frames_to_render = [] @@ -409,6 +428,10 @@ class ExtractSequence(pyblish.api.Extractor): # Store image to output george_script_lines.append("tv_saveimage \"{}\"".format(dst_path)) + # Set density back to origin opacity + if ignore_layer_opacity: + george_script_lines.append("tv_layerdensity orig_opacity") + self.log.debug("Rendering Exposure frames {} of layer {} ({})".format( ",".join(frames_to_render), layer_id, layer["name"] )) From 87b818e37eaf419654468029ff8558215f9dd847 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 10:58:49 +0100 Subject: [PATCH 1010/1271] collect render instances can set 'ignoreLayersTransparency' --- .../tvpaint/plugins/publish/collect_render_instances.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py b/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py index ba89deac5d..e89fbf7882 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_render_instances.py @@ -9,6 +9,8 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): hosts = ["tvpaint"] families = ["render", "review"] + ignore_render_pass_transparency = False + def process(self, instance): context = instance.context creator_identifier = instance.data["creator_identifier"] @@ -63,6 +65,9 @@ class CollectRenderInstances(pyblish.api.InstancePlugin): for layer in layers_data if layer["name"] in layer_names ] + instance.data["ignoreLayersTransparency"] = ( + self.ignore_render_pass_transparency + ) render_layer_data = None render_layer_id = creator_attributes["render_layer_instance_id"] From 4b127dedd68900b93062e4a0394f6afb8f1f1d20 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 10:59:03 +0100 Subject: [PATCH 1011/1271] added settings for Collect Render Instances --- .../defaults/project_settings/tvpaint.json | 3 +++ .../projects_schema/schema_project_tvpaint.json | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index 0b6d3d7e81..87d3601ae4 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -49,6 +49,9 @@ } }, "publish": { + "CollectRenderInstances": { + "ignore_render_pass_transparency": true + }, "ExtractSequence": { "review_bg": [ 255, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json index 57016a8311..708b688ba5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json @@ -241,6 +241,20 @@ "key": "publish", "label": "Publish plugins", "children": [ + { + "type": "dict", + "collapsible": true, + "key": "CollectRenderInstances", + "label": "Collect Render Instances", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "ignore_render_pass_transparency", + "label": "Ignore Render Pass opacity" + } + ] + }, { "type": "dict", "collapsible": true, From bdd869b0a8a953fd8e707dd1caf75c2290961991 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 Feb 2023 11:19:49 +0100 Subject: [PATCH 1012/1271] disable ignore transparency by default --- openpype/settings/defaults/project_settings/tvpaint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index 87d3601ae4..e06a67a254 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -50,7 +50,7 @@ }, "publish": { "CollectRenderInstances": { - "ignore_render_pass_transparency": true + "ignore_render_pass_transparency": false }, "ExtractSequence": { "review_bg": [ From 131e27008c0e1883f7241f731d0ddfd42df3f77b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 10:39:10 +0100 Subject: [PATCH 1013/1271] fix access to not existing settings key --- openpype/hosts/tvpaint/plugins/create/create_render.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render.py b/openpype/hosts/tvpaint/plugins/create/create_render.py index 40386efe91..7e85977b11 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render.py @@ -660,7 +660,6 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator): ["create"] ["auto_detect_render"] ) - self.enabled = plugin_settings["enabled"] self.allow_group_rename = plugin_settings["allow_group_rename"] self.group_name_template = plugin_settings["group_name_template"] self.group_idx_offset = plugin_settings["group_idx_offset"] From 6568db35af8047bdc2ca46016aca62f9f220e4b7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 23 Feb 2023 11:30:15 +0100 Subject: [PATCH 1014/1271] Fix - check existence only if not None review_path might be None --- openpype/modules/slack/plugins/publish/integrate_slack_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index 4e2557ccc7..86c97586d2 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -187,7 +187,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): repre_review_path = get_publish_repre_path( instance, repre, False ) - if os.path.exists(repre_review_path): + if repre_review_path and os.path.exists(repre_review_path): review_path = repre_review_path if "burnin" in tags: # burnin has precedence if exists break From 6ab63823f02fb32edb367567943a9b38ba7cd9c2 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 21 Feb 2023 17:40:29 +0000 Subject: [PATCH 1015/1271] Implement get_multipart --- openpype/hosts/maya/api/lib_renderproducts.py | 82 ++++++++++++------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 60090e9f6d..e635414029 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -196,12 +196,18 @@ class ARenderProducts: """Constructor.""" self.layer = layer self.render_instance = render_instance - self.multipart = False + self.multipart = self.get_multipart() # Initialize self.layer_data = self._get_layer_data() self.layer_data.products = self.get_render_products() + def get_multipart(self): + raise NotImplementedError( + "The render product implementation does not have a " + "\"get_multipart\" method." + ) + def has_camera_token(self): # type: () -> bool """Check if camera token is in image prefix. @@ -344,7 +350,6 @@ class ARenderProducts: separator = file_prefix[matches[0].end(1):matches[1].start(1)] return separator - def _get_layer_data(self): # type: () -> LayerMetadata # ______________________________________________ @@ -531,16 +536,20 @@ class RenderProductsArnold(ARenderProducts): return prefix - def _get_aov_render_products(self, aov, cameras=None): - """Return all render products for the AOV""" - - products = [] - aov_name = self._get_attr(aov, "name") + def get_multipart(self): multipart = False multilayer = bool(self._get_attr("defaultArnoldDriver.multipart")) merge_AOVs = bool(self._get_attr("defaultArnoldDriver.mergeAOVs")) if multilayer or merge_AOVs: multipart = True + + return multipart + + def _get_aov_render_products(self, aov, cameras=None): + """Return all render products for the AOV""" + + products = [] + aov_name = self._get_attr(aov, "name") ai_drivers = cmds.listConnections("{}.outputs".format(aov), source=True, destination=False, @@ -594,7 +603,7 @@ class RenderProductsArnold(ARenderProducts): ext=ext, aov=aov_name, driver=ai_driver, - multipart=multipart, + multipart=self.multipart, camera=camera) products.append(product) @@ -731,6 +740,14 @@ class RenderProductsVray(ARenderProducts): renderer = "vray" + def get_multipart(self): + multipart = False + image_format = self._get_attr("vraySettings.imageFormatStr") + if image_format == "exr (multichannel)": + multipart = True + + return multipart + def get_renderer_prefix(self): # type: () -> str """Get image prefix for V-Ray. @@ -797,11 +814,6 @@ class RenderProductsVray(ARenderProducts): if default_ext in {"exr (multichannel)", "exr (deep)"}: default_ext = "exr" - # Define multipart. - multipart = False - if image_format_str == "exr (multichannel)": - multipart = True - products = [] # add beauty as default when not disabled @@ -813,7 +825,7 @@ class RenderProductsVray(ARenderProducts): productName="", ext=default_ext, camera=camera, - multipart=multipart + multipart=self.multipart ) ) @@ -826,10 +838,10 @@ class RenderProductsVray(ARenderProducts): productName="Alpha", ext=default_ext, camera=camera, - multipart=multipart + multipart=self.multipart ) ) - if multipart: + if self.multipart: # AOVs are merged in m-channel file, only main layer is rendered return products @@ -989,6 +1001,19 @@ class RenderProductsRedshift(ARenderProducts): renderer = "redshift" unmerged_aovs = {"Cryptomatte"} + def get_multipart(self): + # For Redshift we don't directly return upon forcing multilayer + # due to some AOVs still being written into separate files, + # like Cryptomatte. + # AOVs are merged in multi-channel file + multipart = False + force_layer = bool(self._get_attr("redshiftOptions.exrForceMultilayer")) # noqa + exMultipart = bool(self._get_attr("redshiftOptions.exrMultipart")) + if exMultipart or force_layer: + multipart = True + + return multipart + def get_renderer_prefix(self): """Get image prefix for Redshift. @@ -1028,16 +1053,6 @@ class RenderProductsRedshift(ARenderProducts): for c in self.get_renderable_cameras() ] - # For Redshift we don't directly return upon forcing multilayer - # due to some AOVs still being written into separate files, - # like Cryptomatte. - # AOVs are merged in multi-channel file - multipart = False - force_layer = bool(self._get_attr("redshiftOptions.exrForceMultilayer")) # noqa - exMultipart = bool(self._get_attr("redshiftOptions.exrMultipart")) - if exMultipart or force_layer: - multipart = True - # Get Redshift Extension from image format image_format = self._get_attr("redshiftOptions.imageFormat") # integer ext = mel.eval("redshiftGetImageExtension(%i)" % image_format) @@ -1059,7 +1074,7 @@ class RenderProductsRedshift(ARenderProducts): continue aov_type = self._get_attr(aov, "aovType") - if multipart and aov_type not in self.unmerged_aovs: + if self.multipart and aov_type not in self.unmerged_aovs: continue # Any AOVs that still get processed, like Cryptomatte @@ -1094,7 +1109,7 @@ class RenderProductsRedshift(ARenderProducts): productName=aov_light_group_name, aov=aov_name, ext=ext, - multipart=multipart, + multipart=self.multipart, camera=camera) products.append(product) @@ -1108,7 +1123,7 @@ class RenderProductsRedshift(ARenderProducts): product = RenderProduct(productName=aov_name, aov=aov_name, ext=ext, - multipart=multipart, + multipart=self.multipart, camera=camera) products.append(product) @@ -1124,7 +1139,7 @@ class RenderProductsRedshift(ARenderProducts): products.insert(0, RenderProduct(productName=beauty_name, ext=ext, - multipart=multipart, + multipart=self.multipart, camera=camera)) return products @@ -1144,6 +1159,10 @@ class RenderProductsRenderman(ARenderProducts): renderer = "renderman" unmerged_aovs = {"PxrCryptomatte"} + def get_multipart(self): + # Implemented as display specific in "get_render_products". + return False + def get_render_products(self): """Get all AOVs. @@ -1283,6 +1302,9 @@ class RenderProductsMayaHardware(ARenderProducts): {"label": "EXR(exr)", "index": 40, "extension": "exr"} ] + def get_multipart(self): + return False + def _get_extension(self, value): result = None if isinstance(value, int): From d9499d5750c42fc324335f4e7354564ffa7bba0c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 22 Feb 2023 18:00:11 +0000 Subject: [PATCH 1016/1271] Fix Redshift expected files. --- openpype/hosts/maya/api/lib_renderproducts.py | 26 +++++++++++++++---- .../maya/plugins/publish/collect_render.py | 7 ++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index e635414029..4e9e13d2a3 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -1001,6 +1001,20 @@ class RenderProductsRedshift(ARenderProducts): renderer = "redshift" unmerged_aovs = {"Cryptomatte"} + def get_files(self, product): + # When outputting AOVs we need to replace Redshift specific AOV tokens + # with Maya render tokens for generating file sequences. We validate to + # a specific AOV fileprefix so we only need to accout for one + # replacement. + if not product.multipart and product.driver: + file_prefix = self._get_attr(product.driver + ".filePrefix") + self.layer_data.filePrefix = file_prefix.replace( + "/", + "//" + ) + + return super(RenderProductsRedshift, self).get_files(product) + def get_multipart(self): # For Redshift we don't directly return upon forcing multilayer # due to some AOVs still being written into separate files, @@ -1009,7 +1023,7 @@ class RenderProductsRedshift(ARenderProducts): multipart = False force_layer = bool(self._get_attr("redshiftOptions.exrForceMultilayer")) # noqa exMultipart = bool(self._get_attr("redshiftOptions.exrMultipart")) - if exMultipart or force_layer: + if exMultipart and force_layer: multipart = True return multipart @@ -1109,8 +1123,9 @@ class RenderProductsRedshift(ARenderProducts): productName=aov_light_group_name, aov=aov_name, ext=ext, - multipart=self.multipart, - camera=camera) + multipart=False, + camera=camera, + driver=aov) products.append(product) if light_groups: @@ -1123,8 +1138,9 @@ class RenderProductsRedshift(ARenderProducts): product = RenderProduct(productName=aov_name, aov=aov_name, ext=ext, - multipart=self.multipart, - camera=camera) + multipart=False, + camera=camera, + driver=aov) products.append(product) # When a Beauty AOV is added manually, it will be rendered as diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index f2b5262187..338f148f85 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -42,6 +42,7 @@ Provides: import re import os import platform +import json from maya import cmds import maya.app.renderSetup.model.renderSetup as renderSetup @@ -183,7 +184,11 @@ class CollectMayaRender(pyblish.api.ContextPlugin): self.log.info("multipart: {}".format( multipart)) assert exp_files, "no file names were generated, this is bug" - self.log.info(exp_files) + self.log.info( + "expected files: {}".format( + json.dumps(exp_files, indent=4, sort_keys=True) + ) + ) # if we want to attach render to subset, check if we have AOV's # in expectedFiles. If so, raise error as we cannot attach AOV From 52238488f331781525118e83130394ec20ca1d1c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 Feb 2023 09:20:25 +0000 Subject: [PATCH 1017/1271] Only use force options as multipart identifier. --- openpype/hosts/maya/api/lib_renderproducts.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 4e9e13d2a3..02e55601b9 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -1021,9 +1021,10 @@ class RenderProductsRedshift(ARenderProducts): # like Cryptomatte. # AOVs are merged in multi-channel file multipart = False - force_layer = bool(self._get_attr("redshiftOptions.exrForceMultilayer")) # noqa - exMultipart = bool(self._get_attr("redshiftOptions.exrMultipart")) - if exMultipart and force_layer: + force_layer = bool( + self._get_attr("redshiftOptions.exrForceMultilayer") + ) + if force_layer: multipart = True return multipart From 3724c86c185f86d1271eb9c5c98c7f1e302a7baf Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 23 Feb 2023 11:53:55 +0000 Subject: [PATCH 1018/1271] Update openpype/hosts/maya/api/lib_renderproducts.py --- openpype/hosts/maya/api/lib_renderproducts.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 02e55601b9..463324284b 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -1320,6 +1320,7 @@ class RenderProductsMayaHardware(ARenderProducts): ] def get_multipart(self): + # MayaHardware does not support multipart EXRs. return False def _get_extension(self, value): From 09ccf1af77b3f05aec8d99469f526a13ce2bafca Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 31 Jan 2023 06:55:36 +0000 Subject: [PATCH 1019/1271] Batch script for running Openpype on Deadline. --- tools/openpype_console.bat | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tools/openpype_console.bat diff --git a/tools/openpype_console.bat b/tools/openpype_console.bat new file mode 100644 index 0000000000..414b5fdf66 --- /dev/null +++ b/tools/openpype_console.bat @@ -0,0 +1,3 @@ +cd "%~dp0\.." +echo %OPENPYPE_MONGO% +.poetry\bin\poetry.exe run python start.py %* From e7017ff05b70ffbd4c3a6354d76a19f2c3d39a22 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Feb 2023 16:25:35 +0000 Subject: [PATCH 1020/1271] Commenting for documentation --- tools/openpype_console.bat | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/openpype_console.bat b/tools/openpype_console.bat index 414b5fdf66..04b28c389f 100644 --- a/tools/openpype_console.bat +++ b/tools/openpype_console.bat @@ -1,3 +1,15 @@ +goto comment +SYNOPSIS + Helper script running scripts through the OpenPype environment. + +DESCRIPTION + This script is usually used as a replacement for building when tested farm integration like Deadline. + +EXAMPLE + +cmd> .\openpype_console.bat path/to/python_script.py +:comment + cd "%~dp0\.." echo %OPENPYPE_MONGO% .poetry\bin\poetry.exe run python start.py %* From b387441ad67a1d12a012c8bc8bc9f32d2d08ce94 Mon Sep 17 00:00:00 2001 From: Fabia Serra Arrizabalaga Date: Thu, 23 Feb 2023 00:52:40 +0100 Subject: [PATCH 1021/1271] Move get_workfile_build_placeholder_plugins to NukeHost class as workfile template builder expects --- openpype/hosts/nuke/api/__init__.py | 3 --- openpype/hosts/nuke/api/pipeline.py | 13 ++++++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/nuke/api/__init__.py b/openpype/hosts/nuke/api/__init__.py index 3b00ca9f6f..1af5ff365d 100644 --- a/openpype/hosts/nuke/api/__init__.py +++ b/openpype/hosts/nuke/api/__init__.py @@ -30,7 +30,6 @@ from .pipeline import ( parse_container, update_container, - get_workfile_build_placeholder_plugins, ) from .lib import ( INSTANCE_DATA_KNOB, @@ -79,8 +78,6 @@ __all__ = ( "parse_container", "update_container", - "get_workfile_build_placeholder_plugins", - "INSTANCE_DATA_KNOB", "ROOT_DATA_KNOB", "maintained_selection", diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 6dec60d81a..d5289010cb 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -101,6 +101,12 @@ class NukeHost( def get_workfile_extensions(self): return file_extensions() + def get_workfile_build_placeholder_plugins(self): + return [ + NukePlaceholderLoadPlugin, + NukePlaceholderCreatePlugin + ] + def get_containers(self): return ls() @@ -200,13 +206,6 @@ def _show_workfiles(): host_tools.show_workfiles(parent=None, on_top=False) -def get_workfile_build_placeholder_plugins(): - return [ - NukePlaceholderLoadPlugin, - NukePlaceholderCreatePlugin - ] - - def _install_menu(): # uninstall original avalon menu main_window = get_main_window() From 6bf8b7b8ca3cc8961ba271f914fd0abdbcf5bda8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 15:40:55 +0100 Subject: [PATCH 1022/1271] improving deprecation --- openpype/hosts/nuke/api/lib.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 0325838e78..b13c592fbf 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -214,8 +214,9 @@ def update_node_data(node, knobname, data): knob.setValue(knob_value) +@deprecated class Knobby(object): - """[DEPRICATED] For creating knob which it's type isn't + """[DEPRECATED] For creating knob which it's type isn't mapped in `create_knobs` Args: @@ -248,8 +249,9 @@ class Knobby(object): return " ".join(words) +@deprecated def create_knobs(data, tab=None): - """[DEPRICATED] Create knobs by data + """[DEPRECATED] Create knobs by data Depending on the type of each dict value and creates the correct Knob. @@ -342,8 +344,9 @@ def create_knobs(data, tab=None): return knobs +@deprecated def imprint(node, data, tab=None): - """[DEPRICATED] Store attributes with value on node + """[DEPRECATED] Store attributes with value on node Parse user data into Node knobs. Use `collections.OrderedDict` to ensure knob order. @@ -398,8 +401,9 @@ def imprint(node, data, tab=None): node.addKnob(knob) +@deprecated def add_publish_knob(node): - """[DEPRICATED] Add Publish knob to node + """[DEPRECATED] Add Publish knob to node Arguments: node (nuke.Node): nuke node to be processed @@ -416,8 +420,9 @@ def add_publish_knob(node): return node +@deprecated def set_avalon_knob_data(node, data=None, prefix="avalon:"): - """[DEPRICATED] Sets data into nodes's avalon knob + """[DEPRECATED] Sets data into nodes's avalon knob Arguments: node (nuke.Node): Nuke node to imprint with data, @@ -478,8 +483,9 @@ def set_avalon_knob_data(node, data=None, prefix="avalon:"): return node +@deprecated def get_avalon_knob_data(node, prefix="avalon:", create=True): - """[DEPRICATED] Gets a data from nodes's avalon knob + """[DEPRECATED] Gets a data from nodes's avalon knob Arguments: node (obj): Nuke node to search for data, @@ -521,8 +527,9 @@ def get_avalon_knob_data(node, prefix="avalon:", create=True): return data +@deprecated def fix_data_for_node_create(data): - """[DEPRICATED] Fixing data to be used for nuke knobs + """[DEPRECATED] Fixing data to be used for nuke knobs """ for k, v in data.items(): if isinstance(v, six.text_type): @@ -532,8 +539,9 @@ def fix_data_for_node_create(data): return data +@deprecated def add_write_node_legacy(name, **kwarg): - """[DEPRICATED] Adding nuke write node + """[DEPRECATED] Adding nuke write node Arguments: name (str): nuke node name kwarg (attrs): data for nuke knobs @@ -697,7 +705,7 @@ def get_nuke_imageio_settings(): @deprecated("openpype.hosts.nuke.api.lib.get_nuke_imageio_settings") def get_created_node_imageio_setting_legacy(nodeclass, creator, subset): - '''[DEPRICATED] Get preset data for dataflow (fileType, compression, bitDepth) + '''[DEPRECATED] Get preset data for dataflow (fileType, compression, bitDepth) ''' assert any([creator, nodeclass]), nuke.message( From ea23223977420a60168a922eaf4603f6fe7726b5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 17:21:17 +0100 Subject: [PATCH 1023/1271] Nuke: baking with multiple reposition nodes also with settings and defaults --- openpype/hosts/nuke/api/plugin.py | 96 ++++++++++++------- .../defaults/project_settings/nuke.json | 35 +++++++ .../schemas/schema_nuke_publish.json | 47 +++++++++ 3 files changed, 144 insertions(+), 34 deletions(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index d3f8357f7d..5521db99c0 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -558,9 +558,7 @@ class ExporterReview(object): self.path_in = self.instance.data.get("path", None) self.staging_dir = self.instance.data["stagingDir"] self.collection = self.instance.data.get("collection", None) - self.data = dict({ - "representations": list() - }) + self.data = {"representations": []} def get_file_info(self): if self.collection: @@ -626,7 +624,7 @@ class ExporterReview(object): nuke_imageio = opnlib.get_nuke_imageio_settings() # TODO: this is only securing backward compatibility lets remove - # this once all projects's anotomy are updated to newer config + # this once all projects's anatomy are updated to newer config if "baking" in nuke_imageio.keys(): return nuke_imageio["baking"]["viewerProcess"] else: @@ -823,8 +821,41 @@ class ExporterReviewMov(ExporterReview): add_tags = [] self.publish_on_farm = farm read_raw = kwargs["read_raw"] + + # TODO: remove this when `reformat_nodes_config` + # is changed in settings reformat_node_add = kwargs["reformat_node_add"] reformat_node_config = kwargs["reformat_node_config"] + + # TODO: make this required in future + reformat_nodes_config = kwargs.get("reformat_nodes_config", {}) + + # TODO: remove this once deprecated is removed + # make sure only reformat_nodes_config is used in future + if reformat_node_add and reformat_nodes_config.get("enabled"): + self.log.warning( + "`reformat_node_add` is deprecated. " + "Please use only `reformat_nodes_config` instead.") + reformat_nodes_config = None + + # TODO: reformat code when backward compatibility is not needed + # warning if reformat_nodes_config is not set + if not reformat_nodes_config: + self.log.warning( + "Please set `reformat_nodes_config` in settings.") + self.log.warning( + "Using `reformat_node_config` instead.") + reformat_nodes_config = { + "enabled": reformat_node_add, + "reposition_nodes": [ + { + "node_class": "Reformat", + "knobs": reformat_node_config + } + ] + } + + bake_viewer_process = kwargs["bake_viewer_process"] bake_viewer_input_process_node = kwargs[ "bake_viewer_input_process"] @@ -846,7 +877,6 @@ class ExporterReviewMov(ExporterReview): subset = self.instance.data["subset"] self._temp_nodes[subset] = [] - # ---------- start nodes creation # Read node r_node = nuke.createNode("Read") @@ -860,44 +890,39 @@ class ExporterReviewMov(ExporterReview): if read_raw: r_node["raw"].setValue(1) - # connect - self._temp_nodes[subset].append(r_node) - self.previous_node = r_node - self.log.debug("Read... `{}`".format(self._temp_nodes[subset])) + # connect to Read node + self._shift_to_previous_node_and_temp(subset, r_node, "Read... `{}`") # add reformat node - if reformat_node_add: + if reformat_nodes_config["enabled"]: + reposition_nodes = reformat_nodes_config["reposition_nodes"] + for reposition_node in reposition_nodes: + node_class = reposition_node["node_class"] + knobs = reposition_node["knobs"] + node = nuke.createNode(node_class) + set_node_knobs_from_settings(node, knobs) + + # connect in order + self._connect_to_above_nodes( + node, subset, "Reposition node... `{}`" + ) # append reformated tag add_tags.append("reformated") - rf_node = nuke.createNode("Reformat") - set_node_knobs_from_settings(rf_node, reformat_node_config) - - # connect - rf_node.setInput(0, self.previous_node) - self._temp_nodes[subset].append(rf_node) - self.previous_node = rf_node - self.log.debug( - "Reformat... `{}`".format(self._temp_nodes[subset])) - # only create colorspace baking if toggled on if bake_viewer_process: if bake_viewer_input_process_node: # View Process node ipn = get_view_process_node() if ipn is not None: - # connect - ipn.setInput(0, self.previous_node) - self._temp_nodes[subset].append(ipn) - self.previous_node = ipn - self.log.debug( - "ViewProcess... `{}`".format( - self._temp_nodes[subset])) + # connect to ViewProcess node + self._connect_to_above_nodes(ipn, subset, "ViewProcess... `{}`") if not self.viewer_lut_raw: # OCIODisplay dag_node = nuke.createNode("OCIODisplay") + # assign display display, viewer = get_viewer_config_from_string( str(baking_view_profile) ) @@ -907,13 +932,7 @@ class ExporterReviewMov(ExporterReview): # assign viewer dag_node["view"].setValue(viewer) - # connect - dag_node.setInput(0, self.previous_node) - self._temp_nodes[subset].append(dag_node) - self.previous_node = dag_node - self.log.debug("OCIODisplay... `{}`".format( - self._temp_nodes[subset])) - + self._connect_to_above_nodes(dag_node, subset, "OCIODisplay... `{}`") # Write node write_node = nuke.createNode("Write") self.log.debug("Path: {}".format(self.path)) @@ -967,6 +986,15 @@ class ExporterReviewMov(ExporterReview): return self.data + def _shift_to_previous_node_and_temp(self, subset, node, message): + self._temp_nodes[subset].append(node) + self.previous_node = node + self.log.debug(message.format(self._temp_nodes[subset])) + + def _connect_to_above_nodes(self, node, subset, message): + node.setInput(0, self.previous_node) + self._shift_to_previous_node_and_temp(subset, node, message) + @deprecated("openpype.hosts.nuke.api.plugin.NukeWriteCreator") class AbstractWriteRender(OpenPypeCreator): diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index d475c337d9..2545411e0a 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -446,6 +446,41 @@ "value": false } ], + "reformat_nodes_config": { + "enabled": false, + "reposition_nodes": [ + { + "node_class": "Reformat", + "knobs": [ + { + "type": "text", + "name": "type", + "value": "to format" + }, + { + "type": "text", + "name": "format", + "value": "HD_1080" + }, + { + "type": "text", + "name": "filter", + "value": "Lanczos6" + }, + { + "type": "bool", + "name": "black_outside", + "value": true + }, + { + "type": "bool", + "name": "pbb", + "value": false + } + ] + } + ] + }, "extension": "mov", "add_custom_tags": [] } diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index 5b9145e7d9..1c542279fc 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -271,6 +271,10 @@ { "type": "separator" }, + { + "type": "label", + "label": "Currently we are supporting also multiple reposition nodes.
Older single reformat node is still supported
and if it is activated then preference will
be on it. If you want to use multiple reformat
nodes then you need to disable single reformat
node and enable multiple Reformat nodes here." + }, { "type": "boolean", "key": "reformat_node_add", @@ -287,6 +291,49 @@ } ] }, + { + "key": "reformat_nodes_config", + "type": "dict", + "label": "Reformat Nodes", + "collapsible": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Reposition knobs supported only.
You can add multiple reformat nodes
and set their knobs. Order of reformat
nodes is important. First reformat node
will be applied first and last reformat
node will be applied last." + }, + { + "key": "reposition_nodes", + "type": "list", + "label": "Reposition nodes", + "object_type": { + "type": "dict", + "children": [ + { + "key": "node_class", + "label": "Node class", + "type": "text" + }, + { + "type": "schema_template", + "name": "template_nuke_knob_inputs", + "template_data": [ + { + "label": "Node knobs", + "key": "knobs" + } + ] + } + ] + } + } + ] + }, { "type": "separator" }, From 15fa8f551c3131b8019e81d46a3f0ab976bf9a63 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 Feb 2023 10:57:00 +0100 Subject: [PATCH 1024/1271] little fixes --- openpype/hosts/nuke/api/lib.py | 9 +++------ openpype/hosts/nuke/api/plugin.py | 6 +++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index b13c592fbf..73d4986b64 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -214,7 +214,6 @@ def update_node_data(node, knobname, data): knob.setValue(knob_value) -@deprecated class Knobby(object): """[DEPRECATED] For creating knob which it's type isn't mapped in `create_knobs` @@ -249,9 +248,8 @@ class Knobby(object): return " ".join(words) -@deprecated def create_knobs(data, tab=None): - """[DEPRECATED] Create knobs by data + """Create knobs by data Depending on the type of each dict value and creates the correct Knob. @@ -344,9 +342,8 @@ def create_knobs(data, tab=None): return knobs -@deprecated def imprint(node, data, tab=None): - """[DEPRECATED] Store attributes with value on node + """Store attributes with value on node Parse user data into Node knobs. Use `collections.OrderedDict` to ensure knob order. @@ -1249,7 +1246,7 @@ def create_write_node( nodes to be created before write with dependency review (bool)[optional]: adding review knob farm (bool)[optional]: rendering workflow target - kwargs (dict)[optional]: additional key arguments for formating + kwargs (dict)[optional]: additional key arguments for formatting Example: prenodes = { diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 5521db99c0..160ca820a4 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -842,9 +842,9 @@ class ExporterReviewMov(ExporterReview): # warning if reformat_nodes_config is not set if not reformat_nodes_config: self.log.warning( - "Please set `reformat_nodes_config` in settings.") - self.log.warning( - "Using `reformat_node_config` instead.") + "Please set `reformat_nodes_config` in settings. " + "Using `reformat_node_config` instead." + ) reformat_nodes_config = { "enabled": reformat_node_add, "reposition_nodes": [ From ca7bf70e1578f175712c1923683513391a5023e8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 11:47:32 +0100 Subject: [PATCH 1025/1271] nuke assist kickoff --- .../system_settings/applications.json | 128 ++++++++++++++++++ .../system_schema/schema_applications.json | 8 ++ 2 files changed, 136 insertions(+) diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index f84d99e36b..5fd9b926fb 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -337,6 +337,134 @@ } } }, + "nukeassist": { + "enabled": true, + "label": "Nuke Assist", + "icon": "{}/app_icons/nuke.png", + "host_name": "nuke", + "environment": { + "NUKE_PATH": [ + "{NUKE_PATH}", + "{OPENPYPE_STUDIO_PLUGINS}/nuke" + ] + }, + "variants": { + "13-2": { + "use_python_2": false, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke13.2v1\\Nuke13.2.exe" + ], + "darwin": [], + "linux": [ + "/usr/local/Nuke13.2v1/Nuke13.2" + ] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "13-0": { + "use_python_2": false, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke13.0v1\\Nuke13.0.exe" + ], + "darwin": [], + "linux": [ + "/usr/local/Nuke13.0v1/Nuke13.0" + ] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "12-2": { + "use_python_2": true, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke12.2v3\\Nuke12.2.exe" + ], + "darwin": [], + "linux": [ + "/usr/local/Nuke12.2v3Nuke12.2" + ] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "12-0": { + "use_python_2": true, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke12.0v1\\Nuke12.0.exe" + ], + "darwin": [], + "linux": [ + "/usr/local/Nuke12.0v1/Nuke12.0" + ] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "11-3": { + "use_python_2": true, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke11.3v1\\Nuke11.3.exe" + ], + "darwin": [], + "linux": [ + "/usr/local/Nuke11.3v5/Nuke11.3" + ] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "11-2": { + "use_python_2": true, + "executables": { + "windows": [ + "C:\\Program Files\\Nuke11.2v2\\Nuke11.2.exe" + ], + "darwin": [], + "linux": [] + }, + "arguments": { + "windows": ["--nukeassist"], + "darwin": ["--nukeassist"], + "linux": ["--nukeassist"] + }, + "environment": {} + }, + "__dynamic_keys_labels__": { + "13-2": "13.2", + "13-0": "13.0", + "12-2": "12.2", + "12-0": "12.0", + "11-3": "11.3", + "11-2": "11.2" + } + } + }, "nukex": { "enabled": true, "label": "Nuke X", diff --git a/openpype/settings/entities/schemas/system_schema/schema_applications.json b/openpype/settings/entities/schemas/system_schema/schema_applications.json index 36c5811496..b17687cf71 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_applications.json +++ b/openpype/settings/entities/schemas/system_schema/schema_applications.json @@ -25,6 +25,14 @@ "nuke_label": "Nuke" } }, + { + "type": "schema_template", + "name": "template_nuke", + "template_data": { + "nuke_type": "nukeassist", + "nuke_label": "Nuke Assist" + } + }, { "type": "schema_template", "name": "template_nuke", From 8777aa9859e66ecf52cc7cfebb0c1541e5e37785 Mon Sep 17 00:00:00 2001 From: ynput Date: Tue, 21 Feb 2023 14:23:26 +0200 Subject: [PATCH 1026/1271] adding appgroup to prelaunch hook --- 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 85f68c6b60..2092d5025d 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", "nukeassist", "nukex", "hiero", "nukestudio"] platforms = ["windows"] def execute(self): From 3c84b4195d6077ffc4abf51933359f2c69cec7a8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 13:39:02 +0100 Subject: [PATCH 1027/1271] adding nukeassist hook --- openpype/hosts/nuke/hooks/__init__.py | 0 openpype/hosts/nuke/hooks/pre_nukeassist_setup.py | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 openpype/hosts/nuke/hooks/__init__.py create mode 100644 openpype/hosts/nuke/hooks/pre_nukeassist_setup.py diff --git a/openpype/hosts/nuke/hooks/__init__.py b/openpype/hosts/nuke/hooks/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py new file mode 100644 index 0000000000..80696c34e5 --- /dev/null +++ b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py @@ -0,0 +1,10 @@ +from openpype.lib import PreLaunchHook + +class PrelaunchNukeAssistHook(PreLaunchHook): + """ + Adding flag when nukeassist + """ + app_groups = ["nukeassist"] + + def execute(self): + self.launch_context.env["NUKEASSIST"] = True From 633738daa719e78a69e56391f7e46d0c1986a183 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 13:56:17 +0100 Subject: [PATCH 1028/1271] adding hook to host --- openpype/hosts/nuke/addon.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/hosts/nuke/addon.py b/openpype/hosts/nuke/addon.py index 9d25afe2b6..6a4b91a76d 100644 --- a/openpype/hosts/nuke/addon.py +++ b/openpype/hosts/nuke/addon.py @@ -63,5 +63,12 @@ class NukeAddon(OpenPypeModule, IHostAddon): path_paths.append(quick_time_path) env["PATH"] = os.pathsep.join(path_paths) + def get_launch_hook_paths(self, app): + if app.host_name != self.host_name: + return [] + return [ + os.path.join(NUKE_ROOT_DIR, "hooks") + ] + def get_workfile_extensions(self): return [".nk"] From 6bb6aab0f96ac8903d38b93c529c5e7d8b349b6b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 14:54:51 +0100 Subject: [PATCH 1029/1271] adding menu assist variant conditions --- openpype/hosts/nuke/api/pipeline.py | 50 +++++++++++-------- openpype/hosts/nuke/hooks/__init__.py | 0 .../hosts/nuke/hooks/pre_nukeassist_setup.py | 2 +- 3 files changed, 30 insertions(+), 22 deletions(-) delete mode 100644 openpype/hosts/nuke/hooks/__init__.py diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index d5289010cb..306fa50de9 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -71,7 +71,7 @@ CREATE_PATH = os.path.join(PLUGINS_DIR, "create") INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") MENU_LABEL = os.environ["AVALON_LABEL"] - +ASSIST = bool(os.getenv("NUKEASSIST")) # registering pyblish gui regarding settings in presets if os.getenv("PYBLISH_GUI", None): @@ -207,6 +207,7 @@ def _show_workfiles(): def _install_menu(): + # uninstall original avalon menu main_window = get_main_window() menubar = nuke.menu("Nuke") @@ -217,7 +218,9 @@ def _install_menu(): ) Context.context_label = label context_action = menu.addCommand(label) - context_action.setEnabled(False) + + if not ASSIST: + context_action.setEnabled(False) menu.addSeparator() menu.addCommand( @@ -226,18 +229,20 @@ def _install_menu(): ) menu.addSeparator() - menu.addCommand( - "Create...", - lambda: host_tools.show_publisher( - tab="create" + if not ASSIST: + menu.addCommand( + "Create...", + lambda: host_tools.show_publisher( + tab="create" + ) ) - ) - menu.addCommand( - "Publish...", - lambda: host_tools.show_publisher( - tab="publish" + menu.addCommand( + "Publish...", + lambda: host_tools.show_publisher( + tab="publish" + ) ) - ) + menu.addCommand( "Load...", lambda: host_tools.show_loader( @@ -285,15 +290,18 @@ def _install_menu(): "Build Workfile from template", lambda: build_workfile_template() ) - menu_template.addSeparator() - menu_template.addCommand( - "Create Place Holder", - lambda: create_placeholder() - ) - menu_template.addCommand( - "Update Place Holder", - lambda: update_placeholder() - ) + + if not ASSIST: + menu_template.addSeparator() + menu_template.addCommand( + "Create Place Holder", + lambda: create_placeholder() + ) + menu_template.addCommand( + "Update Place Holder", + lambda: update_placeholder() + ) + menu.addSeparator() menu.addCommand( "Experimental tools...", diff --git a/openpype/hosts/nuke/hooks/__init__.py b/openpype/hosts/nuke/hooks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py index 80696c34e5..054bd677a7 100644 --- a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py +++ b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py @@ -7,4 +7,4 @@ class PrelaunchNukeAssistHook(PreLaunchHook): app_groups = ["nukeassist"] def execute(self): - self.launch_context.env["NUKEASSIST"] = True + self.launch_context.env["NUKEASSIST"] = "1" From 3159facb2083a44785cd4fd1378993b167a57c45 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 15:26:07 +0100 Subject: [PATCH 1030/1271] moving Assist switch to api level condition for updating nodes --- openpype/hosts/nuke/api/__init__.py | 7 ++++++- openpype/hosts/nuke/api/lib.py | 21 ++++++++++++--------- openpype/hosts/nuke/api/pipeline.py | 2 +- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/nuke/api/__init__.py b/openpype/hosts/nuke/api/__init__.py index 1af5ff365d..7766a94140 100644 --- a/openpype/hosts/nuke/api/__init__.py +++ b/openpype/hosts/nuke/api/__init__.py @@ -1,3 +1,4 @@ +import os from .workio import ( file_extensions, has_unsaved_changes, @@ -50,6 +51,8 @@ from .utils import ( get_colorspace_list ) +ASSIST = bool(os.getenv("NUKEASSIST")) + __all__ = ( "file_extensions", "has_unsaved_changes", @@ -92,5 +95,7 @@ __all__ = ( "create_write_node", "colorspace_exists_on_node", - "get_colorspace_list" + "get_colorspace_list", + + "ASSIST" ) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 73d4986b64..ec5bc58f9f 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -49,7 +49,7 @@ from openpype.pipeline.colorspace import ( ) from openpype.pipeline.workfile import BuildWorkfile -from . import gizmo_menu +from . import gizmo_menu, ASSIST from .workio import ( save_file, @@ -2263,14 +2263,17 @@ class WorkfileSettings(object): node['frame_range'].setValue(range) node['frame_range_lock'].setValue(True) - set_node_data( - self._root_node, - INSTANCE_DATA_KNOB, - { - "handleStart": int(handle_start), - "handleEnd": int(handle_end) - } - ) + if not ASSIST: + set_node_data( + self._root_node, + INSTANCE_DATA_KNOB, + { + "handleStart": int(handle_start), + "handleEnd": int(handle_end) + } + ) + else: + log.warning("NukeAssist mode is not allowing updating custom knobs...") def reset_resolution(self): """Set resolution to project resolution.""" diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 306fa50de9..94c4518664 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -60,6 +60,7 @@ from .workio import ( work_root, current_file ) +from . import ASSIST log = Logger.get_logger(__name__) @@ -71,7 +72,6 @@ CREATE_PATH = os.path.join(PLUGINS_DIR, "create") INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") MENU_LABEL = os.environ["AVALON_LABEL"] -ASSIST = bool(os.getenv("NUKEASSIST")) # registering pyblish gui regarding settings in presets if os.getenv("PYBLISH_GUI", None): From 02b69f8ba84505c40d01f23048a0ad965a539b72 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 16:06:39 +0100 Subject: [PATCH 1031/1271] moving ASSIST switch to utils --- openpype/hosts/nuke/api/__init__.py | 7 +------ openpype/hosts/nuke/api/lib.py | 3 ++- openpype/hosts/nuke/api/pipeline.py | 2 +- openpype/hosts/nuke/api/utils.py | 1 + 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/nuke/api/__init__.py b/openpype/hosts/nuke/api/__init__.py index 7766a94140..1af5ff365d 100644 --- a/openpype/hosts/nuke/api/__init__.py +++ b/openpype/hosts/nuke/api/__init__.py @@ -1,4 +1,3 @@ -import os from .workio import ( file_extensions, has_unsaved_changes, @@ -51,8 +50,6 @@ from .utils import ( get_colorspace_list ) -ASSIST = bool(os.getenv("NUKEASSIST")) - __all__ = ( "file_extensions", "has_unsaved_changes", @@ -95,7 +92,5 @@ __all__ = ( "create_write_node", "colorspace_exists_on_node", - "get_colorspace_list", - - "ASSIST" + "get_colorspace_list" ) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index ec5bc58f9f..dfc647872b 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -49,7 +49,8 @@ from openpype.pipeline.colorspace import ( ) from openpype.pipeline.workfile import BuildWorkfile -from . import gizmo_menu, ASSIST +from . import gizmo_menu +from .utils import ASSIST from .workio import ( save_file, diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 94c4518664..55cb77bafe 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -60,7 +60,7 @@ from .workio import ( work_root, current_file ) -from . import ASSIST +from .utils import ASSIST log = Logger.get_logger(__name__) diff --git a/openpype/hosts/nuke/api/utils.py b/openpype/hosts/nuke/api/utils.py index 6bcb752dd1..261eba8401 100644 --- a/openpype/hosts/nuke/api/utils.py +++ b/openpype/hosts/nuke/api/utils.py @@ -4,6 +4,7 @@ import nuke from openpype import resources from .lib import maintained_selection +ASSIST = bool(os.getenv("NUKEASSIST")) def set_context_favorites(favorites=None): """ Adding favorite folders to nuke's browser From 888f436def036d0cddcbeb8b6f4340e641aa0f0c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Feb 2023 16:38:54 +0100 Subject: [PATCH 1032/1271] move ASSIST to constant --- openpype/hosts/nuke/api/constants.py | 4 ++++ openpype/hosts/nuke/api/lib.py | 2 +- openpype/hosts/nuke/api/pipeline.py | 2 +- openpype/hosts/nuke/api/utils.py | 1 - 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 openpype/hosts/nuke/api/constants.py diff --git a/openpype/hosts/nuke/api/constants.py b/openpype/hosts/nuke/api/constants.py new file mode 100644 index 0000000000..110199720f --- /dev/null +++ b/openpype/hosts/nuke/api/constants.py @@ -0,0 +1,4 @@ +import os + + +ASSIST = bool(os.getenv("NUKEASSIST")) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index dfc647872b..9e36fb147b 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -50,7 +50,7 @@ from openpype.pipeline.colorspace import ( from openpype.pipeline.workfile import BuildWorkfile from . import gizmo_menu -from .utils import ASSIST +from .constants import ASSIST from .workio import ( save_file, diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 55cb77bafe..f07d150ba5 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -60,7 +60,7 @@ from .workio import ( work_root, current_file ) -from .utils import ASSIST +from .constants import ASSIST log = Logger.get_logger(__name__) diff --git a/openpype/hosts/nuke/api/utils.py b/openpype/hosts/nuke/api/utils.py index 261eba8401..6bcb752dd1 100644 --- a/openpype/hosts/nuke/api/utils.py +++ b/openpype/hosts/nuke/api/utils.py @@ -4,7 +4,6 @@ import nuke from openpype import resources from .lib import maintained_selection -ASSIST = bool(os.getenv("NUKEASSIST")) def set_context_favorites(favorites=None): """ Adding favorite folders to nuke's browser From a6bde83900e369c853cd79696431f79acae3215e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Wed, 22 Feb 2023 14:41:36 +0100 Subject: [PATCH 1033/1271] Update openpype/hosts/nuke/hooks/pre_nukeassist_setup.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/nuke/hooks/pre_nukeassist_setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py index 054bd677a7..3a0f00413a 100644 --- a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py +++ b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py @@ -8,3 +8,4 @@ class PrelaunchNukeAssistHook(PreLaunchHook): def execute(self): self.launch_context.env["NUKEASSIST"] = "1" + From 47c93bb12ac21de36c26595d9a5db2560098dd38 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 14:44:22 +0100 Subject: [PATCH 1034/1271] removing line --- openpype/hosts/nuke/hooks/pre_nukeassist_setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py index 3a0f00413a..054bd677a7 100644 --- a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py +++ b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py @@ -8,4 +8,3 @@ class PrelaunchNukeAssistHook(PreLaunchHook): def execute(self): self.launch_context.env["NUKEASSIST"] = "1" - From 0a9e8265ed4f660eb3512455f222b8d31dae9871 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 14:52:57 +0100 Subject: [PATCH 1035/1271] context label is not needed in nukeassist --- openpype/hosts/nuke/api/pipeline.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index f07d150ba5..4c0f169ade 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -207,22 +207,25 @@ def _show_workfiles(): def _install_menu(): + """Install Avalon menu into Nuke's main menu bar.""" # uninstall original avalon menu main_window = get_main_window() menubar = nuke.menu("Nuke") menu = menubar.addMenu(MENU_LABEL) - label = "{0}, {1}".format( - os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"] - ) - Context.context_label = label - context_action = menu.addCommand(label) if not ASSIST: + label = "{0}, {1}".format( + os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"] + ) + Context.context_label = label + context_action = menu.addCommand(label) context_action.setEnabled(False) - menu.addSeparator() + # add separator after context label + menu.addSeparator() + menu.addCommand( "Work Files...", _show_workfiles From c99b5fdb238bb345b1efeebc651afbbb338f77b7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 14:56:27 +0100 Subject: [PATCH 1036/1271] adding empty lines --- openpype/hosts/nuke/api/pipeline.py | 1 - openpype/hosts/nuke/hooks/pre_nukeassist_setup.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 4c0f169ade..2496d66c1d 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -214,7 +214,6 @@ def _install_menu(): menubar = nuke.menu("Nuke") menu = menubar.addMenu(MENU_LABEL) - if not ASSIST: label = "{0}, {1}".format( os.environ["AVALON_ASSET"], os.environ["AVALON_TASK"] diff --git a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py index 054bd677a7..3948a665c6 100644 --- a/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py +++ b/openpype/hosts/nuke/hooks/pre_nukeassist_setup.py @@ -1,5 +1,6 @@ from openpype.lib import PreLaunchHook + class PrelaunchNukeAssistHook(PreLaunchHook): """ Adding flag when nukeassist From 36c5a91cb5f4615e736cb53c2a0a46883bbd2c0f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Feb 2023 14:59:30 +0100 Subject: [PATCH 1037/1271] hound comments --- openpype/hosts/nuke/api/lib.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 9e36fb147b..c08db978d3 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -2274,7 +2274,10 @@ class WorkfileSettings(object): } ) else: - log.warning("NukeAssist mode is not allowing updating custom knobs...") + log.warning( + "NukeAssist mode is not allowing " + "updating custom knobs..." + ) def reset_resolution(self): """Set resolution to project resolution.""" From 68f0602975d8eea61fc56d7fd81a6cd777e42856 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 24 Feb 2023 12:45:00 +0800 Subject: [PATCH 1038/1271] resolve conflict --- openpype/hosts/maya/plugins/publish/extract_look.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index ca110ceadd..efeddcfbe4 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -536,9 +536,10 @@ class ExtractLook(publish.Extractor): ] if linearize: if cmds.colorManagementPrefs(query=True, cmEnabled=True): - render_colorspace = cmds.colorManagementPrefs(query=True, renderingSpaceName=True) # noqa + render_colorspace = cmds.colorManagementPrefs(query=True, + renderingSpaceName=True) # noqa config_path = cmds.colorManagementPrefs(query=True, - configFilePath=True) + configFilePath=True) # noqa if not os.path.exists(config_path): raise RuntimeError("No OCIO config path found!") @@ -572,7 +573,7 @@ class ExtractLook(publish.Extractor): "is already linear") else: self.log.warning("cannot guess the colorspace" - "color conversion won't be available!") + "color conversion won't be available!") # noqa additional_args.extend(["--colorconfig", config_path]) From ffb4b64137b0ca91b9f51ea3c6f284c9b27f1887 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 24 Feb 2023 12:48:15 +0800 Subject: [PATCH 1039/1271] hound fix --- 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 71ca6b79c8..5e0f80818b 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3577,8 +3577,8 @@ def image_info(file_path): dict: Dictionary with the information about the texture file. """ from arnold import ( - AiTextureGetBitDepth, - AiTextureGetFormat + AiTextureGetBitDepth, + AiTextureGetFormat ) # Get Texture Information img_info = {'filename': file_path} @@ -3603,11 +3603,11 @@ def guess_colorspace(img_info): option of maketx. """ from arnold import ( - AiTextureInvalidate, - # types - AI_TYPE_BYTE, - AI_TYPE_INT, - AI_TYPE_UINT + AiTextureInvalidate, + # types + AI_TYPE_BYTE, + AI_TYPE_INT, + AI_TYPE_UINT ) try: if img_info['bit_depth'] <= 16: From 8bbe10e18dfe88107a02a7f2fd1bebdb9d88fe51 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 24 Feb 2023 12:49:39 +0800 Subject: [PATCH 1040/1271] hound fix --- openpype/hosts/maya/api/lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 5e0f80818b..f3c0f068b8 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3579,7 +3579,7 @@ def image_info(file_path): from arnold import ( AiTextureGetBitDepth, AiTextureGetFormat -) + ) # Get Texture Information img_info = {'filename': file_path} if os.path.isfile(file_path): @@ -3608,7 +3608,7 @@ def guess_colorspace(img_info): AI_TYPE_BYTE, AI_TYPE_INT, AI_TYPE_UINT -) + ) try: if img_info['bit_depth'] <= 16: if img_info['format'] in (AI_TYPE_BYTE, AI_TYPE_INT, AI_TYPE_UINT): # noqa From eae25bbf0b4dcf4b62bbdeab8d61ce9f4f82cfd9 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 Feb 2023 11:24:36 +0000 Subject: [PATCH 1041/1271] Publish user defined attributes option. --- .../maya/plugins/create/create_pointcache.py | 1 + .../maya/plugins/publish/collect_pointcache.py | 18 ++++++++++++++++++ .../maya/plugins/publish/extract_pointcache.py | 3 +-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_pointcache.py b/openpype/hosts/maya/plugins/create/create_pointcache.py index 63c0490dc7..51eab94a1a 100644 --- a/openpype/hosts/maya/plugins/create/create_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_pointcache.py @@ -33,6 +33,7 @@ class CreatePointCache(plugin.Creator): self.data["refresh"] = False # Default to suspend refresh. # Add options for custom attributes + self.data["includeUserDefinedAttributes"] = True self.data["attr"] = "" self.data["attrPrefix"] = "" diff --git a/openpype/hosts/maya/plugins/publish/collect_pointcache.py b/openpype/hosts/maya/plugins/publish/collect_pointcache.py index 332992ca92..72aa37fc11 100644 --- a/openpype/hosts/maya/plugins/publish/collect_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/collect_pointcache.py @@ -42,3 +42,21 @@ class CollectPointcache(pyblish.api.InstancePlugin): if proxy_set: instance.remove(proxy_set) instance.data["setMembers"].remove(proxy_set) + + # Collect user defined attributes. + if not instance.data.get("includeUserDefinedAttributes", False): + return + + all_nodes = ( + instance.data["setMembers"] + instance.data.get("proxy", []) + ) + user_defined_attributes = set() + for node in all_nodes: + attrs = cmds.listAttr(node, userDefined=True) or list() + shapes = cmds.listRelatives(node, shapes=True) or list() + for shape in shapes: + attrs.extend(cmds.listAttr(shape, userDefined=True) or list()) + + user_defined_attributes.update(attrs) + + instance.data["userDefinedAttributes"] = list(user_defined_attributes) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 0eb65e4226..8e794d4e17 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -39,8 +39,7 @@ class ExtractAlembic(publish.Extractor): start = float(instance.data.get("frameStartHandle", 1)) end = float(instance.data.get("frameEndHandle", 1)) - attrs = instance.data.get("attr", "").split(";") - attrs = [value for value in attrs if value.strip()] + attrs = instance.data.get("userDefinedAttributes", []) attrs += ["cbId"] attr_prefixes = instance.data.get("attrPrefix", "").split(";") From 80e19b6d430f239b95d8b14e9965ba3821e28867 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 Feb 2023 12:22:49 +0000 Subject: [PATCH 1042/1271] Include animation family --- .../maya/plugins/create/create_animation.py | 4 ++++ .../maya/plugins/publish/collect_animation.py | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation.py b/openpype/hosts/maya/plugins/create/create_animation.py index e54c12315c..a4b6e86598 100644 --- a/openpype/hosts/maya/plugins/create/create_animation.py +++ b/openpype/hosts/maya/plugins/create/create_animation.py @@ -13,6 +13,7 @@ class CreateAnimation(plugin.Creator): icon = "male" write_color_sets = False write_face_sets = False + include_user_defined_attributes = False def __init__(self, *args, **kwargs): super(CreateAnimation, self).__init__(*args, **kwargs) @@ -47,3 +48,6 @@ class CreateAnimation(plugin.Creator): # Default to write normals. self.data["writeNormals"] = True + + value = self.include_user_defined_attributes + self.data["includeUserDefinedAttributes"] = value diff --git a/openpype/hosts/maya/plugins/publish/collect_animation.py b/openpype/hosts/maya/plugins/publish/collect_animation.py index 549098863f..8f523f770b 100644 --- a/openpype/hosts/maya/plugins/publish/collect_animation.py +++ b/openpype/hosts/maya/plugins/publish/collect_animation.py @@ -46,7 +46,6 @@ class CollectAnimationOutputGeometry(pyblish.api.InstancePlugin): hierarchy = members + descendants - # Ignore certain node types (e.g. constraints) ignore = cmds.ls(hierarchy, type=self.ignore_type, long=True) if ignore: @@ -58,3 +57,18 @@ class CollectAnimationOutputGeometry(pyblish.api.InstancePlugin): if instance.data.get("farm"): instance.data["families"].append("publish.farm") + + # Collect user defined attributes. + if not instance.data.get("includeUserDefinedAttributes", False): + return + + user_defined_attributes = set() + for node in hierarchy: + attrs = cmds.listAttr(node, userDefined=True) or list() + shapes = cmds.listRelatives(node, shapes=True) or list() + for shape in shapes: + attrs.extend(cmds.listAttr(shape, userDefined=True) or list()) + + user_defined_attributes.update(attrs) + + instance.data["userDefinedAttributes"] = list(user_defined_attributes) From b12d1e9ff99f26435a167c6f17c9a9f9e2b79cdb Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 Feb 2023 12:23:10 +0000 Subject: [PATCH 1043/1271] Add project settings. --- .../hosts/maya/plugins/create/create_pointcache.py | 4 +++- openpype/settings/defaults/project_settings/maya.json | 2 ++ .../projects_schema/schemas/schema_maya_create.json | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/create/create_pointcache.py b/openpype/hosts/maya/plugins/create/create_pointcache.py index 51eab94a1a..1b8d5e6850 100644 --- a/openpype/hosts/maya/plugins/create/create_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_pointcache.py @@ -15,6 +15,7 @@ class CreatePointCache(plugin.Creator): icon = "gears" write_color_sets = False write_face_sets = False + include_user_defined_attributes = False def __init__(self, *args, **kwargs): super(CreatePointCache, self).__init__(*args, **kwargs) @@ -33,7 +34,8 @@ class CreatePointCache(plugin.Creator): self.data["refresh"] = False # Default to suspend refresh. # Add options for custom attributes - self.data["includeUserDefinedAttributes"] = True + value = self.include_user_defined_attributes + self.data["includeUserDefinedAttributes"] = value self.data["attr"] = "" self.data["attrPrefix"] = "" diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 32b141566b..2559448900 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -147,6 +147,7 @@ "enabled": true, "write_color_sets": false, "write_face_sets": false, + "include_user_defined_attributes": false, "defaults": [ "Main" ] @@ -165,6 +166,7 @@ "enabled": true, "write_color_sets": false, "write_face_sets": false, + "include_user_defined_attributes": false, "defaults": [ "Main" ] 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 e1a3082616..77a39f692f 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 @@ -132,6 +132,11 @@ "key": "write_face_sets", "label": "Write Face Sets" }, + { + "type": "boolean", + "key": "include_user_defined_attributes", + "label": "Include User Defined Attributes" + }, { "type": "list", "key": "defaults", @@ -192,6 +197,11 @@ "key": "write_face_sets", "label": "Write Face Sets" }, + { + "type": "boolean", + "key": "include_user_defined_attributes", + "label": "Include User Defined Attributes" + }, { "type": "list", "key": "defaults", From ba927f068f115521d9a51564e1e3b7cb0f4a52e6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 Feb 2023 15:39:15 +0000 Subject: [PATCH 1044/1271] BigRoy feedback --- openpype/hosts/maya/plugins/publish/collect_pointcache.py | 5 +---- openpype/hosts/maya/plugins/publish/extract_pointcache.py | 4 +++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_pointcache.py b/openpype/hosts/maya/plugins/publish/collect_pointcache.py index 72aa37fc11..d0430c5612 100644 --- a/openpype/hosts/maya/plugins/publish/collect_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/collect_pointcache.py @@ -47,11 +47,8 @@ class CollectPointcache(pyblish.api.InstancePlugin): if not instance.data.get("includeUserDefinedAttributes", False): return - all_nodes = ( - instance.data["setMembers"] + instance.data.get("proxy", []) - ) user_defined_attributes = set() - for node in all_nodes: + for node in instance: attrs = cmds.listAttr(node, userDefined=True) or list() shapes = cmds.listRelatives(node, shapes=True) or list() for shape in shapes: diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 8e794d4e17..e551858d48 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -39,7 +39,9 @@ class ExtractAlembic(publish.Extractor): start = float(instance.data.get("frameStartHandle", 1)) end = float(instance.data.get("frameEndHandle", 1)) - attrs = instance.data.get("userDefinedAttributes", []) + attrs = instance.data.get("attr", "").split(";") + attrs = [value for value in attrs if value.strip()] + attrs += instance.data.get("userDefinedAttributes", []) attrs += ["cbId"] attr_prefixes = instance.data.get("attrPrefix", "").split(";") From 421e24055b03d3c3a910ebf74e102ef5b6144f3b Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 Feb 2023 17:14:36 +0000 Subject: [PATCH 1045/1271] Documentation --- website/docs/artist_hosts_maya.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/website/docs/artist_hosts_maya.md b/website/docs/artist_hosts_maya.md index 9fab845e62..73bff286b9 100644 --- a/website/docs/artist_hosts_maya.md +++ b/website/docs/artist_hosts_maya.md @@ -314,10 +314,18 @@ Example setup: ![Maya - Point Cache Example](assets/maya-pointcache_setup.png) -:::note Publish on farm -If your studio has Deadline configured, artists could choose to offload potentially long running export of pointache and publish it to the farm. -Only thing that is necessary is to toggle `Farm` property in created pointcache instance to True. -::: +#### Options + +- **Frame Start**: which frame to start the export at. +- **Frame End**: which frame to end the export at. +- **Handle Start**: additional frames to export at frame start. Ei. frame start - handle start = export start. +- **Handle Start**: additional frames to export at frame end. Ei. frame end + handle end = export end. +- **Step**: frequency of sampling the export. For example when dealing with quick movements for motion blur, a step size of less than 1 might be better. +- **Refresh**: refresh the viewport when exporting the pointcache. For performance is best to leave off, but certain situations can require to refresh the viewport, for example using the Bullet plugin. +- **Attr**: specific attributes to publish separated by `;` +- **Include User Defined Attribudes**: include all user defined attributes in the publish. +- **Farm**: if your studio has Deadline configured, artists could choose to offload potentially long running export of pointache and publish it to the farm. Only thing that is necessary is to toggle this attribute in created pointcache instance to True. +- **Priority**: Farm priority. ### Loading Point Caches From aee263d24da72655d48942ee198cf95cd94054fc Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Fri, 24 Feb 2023 08:56:52 +0000 Subject: [PATCH 1046/1271] Update website/docs/artist_hosts_maya.md --- website/docs/artist_hosts_maya.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/docs/artist_hosts_maya.md b/website/docs/artist_hosts_maya.md index 73bff286b9..c2100204b8 100644 --- a/website/docs/artist_hosts_maya.md +++ b/website/docs/artist_hosts_maya.md @@ -322,7 +322,8 @@ Example setup: - **Handle Start**: additional frames to export at frame end. Ei. frame end + handle end = export end. - **Step**: frequency of sampling the export. For example when dealing with quick movements for motion blur, a step size of less than 1 might be better. - **Refresh**: refresh the viewport when exporting the pointcache. For performance is best to leave off, but certain situations can require to refresh the viewport, for example using the Bullet plugin. -- **Attr**: specific attributes to publish separated by `;` +- **Attr**: specific attributes to publish separated by `;`. +- **AttrPrefix**: Prefix filter for determining which geometric attributes to write out, separated by `;`. - **Include User Defined Attribudes**: include all user defined attributes in the publish. - **Farm**: if your studio has Deadline configured, artists could choose to offload potentially long running export of pointache and publish it to the farm. Only thing that is necessary is to toggle this attribute in created pointcache instance to True. - **Priority**: Farm priority. From 30768de8502db34e2d03d2440262733e61002949 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Fri, 24 Feb 2023 10:15:01 +0000 Subject: [PATCH 1047/1271] Update website/docs/artist_hosts_maya.md Co-authored-by: Roy Nieterau --- website/docs/artist_hosts_maya.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/artist_hosts_maya.md b/website/docs/artist_hosts_maya.md index c2100204b8..07495fbc96 100644 --- a/website/docs/artist_hosts_maya.md +++ b/website/docs/artist_hosts_maya.md @@ -323,7 +323,7 @@ Example setup: - **Step**: frequency of sampling the export. For example when dealing with quick movements for motion blur, a step size of less than 1 might be better. - **Refresh**: refresh the viewport when exporting the pointcache. For performance is best to leave off, but certain situations can require to refresh the viewport, for example using the Bullet plugin. - **Attr**: specific attributes to publish separated by `;`. -- **AttrPrefix**: Prefix filter for determining which geometric attributes to write out, separated by `;`. +- **AttrPrefix**: specific attributes which start with this prefix to publish separated by `;`. - **Include User Defined Attribudes**: include all user defined attributes in the publish. - **Farm**: if your studio has Deadline configured, artists could choose to offload potentially long running export of pointache and publish it to the farm. Only thing that is necessary is to toggle this attribute in created pointcache instance to True. - **Priority**: Farm priority. From 90955e577900ad86072468f33cbf26af3b2a5116 Mon Sep 17 00:00:00 2001 From: Joseff Date: Wed, 25 Jan 2023 11:31:27 +0100 Subject: [PATCH 1048/1271] Refactored the generation of UE projects, plugin is now being installed in the engine. --- openpype/hosts/unreal/addon.py | 2 +- .../unreal/hooks/pre_workfile_preparation.py | 28 ++- .../UE_4.7/CommandletProject/.gitignore | 6 + .../CommandletProject.uproject | 12 + .../Config/DefaultEditor.ini | 0 .../Config/DefaultEngine.ini | 16 ++ .../CommandletProject/Config/DefaultGame.ini | 4 + .../UE_4.7/{ => OpenPype}/.gitignore | 0 .../Config/DefaultOpenPypeSettings.ini | 0 .../UE_4.7/OpenPype/Config/FilterPlugin.ini | 8 + .../Content/Python/init_unreal.py | 0 .../OpenPype}/OpenPype.uplugin | 4 +- .../UE_4.7/{ => OpenPype}/README.md | 0 .../{ => OpenPype}/Resources/openpype128.png | Bin .../{ => OpenPype}/Resources/openpype40.png | Bin .../{ => OpenPype}/Resources/openpype512.png | Bin .../Source/OpenPype/OpenPype.Build.cs | 5 +- .../OPGenerateProjectCommandlet.cpp | 140 +++++++++++ .../Private/Commandlets/OPActionResult.cpp | 41 ++++ .../OpenPype/Private/Logging/OP_Log.cpp | 1 + .../Source/OpenPype/Private/OpenPype.cpp | 42 ++-- .../Source/OpenPype/Private/OpenPypeLib.cpp | 0 .../Private/OpenPypePublishInstance.cpp | 4 +- .../OpenPypePublishInstanceFactory.cpp | 0 .../OpenPype/Private/OpenPypePythonBridge.cpp | 0 .../OpenPype/Private/OpenPypeSettings.cpp | 3 +- .../Source/OpenPype/Private/OpenPypeStyle.cpp | 3 +- .../OPGenerateProjectCommandlet.h | 60 +++++ .../Public/Commandlets/OPActionResult.h | 83 +++++++ .../Source/OpenPype/Public/Logging/OP_Log.h | 3 + .../Source/OpenPype/Public/OPConstants.h | 12 + .../Source/OpenPype/Public/OpenPype.h | 0 .../Source/OpenPype/Public/OpenPypeLib.h | 0 .../OpenPype/Public/OpenPypePublishInstance.h | 6 +- .../Public/OpenPypePublishInstanceFactory.h | 0 .../OpenPype/Public/OpenPypePythonBridge.h | 0 .../Source/OpenPype/Public/OpenPypeSettings.h | 1 - .../Source/OpenPype/Public/OpenPypeStyle.h | 0 .../OpenPype/Private/AssetContainer.cpp | 115 --------- .../Private/AssetContainerFactory.cpp | 20 -- .../Source/OpenPype/Public/AssetContainer.h | 39 --- .../OpenPype/Public/AssetContainerFactory.h | 21 -- .../UE_5.0/CommandletProject/.gitignore | 6 + .../CommandletProject.uproject | 20 ++ .../Config/DefaultEditor.ini | 0 .../Config/DefaultEngine.ini | 42 ++++ .../CommandletProject/Config/DefaultGame.ini | 4 + .../UE_5.0/{ => OpenPype}/.gitignore | 0 .../Config/DefaultOpenPypeSettings.ini | 0 .../UE_5.0/OpenPype/Config/FilterPlugin.ini | 8 + .../Content/Python/init_unreal.py | 0 .../OpenPype}/OpenPype.uplugin | 1 + .../UE_5.0/{ => OpenPype}/README.md | 0 .../{ => OpenPype}/Resources/openpype128.png | Bin .../{ => OpenPype}/Resources/openpype40.png | Bin .../{ => OpenPype}/Resources/openpype512.png | Bin .../Source/OpenPype/OpenPype.Build.cs | 5 +- .../OPGenerateProjectCommandlet.cpp | 140 +++++++++++ .../Private/Commandlets/OPActionResult.cpp | 41 ++++ .../OpenPype/Private/Logging/OP_Log.cpp | 1 + .../Source/OpenPype/Private/OpenPype.cpp | 0 .../OpenPype/Private/OpenPypeCommands.cpp | 0 .../Source/OpenPype/Private/OpenPypeLib.cpp | 0 .../Private/OpenPypePublishInstance.cpp | 2 +- .../OpenPypePublishInstanceFactory.cpp | 0 .../OpenPype/Private/OpenPypePythonBridge.cpp | 0 .../OpenPype/Private/OpenPypeSettings.cpp | 0 .../Source/OpenPype/Private/OpenPypeStyle.cpp | 0 .../OPGenerateProjectCommandlet.h | 60 +++++ .../Public/Commandlets/OPActionResult.h | 83 +++++++ .../Source/OpenPype/Public/Logging/OP_Log.h | 3 + .../Source/OpenPype/Public/OPConstants.h | 12 + .../Source/OpenPype/Public/OpenPype.h | 0 .../Source/OpenPype/Public/OpenPypeCommands.h | 0 .../Source/OpenPype/Public/OpenPypeLib.h | 0 .../OpenPype/Public/OpenPypePublishInstance.h | 6 +- .../Public/OpenPypePublishInstanceFactory.h | 0 .../OpenPype/Public/OpenPypePythonBridge.h | 0 .../Source/OpenPype/Public/OpenPypeSettings.h | 0 .../Source/OpenPype/Public/OpenPypeStyle.h | 0 .../OpenPype/Private/AssetContainer.cpp | 115 --------- .../Private/AssetContainerFactory.cpp | 20 -- .../Source/OpenPype/Public/AssetContainer.h | 39 --- .../OpenPype/Public/AssetContainerFactory.h | 21 -- openpype/hosts/unreal/lib.py | 230 +++++++++++++----- 85 files changed, 1029 insertions(+), 509 deletions(-) create mode 100644 openpype/hosts/unreal/integration/UE_4.7/CommandletProject/.gitignore create mode 100644 openpype/hosts/unreal/integration/UE_4.7/CommandletProject/CommandletProject.uproject create mode 100644 openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEditor.ini create mode 100644 openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEngine.ini create mode 100644 openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultGame.ini rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/.gitignore (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Config/DefaultOpenPypeSettings.ini (100%) create mode 100644 openpype/hosts/unreal/integration/UE_4.7/OpenPype/Config/FilterPlugin.ini rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Content/Python/init_unreal.py (100%) rename openpype/hosts/unreal/integration/{UE_5.0 => UE_4.7/OpenPype}/OpenPype.uplugin (90%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/README.md (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Resources/openpype128.png (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Resources/openpype40.png (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Resources/openpype512.png (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/OpenPype.Build.cs (92%) create mode 100644 openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp create mode 100644 openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp create mode 100644 openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Private/OpenPype.cpp (79%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Private/OpenPypeLib.cpp (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Private/OpenPypePublishInstance.cpp (98%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Private/OpenPypePythonBridge.cpp (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Private/OpenPypeSettings.cpp (91%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Private/OpenPypeStyle.cpp (93%) create mode 100644 openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h create mode 100644 openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h create mode 100644 openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h create mode 100644 openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OPConstants.h rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Public/OpenPype.h (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Public/OpenPypeLib.h (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Public/OpenPypePublishInstance.h (94%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Public/OpenPypePythonBridge.h (100%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Public/OpenPypeSettings.h (97%) rename openpype/hosts/unreal/integration/UE_4.7/{ => OpenPype}/Source/OpenPype/Public/OpenPypeStyle.h (100%) delete mode 100644 openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/AssetContainer.cpp delete mode 100644 openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/AssetContainerFactory.cpp delete mode 100644 openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/AssetContainer.h delete mode 100644 openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/AssetContainerFactory.h create mode 100644 openpype/hosts/unreal/integration/UE_5.0/CommandletProject/.gitignore create mode 100644 openpype/hosts/unreal/integration/UE_5.0/CommandletProject/CommandletProject.uproject create mode 100644 openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEditor.ini create mode 100644 openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEngine.ini create mode 100644 openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultGame.ini rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/.gitignore (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Config/DefaultOpenPypeSettings.ini (100%) create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Config/FilterPlugin.ini rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Content/Python/init_unreal.py (100%) rename openpype/hosts/unreal/integration/{UE_4.7 => UE_5.0/OpenPype}/OpenPype.uplugin (95%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/README.md (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Resources/openpype128.png (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Resources/openpype40.png (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Resources/openpype512.png (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/OpenPype.Build.cs (92%) create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Private/OpenPype.cpp (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Private/OpenPypeCommands.cpp (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Private/OpenPypeLib.cpp (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Private/OpenPypePublishInstance.cpp (99%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Private/OpenPypePythonBridge.cpp (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Private/OpenPypeSettings.cpp (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Private/OpenPypeStyle.cpp (100%) create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OPConstants.h rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Public/OpenPype.h (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Public/OpenPypeCommands.h (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Public/OpenPypeLib.h (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Public/OpenPypePublishInstance.h (94%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Public/OpenPypePythonBridge.h (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Public/OpenPypeSettings.h (100%) rename openpype/hosts/unreal/integration/UE_5.0/{ => OpenPype}/Source/OpenPype/Public/OpenPypeStyle.h (100%) delete mode 100644 openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/AssetContainer.cpp delete mode 100644 openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/AssetContainerFactory.cpp delete mode 100644 openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/AssetContainer.h delete mode 100644 openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/AssetContainerFactory.h diff --git a/openpype/hosts/unreal/addon.py b/openpype/hosts/unreal/addon.py index e2c8484651..c92a44870f 100644 --- a/openpype/hosts/unreal/addon.py +++ b/openpype/hosts/unreal/addon.py @@ -17,7 +17,7 @@ class UnrealAddon(OpenPypeModule, IHostAddon): ue_plugin = "UE_5.0" if app.name[:1] == "5" else "UE_4.7" unreal_plugin_path = os.path.join( - UNREAL_ROOT_DIR, "integration", ue_plugin + UNREAL_ROOT_DIR, "integration", ue_plugin, "OpenPype" ) if not env.get("OPENPYPE_UNREAL_PLUGIN"): env["OPENPYPE_UNREAL_PLUGIN"] = unreal_plugin_path diff --git a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py index 2dc6fb9f42..821018ba9d 100644 --- a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py +++ b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py @@ -119,29 +119,33 @@ class UnrealPrelaunchHook(PreLaunchHook): f"detected [ {engine_version} ]" )) - ue_path = unreal_lib.get_editor_executable_path( + ue_path = unreal_lib.get_editor_exe_path( Path(detected[engine_version]), engine_version) self.launch_context.launch_args = [ue_path.as_posix()] project_path.mkdir(parents=True, exist_ok=True) + # Set "OPENPYPE_UNREAL_PLUGIN" to current process environment for + # execution of `create_unreal_project` + if self.launch_context.env.get("OPENPYPE_UNREAL_PLUGIN"): + self.log.info(( + f"{self.signature} using OpenPype plugin from " + f"{self.launch_context.env.get('OPENPYPE_UNREAL_PLUGIN')}" + )) + env_key = "OPENPYPE_UNREAL_PLUGIN" + if self.launch_context.env.get(env_key): + os.environ[env_key] = self.launch_context.env[env_key] + + engine_path = detected[engine_version] + + unreal_lib.try_installing_plugin(Path(engine_path), engine_version) + project_file = project_path / unreal_project_filename if not project_file.is_file(): - engine_path = detected[engine_version] self.log.info(( f"{self.signature} creating unreal " f"project [ {unreal_project_name} ]" )) - # Set "OPENPYPE_UNREAL_PLUGIN" to current process environment for - # execution of `create_unreal_project` - if self.launch_context.env.get("OPENPYPE_UNREAL_PLUGIN"): - self.log.info(( - f"{self.signature} using OpenPype plugin from " - f"{self.launch_context.env.get('OPENPYPE_UNREAL_PLUGIN')}" - )) - env_key = "OPENPYPE_UNREAL_PLUGIN" - if self.launch_context.env.get(env_key): - os.environ[env_key] = self.launch_context.env[env_key] unreal_lib.create_unreal_project( unreal_project_name, diff --git a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/.gitignore b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/.gitignore new file mode 100644 index 0000000000..1004610e4f --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/.gitignore @@ -0,0 +1,6 @@ +/Saved +/DerivedDataCache +/Intermediate +/Binaries +/.idea +/.vs \ No newline at end of file diff --git a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/CommandletProject.uproject b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/CommandletProject.uproject new file mode 100644 index 0000000000..4d75e03bf3 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/CommandletProject.uproject @@ -0,0 +1,12 @@ +{ + "FileVersion": 3, + "EngineAssociation": "4.27", + "Category": "", + "Description": "", + "Plugins": [ + { + "Name": "OpenPype", + "Enabled": true + } + ] +} \ No newline at end of file diff --git a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEditor.ini b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEditor.ini new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEngine.ini b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEngine.ini new file mode 100644 index 0000000000..2845baccca --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEngine.ini @@ -0,0 +1,16 @@ + + +[/Script/EngineSettings.GameMapsSettings] +GameDefaultMap=/Engine/Maps/Templates/Template_Default.Template_Default + + +[/Script/HardwareTargeting.HardwareTargetingSettings] +TargetedHardwareClass=Desktop +AppliedTargetedHardwareClass=Desktop +DefaultGraphicsPerformance=Maximum +AppliedDefaultGraphicsPerformance=Maximum + +[/Script/Engine.Engine] ++ActiveGameNameRedirects=(OldGameName="TP_BlankBP",NewGameName="/Script/CommandletProject") ++ActiveGameNameRedirects=(OldGameName="/Script/TP_BlankBP",NewGameName="/Script/CommandletProject") + diff --git a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultGame.ini b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultGame.ini new file mode 100644 index 0000000000..40956de961 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultGame.ini @@ -0,0 +1,4 @@ + + +[/Script/EngineSettings.GeneralProjectSettings] +ProjectID=95AED0BF45A918DF73ABB3BB27D25356 diff --git a/openpype/hosts/unreal/integration/UE_4.7/.gitignore b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/.gitignore similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/.gitignore rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/.gitignore diff --git a/openpype/hosts/unreal/integration/UE_4.7/Config/DefaultOpenPypeSettings.ini b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Config/DefaultOpenPypeSettings.ini similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Config/DefaultOpenPypeSettings.ini rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Config/DefaultOpenPypeSettings.ini diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Config/FilterPlugin.ini b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Config/FilterPlugin.ini new file mode 100644 index 0000000000..ccebca2f32 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Config/FilterPlugin.ini @@ -0,0 +1,8 @@ +[FilterPlugin] +; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and +; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. +; +; Examples: +; /README.txt +; /Extras/... +; /Binaries/ThirdParty/*.dll diff --git a/openpype/hosts/unreal/integration/UE_4.7/Content/Python/init_unreal.py b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Content/Python/init_unreal.py similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Content/Python/init_unreal.py rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Content/Python/init_unreal.py diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype.uplugin b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/OpenPype.uplugin similarity index 90% rename from openpype/hosts/unreal/integration/UE_5.0/OpenPype.uplugin rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/OpenPype.uplugin index 4c7a74403c..23155cb74d 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype.uplugin +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/OpenPype.uplugin @@ -10,10 +10,10 @@ "DocsURL": "https://openpype.io/docs/artist_hosts_unreal", "MarketplaceURL": "", "SupportURL": "https://pype.club/", + "EngineVersion": "4.27", "CanContainContent": true, "IsBetaVersion": true, - "IsExperimentalVersion": false, - "Installed": false, + "Installed": true, "Modules": [ { "Name": "OpenPype", diff --git a/openpype/hosts/unreal/integration/UE_4.7/README.md b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/README.md similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/README.md rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/README.md diff --git a/openpype/hosts/unreal/integration/UE_4.7/Resources/openpype128.png b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Resources/openpype128.png similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Resources/openpype128.png rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Resources/openpype128.png diff --git a/openpype/hosts/unreal/integration/UE_4.7/Resources/openpype40.png b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Resources/openpype40.png similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Resources/openpype40.png rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Resources/openpype40.png diff --git a/openpype/hosts/unreal/integration/UE_4.7/Resources/openpype512.png b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Resources/openpype512.png similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Resources/openpype512.png rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Resources/openpype512.png diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/OpenPype.Build.cs b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/OpenPype.Build.cs similarity index 92% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/OpenPype.Build.cs rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/OpenPype.Build.cs index 46e5dcb2df..13afb11003 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/OpenPype.Build.cs +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/OpenPype.Build.cs @@ -6,8 +6,8 @@ public class OpenPype : ModuleRules { public OpenPype(ReadOnlyTargetRules Target) : base(Target) { - PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; - + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + PublicIncludePaths.AddRange( new string[] { // ... add public include paths required here ... @@ -34,6 +34,7 @@ public class OpenPype : ModuleRules PrivateDependencyModuleNames.AddRange( new string[] { + "GameProjectGeneration", "Projects", "InputCore", "UnrealEd", diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp new file mode 100644 index 0000000000..024a6097b3 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp @@ -0,0 +1,140 @@ +#include "Commandlets/Implementations/OPGenerateProjectCommandlet.h" + +#include "Editor.h" +#include "GameProjectUtils.h" +#include "OPConstants.h" +#include "Commandlets/OPActionResult.h" +#include "ProjectDescriptor.h" + +int32 UOPGenerateProjectCommandlet::Main(const FString& CommandLineParams) +{ + //Parses command line parameters & creates structure FProjectInformation + const FOPGenerateProjectParams ParsedParams = FOPGenerateProjectParams(CommandLineParams); + ProjectInformation = ParsedParams.GenerateUEProjectInformation(); + + //Creates .uproject & other UE files + EVALUATE_OP_ACTION_RESULT(TryCreateProject()); + + //Loads created .uproject + EVALUATE_OP_ACTION_RESULT(TryLoadProjectDescriptor()); + + //Adds needed plugin to .uproject + AttachPluginsToProjectDescriptor(); + + //Saves .uproject + EVALUATE_OP_ACTION_RESULT(TrySave()); + + //When we are here, there should not be problems in generating Unreal Project for OpenPype + return 0; +} + + +FOPGenerateProjectParams::FOPGenerateProjectParams(): FOPGenerateProjectParams("") +{ +} + +FOPGenerateProjectParams::FOPGenerateProjectParams(const FString& CommandLineParams): CommandLineParams( + CommandLineParams) +{ + UCommandlet::ParseCommandLine(*CommandLineParams, Tokens, Switches); +} + +FProjectInformation FOPGenerateProjectParams::GenerateUEProjectInformation() const +{ + FProjectInformation ProjectInformation = FProjectInformation(); + ProjectInformation.ProjectFilename = GetProjectFileName(); + + ProjectInformation.bShouldGenerateCode = IsSwitchPresent("GenerateCode"); + + return ProjectInformation; +} + +FString FOPGenerateProjectParams::TryGetToken(const int32 Index) const +{ + return Tokens.IsValidIndex(Index) ? Tokens[Index] : ""; +} + +FString FOPGenerateProjectParams::GetProjectFileName() const +{ + return TryGetToken(0); +} + +bool FOPGenerateProjectParams::IsSwitchPresent(const FString& Switch) const +{ + return INDEX_NONE != Switches.IndexOfByPredicate([&Switch](const FString& Item) -> bool + { + return Item.Equals(Switch); + } + ); +} + + +UOPGenerateProjectCommandlet::UOPGenerateProjectCommandlet() +{ + LogToConsole = true; +} + +FOP_ActionResult UOPGenerateProjectCommandlet::TryCreateProject() const +{ + FText FailReason; + FText FailLog; + TArray OutCreatedFiles; + + if (!GameProjectUtils::CreateProject(ProjectInformation, FailReason, FailLog, &OutCreatedFiles)) + return FOP_ActionResult(EOP_ActionResult::ProjectNotCreated, FailReason); + return FOP_ActionResult(); +} + +FOP_ActionResult UOPGenerateProjectCommandlet::TryLoadProjectDescriptor() +{ + FText FailReason; + const bool bLoaded = ProjectDescriptor.Load(ProjectInformation.ProjectFilename, FailReason); + + return FOP_ActionResult(bLoaded ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotLoaded, FailReason); +} + +void UOPGenerateProjectCommandlet::AttachPluginsToProjectDescriptor() +{ + FPluginReferenceDescriptor OPPluginDescriptor; + OPPluginDescriptor.bEnabled = true; + OPPluginDescriptor.Name = OPConstants::OP_PluginName; + ProjectDescriptor.Plugins.Add(OPPluginDescriptor); + + FPluginReferenceDescriptor PythonPluginDescriptor; + PythonPluginDescriptor.bEnabled = true; + PythonPluginDescriptor.Name = OPConstants::PythonScript_PluginName; + ProjectDescriptor.Plugins.Add(PythonPluginDescriptor); + + FPluginReferenceDescriptor SequencerScriptingPluginDescriptor; + SequencerScriptingPluginDescriptor.bEnabled = true; + SequencerScriptingPluginDescriptor.Name = OPConstants::SequencerScripting_PluginName; + ProjectDescriptor.Plugins.Add(SequencerScriptingPluginDescriptor); + + FPluginReferenceDescriptor MovieRenderPipelinePluginDescriptor; + MovieRenderPipelinePluginDescriptor.bEnabled = true; + MovieRenderPipelinePluginDescriptor.Name = OPConstants::MovieRenderPipeline_PluginName; + ProjectDescriptor.Plugins.Add(MovieRenderPipelinePluginDescriptor); + + FPluginReferenceDescriptor EditorScriptingPluginDescriptor; + EditorScriptingPluginDescriptor.bEnabled = true; + EditorScriptingPluginDescriptor.Name = OPConstants::EditorScriptingUtils_PluginName; + ProjectDescriptor.Plugins.Add(EditorScriptingPluginDescriptor); +} + +FOP_ActionResult UOPGenerateProjectCommandlet::TrySave() +{ + FText FailReason; + const bool bSaved = ProjectDescriptor.Save(ProjectInformation.ProjectFilename, FailReason); + + return FOP_ActionResult(bSaved ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotSaved, FailReason); +} + +FOPGenerateProjectParams UOPGenerateProjectCommandlet::ParseParameters(const FString& Params) const +{ + FOPGenerateProjectParams ParamsResult; + + TArray Tokens, Switches; + ParseCommandLine(*Params, Tokens, Switches); + + return ParamsResult; +} diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp new file mode 100644 index 0000000000..9236fbb057 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp @@ -0,0 +1,41 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Commandlets/OPActionResult.h" +#include "Logging/OP_Log.h" + +EOP_ActionResult::Type& FOP_ActionResult::GetStatus() +{ + return Status; +} + +FText& FOP_ActionResult::GetReason() +{ + return Reason; +} + +FOP_ActionResult::FOP_ActionResult():Status(EOP_ActionResult::Type::Ok) +{ + +} + +FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum):Status(InEnum) +{ + TryLog(); +} + +FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason):Status(InEnum), Reason(InReason) +{ + TryLog(); +}; + +bool FOP_ActionResult::IsProblem() const +{ + return Status != EOP_ActionResult::Ok; +} + +void FOP_ActionResult::TryLog() const +{ + if(IsProblem()) + UE_LOG(LogCommandletOPGenerateProject, Error, TEXT("%s"), *Reason.ToString()); +} diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp new file mode 100644 index 0000000000..29b1068c21 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp @@ -0,0 +1 @@ +#include "Logging/OP_Log.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPype.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPype.cpp similarity index 79% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPype.cpp rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPype.cpp index d06a08eb43..a510a5e3bf 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPype.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPype.cpp @@ -16,32 +16,34 @@ static const FName OpenPypeTabName("OpenPype"); // This function is triggered when the plugin is staring up void FOpenPypeModule::StartupModule() { - FOpenPypeStyle::Initialize(); - FOpenPypeStyle::SetIcon("Logo", "openpype40"); + if (!IsRunningCommandlet()) { + FOpenPypeStyle::Initialize(); + FOpenPypeStyle::SetIcon("Logo", "openpype40"); - // Create the Extender that will add content to the menu - FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); + // Create the Extender that will add content to the menu + FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); - TSharedPtr MenuExtender = MakeShareable(new FExtender()); - TSharedPtr ToolbarExtender = MakeShareable(new FExtender()); + TSharedPtr MenuExtender = MakeShareable(new FExtender()); + TSharedPtr ToolbarExtender = MakeShareable(new FExtender()); - MenuExtender->AddMenuExtension( - "LevelEditor", - EExtensionHook::After, - NULL, - FMenuExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddMenuEntry) - ); - ToolbarExtender->AddToolBarExtension( - "Settings", - EExtensionHook::After, - NULL, - FToolBarExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddToobarEntry)); + MenuExtender->AddMenuExtension( + "LevelEditor", + EExtensionHook::After, + NULL, + FMenuExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddMenuEntry) + ); + ToolbarExtender->AddToolBarExtension( + "Settings", + EExtensionHook::After, + NULL, + FToolBarExtensionDelegate::CreateRaw(this, &FOpenPypeModule::AddToobarEntry)); - LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender); - LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender); + LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender); + LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender); - RegisterSettings(); + RegisterSettings(); + } } void FOpenPypeModule::ShutdownModule() diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypeLib.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypeLib.cpp rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypePublishInstance.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp similarity index 98% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypePublishInstance.cpp rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp index 38740f1cbd..424c4ed491 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypePublishInstance.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp @@ -2,10 +2,10 @@ #include "OpenPypePublishInstance.h" #include "AssetRegistryModule.h" -#include "NotificationManager.h" #include "OpenPypeLib.h" #include "OpenPypeSettings.h" -#include "SNotificationList.h" +#include "Framework/Notifications/NotificationManager.h" +#include "Widgets/Notifications/SNotificationList.h" //Moves all the invalid pointers to the end to prepare them for the shrinking #define REMOVE_INVALID_ENTRIES(VAR) VAR.CompactStable(); \ diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypePythonBridge.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypePythonBridge.cpp rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypeSettings.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp similarity index 91% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypeSettings.cpp rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp index 7134614d22..951b522308 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypeSettings.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp @@ -2,8 +2,7 @@ #include "OpenPypeSettings.h" -#include "IPluginManager.h" -#include "UObjectGlobals.h" +#include "Interfaces/IPluginManager.h" /** * Mainly is used for initializing default values if the DefaultOpenPypeSettings.ini file does not exist in the saved config diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypeStyle.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp similarity index 93% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypeStyle.cpp rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp index a51c2d6aa5..b7abc38156 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/OpenPypeStyle.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp @@ -43,7 +43,7 @@ const FVector2D Icon40x40(40.0f, 40.0f); TUniquePtr< FSlateStyleSet > FOpenPypeStyle::Create() { TUniquePtr< FSlateStyleSet > Style = MakeUnique(GetStyleSetName()); - Style->SetContentRoot(FPaths::ProjectPluginsDir() / TEXT("OpenPype/Resources")); + Style->SetContentRoot(FPaths::EnginePluginsDir() / TEXT("Marketplace/OpenPype/Resources")); return Style; } @@ -66,5 +66,4 @@ const ISlateStyle& FOpenPypeStyle::Get() { check(OpenPypeStyleInstance); return *OpenPypeStyleInstance; - return *OpenPypeStyleInstance; } diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h new file mode 100644 index 0000000000..8738de6d4a --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h @@ -0,0 +1,60 @@ +#pragma once + + +#include "GameProjectUtils.h" +#include "Commandlets/OPActionResult.h" +#include "ProjectDescriptor.h" +#include "Commandlets/Commandlet.h" +#include "OPGenerateProjectCommandlet.generated.h" + +struct FProjectDescriptor; +struct FProjectInformation; + +/** +* @brief Structure which parses command line parameters and generates FProjectInformation +*/ +USTRUCT() +struct FOPGenerateProjectParams +{ + GENERATED_BODY() + +private: + FString CommandLineParams; + TArray Tokens; + TArray Switches; + +public: + FOPGenerateProjectParams(); + FOPGenerateProjectParams(const FString& CommandLineParams); + + FProjectInformation GenerateUEProjectInformation() const; + +private: + FString TryGetToken(const int32 Index) const; + FString GetProjectFileName() const; + + bool IsSwitchPresent(const FString& Switch) const; +}; + +UCLASS() +class OPENPYPE_API UOPGenerateProjectCommandlet : public UCommandlet +{ + GENERATED_BODY() + +private: + FProjectInformation ProjectInformation; + FProjectDescriptor ProjectDescriptor; + +public: + UOPGenerateProjectCommandlet(); + + virtual int32 Main(const FString& CommandLineParams) override; + +private: + FOPGenerateProjectParams ParseParameters(const FString& Params) const; + FOP_ActionResult TryCreateProject() const; + FOP_ActionResult TryLoadProjectDescriptor(); + void AttachPluginsToProjectDescriptor(); + FOP_ActionResult TrySave(); +}; + diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h new file mode 100644 index 0000000000..f46ba9c62a --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h @@ -0,0 +1,83 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "OPActionResult.generated.h" + +/** + * @brief This macro returns error code when is problem or does nothing when there is no problem. + * @param ActionResult FOP_ActionResult structure + */ +#define EVALUATE_OP_ACTION_RESULT(ActionResult) \ + if(ActionResult.IsProblem()) \ + return ActionResult.GetStatus(); + +/** +* @brief This enum values are humanly readable mapping of error codes. +* Here should be all error codes to be possible find what went wrong. +* TODO: In the future should exists an web document where is mapped error code & what problem occured & how to repair it... +*/ +UENUM() +namespace EOP_ActionResult +{ + enum Type + { + Ok, + ProjectNotCreated, + ProjectNotLoaded, + ProjectNotSaved, + //....Here insert another values + + //Do not remove! + //Usable for looping through enum values + __Last UMETA(Hidden) + }; +} + + +/** + * @brief This struct holds action result enum and optionally reason of fail + */ +USTRUCT() +struct FOP_ActionResult +{ + GENERATED_BODY() + +public: + /** @brief Default constructor usable when there is no problem */ + FOP_ActionResult(); + + /** + * @brief This constructor initializes variables & attempts to log when is error + * @param InEnum Status + */ + FOP_ActionResult(const EOP_ActionResult::Type& InEnum); + + /** + * @brief This constructor initializes variables & attempts to log when is error + * @param InEnum Status + * @param InReason Reason of potential fail + */ + FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason); + +private: + /** @brief Action status */ + EOP_ActionResult::Type Status; + + /** @brief Optional reason of fail */ + FText Reason; + +public: + /** + * @brief Checks if there is problematic state + * @return true when status is not equal to EOP_ActionResult::Ok + */ + bool IsProblem() const; + EOP_ActionResult::Type& GetStatus(); + FText& GetReason(); + +private: + void TryLog() const; +}; + diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h new file mode 100644 index 0000000000..4f8af3e2e6 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h @@ -0,0 +1,3 @@ +#pragma once + +DEFINE_LOG_CATEGORY_STATIC(LogCommandletOPGenerateProject, Log, All); \ No newline at end of file diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OPConstants.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OPConstants.h new file mode 100644 index 0000000000..21a033e426 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OPConstants.h @@ -0,0 +1,12 @@ +#pragma once + +namespace OPConstants +{ + const FString OP_PluginName = "OpenPype"; + const FString PythonScript_PluginName = "PythonScriptPlugin"; + const FString SequencerScripting_PluginName = "SequencerScripting"; + const FString MovieRenderPipeline_PluginName = "MovieRenderPipeline"; + const FString EditorScriptingUtils_PluginName = "EditorScriptingUtilities"; +} + + diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPype.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPype.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPype.h rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPype.h diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypeLib.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeLib.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypeLib.h rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeLib.h diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypePublishInstance.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h similarity index 94% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypePublishInstance.h rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h index cd414fe2cc..16b3194b96 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypePublishInstance.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h @@ -16,7 +16,7 @@ public: * * @return - Set of UObjects. Careful! They are returning raw pointers. Seems like an issue in UE5 */ - UFUNCTION(BlueprintCallable, BlueprintPure) + UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python") TSet GetInternalAssets() const { //For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed. @@ -33,7 +33,7 @@ public: * * @return - TSet of assets (UObjects). Careful! They are returning raw pointers. Seems like an issue in UE5 */ - UFUNCTION(BlueprintCallable, BlueprintPure) + UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python") TSet GetExternalAssets() const { //For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed. @@ -53,7 +53,7 @@ public: * * @attention If the bAddExternalAssets variable is false, external assets won't be included! */ - UFUNCTION(BlueprintCallable, BlueprintPure) + UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python") TSet GetAllAssets() const { const TSet>& IteratedSet = bAddExternalAssets diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypePythonBridge.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypePythonBridge.h rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypeSettings.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h similarity index 97% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypeSettings.h rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h index 2df6c887cf..9bdcfb2399 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypeSettings.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h @@ -3,7 +3,6 @@ #pragma once #include "CoreMinimal.h" -#include "Object.h" #include "OpenPypeSettings.generated.h" #define OPENPYPE_SETTINGS_FILEPATH IPluginManager::Get().FindPlugin("OpenPype")->GetBaseDir() / TEXT("Config") / TEXT("DefaultOpenPypeSettings.ini") diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypeStyle.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/OpenPypeStyle.h rename to openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/AssetContainer.cpp b/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/AssetContainer.cpp deleted file mode 100644 index c766f87a8e..0000000000 --- a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/AssetContainer.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#include "AssetContainer.h" -#include "AssetRegistryModule.h" -#include "Misc/PackageName.h" -#include "Engine.h" -#include "Containers/UnrealString.h" - -UAssetContainer::UAssetContainer(const FObjectInitializer& ObjectInitializer) -: UAssetUserData(ObjectInitializer) -{ - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); - FString path = UAssetContainer::GetPathName(); - UE_LOG(LogTemp, Warning, TEXT("UAssetContainer %s"), *path); - FARFilter Filter; - Filter.PackagePaths.Add(FName(*path)); - - AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAssetContainer::OnAssetAdded); - AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAssetContainer::OnAssetRemoved); - AssetRegistryModule.Get().OnAssetRenamed().AddUObject(this, &UAssetContainer::OnAssetRenamed); -} - -void UAssetContainer::OnAssetAdded(const FAssetData& AssetData) -{ - TArray split; - - // get directory of current container - FString selfFullPath = UAssetContainer::GetPathName(); - FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath); - - // get asset path and class - FString assetPath = AssetData.GetFullName(); - FString assetFName = AssetData.AssetClass.ToString(); - - // split path - assetPath.ParseIntoArray(split, TEXT(" "), true); - - FString assetDir = FPackageName::GetLongPackagePath(*split[1]); - - // take interest only in paths starting with path of current container - if (assetDir.StartsWith(*selfDir)) - { - // exclude self - if (assetFName != "AssetContainer") - { - assets.Add(assetPath); - assetsData.Add(AssetData); - UE_LOG(LogTemp, Log, TEXT("%s: asset added to %s"), *selfFullPath, *selfDir); - } - } -} - -void UAssetContainer::OnAssetRemoved(const FAssetData& AssetData) -{ - TArray split; - - // get directory of current container - FString selfFullPath = UAssetContainer::GetPathName(); - FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath); - - // get asset path and class - FString assetPath = AssetData.GetFullName(); - FString assetFName = AssetData.AssetClass.ToString(); - - // split path - assetPath.ParseIntoArray(split, TEXT(" "), true); - - FString assetDir = FPackageName::GetLongPackagePath(*split[1]); - - // take interest only in paths starting with path of current container - FString path = UAssetContainer::GetPathName(); - FString lpp = FPackageName::GetLongPackagePath(*path); - - if (assetDir.StartsWith(*selfDir)) - { - // exclude self - if (assetFName != "AssetContainer") - { - // UE_LOG(LogTemp, Warning, TEXT("%s: asset removed"), *lpp); - assets.Remove(assetPath); - assetsData.Remove(AssetData); - } - } -} - -void UAssetContainer::OnAssetRenamed(const FAssetData& AssetData, const FString& str) -{ - TArray split; - - // get directory of current container - FString selfFullPath = UAssetContainer::GetPathName(); - FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath); - - // get asset path and class - FString assetPath = AssetData.GetFullName(); - FString assetFName = AssetData.AssetClass.ToString(); - - // split path - assetPath.ParseIntoArray(split, TEXT(" "), true); - - FString assetDir = FPackageName::GetLongPackagePath(*split[1]); - if (assetDir.StartsWith(*selfDir)) - { - // exclude self - if (assetFName != "AssetContainer") - { - - assets.Remove(str); - assets.Add(assetPath); - assetsData.Remove(AssetData); - // UE_LOG(LogTemp, Warning, TEXT("%s: asset renamed %s"), *lpp, *str); - } - } -} - diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/AssetContainerFactory.cpp b/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/AssetContainerFactory.cpp deleted file mode 100644 index b943150bdd..0000000000 --- a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Private/AssetContainerFactory.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "AssetContainerFactory.h" -#include "AssetContainer.h" - -UAssetContainerFactory::UAssetContainerFactory(const FObjectInitializer& ObjectInitializer) - : UFactory(ObjectInitializer) -{ - SupportedClass = UAssetContainer::StaticClass(); - bCreateNew = false; - bEditorImport = true; -} - -UObject* UAssetContainerFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) -{ - UAssetContainer* AssetContainer = NewObject(InParent, Class, Name, Flags); - return AssetContainer; -} - -bool UAssetContainerFactory::ShouldShowInNewMenu() const { - return false; -} diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/AssetContainer.h b/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/AssetContainer.h deleted file mode 100644 index 3c2a360c78..0000000000 --- a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/AssetContainer.h +++ /dev/null @@ -1,39 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "UObject/NoExportTypes.h" -#include "Engine/AssetUserData.h" -#include "AssetData.h" -#include "AssetContainer.generated.h" - -/** - * - */ -UCLASS(Blueprintable) -class OPENPYPE_API UAssetContainer : public UAssetUserData -{ - GENERATED_BODY() - -public: - - UAssetContainer(const FObjectInitializer& ObjectInitalizer); - // ~UAssetContainer(); - - UPROPERTY(EditAnywhere, BlueprintReadOnly) - TArray assets; - - // There seems to be no reflection option to expose array of FAssetData - /* - UPROPERTY(Transient, BlueprintReadOnly, Category = "Python", meta=(DisplayName="Assets Data")) - TArray assetsData; - */ -private: - TArray assetsData; - void OnAssetAdded(const FAssetData& AssetData); - void OnAssetRemoved(const FAssetData& AssetData); - void OnAssetRenamed(const FAssetData& AssetData, const FString& str); -}; - - diff --git a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/AssetContainerFactory.h b/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/AssetContainerFactory.h deleted file mode 100644 index 331ce6bb50..0000000000 --- a/openpype/hosts/unreal/integration/UE_4.7/Source/OpenPype/Public/AssetContainerFactory.h +++ /dev/null @@ -1,21 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "Factories/Factory.h" -#include "AssetContainerFactory.generated.h" - -/** - * - */ -UCLASS() -class OPENPYPE_API UAssetContainerFactory : public UFactory -{ - GENERATED_BODY() - -public: - UAssetContainerFactory(const FObjectInitializer& ObjectInitializer); - virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; - virtual bool ShouldShowInNewMenu() const override; -}; \ No newline at end of file diff --git a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/.gitignore b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/.gitignore new file mode 100644 index 0000000000..1004610e4f --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/.gitignore @@ -0,0 +1,6 @@ +/Saved +/DerivedDataCache +/Intermediate +/Binaries +/.idea +/.vs \ No newline at end of file diff --git a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/CommandletProject.uproject b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/CommandletProject.uproject new file mode 100644 index 0000000000..c8dc1c673e --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/CommandletProject.uproject @@ -0,0 +1,20 @@ +{ + "FileVersion": 3, + "EngineAssociation": "5.0", + "Category": "", + "Description": "", + "Plugins": [ + { + "Name": "ModelingToolsEditorMode", + "Enabled": true, + "TargetAllowList": [ + "Editor" + ] + }, + { + "Name": "OpenPype", + "Enabled": true, + "Type": "Editor" + } + ] +} \ No newline at end of file diff --git a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEditor.ini b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEditor.ini new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEngine.ini b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEngine.ini new file mode 100644 index 0000000000..3f5357dac4 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEngine.ini @@ -0,0 +1,42 @@ + + +[/Script/EngineSettings.GameMapsSettings] +GameDefaultMap=/Engine/Maps/Templates/OpenWorld + + +[/Script/HardwareTargeting.HardwareTargetingSettings] +TargetedHardwareClass=Desktop +AppliedTargetedHardwareClass=Desktop +DefaultGraphicsPerformance=Maximum +AppliedDefaultGraphicsPerformance=Maximum + +[/Script/WindowsTargetPlatform.WindowsTargetSettings] +DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 + +[/Script/Engine.RendererSettings] +r.GenerateMeshDistanceFields=True +r.DynamicGlobalIlluminationMethod=1 +r.ReflectionMethod=1 +r.Shadow.Virtual.Enable=1 + +[/Script/WorldPartitionEditor.WorldPartitionEditorSettings] +CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet' + +[/Script/Engine.Engine] ++ActiveGameNameRedirects=(OldGameName="TP_BlankBP",NewGameName="/Script/CommandletProject") ++ActiveGameNameRedirects=(OldGameName="/Script/TP_BlankBP",NewGameName="/Script/CommandletProject") + +[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] +bEnablePlugin=True +bAllowNetworkConnection=True +SecurityToken=684C16AF4BD96F1D6828A6B067693175 +bIncludeInShipping=False +bAllowExternalStartInShipping=False +bCompileAFSProject=False +bUseCompression=False +bLogFiles=False +bReportStats=False +ConnectionType=USBOnly +bUseManualIPAddress=False +ManualIPAddress= + diff --git a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultGame.ini b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultGame.ini new file mode 100644 index 0000000000..c661b739ab --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultGame.ini @@ -0,0 +1,4 @@ + + +[/Script/EngineSettings.GeneralProjectSettings] +ProjectID=D528076140C577E5807BA5BA135366BB diff --git a/openpype/hosts/unreal/integration/UE_5.0/.gitignore b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/.gitignore similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/.gitignore rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/.gitignore diff --git a/openpype/hosts/unreal/integration/UE_5.0/Config/DefaultOpenPypeSettings.ini b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Config/DefaultOpenPypeSettings.ini similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Config/DefaultOpenPypeSettings.ini rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Config/DefaultOpenPypeSettings.ini diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Config/FilterPlugin.ini b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Config/FilterPlugin.ini new file mode 100644 index 0000000000..ccebca2f32 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Config/FilterPlugin.ini @@ -0,0 +1,8 @@ +[FilterPlugin] +; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and +; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. +; +; Examples: +; /README.txt +; /Extras/... +; /Binaries/ThirdParty/*.dll diff --git a/openpype/hosts/unreal/integration/UE_5.0/Content/Python/init_unreal.py b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Content/Python/init_unreal.py similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Content/Python/init_unreal.py rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Content/Python/init_unreal.py diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype.uplugin b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/OpenPype.uplugin similarity index 95% rename from openpype/hosts/unreal/integration/UE_4.7/OpenPype.uplugin rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/OpenPype.uplugin index 4c7a74403c..b89eb43949 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype.uplugin +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/OpenPype.uplugin @@ -11,6 +11,7 @@ "MarketplaceURL": "", "SupportURL": "https://pype.club/", "CanContainContent": true, + "EngineVersion": "5.0", "IsBetaVersion": true, "IsExperimentalVersion": false, "Installed": false, diff --git a/openpype/hosts/unreal/integration/UE_5.0/README.md b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/README.md similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/README.md rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/README.md diff --git a/openpype/hosts/unreal/integration/UE_5.0/Resources/openpype128.png b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Resources/openpype128.png similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Resources/openpype128.png rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Resources/openpype128.png diff --git a/openpype/hosts/unreal/integration/UE_5.0/Resources/openpype40.png b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Resources/openpype40.png similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Resources/openpype40.png rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Resources/openpype40.png diff --git a/openpype/hosts/unreal/integration/UE_5.0/Resources/openpype512.png b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Resources/openpype512.png similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Resources/openpype512.png rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Resources/openpype512.png diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/OpenPype.Build.cs b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/OpenPype.Build.cs similarity index 92% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/OpenPype.Build.cs rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/OpenPype.Build.cs index d853ec028f..99c1c7b306 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/OpenPype.Build.cs +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/OpenPype.Build.cs @@ -10,7 +10,7 @@ public class OpenPype : ModuleRules bLegacyPublicIncludePaths = false; ShadowVariableWarningLevel = WarningLevel.Error; PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; - IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_0; + //IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_0; PublicIncludePaths.AddRange( new string[] { @@ -30,14 +30,15 @@ public class OpenPype : ModuleRules new string[] { "Core", + "CoreUObject" // ... add other public dependencies that you statically link with here ... } ); - PrivateDependencyModuleNames.AddRange( new string[] { + "GameProjectGeneration", "Projects", "InputCore", "EditorFramework", diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp new file mode 100644 index 0000000000..024a6097b3 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp @@ -0,0 +1,140 @@ +#include "Commandlets/Implementations/OPGenerateProjectCommandlet.h" + +#include "Editor.h" +#include "GameProjectUtils.h" +#include "OPConstants.h" +#include "Commandlets/OPActionResult.h" +#include "ProjectDescriptor.h" + +int32 UOPGenerateProjectCommandlet::Main(const FString& CommandLineParams) +{ + //Parses command line parameters & creates structure FProjectInformation + const FOPGenerateProjectParams ParsedParams = FOPGenerateProjectParams(CommandLineParams); + ProjectInformation = ParsedParams.GenerateUEProjectInformation(); + + //Creates .uproject & other UE files + EVALUATE_OP_ACTION_RESULT(TryCreateProject()); + + //Loads created .uproject + EVALUATE_OP_ACTION_RESULT(TryLoadProjectDescriptor()); + + //Adds needed plugin to .uproject + AttachPluginsToProjectDescriptor(); + + //Saves .uproject + EVALUATE_OP_ACTION_RESULT(TrySave()); + + //When we are here, there should not be problems in generating Unreal Project for OpenPype + return 0; +} + + +FOPGenerateProjectParams::FOPGenerateProjectParams(): FOPGenerateProjectParams("") +{ +} + +FOPGenerateProjectParams::FOPGenerateProjectParams(const FString& CommandLineParams): CommandLineParams( + CommandLineParams) +{ + UCommandlet::ParseCommandLine(*CommandLineParams, Tokens, Switches); +} + +FProjectInformation FOPGenerateProjectParams::GenerateUEProjectInformation() const +{ + FProjectInformation ProjectInformation = FProjectInformation(); + ProjectInformation.ProjectFilename = GetProjectFileName(); + + ProjectInformation.bShouldGenerateCode = IsSwitchPresent("GenerateCode"); + + return ProjectInformation; +} + +FString FOPGenerateProjectParams::TryGetToken(const int32 Index) const +{ + return Tokens.IsValidIndex(Index) ? Tokens[Index] : ""; +} + +FString FOPGenerateProjectParams::GetProjectFileName() const +{ + return TryGetToken(0); +} + +bool FOPGenerateProjectParams::IsSwitchPresent(const FString& Switch) const +{ + return INDEX_NONE != Switches.IndexOfByPredicate([&Switch](const FString& Item) -> bool + { + return Item.Equals(Switch); + } + ); +} + + +UOPGenerateProjectCommandlet::UOPGenerateProjectCommandlet() +{ + LogToConsole = true; +} + +FOP_ActionResult UOPGenerateProjectCommandlet::TryCreateProject() const +{ + FText FailReason; + FText FailLog; + TArray OutCreatedFiles; + + if (!GameProjectUtils::CreateProject(ProjectInformation, FailReason, FailLog, &OutCreatedFiles)) + return FOP_ActionResult(EOP_ActionResult::ProjectNotCreated, FailReason); + return FOP_ActionResult(); +} + +FOP_ActionResult UOPGenerateProjectCommandlet::TryLoadProjectDescriptor() +{ + FText FailReason; + const bool bLoaded = ProjectDescriptor.Load(ProjectInformation.ProjectFilename, FailReason); + + return FOP_ActionResult(bLoaded ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotLoaded, FailReason); +} + +void UOPGenerateProjectCommandlet::AttachPluginsToProjectDescriptor() +{ + FPluginReferenceDescriptor OPPluginDescriptor; + OPPluginDescriptor.bEnabled = true; + OPPluginDescriptor.Name = OPConstants::OP_PluginName; + ProjectDescriptor.Plugins.Add(OPPluginDescriptor); + + FPluginReferenceDescriptor PythonPluginDescriptor; + PythonPluginDescriptor.bEnabled = true; + PythonPluginDescriptor.Name = OPConstants::PythonScript_PluginName; + ProjectDescriptor.Plugins.Add(PythonPluginDescriptor); + + FPluginReferenceDescriptor SequencerScriptingPluginDescriptor; + SequencerScriptingPluginDescriptor.bEnabled = true; + SequencerScriptingPluginDescriptor.Name = OPConstants::SequencerScripting_PluginName; + ProjectDescriptor.Plugins.Add(SequencerScriptingPluginDescriptor); + + FPluginReferenceDescriptor MovieRenderPipelinePluginDescriptor; + MovieRenderPipelinePluginDescriptor.bEnabled = true; + MovieRenderPipelinePluginDescriptor.Name = OPConstants::MovieRenderPipeline_PluginName; + ProjectDescriptor.Plugins.Add(MovieRenderPipelinePluginDescriptor); + + FPluginReferenceDescriptor EditorScriptingPluginDescriptor; + EditorScriptingPluginDescriptor.bEnabled = true; + EditorScriptingPluginDescriptor.Name = OPConstants::EditorScriptingUtils_PluginName; + ProjectDescriptor.Plugins.Add(EditorScriptingPluginDescriptor); +} + +FOP_ActionResult UOPGenerateProjectCommandlet::TrySave() +{ + FText FailReason; + const bool bSaved = ProjectDescriptor.Save(ProjectInformation.ProjectFilename, FailReason); + + return FOP_ActionResult(bSaved ? EOP_ActionResult::Ok : EOP_ActionResult::ProjectNotSaved, FailReason); +} + +FOPGenerateProjectParams UOPGenerateProjectCommandlet::ParseParameters(const FString& Params) const +{ + FOPGenerateProjectParams ParamsResult; + + TArray Tokens, Switches; + ParseCommandLine(*Params, Tokens, Switches); + + return ParamsResult; +} diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp new file mode 100644 index 0000000000..9236fbb057 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp @@ -0,0 +1,41 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Commandlets/OPActionResult.h" +#include "Logging/OP_Log.h" + +EOP_ActionResult::Type& FOP_ActionResult::GetStatus() +{ + return Status; +} + +FText& FOP_ActionResult::GetReason() +{ + return Reason; +} + +FOP_ActionResult::FOP_ActionResult():Status(EOP_ActionResult::Type::Ok) +{ + +} + +FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum):Status(InEnum) +{ + TryLog(); +} + +FOP_ActionResult::FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason):Status(InEnum), Reason(InReason) +{ + TryLog(); +}; + +bool FOP_ActionResult::IsProblem() const +{ + return Status != EOP_ActionResult::Ok; +} + +void FOP_ActionResult::TryLog() const +{ + if(IsProblem()) + UE_LOG(LogCommandletOPGenerateProject, Error, TEXT("%s"), *Reason.ToString()); +} diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp new file mode 100644 index 0000000000..29b1068c21 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp @@ -0,0 +1 @@ +#include "Logging/OP_Log.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPype.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPype.cpp similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPype.cpp rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPype.cpp diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypeCommands.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeCommands.cpp similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypeCommands.cpp rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeCommands.cpp diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypeLib.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypeLib.cpp rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypePublishInstance.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp similarity index 99% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypePublishInstance.cpp rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp index 0b56111a49..e6a85002c7 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypePublishInstance.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp @@ -55,7 +55,7 @@ void UOpenPypePublishInstance::OnAssetCreated(const FAssetData& InAssetData) if (!IsValid(Asset)) { UE_LOG(LogAssetData, Warning, TEXT("Asset \"%s\" is not valid! Skipping the addition."), - *InAssetData.GetObjectPathString()); + *InAssetData.ObjectPath.ToString()); return; } diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypePythonBridge.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypePythonBridge.cpp rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypeSettings.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypeSettings.cpp rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypeStyle.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/OpenPypeStyle.cpp rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h new file mode 100644 index 0000000000..8738de6d4a --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h @@ -0,0 +1,60 @@ +#pragma once + + +#include "GameProjectUtils.h" +#include "Commandlets/OPActionResult.h" +#include "ProjectDescriptor.h" +#include "Commandlets/Commandlet.h" +#include "OPGenerateProjectCommandlet.generated.h" + +struct FProjectDescriptor; +struct FProjectInformation; + +/** +* @brief Structure which parses command line parameters and generates FProjectInformation +*/ +USTRUCT() +struct FOPGenerateProjectParams +{ + GENERATED_BODY() + +private: + FString CommandLineParams; + TArray Tokens; + TArray Switches; + +public: + FOPGenerateProjectParams(); + FOPGenerateProjectParams(const FString& CommandLineParams); + + FProjectInformation GenerateUEProjectInformation() const; + +private: + FString TryGetToken(const int32 Index) const; + FString GetProjectFileName() const; + + bool IsSwitchPresent(const FString& Switch) const; +}; + +UCLASS() +class OPENPYPE_API UOPGenerateProjectCommandlet : public UCommandlet +{ + GENERATED_BODY() + +private: + FProjectInformation ProjectInformation; + FProjectDescriptor ProjectDescriptor; + +public: + UOPGenerateProjectCommandlet(); + + virtual int32 Main(const FString& CommandLineParams) override; + +private: + FOPGenerateProjectParams ParseParameters(const FString& Params) const; + FOP_ActionResult TryCreateProject() const; + FOP_ActionResult TryLoadProjectDescriptor(); + void AttachPluginsToProjectDescriptor(); + FOP_ActionResult TrySave(); +}; + diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h new file mode 100644 index 0000000000..f46ba9c62a --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h @@ -0,0 +1,83 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "OPActionResult.generated.h" + +/** + * @brief This macro returns error code when is problem or does nothing when there is no problem. + * @param ActionResult FOP_ActionResult structure + */ +#define EVALUATE_OP_ACTION_RESULT(ActionResult) \ + if(ActionResult.IsProblem()) \ + return ActionResult.GetStatus(); + +/** +* @brief This enum values are humanly readable mapping of error codes. +* Here should be all error codes to be possible find what went wrong. +* TODO: In the future should exists an web document where is mapped error code & what problem occured & how to repair it... +*/ +UENUM() +namespace EOP_ActionResult +{ + enum Type + { + Ok, + ProjectNotCreated, + ProjectNotLoaded, + ProjectNotSaved, + //....Here insert another values + + //Do not remove! + //Usable for looping through enum values + __Last UMETA(Hidden) + }; +} + + +/** + * @brief This struct holds action result enum and optionally reason of fail + */ +USTRUCT() +struct FOP_ActionResult +{ + GENERATED_BODY() + +public: + /** @brief Default constructor usable when there is no problem */ + FOP_ActionResult(); + + /** + * @brief This constructor initializes variables & attempts to log when is error + * @param InEnum Status + */ + FOP_ActionResult(const EOP_ActionResult::Type& InEnum); + + /** + * @brief This constructor initializes variables & attempts to log when is error + * @param InEnum Status + * @param InReason Reason of potential fail + */ + FOP_ActionResult(const EOP_ActionResult::Type& InEnum, const FText& InReason); + +private: + /** @brief Action status */ + EOP_ActionResult::Type Status; + + /** @brief Optional reason of fail */ + FText Reason; + +public: + /** + * @brief Checks if there is problematic state + * @return true when status is not equal to EOP_ActionResult::Ok + */ + bool IsProblem() const; + EOP_ActionResult::Type& GetStatus(); + FText& GetReason(); + +private: + void TryLog() const; +}; + diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h new file mode 100644 index 0000000000..4f8af3e2e6 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h @@ -0,0 +1,3 @@ +#pragma once + +DEFINE_LOG_CATEGORY_STATIC(LogCommandletOPGenerateProject, Log, All); \ No newline at end of file diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OPConstants.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OPConstants.h new file mode 100644 index 0000000000..21a033e426 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OPConstants.h @@ -0,0 +1,12 @@ +#pragma once + +namespace OPConstants +{ + const FString OP_PluginName = "OpenPype"; + const FString PythonScript_PluginName = "PythonScriptPlugin"; + const FString SequencerScripting_PluginName = "SequencerScripting"; + const FString MovieRenderPipeline_PluginName = "MovieRenderPipeline"; + const FString EditorScriptingUtils_PluginName = "EditorScriptingUtilities"; +} + + diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPype.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPype.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPype.h rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPype.h diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypeCommands.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeCommands.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypeCommands.h rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeCommands.h diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypeLib.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeLib.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypeLib.h rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeLib.h diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypePublishInstance.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h similarity index 94% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypePublishInstance.h rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h index 146025bd6d..c221f64135 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypePublishInstance.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h @@ -17,7 +17,7 @@ public: * * @return - Set of UObjects. Careful! They are returning raw pointers. Seems like an issue in UE5 */ - UFUNCTION(BlueprintCallable, BlueprintPure) + UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python") TSet GetInternalAssets() const { //For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed. @@ -34,7 +34,7 @@ public: * * @return - TSet of assets (UObjects). Careful! They are returning raw pointers. Seems like an issue in UE5 */ - UFUNCTION(BlueprintCallable, BlueprintPure) + UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python") TSet GetExternalAssets() const { //For some reason it can only return Raw Pointers? Seems like an issue which they haven't fixed. @@ -54,7 +54,7 @@ public: * * @attention If the bAddExternalAssets variable is false, external assets won't be included! */ - UFUNCTION(BlueprintCallable, BlueprintPure) + UFUNCTION(BlueprintCallable, BlueprintPure, Category="Python") TSet GetAllAssets() const { const TSet>& IteratedSet = bAddExternalAssets diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypePythonBridge.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypePythonBridge.h rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypeSettings.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypeSettings.h rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypeStyle.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h similarity index 100% rename from openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/OpenPypeStyle.h rename to openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/AssetContainer.cpp b/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/AssetContainer.cpp deleted file mode 100644 index 61e563f729..0000000000 --- a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/AssetContainer.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#include "AssetContainer.h" -#include "AssetRegistry/AssetRegistryModule.h" -#include "Misc/PackageName.h" -#include "Engine.h" -#include "Containers/UnrealString.h" - -UAssetContainer::UAssetContainer(const FObjectInitializer& ObjectInitializer) -: UAssetUserData(ObjectInitializer) -{ - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); - FString path = UAssetContainer::GetPathName(); - UE_LOG(LogTemp, Warning, TEXT("UAssetContainer %s"), *path); - FARFilter Filter; - Filter.PackagePaths.Add(FName(*path)); - - AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAssetContainer::OnAssetAdded); - AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAssetContainer::OnAssetRemoved); - AssetRegistryModule.Get().OnAssetRenamed().AddUObject(this, &UAssetContainer::OnAssetRenamed); -} - -void UAssetContainer::OnAssetAdded(const FAssetData& AssetData) -{ - TArray split; - - // get directory of current container - FString selfFullPath = UAssetContainer::GetPathName(); - FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath); - - // get asset path and class - FString assetPath = AssetData.GetFullName(); - FString assetFName = AssetData.AssetClassPath.ToString(); - UE_LOG(LogTemp, Log, TEXT("asset name %s"), *assetFName); - // split path - assetPath.ParseIntoArray(split, TEXT(" "), true); - - FString assetDir = FPackageName::GetLongPackagePath(*split[1]); - - // take interest only in paths starting with path of current container - if (assetDir.StartsWith(*selfDir)) - { - // exclude self - if (assetFName != "AssetContainer") - { - assets.Add(assetPath); - assetsData.Add(AssetData); - UE_LOG(LogTemp, Log, TEXT("%s: asset added to %s"), *selfFullPath, *selfDir); - } - } -} - -void UAssetContainer::OnAssetRemoved(const FAssetData& AssetData) -{ - TArray split; - - // get directory of current container - FString selfFullPath = UAssetContainer::GetPathName(); - FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath); - - // get asset path and class - FString assetPath = AssetData.GetFullName(); - FString assetFName = AssetData.AssetClassPath.ToString(); - - // split path - assetPath.ParseIntoArray(split, TEXT(" "), true); - - FString assetDir = FPackageName::GetLongPackagePath(*split[1]); - - // take interest only in paths starting with path of current container - FString path = UAssetContainer::GetPathName(); - FString lpp = FPackageName::GetLongPackagePath(*path); - - if (assetDir.StartsWith(*selfDir)) - { - // exclude self - if (assetFName != "AssetContainer") - { - // UE_LOG(LogTemp, Warning, TEXT("%s: asset removed"), *lpp); - assets.Remove(assetPath); - assetsData.Remove(AssetData); - } - } -} - -void UAssetContainer::OnAssetRenamed(const FAssetData& AssetData, const FString& str) -{ - TArray split; - - // get directory of current container - FString selfFullPath = UAssetContainer::GetPathName(); - FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath); - - // get asset path and class - FString assetPath = AssetData.GetFullName(); - FString assetFName = AssetData.AssetClassPath.ToString(); - - // split path - assetPath.ParseIntoArray(split, TEXT(" "), true); - - FString assetDir = FPackageName::GetLongPackagePath(*split[1]); - if (assetDir.StartsWith(*selfDir)) - { - // exclude self - if (assetFName != "AssetContainer") - { - - assets.Remove(str); - assets.Add(assetPath); - assetsData.Remove(AssetData); - // UE_LOG(LogTemp, Warning, TEXT("%s: asset renamed %s"), *lpp, *str); - } - } -} - diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/AssetContainerFactory.cpp b/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/AssetContainerFactory.cpp deleted file mode 100644 index b943150bdd..0000000000 --- a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Private/AssetContainerFactory.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "AssetContainerFactory.h" -#include "AssetContainer.h" - -UAssetContainerFactory::UAssetContainerFactory(const FObjectInitializer& ObjectInitializer) - : UFactory(ObjectInitializer) -{ - SupportedClass = UAssetContainer::StaticClass(); - bCreateNew = false; - bEditorImport = true; -} - -UObject* UAssetContainerFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) -{ - UAssetContainer* AssetContainer = NewObject(InParent, Class, Name, Flags); - return AssetContainer; -} - -bool UAssetContainerFactory::ShouldShowInNewMenu() const { - return false; -} diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/AssetContainer.h b/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/AssetContainer.h deleted file mode 100644 index 2c06e59d6f..0000000000 --- a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/AssetContainer.h +++ /dev/null @@ -1,39 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "UObject/NoExportTypes.h" -#include "Engine/AssetUserData.h" -#include "AssetRegistry/AssetData.h" -#include "AssetContainer.generated.h" - -/** - * - */ -UCLASS(Blueprintable) -class OPENPYPE_API UAssetContainer : public UAssetUserData -{ - GENERATED_BODY() - -public: - - UAssetContainer(const FObjectInitializer& ObjectInitalizer); - // ~UAssetContainer(); - - UPROPERTY(EditAnywhere, BlueprintReadOnly) - TArray assets; - - // There seems to be no reflection option to expose array of FAssetData - /* - UPROPERTY(Transient, BlueprintReadOnly, Category = "Python", meta=(DisplayName="Assets Data")) - TArray assetsData; - */ -private: - TArray assetsData; - void OnAssetAdded(const FAssetData& AssetData); - void OnAssetRemoved(const FAssetData& AssetData); - void OnAssetRenamed(const FAssetData& AssetData, const FString& str); -}; - - diff --git a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/AssetContainerFactory.h b/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/AssetContainerFactory.h deleted file mode 100644 index 331ce6bb50..0000000000 --- a/openpype/hosts/unreal/integration/UE_5.0/Source/OpenPype/Public/AssetContainerFactory.h +++ /dev/null @@ -1,21 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "Factories/Factory.h" -#include "AssetContainerFactory.generated.h" - -/** - * - */ -UCLASS() -class OPENPYPE_API UAssetContainerFactory : public UFactory -{ - GENERATED_BODY() - -public: - UAssetContainerFactory(const FObjectInitializer& ObjectInitializer); - virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; - virtual bool ShouldShowInNewMenu() const override; -}; \ No newline at end of file diff --git a/openpype/hosts/unreal/lib.py b/openpype/hosts/unreal/lib.py index 095f5e414b..5bde65edb6 100644 --- a/openpype/hosts/unreal/lib.py +++ b/openpype/hosts/unreal/lib.py @@ -4,6 +4,10 @@ import os import platform import json + +from typing import List + +import openpype from distutils import dir_util import subprocess import re @@ -73,7 +77,7 @@ def get_engine_versions(env=None): return OrderedDict() -def get_editor_executable_path(engine_path: Path, engine_version: str) -> Path: +def get_editor_exe_path(engine_path: Path, engine_version: str) -> Path: """Get UE Editor executable path.""" ue_path = engine_path / "Engine/Binaries" if platform.system().lower() == "windows": @@ -214,77 +218,58 @@ def create_unreal_project(project_name: str, # created in different UE4 version. When user convert such project # to his UE4 version, Engine ID is replaced in uproject file. If some # other user tries to open it, it will present him with similar error. - ue_modules = Path() - if platform.system().lower() == "windows": - ue_modules_path = engine_path / "Engine/Binaries/Win64" - if ue_version.split(".")[0] == "4": - ue_modules_path /= "UE4Editor.modules" - elif ue_version.split(".")[0] == "5": - ue_modules_path /= "UnrealEditor.modules" - ue_modules = Path(ue_modules_path) - if platform.system().lower() == "linux": - ue_modules = Path(os.path.join(engine_path, "Engine", "Binaries", - "Linux", "UE4Editor.modules")) + # engine_path should be the location of UE_X.X folder - if platform.system().lower() == "darwin": - ue_modules = Path(os.path.join(engine_path, "Engine", "Binaries", - "Mac", "UE4Editor.modules")) + ue_editor_exe_path: Path = get_editor_exe_path(engine_path, ue_version) + cmdlet_project_path = get_path_to_cmdlet_project(ue_version) - if ue_modules.exists(): - print("--- Loading Engine ID from modules file ...") - with open(ue_modules, "r") as mp: - loaded_modules = json.load(mp) + project_file = pr_dir / f"{project_name}.uproject" - if loaded_modules.get("BuildId"): - ue_id = "{" + loaded_modules.get("BuildId") + "}" - - plugins_path = None - if os.path.isdir(env.get("OPENPYPE_UNREAL_PLUGIN", "")): - # copy plugin to correct path under project - plugins_path = pr_dir / "Plugins" - openpype_plugin_path = plugins_path / "OpenPype" - if not openpype_plugin_path.is_dir(): - openpype_plugin_path.mkdir(parents=True, exist_ok=True) - dir_util._path_created = {} - dir_util.copy_tree(os.environ.get("OPENPYPE_UNREAL_PLUGIN"), - openpype_plugin_path.as_posix()) - - if not (openpype_plugin_path / "Binaries").is_dir() \ - or not (openpype_plugin_path / "Intermediate").is_dir(): - dev_mode = True - - # data for project file - data = { - "FileVersion": 3, - "EngineAssociation": ue_id, - "Category": "", - "Description": "", - "Plugins": [ - {"Name": "PythonScriptPlugin", "Enabled": True}, - {"Name": "EditorScriptingUtilities", "Enabled": True}, - {"Name": "SequencerScripting", "Enabled": True}, - {"Name": "MovieRenderPipeline", "Enabled": True}, - {"Name": "OpenPype", "Enabled": True} - ] - } + print("--- Generating a new project ...") + commandlet_cmd = [f'{ue_editor_exe_path.as_posix()}', + f'{cmdlet_project_path.as_posix()}', + f'-run=OPGenerateProject', + f'{project_file.resolve().as_posix()}'] if dev_mode or preset["dev_mode"]: - # this will add the project module and necessary source file to - # make it a C++ project and to (hopefully) make Unreal Editor to - # compile all # sources at start + commandlet_cmd.append('-GenerateCode') - data["Modules"] = [{ - "Name": project_name, - "Type": "Runtime", - "LoadingPhase": "Default", - "AdditionalDependencies": ["Engine"], - }] + subprocess.run(commandlet_cmd) - # write project file - project_file = pr_dir / f"{project_name}.uproject" - with open(project_file, mode="w") as pf: - json.dump(data, pf, indent=4) + with open(project_file, mode="r+") as pf: + pf_json = json.load(pf) + pf_json["EngineAssociation"] = _get_build_id(engine_path, ue_version) + pf.seek(0) + json.dump(pf_json, pf, indent=4) + pf.truncate() + print(f'--- Engine ID has been writen into the project file') + + if dev_mode or preset["dev_mode"]: + u_build_tool = get_path_to_ubt(engine_path, ue_version) + + arch = "Win64" + if platform.system().lower() == "windows": + arch = "Win64" + elif platform.system().lower() == "linux": + arch = "Linux" + elif platform.system().lower() == "darwin": + # we need to test this out + arch = "Mac" + + command1 = [u_build_tool.as_posix(), "-projectfiles", + f"-project={project_file}", "-progress"] + + subprocess.run(command1) + + command2 = [u_build_tool.as_posix(), + f"-ModuleWithSuffix={project_name},3555", arch, + "Development", "-TargetType=Editor", + f'-Project={project_file}', + f'{project_file}', + "-IgnoreJunk"] + + subprocess.run(command2) # ensure we have PySide2 installed in engine python_path = None @@ -307,8 +292,121 @@ def create_unreal_project(project_name: str, subprocess.check_call( [python_path.as_posix(), "-m", "pip", "install", "pyside2"]) - if dev_mode or preset["dev_mode"]: - _prepare_cpp_project(project_file, engine_path, ue_version) + +def get_path_to_uat(engine_path: Path) -> Path: + if platform.system().lower() == "windows": + return engine_path / "Engine/Build/BatchFiles/RunUAT.bat" + + if platform.system().lower() == "linux" or platform.system().lower() == "darwin": + return engine_path / "Engine/Build/BatchFiles/RunUAT.sh" + + +def get_path_to_cmdlet_project(ue_version: str) -> Path: + commandlet_project_path: Path = Path(os.path.dirname(os.path.abspath(openpype.__file__))) + + # For now, only tested on Windows (For Linux and Mac it has to be implemented) + if ue_version.split(".")[0] == "4": + return commandlet_project_path / "hosts/unreal/integration/UE_4.7/CommandletProject/CommandletProject.uproject" + elif ue_version.split(".")[0] == "5": + return commandlet_project_path / "hosts/unreal/integration/UE_5.0/CommandletProject/CommandletProject.uproject" + + +def get_path_to_ubt(engine_path: Path, ue_version: str) -> Path: + u_build_tool_path = engine_path / "Engine/Binaries/DotNET" + + if ue_version.split(".")[0] == "4": + u_build_tool_path /= "UnrealBuildTool.exe" + elif ue_version.split(".")[0] == "5": + u_build_tool_path /= "UnrealBuildTool/UnrealBuildTool.exe" + + return Path(u_build_tool_path) + + +def _get_build_id(engine_path: Path, ue_version: str) -> str: + ue_modules = Path() + if platform.system().lower() == "windows": + ue_modules_path = engine_path / "Engine/Binaries/Win64" + if ue_version.split(".")[0] == "4": + ue_modules_path /= "UE4Editor.modules" + elif ue_version.split(".")[0] == "5": + ue_modules_path /= "UnrealEditor.modules" + ue_modules = Path(ue_modules_path) + + if platform.system().lower() == "linux": + ue_modules = Path(os.path.join(engine_path, "Engine", "Binaries", + "Linux", "UE4Editor.modules")) + + if platform.system().lower() == "darwin": + ue_modules = Path(os.path.join(engine_path, "Engine", "Binaries", + "Mac", "UE4Editor.modules")) + + if ue_modules.exists(): + print("--- Loading Engine ID from modules file ...") + with open(ue_modules, "r") as mp: + loaded_modules = json.load(mp) + + if loaded_modules.get("BuildId"): + return "{" + loaded_modules.get("BuildId") + "}" + + +def try_installing_plugin(engine_path: Path, + ue_version: str, + env: dict = None) -> None: + env = env or os.environ + + integration_plugin_path: Path = Path(env.get("OPENPYPE_UNREAL_PLUGIN", "")) + + if not os.path.isdir(integration_plugin_path): + raise RuntimeError("Path to the integration plugin is null!") + + # Create a path to the plugin in the engine + openpype_plugin_path: Path = engine_path / "Engine/Plugins/Marketplace/OpenPype" + + if not openpype_plugin_path.is_dir(): + print("--- OpenPype Plugin is not present. Creating a new plugin directory ...") + openpype_plugin_path.mkdir(parents=True, exist_ok=True) + + engine_plugin_config_path: Path = openpype_plugin_path / "Config" + engine_plugin_config_path.mkdir(exist_ok=True) + + dir_util._path_created = {} + + if not (openpype_plugin_path / "Binaries").is_dir() \ + or not (openpype_plugin_path / "Intermediate").is_dir(): + print("--- Binaries are not present. Building the plugin ...") + _build_and_move_integration_plugin(engine_path, openpype_plugin_path, env) + + +def _build_and_move_integration_plugin(engine_path: Path, + plugin_build_path: Path, + env: dict = None) -> None: + uat_path: Path = get_path_to_uat(engine_path) + + env = env or os.environ + integration_plugin_path: Path = Path(env.get("OPENPYPE_UNREAL_PLUGIN", "")) + + if uat_path.is_file(): + temp_dir: Path = integration_plugin_path.parent / "Temp" + temp_dir.mkdir(exist_ok=True) + uplugin_path: Path = integration_plugin_path / "OpenPype.uplugin" + + # in order to successfully build the plugin, It must be built outside the Engine directory and then moved + build_plugin_cmd: List[str] = [f'{uat_path.as_posix()}', + 'BuildPlugin', + f'-Plugin={uplugin_path.as_posix()}', + f'-Package={temp_dir.as_posix()}'] + subprocess.run(build_plugin_cmd) + + # Copy the contents of the 'Temp' dir into the 'OpenPype' directory in the engine + dir_util.copy_tree(temp_dir.as_posix(), plugin_build_path.as_posix()) + + # We need to also copy the config folder. The UAT doesn't include the Config folder in the build + plugin_install_config_path: Path = plugin_build_path / "Config" + integration_plugin_config_path = integration_plugin_path / "Config" + + dir_util.copy_tree(integration_plugin_config_path.as_posix(), plugin_install_config_path.as_posix()) + + dir_util.remove_tree(temp_dir.as_posix()) def _prepare_cpp_project( From 222a2a0631293f5c9640af10c99f15fc85372ada Mon Sep 17 00:00:00 2001 From: Joseff Date: Wed, 25 Jan 2023 13:25:57 +0100 Subject: [PATCH 1049/1271] Cleaning up and refactoring the code. --- openpype/hosts/unreal/lib.py | 210 ++++------------------------------- 1 file changed, 23 insertions(+), 187 deletions(-) diff --git a/openpype/hosts/unreal/lib.py b/openpype/hosts/unreal/lib.py index 5bde65edb6..8c4299be53 100644 --- a/openpype/hosts/unreal/lib.py +++ b/openpype/hosts/unreal/lib.py @@ -221,14 +221,14 @@ def create_unreal_project(project_name: str, # engine_path should be the location of UE_X.X folder - ue_editor_exe_path: Path = get_editor_exe_path(engine_path, ue_version) - cmdlet_project_path = get_path_to_cmdlet_project(ue_version) + ue_editor_exe: Path = get_editor_exe_path(engine_path, ue_version) + cmdlet_project: Path = get_path_to_cmdlet_project(ue_version) project_file = pr_dir / f"{project_name}.uproject" print("--- Generating a new project ...") - commandlet_cmd = [f'{ue_editor_exe_path.as_posix()}', - f'{cmdlet_project_path.as_posix()}', + commandlet_cmd = [f'{ue_editor_exe.as_posix()}', + f'{cmdlet_project.as_posix()}', f'-run=OPGenerateProject', f'{project_file.resolve().as_posix()}'] @@ -297,18 +297,21 @@ def get_path_to_uat(engine_path: Path) -> Path: if platform.system().lower() == "windows": return engine_path / "Engine/Build/BatchFiles/RunUAT.bat" - if platform.system().lower() == "linux" or platform.system().lower() == "darwin": + if platform.system().lower() == "linux" \ + or platform.system().lower() == "darwin": return engine_path / "Engine/Build/BatchFiles/RunUAT.sh" def get_path_to_cmdlet_project(ue_version: str) -> Path: - commandlet_project_path: Path = Path(os.path.dirname(os.path.abspath(openpype.__file__))) + cmdlet_project: Path = Path(os.path.dirname(os.path.abspath(openpype.__file__))) # For now, only tested on Windows (For Linux and Mac it has to be implemented) if ue_version.split(".")[0] == "4": - return commandlet_project_path / "hosts/unreal/integration/UE_4.7/CommandletProject/CommandletProject.uproject" + cmdlet_project /= "hosts/unreal/integration/UE_4.7" elif ue_version.split(".")[0] == "5": - return commandlet_project_path / "hosts/unreal/integration/UE_5.0/CommandletProject/CommandletProject.uproject" + cmdlet_project /= "hosts/unreal/integration/UE_5.0" + + return cmdlet_project / "CommandletProject/CommandletProject.uproject" def get_path_to_ubt(engine_path: Path, ue_version: str) -> Path: @@ -374,12 +377,12 @@ def try_installing_plugin(engine_path: Path, if not (openpype_plugin_path / "Binaries").is_dir() \ or not (openpype_plugin_path / "Intermediate").is_dir(): print("--- Binaries are not present. Building the plugin ...") - _build_and_move_integration_plugin(engine_path, openpype_plugin_path, env) + _build_and_move_plugin(engine_path, openpype_plugin_path, env) -def _build_and_move_integration_plugin(engine_path: Path, - plugin_build_path: Path, - env: dict = None) -> None: +def _build_and_move_plugin(engine_path: Path, + plugin_build_path: Path, + env: dict = None) -> None: uat_path: Path = get_path_to_uat(engine_path) env = env or os.environ @@ -390,191 +393,24 @@ def _build_and_move_integration_plugin(engine_path: Path, temp_dir.mkdir(exist_ok=True) uplugin_path: Path = integration_plugin_path / "OpenPype.uplugin" - # in order to successfully build the plugin, It must be built outside the Engine directory and then moved + # in order to successfully build the plugin, + # It must be built outside the Engine directory and then moved build_plugin_cmd: List[str] = [f'{uat_path.as_posix()}', 'BuildPlugin', f'-Plugin={uplugin_path.as_posix()}', f'-Package={temp_dir.as_posix()}'] subprocess.run(build_plugin_cmd) - # Copy the contents of the 'Temp' dir into the 'OpenPype' directory in the engine + # Copy the contents of the 'Temp' dir into the + # 'OpenPype' directory in the engine dir_util.copy_tree(temp_dir.as_posix(), plugin_build_path.as_posix()) - # We need to also copy the config folder. The UAT doesn't include the Config folder in the build + # We need to also copy the config folder. + # The UAT doesn't include the Config folder in the build plugin_install_config_path: Path = plugin_build_path / "Config" integration_plugin_config_path = integration_plugin_path / "Config" - dir_util.copy_tree(integration_plugin_config_path.as_posix(), plugin_install_config_path.as_posix()) + dir_util.copy_tree(integration_plugin_config_path.as_posix(), + plugin_install_config_path.as_posix()) dir_util.remove_tree(temp_dir.as_posix()) - - -def _prepare_cpp_project( - project_file: Path, engine_path: Path, ue_version: str) -> None: - """Prepare CPP Unreal Project. - - This function will add source files needed for project to be - rebuild along with the OpenPype integration plugin. - - There seems not to be automated way to do it from command line. - But there might be way to create at least those target and build files - by some generator. This needs more research as manually writing - those files is rather hackish. :skull_and_crossbones: - - - Args: - project_file (str): Path to .uproject file. - engine_path (str): Path to unreal engine associated with project. - - """ - project_name = project_file.stem - project_dir = project_file.parent - targets_dir = project_dir / "Source" - sources_dir = targets_dir / project_name - - sources_dir.mkdir(parents=True, exist_ok=True) - (project_dir / "Content").mkdir(parents=True, exist_ok=True) - - module_target = ''' -using UnrealBuildTool; -using System.Collections.Generic; - -public class {0}Target : TargetRules -{{ - public {0}Target( TargetInfo Target) : base(Target) - {{ - Type = TargetType.Game; - ExtraModuleNames.AddRange( new string[] {{ "{0}" }} ); - }} -}} -'''.format(project_name) - - editor_module_target = ''' -using UnrealBuildTool; -using System.Collections.Generic; - -public class {0}EditorTarget : TargetRules -{{ - public {0}EditorTarget( TargetInfo Target) : base(Target) - {{ - Type = TargetType.Editor; - - ExtraModuleNames.AddRange( new string[] {{ "{0}" }} ); - }} -}} -'''.format(project_name) - - module_build = ''' -using UnrealBuildTool; -public class {0} : ModuleRules -{{ - public {0}(ReadOnlyTargetRules Target) : base(Target) - {{ - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDependencyModuleNames.AddRange(new string[] {{ "Core", - "CoreUObject", "Engine", "InputCore" }}); - PrivateDependencyModuleNames.AddRange(new string[] {{ }}); - }} -}} -'''.format(project_name) - - module_cpp = ''' -#include "{0}.h" -#include "Modules/ModuleManager.h" - -IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, {0}, "{0}" ); -'''.format(project_name) - - module_header = ''' -#pragma once -#include "CoreMinimal.h" -''' - - game_mode_cpp = ''' -#include "{0}GameModeBase.h" -'''.format(project_name) - - game_mode_h = ''' -#pragma once - -#include "CoreMinimal.h" -#include "GameFramework/GameModeBase.h" -#include "{0}GameModeBase.generated.h" - -UCLASS() -class {1}_API A{0}GameModeBase : public AGameModeBase -{{ - GENERATED_BODY() -}}; -'''.format(project_name, project_name.upper()) - - with open(targets_dir / f"{project_name}.Target.cs", mode="w") as f: - f.write(module_target) - - with open(targets_dir / f"{project_name}Editor.Target.cs", mode="w") as f: - f.write(editor_module_target) - - with open(sources_dir / f"{project_name}.Build.cs", mode="w") as f: - f.write(module_build) - - with open(sources_dir / f"{project_name}.cpp", mode="w") as f: - f.write(module_cpp) - - with open(sources_dir / f"{project_name}.h", mode="w") as f: - f.write(module_header) - - with open(sources_dir / f"{project_name}GameModeBase.cpp", mode="w") as f: - f.write(game_mode_cpp) - - with open(sources_dir / f"{project_name}GameModeBase.h", mode="w") as f: - f.write(game_mode_h) - - u_build_tool_path = engine_path / "Engine/Binaries/DotNET" - if ue_version.split(".")[0] == "4": - u_build_tool_path /= "UnrealBuildTool.exe" - elif ue_version.split(".")[0] == "5": - u_build_tool_path /= "UnrealBuildTool/UnrealBuildTool.exe" - u_build_tool = Path(u_build_tool_path) - u_header_tool = None - - arch = "Win64" - if platform.system().lower() == "windows": - arch = "Win64" - u_header_tool = Path( - engine_path / "Engine/Binaries/Win64/UnrealHeaderTool.exe") - elif platform.system().lower() == "linux": - arch = "Linux" - u_header_tool = Path( - engine_path / "Engine/Binaries/Linux/UnrealHeaderTool") - elif platform.system().lower() == "darwin": - # we need to test this out - arch = "Mac" - u_header_tool = Path( - engine_path / "Engine/Binaries/Mac/UnrealHeaderTool") - - if not u_header_tool: - raise NotImplementedError("Unsupported platform") - - command1 = [u_build_tool.as_posix(), "-projectfiles", - f"-project={project_file}", "-progress"] - - subprocess.run(command1) - - command2 = [u_build_tool.as_posix(), - f"-ModuleWithSuffix={project_name},3555", arch, - "Development", "-TargetType=Editor", - f'-Project={project_file}', - f'{project_file}', - "-IgnoreJunk"] - - subprocess.run(command2) - - """ - uhtmanifest = os.path.join(os.path.dirname(project_file), - f"{project_name}.uhtmanifest") - - command3 = [u_header_tool, f'"{project_file}"', f'"{uhtmanifest}"', - "-Unattended", "-WarningsAsErrors", "-installed"] - - subprocess.run(command3) - """ From 87d8c912cab99ac4fc9cae7a354de4c24aa5a456 Mon Sep 17 00:00:00 2001 From: Joseff Date: Wed, 25 Jan 2023 13:34:48 +0100 Subject: [PATCH 1050/1271] Shortening the lines of code --- openpype/hosts/unreal/lib.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/unreal/lib.py b/openpype/hosts/unreal/lib.py index 8c4299be53..3b842a112e 100644 --- a/openpype/hosts/unreal/lib.py +++ b/openpype/hosts/unreal/lib.py @@ -303,15 +303,15 @@ def get_path_to_uat(engine_path: Path) -> Path: def get_path_to_cmdlet_project(ue_version: str) -> Path: - cmdlet_project: Path = Path(os.path.dirname(os.path.abspath(openpype.__file__))) + cmd_project: Path = Path(os.path.dirname(os.path.abspath(openpype.__file__))) # For now, only tested on Windows (For Linux and Mac it has to be implemented) if ue_version.split(".")[0] == "4": - cmdlet_project /= "hosts/unreal/integration/UE_4.7" + cmd_project /= "hosts/unreal/integration/UE_4.7" elif ue_version.split(".")[0] == "5": - cmdlet_project /= "hosts/unreal/integration/UE_5.0" + cmd_project /= "hosts/unreal/integration/UE_5.0" - return cmdlet_project / "CommandletProject/CommandletProject.uproject" + return cmd_project / "CommandletProject/CommandletProject.uproject" def get_path_to_ubt(engine_path: Path, ue_version: str) -> Path: @@ -363,21 +363,21 @@ def try_installing_plugin(engine_path: Path, raise RuntimeError("Path to the integration plugin is null!") # Create a path to the plugin in the engine - openpype_plugin_path: Path = engine_path / "Engine/Plugins/Marketplace/OpenPype" + op_plugin_path: Path = engine_path / "Engine/Plugins/Marketplace/OpenPype" - if not openpype_plugin_path.is_dir(): - print("--- OpenPype Plugin is not present. Creating a new plugin directory ...") - openpype_plugin_path.mkdir(parents=True, exist_ok=True) + if not op_plugin_path.is_dir(): + print("--- OpenPype Plugin is not present. Installing ...") + op_plugin_path.mkdir(parents=True, exist_ok=True) - engine_plugin_config_path: Path = openpype_plugin_path / "Config" + engine_plugin_config_path: Path = op_plugin_path / "Config" engine_plugin_config_path.mkdir(exist_ok=True) dir_util._path_created = {} - if not (openpype_plugin_path / "Binaries").is_dir() \ - or not (openpype_plugin_path / "Intermediate").is_dir(): + if not (op_plugin_path / "Binaries").is_dir() \ + or not (op_plugin_path / "Intermediate").is_dir(): print("--- Binaries are not present. Building the plugin ...") - _build_and_move_plugin(engine_path, openpype_plugin_path, env) + _build_and_move_plugin(engine_path, op_plugin_path, env) def _build_and_move_plugin(engine_path: Path, From ae3248b6d9ad95c9c816ba832545bb87c4f04809 Mon Sep 17 00:00:00 2001 From: Joseff Date: Wed, 25 Jan 2023 11:31:27 +0100 Subject: [PATCH 1051/1271] Refactored the generation of UE projects, plugin is now being installed in the engine. --- openpype/hosts/unreal/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/lib.py b/openpype/hosts/unreal/lib.py index 3b842a112e..b502737771 100644 --- a/openpype/hosts/unreal/lib.py +++ b/openpype/hosts/unreal/lib.py @@ -303,7 +303,7 @@ def get_path_to_uat(engine_path: Path) -> Path: def get_path_to_cmdlet_project(ue_version: str) -> Path: - cmd_project: Path = Path(os.path.dirname(os.path.abspath(openpype.__file__))) + cmd_project = Path(os.path.dirname(os.path.abspath(openpype.__file__))) # For now, only tested on Windows (For Linux and Mac it has to be implemented) if ue_version.split(".")[0] == "4": From 141214d86c8e1055210e517fd7cd467d2dd93496 Mon Sep 17 00:00:00 2001 From: Joseff Date: Fri, 3 Feb 2023 11:03:10 +0100 Subject: [PATCH 1052/1271] Added a stdout printing for the project generation command --- openpype/hosts/unreal/lib.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/unreal/lib.py b/openpype/hosts/unreal/lib.py index b502737771..04171f3ac0 100644 --- a/openpype/hosts/unreal/lib.py +++ b/openpype/hosts/unreal/lib.py @@ -235,15 +235,27 @@ def create_unreal_project(project_name: str, if dev_mode or preset["dev_mode"]: commandlet_cmd.append('-GenerateCode') - subprocess.run(commandlet_cmd) + gen_process = subprocess.Popen(commandlet_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) - with open(project_file, mode="r+") as pf: + for line in gen_process.stdout: + print(line.decode(), end='') + gen_process.stdout.close() + return_code = gen_process.wait() + + if return_code and return_code != 0: + raise RuntimeError(f'Failed to generate \'{project_name}\' project! Exited with return code {return_code}') + + print("--- Project has been generated successfully.") + + with open(project_file.as_posix(), mode="r+") as pf: pf_json = json.load(pf) pf_json["EngineAssociation"] = _get_build_id(engine_path, ue_version) pf.seek(0) json.dump(pf_json, pf, indent=4) pf.truncate() - print(f'--- Engine ID has been writen into the project file') + print(f'--- Engine ID has been written into the project file') if dev_mode or preset["dev_mode"]: u_build_tool = get_path_to_ubt(engine_path, ue_version) From 283a5fb8e4cafc33eb6ad4bd36a82a02e5893222 Mon Sep 17 00:00:00 2001 From: Joseff Date: Fri, 3 Feb 2023 11:07:47 +0100 Subject: [PATCH 1053/1271] Shortened lines for the error message --- openpype/hosts/unreal/lib.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/unreal/lib.py b/openpype/hosts/unreal/lib.py index 04171f3ac0..2e1f59d439 100644 --- a/openpype/hosts/unreal/lib.py +++ b/openpype/hosts/unreal/lib.py @@ -229,7 +229,7 @@ def create_unreal_project(project_name: str, print("--- Generating a new project ...") commandlet_cmd = [f'{ue_editor_exe.as_posix()}', f'{cmdlet_project.as_posix()}', - f'-run=OPGenerateProject', + f'-run=OPGenerateProjec', f'{project_file.resolve().as_posix()}'] if dev_mode or preset["dev_mode"]: @@ -245,7 +245,8 @@ def create_unreal_project(project_name: str, return_code = gen_process.wait() if return_code and return_code != 0: - raise RuntimeError(f'Failed to generate \'{project_name}\' project! Exited with return code {return_code}') + raise RuntimeError(f'Failed to generate \'{project_name}\' project! ' + f'Exited with return code {return_code}') print("--- Project has been generated successfully.") From 5ad3bfbaf2cd732a08fc0f9e00600017b4ef0e95 Mon Sep 17 00:00:00 2001 From: Joseff Date: Mon, 20 Feb 2023 15:57:45 +0100 Subject: [PATCH 1054/1271] Fixed the generation of the project, added copyrights notices, removed .ini files. --- openpype/hosts/unreal/addon.py | 3 +- .../unreal/hooks/pre_workfile_preparation.py | 3 +- .../UE_4.7/CommandletProject/.gitignore | 2 + .../Config/DefaultEditor.ini | 0 .../Config/DefaultEngine.ini | 16 ------- .../CommandletProject/Config/DefaultGame.ini | 4 -- .../integration/UE_4.7/OpenPype/.gitignore | 6 +++ .../UE_4.7/OpenPype/OpenPype.uplugin | 1 - .../Source/OpenPype/OpenPype.Build.cs | 2 +- .../OPGenerateProjectCommandlet.cpp | 1 + .../Private/Commandlets/OPActionResult.cpp | 2 +- .../Source/OpenPype/Private/OpenPype.cpp | 1 + .../Source/OpenPype/Private/OpenPypeLib.cpp | 1 + .../Private/OpenPypePublishInstance.cpp | 1 + .../OpenPypePublishInstanceFactory.cpp | 1 + .../OpenPype/Private/OpenPypePythonBridge.cpp | 1 + .../OpenPype/Private/OpenPypeSettings.cpp | 2 +- .../Source/OpenPype/Private/OpenPypeStyle.cpp | 1 + .../OPGenerateProjectCommandlet.h | 2 +- .../Public/Commandlets/OPActionResult.h | 2 +- .../Source/OpenPype/Public/Logging/OP_Log.h | 1 + .../Source/OpenPype/Public/OPConstants.h | 1 + .../Source/OpenPype/Public/OpenPype.h | 2 +- .../Source/OpenPype/Public/OpenPypeLib.h | 1 + .../OpenPype/Public/OpenPypePublishInstance.h | 1 + .../Public/OpenPypePublishInstanceFactory.h | 1 + .../OpenPype/Public/OpenPypePythonBridge.h | 1 + .../Source/OpenPype/Public/OpenPypeSettings.h | 2 +- .../Source/OpenPype/Public/OpenPypeStyle.h | 1 + .../UE_5.0/CommandletProject/.gitignore | 35 ++++++++++++++++ .../Config/DefaultEditor.ini | 0 .../Config/DefaultEngine.ini | 42 ------------------- .../CommandletProject/Config/DefaultGame.ini | 4 -- .../UE_5.0/OpenPype/OpenPype.uplugin | 3 +- .../Source/OpenPype/OpenPype.Build.cs | 2 +- .../OPGenerateProjectCommandlet.cpp | 1 + .../Private/Commandlets/OPActionResult.cpp | 3 +- .../OpenPype/Private/Logging/OP_Log.cpp | 2 + .../Source/OpenPype/Private/OpenPype.cpp | 1 + .../OpenPype/Private/OpenPypeCommands.cpp | 2 +- .../Source/OpenPype/Private/OpenPypeLib.cpp | 1 + .../Private/OpenPypePublishInstance.cpp | 1 + .../OpenPypePublishInstanceFactory.cpp | 1 + .../OpenPype/Private/OpenPypePythonBridge.cpp | 1 + .../OpenPype/Private/OpenPypeSettings.cpp | 2 +- .../Source/OpenPype/Private/OpenPypeStyle.cpp | 2 + .../OPGenerateProjectCommandlet.h | 1 + .../Public/Commandlets/OPActionResult.h | 2 +- .../Source/OpenPype/Public/Logging/OP_Log.h | 1 + .../Source/OpenPype/Public/OPConstants.h | 1 + .../Source/OpenPype/Public/OpenPype.h | 2 +- .../Source/OpenPype/Public/OpenPypeCommands.h | 2 +- .../Source/OpenPype/Public/OpenPypeLib.h | 1 + .../OpenPype/Public/OpenPypePublishInstance.h | 1 + .../Public/OpenPypePublishInstanceFactory.h | 1 + .../OpenPype/Public/OpenPypePythonBridge.h | 1 + .../Source/OpenPype/Public/OpenPypeSettings.h | 2 +- .../Source/OpenPype/Public/OpenPypeStyle.h | 1 + openpype/hosts/unreal/lib.py | 6 +-- 59 files changed, 97 insertions(+), 91 deletions(-) delete mode 100644 openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEditor.ini delete mode 100644 openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEngine.ini delete mode 100644 openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultGame.ini delete mode 100644 openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEditor.ini delete mode 100644 openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEngine.ini delete mode 100644 openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultGame.ini diff --git a/openpype/hosts/unreal/addon.py b/openpype/hosts/unreal/addon.py index c92a44870f..24e2db975d 100644 --- a/openpype/hosts/unreal/addon.py +++ b/openpype/hosts/unreal/addon.py @@ -19,7 +19,8 @@ class UnrealAddon(OpenPypeModule, IHostAddon): unreal_plugin_path = os.path.join( UNREAL_ROOT_DIR, "integration", ue_plugin, "OpenPype" ) - if not env.get("OPENPYPE_UNREAL_PLUGIN"): + if not env.get("OPENPYPE_UNREAL_PLUGIN") or \ + env.get("OPENPYPE_UNREAL_PLUGIN") != unreal_plugin_path: env["OPENPYPE_UNREAL_PLUGIN"] = unreal_plugin_path # Set default environments if are not set via settings diff --git a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py index 821018ba9d..14285cb78c 100644 --- a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py +++ b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py @@ -127,6 +127,7 @@ class UnrealPrelaunchHook(PreLaunchHook): # Set "OPENPYPE_UNREAL_PLUGIN" to current process environment for # execution of `create_unreal_project` + if self.launch_context.env.get("OPENPYPE_UNREAL_PLUGIN"): self.log.info(( f"{self.signature} using OpenPype plugin from " @@ -138,7 +139,7 @@ class UnrealPrelaunchHook(PreLaunchHook): engine_path = detected[engine_version] - unreal_lib.try_installing_plugin(Path(engine_path), engine_version) + unreal_lib.try_installing_plugin(Path(engine_path), os.environ) project_file = project_path / unreal_project_filename if not project_file.is_file(): diff --git a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/.gitignore b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/.gitignore index 1004610e4f..e74e6886b7 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/.gitignore +++ b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/.gitignore @@ -1,6 +1,8 @@ /Saved /DerivedDataCache /Intermediate +/Content +/Config /Binaries /.idea /.vs \ No newline at end of file diff --git a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEditor.ini b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEditor.ini deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEngine.ini b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEngine.ini deleted file mode 100644 index 2845baccca..0000000000 --- a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultEngine.ini +++ /dev/null @@ -1,16 +0,0 @@ - - -[/Script/EngineSettings.GameMapsSettings] -GameDefaultMap=/Engine/Maps/Templates/Template_Default.Template_Default - - -[/Script/HardwareTargeting.HardwareTargetingSettings] -TargetedHardwareClass=Desktop -AppliedTargetedHardwareClass=Desktop -DefaultGraphicsPerformance=Maximum -AppliedDefaultGraphicsPerformance=Maximum - -[/Script/Engine.Engine] -+ActiveGameNameRedirects=(OldGameName="TP_BlankBP",NewGameName="/Script/CommandletProject") -+ActiveGameNameRedirects=(OldGameName="/Script/TP_BlankBP",NewGameName="/Script/CommandletProject") - diff --git a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultGame.ini b/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultGame.ini deleted file mode 100644 index 40956de961..0000000000 --- a/openpype/hosts/unreal/integration/UE_4.7/CommandletProject/Config/DefaultGame.ini +++ /dev/null @@ -1,4 +0,0 @@ - - -[/Script/EngineSettings.GeneralProjectSettings] -ProjectID=95AED0BF45A918DF73ABB3BB27D25356 diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/.gitignore b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/.gitignore index b32a6f55e5..5add07aef8 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/.gitignore +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/.gitignore @@ -33,3 +33,9 @@ /Binaries /Intermediate +/Saved +/DerivedDataCache +/Content +/Config +/.idea +/.vs diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/OpenPype.uplugin b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/OpenPype.uplugin index 23155cb74d..b2cbe3cff3 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/OpenPype.uplugin +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/OpenPype.uplugin @@ -12,7 +12,6 @@ "SupportURL": "https://pype.club/", "EngineVersion": "4.27", "CanContainContent": true, - "IsBetaVersion": true, "Installed": true, "Modules": [ { diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/OpenPype.Build.cs b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/OpenPype.Build.cs index 13afb11003..f77c1383eb 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/OpenPype.Build.cs +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/OpenPype.Build.cs @@ -1,4 +1,4 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +// Copyright 2023, Ayon, All rights reserved. using UnrealBuildTool; diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp index 024a6097b3..abb1975027 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #include "Commandlets/Implementations/OPGenerateProjectCommandlet.h" #include "Editor.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp index 9236fbb057..6e50ef2221 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Copyright 2023, Ayon, All rights reserved. #include "Commandlets/OPActionResult.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPype.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPype.cpp index a510a5e3bf..9bf7b341c5 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPype.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPype.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #include "OpenPype.h" #include "ISettingsContainer.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp index a58e921288..008025e816 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #include "OpenPypeLib.h" #include "AssetViewUtils.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp index 424c4ed491..05638fbd0b 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "OpenPypePublishInstance.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp index 9b26da7fa4..a32ebe32cb 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #include "OpenPypePublishInstanceFactory.h" #include "OpenPypePublishInstance.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp index 8113231503..6ebfc528f0 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #include "OpenPypePythonBridge.h" UOpenPypePythonBridge* UOpenPypePythonBridge::Get() diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp index 951b522308..dd4228dfd0 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Copyright 2023, Ayon, All rights reserved. #include "OpenPypeSettings.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp index b7abc38156..0cc854c5ef 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #include "OpenPypeStyle.h" #include "Framework/Application/SlateApplication.h" #include "Styling/SlateStyle.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h index 8738de6d4a..d1129aa070 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h @@ -1,6 +1,6 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once - #include "GameProjectUtils.h" #include "Commandlets/OPActionResult.h" #include "ProjectDescriptor.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h index f46ba9c62a..c960bbf190 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Copyright 2023, Ayon, All rights reserved. #pragma once diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h index 4f8af3e2e6..3740c5285a 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once DEFINE_LOG_CATEGORY_STATIC(LogCommandletOPGenerateProject, Log, All); \ No newline at end of file diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OPConstants.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OPConstants.h index 21a033e426..f4587f7a50 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OPConstants.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OPConstants.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once namespace OPConstants diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPype.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPype.h index 9cfa60176c..2454344128 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPype.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPype.h @@ -1,4 +1,4 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +// Copyright 2023, Ayon, All rights reserved. #pragma once diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeLib.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeLib.h index 06425c7c7d..ef4d1027ea 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeLib.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeLib.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "Engine.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h index 16b3194b96..8cfcd067c0 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "Engine.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h index 7d2c77fe6e..3fdb984411 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "CoreMinimal.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h index 692aab2e5e..827f76f56b 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "Engine.h" #include "OpenPypePythonBridge.generated.h" diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h index 9bdcfb2399..88defaa773 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Copyright 2023, Ayon, All rights reserved. #pragma once diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h index fbc8bcdd5b..0e4af129d0 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "CoreMinimal.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/.gitignore b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/.gitignore index 1004610e4f..80814ef0a6 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/.gitignore +++ b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/.gitignore @@ -1,6 +1,41 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + /Saved /DerivedDataCache /Intermediate /Binaries +/Content +/Config /.idea /.vs \ No newline at end of file diff --git a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEditor.ini b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEditor.ini deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEngine.ini b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEngine.ini deleted file mode 100644 index 3f5357dac4..0000000000 --- a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultEngine.ini +++ /dev/null @@ -1,42 +0,0 @@ - - -[/Script/EngineSettings.GameMapsSettings] -GameDefaultMap=/Engine/Maps/Templates/OpenWorld - - -[/Script/HardwareTargeting.HardwareTargetingSettings] -TargetedHardwareClass=Desktop -AppliedTargetedHardwareClass=Desktop -DefaultGraphicsPerformance=Maximum -AppliedDefaultGraphicsPerformance=Maximum - -[/Script/WindowsTargetPlatform.WindowsTargetSettings] -DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 - -[/Script/Engine.RendererSettings] -r.GenerateMeshDistanceFields=True -r.DynamicGlobalIlluminationMethod=1 -r.ReflectionMethod=1 -r.Shadow.Virtual.Enable=1 - -[/Script/WorldPartitionEditor.WorldPartitionEditorSettings] -CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet' - -[/Script/Engine.Engine] -+ActiveGameNameRedirects=(OldGameName="TP_BlankBP",NewGameName="/Script/CommandletProject") -+ActiveGameNameRedirects=(OldGameName="/Script/TP_BlankBP",NewGameName="/Script/CommandletProject") - -[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] -bEnablePlugin=True -bAllowNetworkConnection=True -SecurityToken=684C16AF4BD96F1D6828A6B067693175 -bIncludeInShipping=False -bAllowExternalStartInShipping=False -bCompileAFSProject=False -bUseCompression=False -bLogFiles=False -bReportStats=False -ConnectionType=USBOnly -bUseManualIPAddress=False -ManualIPAddress= - diff --git a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultGame.ini b/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultGame.ini deleted file mode 100644 index c661b739ab..0000000000 --- a/openpype/hosts/unreal/integration/UE_5.0/CommandletProject/Config/DefaultGame.ini +++ /dev/null @@ -1,4 +0,0 @@ - - -[/Script/EngineSettings.GeneralProjectSettings] -ProjectID=D528076140C577E5807BA5BA135366BB diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/OpenPype.uplugin b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/OpenPype.uplugin index b89eb43949..ff08edc13e 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/OpenPype.uplugin +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/OpenPype.uplugin @@ -12,9 +12,8 @@ "SupportURL": "https://pype.club/", "CanContainContent": true, "EngineVersion": "5.0", - "IsBetaVersion": true, "IsExperimentalVersion": false, - "Installed": false, + "Installed": true, "Modules": [ { "Name": "OpenPype", diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/OpenPype.Build.cs b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/OpenPype.Build.cs index 99c1c7b306..e1087fd720 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/OpenPype.Build.cs +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/OpenPype.Build.cs @@ -1,4 +1,4 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +// Copyright 2023, Ayon, All rights reserved. using UnrealBuildTool; diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp index 024a6097b3..abb1975027 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/Implementations/OPGenerateProjectCommandlet.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #include "Commandlets/Implementations/OPGenerateProjectCommandlet.h" #include "Editor.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp index 9236fbb057..23ae2dd329 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Commandlets/OPActionResult.cpp @@ -1,5 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. - +// Copyright 2023, Ayon, All rights reserved. #include "Commandlets/OPActionResult.h" #include "Logging/OP_Log.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp index 29b1068c21..198fb9df0c 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/Logging/OP_Log.cpp @@ -1 +1,3 @@ +// Copyright 2023, Ayon, All rights reserved. + #include "Logging/OP_Log.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPype.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPype.cpp index d23de61102..65da29da35 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPype.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPype.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #include "OpenPype.h" #include "ISettingsContainer.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeCommands.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeCommands.cpp index 6187bd7c7e..881814e278 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeCommands.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeCommands.cpp @@ -1,4 +1,4 @@ -// Copyright Epic Games, Inc. All Rights Reserved. +// Copyright 2023, Ayon, All rights reserved. #include "OpenPypeCommands.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp index a58e921288..008025e816 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeLib.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #include "OpenPypeLib.h" #include "AssetViewUtils.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp index e6a85002c7..05d5c8a87d 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstance.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "OpenPypePublishInstance.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp index 9b26da7fa4..a32ebe32cb 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePublishInstanceFactory.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #include "OpenPypePublishInstanceFactory.h" #include "OpenPypePublishInstance.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp index 8113231503..6ebfc528f0 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypePythonBridge.cpp @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #include "OpenPypePythonBridge.h" UOpenPypePythonBridge* UOpenPypePythonBridge::Get() diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp index a6b9eba749..6562a81138 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeSettings.cpp @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Copyright 2023, Ayon, All rights reserved. #include "OpenPypeSettings.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp index 49e805da4d..a4d75e048e 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/OpenPypeStyle.cpp @@ -1,3 +1,5 @@ +// Copyright 2023, Ayon, All rights reserved. + #include "OpenPypeStyle.h" #include "OpenPype.h" #include "Framework/Application/SlateApplication.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h index 8738de6d4a..6a6c6406e7 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/Implementations/OPGenerateProjectCommandlet.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h index f46ba9c62a..c960bbf190 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Commandlets/OPActionResult.h @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Copyright 2023, Ayon, All rights reserved. #pragma once diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h index 4f8af3e2e6..3740c5285a 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/Logging/OP_Log.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once DEFINE_LOG_CATEGORY_STATIC(LogCommandletOPGenerateProject, Log, All); \ No newline at end of file diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OPConstants.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OPConstants.h index 21a033e426..f4587f7a50 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OPConstants.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OPConstants.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once namespace OPConstants diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPype.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPype.h index 4261476da8..b89760099b 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPype.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPype.h @@ -1,4 +1,4 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +// Copyright 2023, Ayon, All rights reserved. #pragma once diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeCommands.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeCommands.h index 62ffb8de33..99b0be26f0 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeCommands.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeCommands.h @@ -1,4 +1,4 @@ -// Copyright Epic Games, Inc. All Rights Reserved. +// Copyright 2023, Ayon, All rights reserved. #pragma once diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeLib.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeLib.h index 06425c7c7d..ef4d1027ea 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeLib.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeLib.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "Engine.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h index c221f64135..bce41ef1b1 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstance.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "Engine.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h index 7d2c77fe6e..3fdb984411 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePublishInstanceFactory.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "CoreMinimal.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h index 692aab2e5e..827f76f56b 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypePythonBridge.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "Engine.h" #include "OpenPypePythonBridge.generated.h" diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h index aca80946bb..b818fe0e95 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeSettings.h @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Copyright 2023, Ayon, All rights reserved. #pragma once diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h index ae704251e1..039abe96ef 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/OpenPypeStyle.h @@ -1,3 +1,4 @@ +// Copyright 2023, Ayon, All rights reserved. #pragma once #include "CoreMinimal.h" #include "Styling/SlateStyle.h" diff --git a/openpype/hosts/unreal/lib.py b/openpype/hosts/unreal/lib.py index 2e1f59d439..28a5106042 100644 --- a/openpype/hosts/unreal/lib.py +++ b/openpype/hosts/unreal/lib.py @@ -229,7 +229,7 @@ def create_unreal_project(project_name: str, print("--- Generating a new project ...") commandlet_cmd = [f'{ue_editor_exe.as_posix()}', f'{cmdlet_project.as_posix()}', - f'-run=OPGenerateProjec', + f'-run=OPGenerateProject', f'{project_file.resolve().as_posix()}'] if dev_mode or preset["dev_mode"]: @@ -365,9 +365,7 @@ def _get_build_id(engine_path: Path, ue_version: str) -> str: return "{" + loaded_modules.get("BuildId") + "}" -def try_installing_plugin(engine_path: Path, - ue_version: str, - env: dict = None) -> None: +def try_installing_plugin(engine_path: Path, env: dict = None) -> None: env = env or os.environ integration_plugin_path: Path = Path(env.get("OPENPYPE_UNREAL_PLUGIN", "")) From f91cefa2fd1089ac564848254e16b7a0ba95ea37 Mon Sep 17 00:00:00 2001 From: Joseff Date: Mon, 20 Feb 2023 16:03:03 +0100 Subject: [PATCH 1055/1271] Updated .gitignore for the OpenPype plugin --- .../hosts/unreal/integration/UE_4.7/OpenPype/.gitignore | 6 ------ 1 file changed, 6 deletions(-) diff --git a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/.gitignore b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/.gitignore index 5add07aef8..b32a6f55e5 100644 --- a/openpype/hosts/unreal/integration/UE_4.7/OpenPype/.gitignore +++ b/openpype/hosts/unreal/integration/UE_4.7/OpenPype/.gitignore @@ -33,9 +33,3 @@ /Binaries /Intermediate -/Saved -/DerivedDataCache -/Content -/Config -/.idea -/.vs From b1fa39439603a19b8884d1613f4a67ca55d33ff6 Mon Sep 17 00:00:00 2001 From: Joseff Date: Tue, 21 Feb 2023 20:23:32 +0100 Subject: [PATCH 1056/1271] Replaced the warning for exceeding the project name length with an exception. --- openpype/hosts/unreal/hooks/pre_workfile_preparation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py index 14285cb78c..4c9f8258f5 100644 --- a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py +++ b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py @@ -79,9 +79,9 @@ class UnrealPrelaunchHook(PreLaunchHook): unreal_project_name = os.path.splitext(unreal_project_filename)[0] # Unreal is sensitive about project names longer then 20 chars if len(unreal_project_name) > 20: - self.log.warning(( - f"Project name exceed 20 characters ({unreal_project_name})!" - )) + raise ApplicationLaunchFailed( + f"Project name exceeds 20 characters ({unreal_project_name})!" + ) # Unreal doesn't accept non alphabet characters at the start # of the project name. This is because project name is then used From c2685a6c57394a4fb839a1618846f1363f470657 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 Feb 2023 17:02:18 +0100 Subject: [PATCH 1057/1271] Nuke: caching settings and retrieved active farm module --- openpype/hosts/nuke/api/lib_rendersettings.py | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/nuke/api/lib_rendersettings.py b/openpype/hosts/nuke/api/lib_rendersettings.py index 4d5440fe48..6784890160 100644 --- a/openpype/hosts/nuke/api/lib_rendersettings.py +++ b/openpype/hosts/nuke/api/lib_rendersettings.py @@ -12,25 +12,40 @@ class RenderFarmSettings: log = Logger.get_logger("RenderFarmSettings") _active_farm_module: str = None - _farm_modules: list = [ - "deadline", "muster", "royalrender"] + _farm_modules: list = ["deadline"] _farm_plugins: dict = { "deadline": "NukeSubmitDeadline" } _creator_farm_keys: list = [ "chunk_size", "priority", "concurrent_tasks"] + _cached_project_settings = None + _cached_system_settings = None + def __init__(self, project_settings=None, log=None): """ Get project settings and active farm module """ if log: self.log = log - self._project_settings = ( - project_settings or get_current_project_settings() - ) - # Get active farm module from system settings - self._get_active_farm_module_from_system_settings() + if project_settings: + self._cached_project_settings = project_settings + + @property + def project_settings(self): + """ returning cached project settings or getting new one + """ + if not self._cached_project_settings: + self._cached_project_settings = get_current_project_settings() + return self._cached_project_settings + + @property + def system_settings(self): + """ returning cached project settings or getting new one + """ + if not self._cached_system_settings: + self._cached_system_settings = get_system_settings() + return self._cached_system_settings def _get_active_farm_module_from_system_settings(self): """ Get active farm module from system settings @@ -38,7 +53,7 @@ class RenderFarmSettings: active_modules = [ module_ for module_ in self._farm_modules - if get_system_settings()["modules"][module_]["enabled"] + if self.system_settings["modules"][module_]["enabled"] ] if not active_modules: raise ValueError(( @@ -54,6 +69,10 @@ class RenderFarmSettings: @property def active_farm_module(self): + # cache active farm module + if self._active_farm_module is None: + self._get_active_farm_module_from_system_settings() + return self._active_farm_module def get_rendering_attributes(self): From 61cf3a068d5aea4daab92201540e6b33c9a0fe0a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 Feb 2023 17:07:43 +0100 Subject: [PATCH 1058/1271] Nuke: missing bits --- openpype/hosts/nuke/api/lib_rendersettings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/api/lib_rendersettings.py b/openpype/hosts/nuke/api/lib_rendersettings.py index 6784890160..5c23bcb1bc 100644 --- a/openpype/hosts/nuke/api/lib_rendersettings.py +++ b/openpype/hosts/nuke/api/lib_rendersettings.py @@ -41,7 +41,7 @@ class RenderFarmSettings: @property def system_settings(self): - """ returning cached project settings or getting new one + """ returning cached system settings or getting new one """ if not self._cached_system_settings: self._cached_system_settings = get_system_settings() @@ -91,7 +91,7 @@ class RenderFarmSettings: ).format(farm_plugin)) # Get farm module settings - module_settings = self._project_settings[self.active_farm_module] + module_settings = self.project_settings[self.active_farm_module] # Get farm plugin settings farm_plugin_settings = ( From 9bd3ff7184c14394057e96568fff574a5e0a22cb Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 24 Feb 2023 17:51:24 +0000 Subject: [PATCH 1059/1271] Fix broken lib. --- openpype/hosts/maya/api/lib.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index ce80396326..4324d321dc 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -403,9 +403,9 @@ def lsattrs(attrs): """ - dep_fn = om.MFnDependencyNode() - dag_fn = om.MFnDagNode() - selection_list = om.MSelectionList() + dep_fn = OpenMaya.MFnDependencyNode() + dag_fn = OpenMaya.MFnDagNode() + selection_list = OpenMaya.MSelectionList() first_attr = next(iter(attrs)) @@ -419,7 +419,7 @@ def lsattrs(attrs): matches = set() for i in range(selection_list.length()): node = selection_list.getDependNode(i) - if node.hasFn(om.MFn.kDagNode): + if node.hasFn(OpenMaya.MFn.kDagNode): fn_node = dag_fn.setObject(node) full_path_names = [path.fullPathName() for path in fn_node.getAllPaths()] @@ -868,11 +868,11 @@ def maintained_selection_api(): Warning: This is *not* added to the undo stack. """ - original = om.MGlobal.getActiveSelectionList() + original = OpenMaya.MGlobal.getActiveSelectionList() try: yield finally: - om.MGlobal.setActiveSelectionList(original) + OpenMaya.MGlobal.setActiveSelectionList(original) @contextlib.contextmanager @@ -1282,11 +1282,11 @@ def get_id(node): if node is None: return - sel = om.MSelectionList() + sel = OpenMaya.MSelectionList() sel.add(node) api_node = sel.getDependNode(0) - fn = om.MFnDependencyNode(api_node) + fn = OpenMaya.MFnDependencyNode(api_node) if not fn.hasAttribute("cbId"): return @@ -3341,15 +3341,15 @@ def iter_visible_nodes_in_range(nodes, start, end): @memodict def get_visibility_mplug(node): """Return api 2.0 MPlug with cached memoize decorator""" - sel = om.MSelectionList() + sel = OpenMaya.MSelectionList() sel.add(node) dag = sel.getDagPath(0) - return om.MFnDagNode(dag).findPlug("visibility", True) + return OpenMaya.MFnDagNode(dag).findPlug("visibility", True) @contextlib.contextmanager def dgcontext(mtime): """MDGContext context manager""" - context = om.MDGContext(mtime) + context = OpenMaya.MDGContext(mtime) try: previous = context.makeCurrent() yield context @@ -3358,9 +3358,9 @@ def iter_visible_nodes_in_range(nodes, start, end): # We skip the first frame as we already used that frame to check for # overall visibilities. And end+1 to include the end frame. - scene_units = om.MTime.uiUnit() + scene_units = OpenMaya.MTime.uiUnit() for frame in range(start + 1, end + 1): - mtime = om.MTime(frame, unit=scene_units) + mtime = OpenMaya.MTime(frame, unit=scene_units) # Build little cache so we don't query the same MPlug's value # again if it was checked on this frame and also is a dependency From 3ae3d01a200ee0b007d38964ffa22573f441ee62 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 23 Feb 2023 22:02:03 +0100 Subject: [PATCH 1060/1271] Don't use ObjectId in scene inventory view --- openpype/tools/sceneinventory/view.py | 42 ++++++++++++--------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 3c4e03a195..d7c80df692 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -4,7 +4,6 @@ from functools import partial from qtpy import QtWidgets, QtCore import qtawesome -from bson.objectid import ObjectId from openpype.client import ( get_version_by_id, @@ -84,22 +83,20 @@ class SceneInventoryView(QtWidgets.QTreeView): if not items: return - repre_ids = [] - for item in items: - item_id = ObjectId(item["representation"]) - if item_id not in repre_ids: - repre_ids.append(item_id) + repre_ids = { + item["representation"] + for item in items + } project_name = legacy_io.active_project() repre_docs = get_representations( project_name, representation_ids=repre_ids, fields=["parent"] ) - version_ids = [] - for repre_doc in repre_docs: - version_id = repre_doc["parent"] - if version_id not in version_ids: - version_ids.append(version_id) + version_ids = { + repre_doc["parent"] + for repre_doc in repre_docs + } loaded_versions = get_versions( project_name, version_ids=version_ids, hero=True @@ -107,18 +104,17 @@ class SceneInventoryView(QtWidgets.QTreeView): loaded_hero_versions = [] versions_by_parent_id = collections.defaultdict(list) - version_parents = [] + subset_ids = set() for version in loaded_versions: if version["type"] == "hero_version": loaded_hero_versions.append(version) else: parent_id = version["parent"] versions_by_parent_id[parent_id].append(version) - if parent_id not in version_parents: - version_parents.append(parent_id) + subset_ids.add(parent_id) all_versions = get_versions( - project_name, subset_ids=version_parents, hero=True + project_name, subset_ids=subset_ids, hero=True ) hero_versions = [] versions = [] @@ -146,11 +142,11 @@ class SceneInventoryView(QtWidgets.QTreeView): switch_to_versioned = None if has_loaded_hero_versions: def _on_switch_to_versioned(items): - repre_ids = [] + repre_ids = set() for item in items: - item_id = ObjectId(item["representation"]) + item_id = item["representation"] if item_id not in repre_ids: - repre_ids.append(item_id) + repre_ids.add(item_id) repre_docs = get_representations( project_name, @@ -158,13 +154,13 @@ class SceneInventoryView(QtWidgets.QTreeView): fields=["parent"] ) - version_ids = [] + version_ids = set() version_id_by_repre_id = {} for repre_doc in repre_docs: version_id = repre_doc["parent"] - version_id_by_repre_id[repre_doc["_id"]] = version_id - if version_id not in version_ids: - version_ids.append(version_id) + repre_id = str(repre_doc["_id"]) + version_id_by_repre_id[repre_id] = version_id + version_ids.add(version_id) hero_versions = get_hero_versions( project_name, @@ -194,7 +190,7 @@ class SceneInventoryView(QtWidgets.QTreeView): version_doc["name"] for item in items: - repre_id = ObjectId(item["representation"]) + repre_id = item["representation"] version_id = version_id_by_repre_id.get(repre_id) version_name = version_name_by_id.get(version_id) if version_name is not None: From cba1b46765ca2d03b3881b8fdb19fa73b205be30 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 23 Feb 2023 22:02:28 +0100 Subject: [PATCH 1061/1271] don't use ObjectId in switch dialog --- openpype/tools/sceneinventory/switch_dialog.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py index 47baeaebea..e9575f254b 100644 --- a/openpype/tools/sceneinventory/switch_dialog.py +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -2,7 +2,6 @@ import collections import logging from qtpy import QtWidgets, QtCore import qtawesome -from bson.objectid import ObjectId from openpype.client import ( get_asset_by_name, @@ -161,7 +160,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): repre_ids = set() content_loaders = set() for item in self._items: - repre_ids.add(ObjectId(item["representation"])) + repre_ids.add(str(item["representation"])) content_loaders.add(item["loader"]) project_name = self.active_project() @@ -170,7 +169,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): representation_ids=repre_ids, archived=True )) - repres_by_id = {repre["_id"]: repre for repre in repres} + repres_by_id = {str(repre["_id"]): repre for repre in repres} # stash context values, works only for single representation if len(repres) == 1: @@ -181,18 +180,18 @@ class SwitchAssetDialog(QtWidgets.QDialog): content_repres = {} archived_repres = [] missing_repres = [] - version_ids = [] + version_ids = set() for repre_id in repre_ids: if repre_id not in repres_by_id: missing_repres.append(repre_id) elif repres_by_id[repre_id]["type"] == "archived_representation": repre = repres_by_id[repre_id] archived_repres.append(repre) - version_ids.append(repre["parent"]) + version_ids.add(repre["parent"]) else: repre = repres_by_id[repre_id] content_repres[repre_id] = repres_by_id[repre_id] - version_ids.append(repre["parent"]) + version_ids.add(repre["parent"]) versions = get_versions( project_name, @@ -1249,7 +1248,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): repre_docs_by_parent_id_by_name[parent_id][name] = repre_doc for container in self._items: - container_repre_id = ObjectId(container["representation"]) + container_repre_id = container["representation"] container_repre = self.content_repres[container_repre_id] container_repre_name = container_repre["name"] From 81ed872905bb1d43ef1c2340e1890e329cf31867 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 24 Feb 2023 10:31:49 +0100 Subject: [PATCH 1062/1271] apply suggested changes --- openpype/tools/sceneinventory/switch_dialog.py | 2 +- openpype/tools/sceneinventory/view.py | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py index e9575f254b..4aaad38bbc 100644 --- a/openpype/tools/sceneinventory/switch_dialog.py +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -195,7 +195,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): versions = get_versions( project_name, - version_ids=set(version_ids), + version_ids=version_ids, hero=True ) content_versions = {} diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index d7c80df692..a04171e429 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -142,11 +142,10 @@ class SceneInventoryView(QtWidgets.QTreeView): switch_to_versioned = None if has_loaded_hero_versions: def _on_switch_to_versioned(items): - repre_ids = set() - for item in items: - item_id = item["representation"] - if item_id not in repre_ids: - repre_ids.add(item_id) + repre_ids = { + item["representation"] + for item in items + } repre_docs = get_representations( project_name, @@ -168,10 +167,10 @@ class SceneInventoryView(QtWidgets.QTreeView): fields=["version_id"] ) - version_ids = set() + hero_src_version_ids = set() for hero_version in hero_versions: version_id = hero_version["version_id"] - version_ids.add(version_id) + hero_src_version_ids.add(version_id) hero_version_id = hero_version["_id"] for _repre_id, current_version_id in ( version_id_by_repre_id.items() @@ -181,7 +180,7 @@ class SceneInventoryView(QtWidgets.QTreeView): version_docs = get_versions( project_name, - version_ids=version_ids, + version_ids=hero_src_version_ids, fields=["name"] ) version_name_by_id = {} From eb72d10f934e816f8df966cb200e1b686d366e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Fri, 24 Feb 2023 21:43:21 +0100 Subject: [PATCH 1063/1271] global: source template fixed frame duplication (#4503) --- openpype/settings/defaults/project_anatomy/templates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index 99a869963b..02c0e35377 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -55,7 +55,7 @@ }, "source": { "folder": "{root[work]}/{originalDirname}", - "file": "{originalBasename}<.{@frame}><_{udim}>.{ext}", + "file": "{originalBasename}.{ext}", "path": "{@folder}/{@file}" }, "__dynamic_keys_labels__": { From 0e896d981e87068659deda3f4daabcd79ca21490 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 14 Feb 2023 15:48:32 +0000 Subject: [PATCH 1064/1271] Simplify pointcache proxy integration --- .../plugins/publish/extract_pointcache.py | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index e551858d48..153c177043 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -1,5 +1,4 @@ import os -import copy from maya import cmds @@ -10,7 +9,6 @@ from openpype.hosts.maya.api.lib import ( maintained_selection, iter_visible_nodes_in_range ) -from openpype.lib import StringTemplate class ExtractAlembic(publish.Extractor): @@ -135,26 +133,14 @@ class ExtractAlembic(publish.Extractor): **options ) - template_data = copy.deepcopy(instance.data["anatomyData"]) - template_data.update({"ext": "abc"}) - templates = instance.context.data["anatomy"].templates["publish"] - published_filename_without_extension = StringTemplate( - templates["file"] - ).format(template_data).replace(".abc", "_proxy") - transfers = [] - destination = os.path.join( - instance.data["resourcesDir"], - filename.replace( - filename.split(".")[0], - published_filename_without_extension - ) - ) - transfers.append((path, destination)) - - for source, destination in transfers: - self.log.debug("Transfer: {} > {}".format(source, destination)) - - instance.data["transfers"] = transfers + representation = { + "name": "proxy", + "ext": "abc", + "files": path, + "stagingDir": dirname, + "outputName": "proxy" + } + instance.data["representations"].append(representation) def get_members_and_roots(self, instance): return instance[:], instance.data.get("setMembers") From 458eaa8d80889a9d2f1e812b8bd56e2aac500dad Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 16 Feb 2023 16:09:15 +0000 Subject: [PATCH 1065/1271] Fix pointcache proxy publishing --- openpype/hosts/maya/plugins/publish/extract_pointcache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 153c177043..892603535c 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -136,7 +136,7 @@ class ExtractAlembic(publish.Extractor): representation = { "name": "proxy", "ext": "abc", - "files": path, + "files": os.path.basename(path), "stagingDir": dirname, "outputName": "proxy" } From 2a213ec5618923336cb9abe38192c0aa5f606252 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 17 Feb 2023 15:18:01 +0000 Subject: [PATCH 1066/1271] Inform about skipping proxy extraction. --- openpype/hosts/maya/plugins/publish/extract_pointcache.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 892603535c..a3b0560099 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -114,6 +114,7 @@ class ExtractAlembic(publish.Extractor): # Extract proxy. if not instance.data.get("proxy"): + self.log.info("No proxy nodes found. Skipping proxy extraction.") return path = path.replace(".abc", "_proxy.abc") From 604f66b71e374f7f2f573a01ecf93ee1ff5465b8 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Sat, 25 Feb 2023 03:28:37 +0000 Subject: [PATCH 1067/1271] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index bb5171764c..bd1ba5309d 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.2-nightly.1" +__version__ = "3.15.2-nightly.2" From d81debdc1050a008597515b1655dfe341566eba5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 16:18:14 +0100 Subject: [PATCH 1068/1271] implemented methods to get active site type from sync server module --- .../modules/sync_server/sync_server_module.py | 101 +++++++++++++++++- 1 file changed, 99 insertions(+), 2 deletions(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index ba0abe7d3b..1c2e820031 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -3,7 +3,6 @@ import sys import time from datetime import datetime import threading -import platform import copy import signal from collections import deque, defaultdict @@ -25,7 +24,11 @@ from openpype.lib import Logger, get_local_site_id from openpype.pipeline import AvalonMongoDB, Anatomy from openpype.settings.lib import ( get_default_anatomy_settings, - get_anatomy_settings + get_anatomy_settings, + get_local_settings, +) +from openpype.settings.constants import ( + DEFAULT_PROJECT_KEY ) from .providers.local_drive import LocalDriveHandler @@ -639,6 +642,100 @@ class SyncServerModule(OpenPypeModule, ITrayModule): return get_local_site_id() return active_site + def get_active_site_type(self, project_name, local_settings=None): + """Active site which is defined by artist. + + Unlike 'get_active_site' is this method also checking local settings + where might be different active site set by user. The output is limited + to "studio" and "local". + + This method is used by Anatomy when is decided which + + Todos: + Check if sync server is enabled for the project. + - To be able to do that the sync settings MUST NOT be cached for + all projects at once. The sync settings preparation for all + projects is reasonable only in sync server loop. + + Args: + project_name (str): Name of project where to look for active site. + local_settings (Optional[dict[str, Any]]): Prepared local settings. + + Returns: + Literal["studio", "local"]: Active site. + """ + + if not self.enabled: + return "studio" + + if local_settings is None: + local_settings = get_local_settings() + + local_project_settings = local_settings.get("projects") + project_settings = get_project_settings(project_name) + sync_server_settings = project_settings["global"]["sync_server"] + if not sync_server_settings["enabled"]: + return "studio" + + project_active_site = sync_server_settings["config"]["active_site"] + if not local_project_settings: + return project_active_site + + project_locals = local_project_settings.get(project_name) or {} + default_locals = local_project_settings.get(DEFAULT_PROJECT_KEY) or {} + active_site = ( + project_locals.get("active_site") + or default_locals.get("active_site") + ) + if active_site: + return active_site + return project_active_site + + def get_local_site_root_overrides( + self, project_name, site_name, local_settings=None + ): + """Get root overrides for project on a site. + + Implemented to be used in 'Anatomy' for other than 'studio' site. + + Args: + project_name (str): Project for which root overrides should be + received. + site_name (str): Name of site for which should be received roots. + local_settings (Optional[dict[str, Any]]): Prepare local settigns + values. + + Returns: + Union[dict[str, Any], None]: Root overrides for this machine. + """ + + if local_settings is None: + local_settings = get_local_settings() + + if not local_settings: + return + + local_project_settings = local_settings.get("projects") or {} + + # Check for roots existence in local settings first + roots_project_locals = ( + local_project_settings + .get(project_name, {}) + ) + roots_default_locals = ( + local_project_settings + .get(DEFAULT_PROJECT_KEY, {}) + ) + + # Skip rest of processing if roots are not set + if not roots_project_locals and not roots_default_locals: + return + + # Combine roots from local settings + roots_locals = roots_default_locals.get(site_name) or {} + roots_locals.update(roots_project_locals.get(site_name) or {}) + return roots_locals + # remote sites def get_remote_sites(self, project_name): """ From ded3933a0c461f734a07c86e3495479fb64f4573 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 16:22:29 +0100 Subject: [PATCH 1069/1271] Base anatomy expect only root overrides --- openpype/pipeline/anatomy.py | 63 +++++++----------------------------- 1 file changed, 12 insertions(+), 51 deletions(-) diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index 49d86d69d6..0768167885 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -57,20 +57,13 @@ class BaseAnatomy(object): root_key_regex = re.compile(r"{(root?[^}]+)}") root_name_regex = re.compile(r"root\[([^]]+)\]") - def __init__(self, project_doc, local_settings, site_name): + def __init__(self, project_doc, root_overrides=None): project_name = project_doc["name"] self.project_name = project_name self.project_code = project_doc["data"]["code"] - if (site_name and - site_name not in ["studio", "local", get_local_site_id()]): - raise RuntimeError("Anatomy could be created only for default " - "local sites not for {}".format(site_name)) - - self._site_name = site_name - self._data = self._prepare_anatomy_data( - project_doc, local_settings, site_name + project_doc, root_overrides ) self._templates_obj = AnatomyTemplates(self) self._roots_obj = Roots(self) @@ -92,28 +85,18 @@ class BaseAnatomy(object): def items(self): return copy.deepcopy(self._data).items() - def _prepare_anatomy_data(self, project_doc, local_settings, site_name): + def _prepare_anatomy_data(self, project_doc, root_overrides): """Prepare anatomy data for further processing. Method added to replace `{task}` with `{task[name]}` in templates. """ - project_name = project_doc["name"] + anatomy_data = self._project_doc_to_anatomy_data(project_doc) - templates_data = anatomy_data.get("templates") - if templates_data: - # Replace `{task}` with `{task[name]}` in templates - value_queue = collections.deque() - value_queue.append(templates_data) - while value_queue: - item = value_queue.popleft() - if not isinstance(item, dict): - continue - - self._apply_local_settings_on_anatomy_data(anatomy_data, - local_settings, - project_name, - site_name) + self._apply_local_settings_on_anatomy_data( + anatomy_data, + root_overrides + ) return anatomy_data @@ -347,7 +330,7 @@ class BaseAnatomy(object): return output def _apply_local_settings_on_anatomy_data( - self, anatomy_data, local_settings, project_name, site_name + self, anatomy_data, root_overrides ): """Apply local settings on anatomy data. @@ -366,40 +349,18 @@ class BaseAnatomy(object): Args: anatomy_data (dict): Data for anatomy. - local_settings (dict): Data of local settings. - project_name (str): Name of project for which anatomy data are. + root_overrides (dict): Data of local settings. """ - if not local_settings: - return - local_project_settings = local_settings.get("projects") or {} - - # Check for roots existence in local settings first - roots_project_locals = ( - local_project_settings - .get(project_name, {}) - ) - roots_default_locals = ( - local_project_settings - .get(DEFAULT_PROJECT_KEY, {}) - ) - - # Skip rest of processing if roots are not set - if not roots_project_locals and not roots_default_locals: - return - - # Combine roots from local settings - roots_locals = roots_default_locals.get(site_name) or {} - roots_locals.update(roots_project_locals.get(site_name) or {}) # Skip processing if roots for current active site are not available in # local settings - if not roots_locals: + if not root_overrides: return current_platform = platform.system().lower() root_data = anatomy_data["roots"] - for root_name, path in roots_locals.items(): + for root_name, path in root_overrides.items(): if root_name not in root_data: continue anatomy_data["roots"][root_name][current_platform] = ( From c55104afdb868a4b5dea6ec69c5a164250cc51be Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 16:22:53 +0100 Subject: [PATCH 1070/1271] Use CacheItem for keeping track about caches --- openpype/pipeline/anatomy.py | 57 ++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index 0768167885..a5cb97b60f 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -368,9 +368,62 @@ class BaseAnatomy(object): ) +class CacheItem: + """Helper to cache data. + + Helper does not handle refresh of data and does not mark data as outdated. + Who uses the object should check of outdated state on his own will. + """ + + default_lifetime = 10 + + def __init__(self, lifetime=None): + self._data = None + self._cached = None + self._lifetime = lifetime or self.default_lifetime + + @property + def data(self): + """Cached data/object. + + Returns: + Any: Whatever was cached. + """ + + return self._data + + @property + def is_outdated(self): + """Item has outdated cache. + + Lifetime of cache item expired or was not yet set. + + Returns: + bool: Item is outdated. + """ + + if self._cached is None: + return True + return (time.time() - self._cached) > self._lifetime + + def update_data(self, data): + """Update cache of data. + + Args: + data (Any): Data to cache. + """ + + self._data = data + self._cached = time.time() + + class Anatomy(BaseAnatomy): - _project_cache = {} - _site_cache = {} + _sync_server_addon_cache = CacheItem() + _project_cache = collections.defaultdict(CacheItem) + _default_site_id_cache = collections.defaultdict(CacheItem) + _root_overrides_cache = collections.defaultdict( + lambda: collections.defaultdict(CacheItem) + ) def __init__(self, project_name=None, site_name=None): if not project_name: From 72eac0b31edaed55347f0f047f28eec894417055 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 16:23:28 +0100 Subject: [PATCH 1071/1271] change how site resolving and it's root overrides work --- openpype/pipeline/anatomy.py | 170 +++++++++++++++++++++++------------ 1 file changed, 114 insertions(+), 56 deletions(-) diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index a5cb97b60f..7f5be18b12 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -9,7 +9,6 @@ import six import time from openpype.settings.lib import ( - get_project_settings, get_local_settings, ) from openpype.settings.constants import ( @@ -25,6 +24,7 @@ from openpype.lib.path_templates import ( ) from openpype.lib.log import Logger from openpype.lib import get_local_site_id +from openpype.modules import ModulesManager log = Logger.get_logger(__name__) @@ -436,73 +436,131 @@ class Anatomy(BaseAnatomy): )) project_doc = self.get_project_doc_from_cache(project_name) - local_settings = get_local_settings() - if not site_name: - site_name = self.get_site_name_from_cache( - project_name, local_settings - ) + root_overrides = self._get_site_root_overrides(project_name, site_name) - super(Anatomy, self).__init__( - project_doc, - local_settings, - site_name - ) + super(Anatomy, self).__init__(project_doc, root_overrides) @classmethod def get_project_doc_from_cache(cls, project_name): - project_cache = cls._project_cache.get(project_name) - if project_cache is not None: - if time.time() - project_cache["start"] > 10: - cls._project_cache.pop(project_name) - project_cache = None - - if project_cache is None: - project_cache = { - "project_doc": get_project(project_name), - "start": time.time() - } - cls._project_cache[project_name] = project_cache - - return copy.deepcopy( - cls._project_cache[project_name]["project_doc"] - ) + project_cache = cls._project_cache[project_name] + if project_cache.is_outdated: + project_cache.update_data(get_project(project_name)) + return copy.deepcopy(project_cache.data) @classmethod - def get_site_name_from_cache(cls, project_name, local_settings): - site_cache = cls._site_cache.get(project_name) - if site_cache is not None: - if time.time() - site_cache["start"] > 10: - cls._site_cache.pop(project_name) - site_cache = None + def get_sync_server_addon(cls): + if cls._sync_server_addon_cache.is_outdated: + manager = ModulesManager() + cls._sync_server_addon_cache.update_data( + manager.enabled_modules.get("sync_server") + ) + return cls._sync_server_addon_cache.data - if site_cache: - return site_cache["site_name"] + @classmethod + def _get_studio_roots_overrides(cls, project_name, local_settings=None): + """This would return 'studio' site override by local settings. - local_project_settings = local_settings.get("projects") + Notes: + This logic handles local overrides of studio site which may be + available even when sync server is not enabled. + Handling of 'studio' and 'local' site was separated as preparation + for AYON development where that will be received from + separated sources. + + Args: + project_name (str): Name of project. + local_settings (Optional[dict[str, Any]]): Prepared local settings. + + Returns: + Union[Dict[str, str], None]): Local root overrides. + """ + + if local_settings is None: + local_settings = get_local_settings() + + local_project_settings = local_settings.get("projects") or {} if not local_project_settings: + return None + + # Check for roots existence in local settings first + roots_project_locals = ( + local_project_settings + .get(project_name, {}) + ) + roots_default_locals = ( + local_project_settings + .get(DEFAULT_PROJECT_KEY, {}) + ) + + # Skip rest of processing if roots are not set + if not roots_project_locals and not roots_default_locals: return - project_locals = local_project_settings.get(project_name) or {} - default_locals = local_project_settings.get(DEFAULT_PROJECT_KEY) or {} - active_site = ( - project_locals.get("active_site") - or default_locals.get("active_site") - ) - if not active_site: - project_settings = get_project_settings(project_name) - active_site = ( - project_settings - ["global"] - ["sync_server"] - ["config"] - ["active_site"] - ) + # Combine roots from local settings + roots_locals = roots_default_locals.get("studio") or {} + roots_locals.update_data(roots_project_locals.get("studio") or {}) + return roots_locals - cls._site_cache[project_name] = { - "site_name": active_site, - "start": time.time() - } - return active_site + + @classmethod + def _get_site_root_overrides(cls, project_name, site_name): + """Get root overrides for site. + + Args: + project_name (str): Project name for which root overrides should be + received. + site_name (Union[str, None]): Name of site for which root overrides + should be returned. + """ + + # Local settings may be used more than once or may not be used at all + # - to avoid slowdowns 'get_local_settings' is not called until it's + # really needed + local_settings = None + local_site_id = get_local_site_id() + + # First check if sync server is available and enabled + sync_server = cls.get_sync_server_addon() + if sync_server is None or not sync_server.enabled: + # QUESTION is ok to force 'studio' when site sync is not enabled? + site_name = "studio" + + elif not site_name: + # Use sync server to receive active site name + project_cache = cls._default_site_id_cache[project_name] + if project_cache.is_outdated: + local_settings = get_local_settings() + project_cache.update_data( + sync_server.get_active_site_type( + project_name, local_settings + ) + ) + site_name = project_cache.data + + elif site_name not in ("studio", "local"): + # Validate that site name is valid + if site_name != local_site_id: + raise RuntimeError(( + "Anatomy could be created only for" + " default local sites not for {}" + ).format(site_name)) + site_name = "local" + + site_cache = cls._root_overrides_cache[project_name][site_name] + if site_cache.is_outdated: + if site_name == "studio": + # Handle studio root overrides without sync server + # - studio root overrides can be done even without sync server + roots_overrides = cls._get_studio_roots_overrides( + project_name, local_settings + ) + else: + # Ask sync server to get roots overrides + roots_overrides = sync_server.get_local_site_root_overrides( + project_name, site_name, local_settings + ) + site_cache.update_data(roots_overrides) + return site_cache.data class AnatomyTemplateUnsolved(TemplateUnsolved): From 43179444397f57cfbeca04803caf503a3635e1a5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 16:32:59 +0100 Subject: [PATCH 1072/1271] fix modules access --- openpype/pipeline/anatomy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index 7f5be18b12..bbc696d7f1 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -452,7 +452,7 @@ class Anatomy(BaseAnatomy): if cls._sync_server_addon_cache.is_outdated: manager = ModulesManager() cls._sync_server_addon_cache.update_data( - manager.enabled_modules.get("sync_server") + manager.get_enabled_module("sync_server") ) return cls._sync_server_addon_cache.data From f4c47d41d493050adc40324ab0c32c77efc217ae Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 18:12:34 +0100 Subject: [PATCH 1073/1271] move validation of site name to sync server --- openpype/modules/sync_server/sync_server_module.py | 10 ++++++++++ openpype/pipeline/anatomy.py | 11 ----------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 1c2e820031..c3e28bc4f2 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -709,6 +709,16 @@ class SyncServerModule(OpenPypeModule, ITrayModule): Union[dict[str, Any], None]: Root overrides for this machine. """ + # Validate that site name is valid + if site_name not in ("studio", "local"): + # Considure local site id as 'local' + if site_name != get_local_site_id(): + raise RuntimeError(( + "Anatomy could be created only for" + " default local sites not for {}" + ).format(site_name)) + site_name = "local" + if local_settings is None: local_settings = get_local_settings() diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index bbc696d7f1..bb365c916e 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -23,7 +23,6 @@ from openpype.lib.path_templates import ( FormatObject, ) from openpype.lib.log import Logger -from openpype.lib import get_local_site_id from openpype.modules import ModulesManager log = Logger.get_logger(__name__) @@ -517,7 +516,6 @@ class Anatomy(BaseAnatomy): # - to avoid slowdowns 'get_local_settings' is not called until it's # really needed local_settings = None - local_site_id = get_local_site_id() # First check if sync server is available and enabled sync_server = cls.get_sync_server_addon() @@ -537,15 +535,6 @@ class Anatomy(BaseAnatomy): ) site_name = project_cache.data - elif site_name not in ("studio", "local"): - # Validate that site name is valid - if site_name != local_site_id: - raise RuntimeError(( - "Anatomy could be created only for" - " default local sites not for {}" - ).format(site_name)) - site_name = "local" - site_cache = cls._root_overrides_cache[project_name][site_name] if site_cache.is_outdated: if site_name == "studio": From 5de35b48e57ac958eadb6cfd387a146e962bc90d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 18:13:14 +0100 Subject: [PATCH 1074/1271] change exception type and message --- openpype/modules/sync_server/sync_server_module.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index c3e28bc4f2..7ea8f62d03 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -713,9 +713,9 @@ class SyncServerModule(OpenPypeModule, ITrayModule): if site_name not in ("studio", "local"): # Considure local site id as 'local' if site_name != get_local_site_id(): - raise RuntimeError(( - "Anatomy could be created only for" - " default local sites not for {}" + raise ValueError(( + "Root overrides are available only for" + " default sites not for \"{}\"" ).format(site_name)) site_name = "local" From 1b5d7a1d385b275d84fcc2bb4ee8d3a71b569f3e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 18:13:37 +0100 Subject: [PATCH 1075/1271] rename method --- openpype/modules/sync_server/sync_server_module.py | 2 +- openpype/pipeline/anatomy.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 7ea8f62d03..28863c091a 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -691,7 +691,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): return active_site return project_active_site - def get_local_site_root_overrides( + def get_site_root_overrides( self, project_name, site_name, local_settings=None ): """Get root overrides for project on a site. diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index bb365c916e..d1bda2cf18 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -545,7 +545,7 @@ class Anatomy(BaseAnatomy): ) else: # Ask sync server to get roots overrides - roots_overrides = sync_server.get_local_site_root_overrides( + roots_overrides = sync_server.get_site_root_overrides( project_name, site_name, local_settings ) site_cache.update_data(roots_overrides) From 3408c25f7957805ac5b250c154ef2da464e0662e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 18:24:35 +0100 Subject: [PATCH 1076/1271] remove doubled empty line --- openpype/pipeline/anatomy.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index d1bda2cf18..ac915b61f0 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -500,7 +500,6 @@ class Anatomy(BaseAnatomy): roots_locals.update_data(roots_project_locals.get("studio") or {}) return roots_locals - @classmethod def _get_site_root_overrides(cls, project_name, site_name): """Get root overrides for site. From afec68dcba636ccc345ab080bf049c37ec692068 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 27 Feb 2023 10:56:11 +0100 Subject: [PATCH 1077/1271] fix dict update --- openpype/pipeline/anatomy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index ac915b61f0..683960f3d8 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -497,7 +497,7 @@ class Anatomy(BaseAnatomy): # Combine roots from local settings roots_locals = roots_default_locals.get("studio") or {} - roots_locals.update_data(roots_project_locals.get("studio") or {}) + roots_locals.update(roots_project_locals.get("studio") or {}) return roots_locals @classmethod From 3c215350931b4b65d5eab9a51cdc804ed128478a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 27 Feb 2023 11:34:30 +0100 Subject: [PATCH 1078/1271] removed unnecessary try catch --- openpype/modules/sync_server/sync_server.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/openpype/modules/sync_server/sync_server.py b/openpype/modules/sync_server/sync_server.py index aef3623efa..5b873a37cf 100644 --- a/openpype/modules/sync_server/sync_server.py +++ b/openpype/modules/sync_server/sync_server.py @@ -50,13 +50,10 @@ async def upload(module, project_name, file, representation, provider_name, presets=preset) file_path = file.get("path", "") - try: - local_file_path, remote_file_path = resolve_paths( - module, file_path, project_name, - remote_site_name, remote_handler - ) - except Exception as exp: - print(exp) + local_file_path, remote_file_path = resolve_paths( + module, file_path, project_name, + remote_site_name, remote_handler + ) target_folder = os.path.dirname(remote_file_path) folder_id = remote_handler.create_folder(target_folder) From 557ce7c016515f15878d9c37d6a8cf6fb777be43 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 27 Feb 2023 15:08:03 +0100 Subject: [PATCH 1079/1271] Nuke, Deadline: moving to module plugin centric approach --- openpype/hosts/nuke/api/lib_rendersettings.py | 111 ------------------ .../plugins/create/create_write_prerender.py | 37 +----- .../plugins/create/create_write_render.py | 36 +----- .../plugins/publish/submit_nuke_deadline.py | 81 +++++++++---- 4 files changed, 60 insertions(+), 205 deletions(-) delete mode 100644 openpype/hosts/nuke/api/lib_rendersettings.py diff --git a/openpype/hosts/nuke/api/lib_rendersettings.py b/openpype/hosts/nuke/api/lib_rendersettings.py deleted file mode 100644 index 5c23bcb1bc..0000000000 --- a/openpype/hosts/nuke/api/lib_rendersettings.py +++ /dev/null @@ -1,111 +0,0 @@ - -from openpype.lib import Logger -from openpype.settings import ( - get_current_project_settings, - get_system_settings -) - - -class RenderFarmSettings: - """ Class for getting farm settings from project settings - """ - log = Logger.get_logger("RenderFarmSettings") - - _active_farm_module: str = None - _farm_modules: list = ["deadline"] - _farm_plugins: dict = { - "deadline": "NukeSubmitDeadline" - } - _creator_farm_keys: list = [ - "chunk_size", "priority", "concurrent_tasks"] - - _cached_project_settings = None - _cached_system_settings = None - - def __init__(self, project_settings=None, log=None): - """ Get project settings and active farm module - """ - if log: - self.log = log - - if project_settings: - self._cached_project_settings = project_settings - - @property - def project_settings(self): - """ returning cached project settings or getting new one - """ - if not self._cached_project_settings: - self._cached_project_settings = get_current_project_settings() - return self._cached_project_settings - - @property - def system_settings(self): - """ returning cached system settings or getting new one - """ - if not self._cached_system_settings: - self._cached_system_settings = get_system_settings() - return self._cached_system_settings - - def _get_active_farm_module_from_system_settings(self): - """ Get active farm module from system settings - """ - active_modules = [ - module_ - for module_ in self._farm_modules - if self.system_settings["modules"][module_]["enabled"] - ] - if not active_modules: - raise ValueError(( - "No active farm module found in system settings." - )) - if len(active_modules) > 1: - raise ValueError(( - "Multiple active farm modules " - "found in system settings. {}".format(active_modules) - )) - - self._active_farm_module = active_modules.pop() - - @property - def active_farm_module(self): - # cache active farm module - if self._active_farm_module is None: - self._get_active_farm_module_from_system_settings() - - return self._active_farm_module - - def get_rendering_attributes(self): - ''' Get rendering attributes from project settings - - Returns: - dict: rendering attributes - ''' - return_dict = {} - farm_plugin = self._farm_plugins.get(self.active_farm_module) - self.log.debug("Farm plugin: \"{}\"".format(farm_plugin)) - - if not farm_plugin: - raise ValueError(( - "Farm plugin \"{}\" not found in farm plugins." - ).format(farm_plugin)) - - # Get farm module settings - module_settings = self.project_settings[self.active_farm_module] - - # Get farm plugin settings - farm_plugin_settings = ( - module_settings["publish"][farm_plugin]) - self.log.debug( - "Farm plugin settings: \"{}\"".format(farm_plugin_settings)) - - # Get all keys from farm_plugin_settings - for key in self._creator_farm_keys: - if key not in farm_plugin_settings: - self.log.warning(( - "Key \"{}\" not found in farm plugin \"{}\" settings." - ).format(key, farm_plugin)) - continue - return_dict[key] = farm_plugin_settings[key] - - return return_dict diff --git a/openpype/hosts/nuke/plugins/create/create_write_prerender.py b/openpype/hosts/nuke/plugins/create/create_write_prerender.py index 411a79dbf4..1603bf17e3 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_prerender.py +++ b/openpype/hosts/nuke/plugins/create/create_write_prerender.py @@ -6,13 +6,9 @@ from openpype.pipeline import ( CreatedInstance ) from openpype.lib import ( - BoolDef, - NumberDef, - UISeparatorDef, - UILabelDef + BoolDef ) from openpype.hosts.nuke import api as napi -from openpype.hosts.nuke.api.lib_rendersettings import RenderFarmSettings class CreateWritePrerender(napi.NukeWriteCreator): @@ -50,37 +46,6 @@ class CreateWritePrerender(napi.NukeWriteCreator): self._get_render_target_enum(), self._get_reviewable_bool() ] - if "farm_rendering" in self.instance_attributes: - render_farm_settings = RenderFarmSettings( - log=self.log).get_rendering_attributes() - - - attr_defs.extend([ - UISeparatorDef(), - UILabelDef("Farm rendering attributes"), - BoolDef("suspended_publish", label="Suspended publishing"), - NumberDef( - "farm_priority", - label="Priority", - minimum=1, - maximum=99, - default=render_farm_settings.get("priority", 50) - ), - NumberDef( - "farm_chunk", - label="Chunk size", - minimum=1, - maximum=99, - default=render_farm_settings.get("chunk_size", 10) - ), - NumberDef( - "farm_concurrency", - label="Concurrent tasks", - minimum=1, - maximum=10, - default=render_farm_settings.get("concurrent_tasks", 1) - ) - ]) return attr_defs def create_instance_node(self, subset_name, instance_data): diff --git a/openpype/hosts/nuke/plugins/create/create_write_render.py b/openpype/hosts/nuke/plugins/create/create_write_render.py index a51661425f..72fcb4f232 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_render.py +++ b/openpype/hosts/nuke/plugins/create/create_write_render.py @@ -6,13 +6,9 @@ from openpype.pipeline import ( CreatedInstance ) from openpype.lib import ( - BoolDef, - NumberDef, - UISeparatorDef, - UILabelDef + BoolDef ) from openpype.hosts.nuke import api as napi -from openpype.hosts.nuke.api.lib_rendersettings import RenderFarmSettings class CreateWriteRender(napi.NukeWriteCreator): @@ -47,36 +43,6 @@ class CreateWriteRender(napi.NukeWriteCreator): self._get_render_target_enum(), self._get_reviewable_bool() ] - if "farm_rendering" in self.instance_attributes: - render_farm_settings = RenderFarmSettings( - log=self.log).get_rendering_attributes() - - attr_defs.extend([ - UISeparatorDef(), - UILabelDef("Farm rendering attributes"), - BoolDef("suspended_publish", label="Suspended publishing"), - NumberDef( - "farm_priority", - label="Priority", - minimum=1, - maximum=99, - default=render_farm_settings.get("priority", 50) - ), - NumberDef( - "farm_chunk", - label="Chunk size", - minimum=1, - maximum=99, - default=render_farm_settings.get("chunk_size", 10) - ), - NumberDef( - "farm_concurrency", - label="Concurrent tasks", - minimum=1, - maximum=10, - default=render_farm_settings.get("concurrent_tasks", 1) - ) - ]) return attr_defs def create_instance_node(self, subset_name, instance_data): diff --git a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py index b4b59c4c77..51e380dc03 100644 --- a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -9,11 +9,19 @@ import pyblish.api import nuke from openpype.pipeline import legacy_io +from openpype.pipeline.publish import ( + OpenPypePyblishPluginMixin +) from openpype.tests.lib import is_in_tests -from openpype.lib import is_running_from_build +from openpype.lib import ( + is_running_from_build, + BoolDef, + NumberDef, + UISeparatorDef +) - -class NukeSubmitDeadline(pyblish.api.InstancePlugin): +class NukeSubmitDeadline(pyblish.api.InstancePlugin, + OpenPypePyblishPluginMixin): """Submit write to Deadline Renders are submitted to a Deadline Web Service as @@ -21,10 +29,10 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): """ - label = "Submit to Deadline" + label = "Submit Nuke to Deadline" order = pyblish.api.IntegratorOrder + 0.1 hosts = ["nuke"] - families = ["render.farm", "prerender.farm"] + families = ["render", "prerender.farm"] optional = True targets = ["local"] @@ -39,7 +47,42 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): env_allowed_keys = [] env_search_replace_values = {} + @classmethod + def get_attribute_defs(cls): + return [ + NumberDef( + "priority", + label="Priority", + default=cls.priority, + decimals=0 + ), + NumberDef( + "chunk", + label="Frames Per Task", + default=cls.chunk_size, + decimals=0, + minimum=1, + maximum=1000 + ), + NumberDef( + "concurrency", + label="Concurency", + default=cls.concurrent_tasks, + decimals=0, + minimum=1, + maximum=10 + ), + BoolDef( + "use_gpu", + default=cls.use_gpu, + label="Use GPU" + ) + ] + def process(self, instance): + instance.data["attributeValues"] = self.get_attr_values_from_data( + instance.data) + instance.data["toBeRenderedOn"] = "deadline" families = instance.data["families"] @@ -161,20 +204,6 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): except OSError: pass - # define chunk and priority - chunk_size = instance.data.get("farm_chunk") - if not chunk_size: - chunk_size = self.chunk_size - - # define chunk and priority - concurrent_tasks = instance.data.get("farm_concurrency") - if not concurrent_tasks: - concurrent_tasks = self.concurrent_tasks - - priority = instance.data.get("farm_priority") - if not priority: - priority = self.priority - # resolve any limit groups limit_groups = self.get_limit_groups() self.log.info("Limit groups: `{}`".format(limit_groups)) @@ -193,9 +222,14 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): # Arbitrary username, for visualisation in Monitor "UserName": self._deadline_user, - "Priority": priority, - "ChunkSize": chunk_size, - "ConcurrentTasks": concurrent_tasks, + "Priority": instance.data["attributeValues"].get( + "priority", self.priority), + "ChunkSize": instance.data["attributeValues"].get( + "chunk", self.chunk_size), + "ConcurrentTasks": instance.data["attributeValues"].get( + "concurrency", + self.concurrent_tasks + ), "Department": self.department, @@ -234,7 +268,8 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): "AWSAssetFile0": render_path, # using GPU by default - "UseGpu": self.use_gpu, + "UseGpu": instance.data["attributeValues"].get( + "use_gpu", self.use_gpu), # Only the specific write node is rendered. "WriteNode": exe_node_name From e444ab3f5e3037cc0528a748df4f0554a597fb78 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 27 Feb 2023 15:16:27 +0100 Subject: [PATCH 1080/1271] hound comments --- .../modules/deadline/plugins/publish/submit_nuke_deadline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py index 51e380dc03..aff34c7e4a 100644 --- a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -16,10 +16,10 @@ from openpype.tests.lib import is_in_tests from openpype.lib import ( is_running_from_build, BoolDef, - NumberDef, - UISeparatorDef + NumberDef ) + class NukeSubmitDeadline(pyblish.api.InstancePlugin, OpenPypePyblishPluginMixin): """Submit write to Deadline From fa879c9beb1982cb02f12d36925d28c81943589a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 27 Feb 2023 17:06:14 +0100 Subject: [PATCH 1081/1271] fix UI definitions for publish plugins (#4534) --- openpype/tools/publisher/control.py | 3 ++ openpype/tools/publisher/widgets/widgets.py | 36 +++++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 023a20ca5e..49e7eeb4f7 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -18,6 +18,7 @@ from openpype.client import ( ) from openpype.lib.events import EventSystem from openpype.lib.attribute_definitions import ( + UIDef, serialize_attr_defs, deserialize_attr_defs, ) @@ -1938,6 +1939,8 @@ class PublisherController(BasePublisherController): plugin_values = all_plugin_values[plugin_name] for attr_def in attr_defs: + if isinstance(attr_def, UIDef): + continue if attr_def.key not in plugin_values: plugin_values[attr_def.key] = [] attr_values = plugin_values[attr_def.key] diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 8da3886419..86475460aa 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -9,7 +9,7 @@ import collections from qtpy import QtWidgets, QtCore, QtGui import qtawesome -from openpype.lib.attribute_definitions import UnknownDef +from openpype.lib.attribute_definitions import UnknownDef, UIDef from openpype.tools.attribute_defs import create_widget_for_attr_def from openpype.tools import resources from openpype.tools.flickcharm import FlickCharm @@ -1442,7 +1442,16 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): ) content_widget = QtWidgets.QWidget(self._scroll_area) - content_layout = QtWidgets.QFormLayout(content_widget) + attr_def_widget = QtWidgets.QWidget(content_widget) + attr_def_layout = QtWidgets.QGridLayout(attr_def_widget) + attr_def_layout.setColumnStretch(0, 0) + attr_def_layout.setColumnStretch(1, 1) + + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.addWidget(attr_def_widget, 0) + content_layout.addStretch(1) + + row = 0 for plugin_name, attr_defs, all_plugin_values in result: plugin_values = all_plugin_values[plugin_name] @@ -1459,8 +1468,29 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): hidden_widget = True if not hidden_widget: + expand_cols = 2 + if attr_def.is_value_def and attr_def.is_label_horizontal: + expand_cols = 1 + + col_num = 2 - expand_cols label = attr_def.label or attr_def.key - content_layout.addRow(label, widget) + if label: + label_widget = QtWidgets.QLabel(label, content_widget) + tooltip = attr_def.tooltip + if tooltip: + label_widget.setToolTip(tooltip) + attr_def_layout.addWidget( + label_widget, row, 0, 1, expand_cols + ) + if not attr_def.is_label_horizontal: + row += 1 + attr_def_layout.addWidget( + widget, row, col_num, 1, expand_cols + ) + row += 1 + + if isinstance(attr_def, UIDef): + continue widget.value_changed.connect(self._input_value_changed) From 2839e775e43eaddaa28d58bb11471c450198b4dd Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 27 Feb 2023 18:55:23 +0100 Subject: [PATCH 1082/1271] Deadline: Hint to use Python 3 (#4518) * add shebank for python 3 --- .../deadline/repository/custom/plugins/GlobalJobPreLoad.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index e4fc64269a..20a58c9131 100644 --- a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -1,3 +1,4 @@ +# /usr/bin/env python3 # -*- coding: utf-8 -*- import os import tempfile @@ -341,7 +342,7 @@ def inject_openpype_environment(deadlinePlugin): "app": job.GetJobEnvironmentKeyValue("AVALON_APP_NAME"), "envgroup": "farm" } - + if job.GetJobEnvironmentKeyValue('IS_TEST'): args.append("--automatic-tests") From 8a6efcd6ef9699f977e58ab8fc6042a6e8e3a0f5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 28 Feb 2023 10:00:25 +0100 Subject: [PATCH 1083/1271] Nuke: new version builder from tempate wip --- .../nuke/api/workfile_template_builder.py | 3 +- .../workfile/workfile_template_builder.py | 29 ++++++++++++++++++- .../defaults/project_settings/nuke.json | 12 +++++++- .../schema_templated_workfile_build.json | 6 ++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/nuke/api/workfile_template_builder.py b/openpype/hosts/nuke/api/workfile_template_builder.py index 1b81f24e86..739c10d56b 100644 --- a/openpype/hosts/nuke/api/workfile_template_builder.py +++ b/openpype/hosts/nuke/api/workfile_template_builder.py @@ -1,3 +1,4 @@ +import os import collections import nuke @@ -45,7 +46,7 @@ class NukeTemplateBuilder(AbstractTemplateBuilder): get_template_preset implementation) Returns: - bool: Wether the template was succesfully imported or not + bool: Wether the template was successfully imported or not """ # TODO check if the template is already imported diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 119e4aaeb7..6bbe5f5d13 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -28,6 +28,7 @@ from openpype.settings import ( get_project_settings, get_system_settings, ) +from openpype.host import IWorkfileHost from openpype.host import HostBase from openpype.lib import ( Logger, @@ -416,7 +417,8 @@ class AbstractTemplateBuilder(object): self, template_path=None, level_limit=None, - keep_placeholders=None + keep_placeholders=None, + create_first_version=None ): """Main callback for building workfile from template path. @@ -433,6 +435,7 @@ class AbstractTemplateBuilder(object): keep_placeholders (bool): Add flag to placeholder data for hosts to decide if they want to remove placeholder after it is used. + create_first_version (bool): create first version of a workfile """ template_preset = self.get_template_preset() @@ -441,6 +444,11 @@ class AbstractTemplateBuilder(object): if keep_placeholders is None: keep_placeholders = template_preset["keep_placeholder"] + if create_first_version is None: + create_first_version = template_preset["create_first_version"] + + if create_first_version: + self.create_first_workfile_version() self.import_template(template_path) self.populate_scene_placeholders( @@ -492,6 +500,25 @@ class AbstractTemplateBuilder(object): pass + @abstractmethod + def create_first_workfile_version(self): + """ + Create first version of workfile. + + Should load the content of template into scene so + 'populate_scene_placeholders' can be started. + + Args: + template_path (str): Fullpath for current task and + host's template file. + """ + last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE") + # Save current scene, continue to open file + if isinstance(self.host, IWorkfileHost): + self.host.save_workfile(last_workfile_path) + else: + self.host.save_file(last_workfile_path) + def _prepare_placeholders(self, placeholders): """Run preparation part for placeholders on plugins. diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 2545411e0a..c249955dc8 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -565,7 +565,17 @@ ] }, "templated_workfile_build": { - "profiles": [] + "profiles": [ + { + "task_types": [ + "Compositing" + ], + "task_names": [], + "path": "{project[name]}/templates/comp.nk", + "keep_placeholder": true, + "create_first_version": true + } + ] }, "filters": {} } diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_templated_workfile_build.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_templated_workfile_build.json index b244460bbf..7bab28fd88 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_templated_workfile_build.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_templated_workfile_build.json @@ -34,6 +34,12 @@ "label": "Keep placeholders", "type": "boolean", "default": true + }, + { + "key": "create_first_version", + "label": "Create first version", + "type": "boolean", + "default": true } ] } From 86f0383f183251cb2b725f06ad33ba887e2305ef Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 28 Feb 2023 11:13:44 +0100 Subject: [PATCH 1084/1271] Publisher: Prevent access to create tab after publish start (#4528) make sure it is not possible to go to create tab if the tab is not enabled --- openpype/tools/publisher/widgets/overview_widget.py | 3 +++ openpype/tools/publisher/window.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/overview_widget.py b/openpype/tools/publisher/widgets/overview_widget.py index 022de2dc34..8706daeda6 100644 --- a/openpype/tools/publisher/widgets/overview_widget.py +++ b/openpype/tools/publisher/widgets/overview_widget.py @@ -146,6 +146,7 @@ class OverviewWidget(QtWidgets.QFrame): self._subset_list_view = subset_list_view self._subset_views_layout = subset_views_layout + self._create_btn = create_btn self._delete_btn = delete_btn self._subset_attributes_widget = subset_attributes_widget @@ -388,11 +389,13 @@ class OverviewWidget(QtWidgets.QFrame): def _on_publish_start(self): """Publish started.""" + self._create_btn.setEnabled(False) self._subset_attributes_wrap.setEnabled(False) def _on_publish_reset(self): """Context in controller has been refreshed.""" + self._create_btn.setEnabled(True) self._subset_attributes_wrap.setEnabled(True) self._subset_content_widget.setEnabled(self._controller.host_is_valid) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 6f7ffdb8ea..74977d65d8 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -561,7 +561,8 @@ class PublisherWindow(QtWidgets.QDialog): return self._tabs_widget.is_current_tab(identifier) def _go_to_create_tab(self): - self._set_current_tab("create") + if self._create_tab.isEnabled(): + self._set_current_tab("create") def _go_to_publish_tab(self): self._set_current_tab("publish") From f83fefd8a91f90133216f819a251594a72fe920c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 Feb 2023 18:35:21 +0100 Subject: [PATCH 1085/1271] Added support for extensions filtering --- openpype/pipeline/load/plugins.py | 82 +++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index 9b891a4da3..8372743a23 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -21,16 +21,18 @@ class LoaderPlugin(list): Arguments: context (dict): avalon-core:context-1.0 - name (str, optional): Use pre-defined name - namespace (str, optional): Use pre-defined namespace .. versionadded:: 4.0 This class was introduced """ - families = list() - representations = list() + families = [] + representations = [] + # Extensions filtering was not available until 20/2/2023 + # - filtering by extensions is not enabled if is set to 'None' + # which is to keep backwards compatibility + extensions = None order = 0 is_multiple_contexts_compatible = False enabled = True @@ -82,20 +84,72 @@ class LoaderPlugin(list): print(" - setting `{}`: `{}`".format(option, value)) setattr(cls, option, value) + @classmethod + def has_valid_extension(cls, repre_doc): + """Has representation document valid extension for loader. + + Args: + repre_doc (dict[str, Any]): Representation document. + + Returns: + bool: Representation has valid extension + """ + + # Empty list of extensions is considered as all representations are + # invalid -> use '["*"]' to support all extensions + valid_extensions = cls.extensions + if not valid_extensions: + return False + + # Get representation main file extension from 'context' + repre_context = repre_doc.get("context") or {} + ext = repre_context.get("ext") + if not ext: + # Legacy way how to get extensions + path = repre_doc["data"].get("path") + if not path: + cls.log.info( + "Representation doesn't have known source of extension" + " information." + ) + return False + + cls.log.info("Using legacy source of extension on representation.") + ext = os.path.splitext(path)[-1] + while ext.startswith("."): + ext = ext[1:] + + # If representation does not have extension then can't be valid + if not ext: + return False + + if "*" in valid_extensions: + return True + + valid_extensions_low = {ext.lower() for ext in valid_extensions} + return ext.lower() in valid_extensions_low + + @classmethod def is_compatible_loader(cls, context): """Return whether a loader is compatible with a context. + On override make sure it is overriden as class or static method. + This checks the version's families and the representation for the given - Loader. + loader plugin. + + Args: + context (dict[str, Any]): Documents of context for which should + be loader used. Returns: - bool + bool: Is loader compatible for context. """ plugin_repre_names = cls.get_representations() plugin_families = cls.families - if not plugin_repre_names or not plugin_families: + if not plugin_repre_names or not plugin_families or not cls.extensions: return False repre_doc = context.get("representation") @@ -109,11 +163,19 @@ class LoaderPlugin(list): ): return False - maj_version, _ = schema.get_schema_version(context["subset"]["schema"]) + if ( + cls.extensions is not None + and not cls.has_valid_extension(repre_doc) + ): + return False + + verison_doc = context["version"] + subset_doc = context["subset"] + maj_version, _ = schema.get_schema_version(subset_doc["schema"]) if maj_version < 3: - families = context["version"]["data"].get("families", []) + families = verison_doc["data"].get("families", []) else: - families = context["subset"]["data"]["families"] + families = subset_doc["families"] plugin_families = set(plugin_families) return ( From 1b7af3e2c6299df7d6aee1f9c3e5f3e12e60db3f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 23 Feb 2023 18:53:32 +0100 Subject: [PATCH 1086/1271] fix filtering --- openpype/pipeline/load/plugins.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index 8372743a23..4ce7d47fd9 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -149,7 +149,7 @@ class LoaderPlugin(list): plugin_repre_names = cls.get_representations() plugin_families = cls.families - if not plugin_repre_names or not plugin_families or not cls.extensions: + if not plugin_repre_names or not plugin_families: return False repre_doc = context.get("representation") @@ -173,14 +173,21 @@ class LoaderPlugin(list): subset_doc = context["subset"] maj_version, _ = schema.get_schema_version(subset_doc["schema"]) if maj_version < 3: - families = verison_doc["data"].get("families", []) + families = verison_doc["data"].get("families") else: - families = subset_doc["families"] + families = context["subset"]["data"].get("families") + if families is None: + family = context["subset"]["data"].get("family") + if family: + families = [family] plugin_families = set(plugin_families) return ( "*" in plugin_families - or any(family in plugin_families for family in families) + or any( + family in plugin_families + for family in (families or []) + ) ) @classmethod From 7a8aa123ff8bc159cbc7b4a0c7acedb01847b9e9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 23 Feb 2023 21:48:32 +0100 Subject: [PATCH 1087/1271] cleanup changes based on comments --- openpype/pipeline/load/plugins.py | 57 ++++++++++++------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index 4ce7d47fd9..f5b6e858b4 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -29,10 +29,7 @@ class LoaderPlugin(list): families = [] representations = [] - # Extensions filtering was not available until 20/2/2023 - # - filtering by extensions is not enabled if is set to 'None' - # which is to keep backwards compatibility - extensions = None + extensions = {"*"} order = 0 is_multiple_contexts_compatible = False enabled = True @@ -95,11 +92,8 @@ class LoaderPlugin(list): bool: Representation has valid extension """ - # Empty list of extensions is considered as all representations are - # invalid -> use '["*"]' to support all extensions - valid_extensions = cls.extensions - if not valid_extensions: - return False + if "*" in cls.extensions: + return True # Get representation main file extension from 'context' repre_context = repre_doc.get("context") or {} @@ -114,22 +108,16 @@ class LoaderPlugin(list): ) return False - cls.log.info("Using legacy source of extension on representation.") - ext = os.path.splitext(path)[-1] - while ext.startswith("."): - ext = ext[1:] + cls.log.info("Using legacy source of extension from path.") + ext = os.path.splitext(path)[-1].lstrip(".") # If representation does not have extension then can't be valid if not ext: return False - if "*" in valid_extensions: - return True - - valid_extensions_low = {ext.lower() for ext in valid_extensions} + valid_extensions_low = {ext.lower() for ext in cls.extensions} return ext.lower() in valid_extensions_low - @classmethod def is_compatible_loader(cls, context): """Return whether a loader is compatible with a context. @@ -149,7 +137,11 @@ class LoaderPlugin(list): plugin_repre_names = cls.get_representations() plugin_families = cls.families - if not plugin_repre_names or not plugin_families: + if ( + not plugin_repre_names + or not plugin_families + or not cls.extensions + ): return False repre_doc = context.get("representation") @@ -163,32 +155,27 @@ class LoaderPlugin(list): ): return False - if ( - cls.extensions is not None - and not cls.has_valid_extension(repre_doc) - ): + if not cls.has_valid_extension(repre_doc): return False - verison_doc = context["version"] + plugin_families = set(plugin_families) + if "*" in plugin_families: + return True + subset_doc = context["subset"] maj_version, _ = schema.get_schema_version(subset_doc["schema"]) if maj_version < 3: - families = verison_doc["data"].get("families") + families = context["version"]["data"].get("families") else: - families = context["subset"]["data"].get("families") + families = subset_doc["data"].get("families") if families is None: - family = context["subset"]["data"].get("family") + family = subset_doc["data"].get("family") if family: families = [family] - plugin_families = set(plugin_families) - return ( - "*" in plugin_families - or any( - family in plugin_families - for family in (families or []) - ) - ) + if not families: + return False + return any(family in plugin_families for family in families) @classmethod def get_representations(cls): From 8180076c08efa2e1ad3e1ece939b1b40899b3ef2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 24 Feb 2023 10:20:40 +0100 Subject: [PATCH 1088/1271] safer data access Co-authored-by: Roy Nieterau --- openpype/pipeline/load/plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index f5b6e858b4..d35281e823 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -100,7 +100,7 @@ class LoaderPlugin(list): ext = repre_context.get("ext") if not ext: # Legacy way how to get extensions - path = repre_doc["data"].get("path") + path = repre_doc.get("data", {}).get("path") if not path: cls.log.info( "Representation doesn't have known source of extension" From 9e432f7c5c7fa19dec06123d6e0867c9735be147 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 24 Feb 2023 10:55:30 +0100 Subject: [PATCH 1089/1271] add data and context to repre document fields --- openpype/tools/loader/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 0c5c9391cf..538925d141 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -339,7 +339,7 @@ class SubsetWidget(QtWidgets.QWidget): repre_docs = get_representations( project_name, version_ids=version_ids, - fields=["name", "parent"] + fields=["name", "parent", "data", "context"] ) repre_docs_by_version_id = { From 8a34313b97b7d6dc370aac485f188ffdb705a3f9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 24 Feb 2023 11:01:24 +0100 Subject: [PATCH 1090/1271] added 'data' and 'context' to representations widget too --- openpype/tools/loader/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 538925d141..98ac9c871f 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -1264,7 +1264,7 @@ class RepresentationWidget(QtWidgets.QWidget): repre_docs = list(get_representations( project_name, representation_ids=repre_ids, - fields=["name", "parent"] + fields=["name", "parent", "data", "context"] )) version_ids = [ From 1bd5f04880611a4461ead821dc2a14cf60d466b3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 Feb 2023 11:16:25 +0100 Subject: [PATCH 1091/1271] nuke: extension to loaders --- openpype/hosts/nuke/plugins/load/load_clip.py | 28 ++++++++++--------- .../hosts/nuke/plugins/load/load_image.py | 10 +++++-- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index 8f9b463037..18ecaf4d2b 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -21,6 +21,10 @@ from openpype.hosts.nuke.api import ( viewer_update_and_undo_stop, colorspace_exists_on_node ) +from openpype.lib.transcoding import ( + VIDEO_EXTENSIONS, + IMAGE_EXTENSIONS +) from openpype.hosts.nuke.api import plugin @@ -38,13 +42,11 @@ class LoadClip(plugin.NukeLoader): "prerender", "review" ] - representations = [ - "exr", - "dpx", - "mov", - "review", - "mp4" - ] + representations = ["*"] + + extensions = set( + ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS) + ) label = "Load Clip" order = -20 @@ -81,17 +83,17 @@ class LoadClip(plugin.NukeLoader): @classmethod def get_representations(cls): - return ( - cls.representations - + cls._representations - + plugin.get_review_presets_config() - ) + return cls._representations or cls.representations def load(self, context, name, namespace, options): + """Load asset via database + """ representation = context["representation"] - # reste container id so it is always unique for each instance + # reset container id so it is always unique for each instance self.reset_container_id() + self.log.warning(self.extensions) + is_sequence = len(representation["files"]) > 1 if is_sequence: diff --git a/openpype/hosts/nuke/plugins/load/load_image.py b/openpype/hosts/nuke/plugins/load/load_image.py index 49dc12f588..f82ee4db88 100644 --- a/openpype/hosts/nuke/plugins/load/load_image.py +++ b/openpype/hosts/nuke/plugins/load/load_image.py @@ -19,6 +19,9 @@ from openpype.hosts.nuke.api import ( update_container, viewer_update_and_undo_stop ) +from openpype.lib.transcoding import ( + IMAGE_EXTENSIONS +) class LoadImage(load.LoaderPlugin): @@ -33,7 +36,10 @@ class LoadImage(load.LoaderPlugin): "review", "image" ] - representations = ["exr", "dpx", "jpg", "jpeg", "png", "psd", "tiff"] + representations = ["*"] + extensions = set( + ext.lstrip(".") for ext in IMAGE_EXTENSIONS + ) label = "Load Image" order = -10 @@ -58,7 +64,7 @@ class LoadImage(load.LoaderPlugin): @classmethod def get_representations(cls): - return cls.representations + cls._representations + return cls._representations or cls.representations def load(self, context, name, namespace, options): self.log.info("__ options: `{}`".format(options)) From f432fb29de1cc9e01ea59a2482a9430086c987fa Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 Feb 2023 11:26:46 +0100 Subject: [PATCH 1092/1271] nuke: adding extension to plugins --- openpype/hosts/nuke/plugins/load/actions.py | 1 + openpype/hosts/nuke/plugins/load/load_backdrop.py | 3 ++- openpype/hosts/nuke/plugins/load/load_camera_abc.py | 3 ++- openpype/hosts/nuke/plugins/load/load_clip.py | 1 - openpype/hosts/nuke/plugins/load/load_effects.py | 3 ++- openpype/hosts/nuke/plugins/load/load_effects_ip.py | 3 ++- openpype/hosts/nuke/plugins/load/load_gizmo.py | 3 ++- openpype/hosts/nuke/plugins/load/load_gizmo_ip.py | 3 ++- openpype/hosts/nuke/plugins/load/load_matchmove.py | 4 +++- openpype/hosts/nuke/plugins/load/load_model.py | 3 ++- openpype/hosts/nuke/plugins/load/load_script_precomp.py | 3 ++- 11 files changed, 20 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/nuke/plugins/load/actions.py b/openpype/hosts/nuke/plugins/load/actions.py index 69f56c7305..e562c74c58 100644 --- a/openpype/hosts/nuke/plugins/load/actions.py +++ b/openpype/hosts/nuke/plugins/load/actions.py @@ -17,6 +17,7 @@ class SetFrameRangeLoader(load.LoaderPlugin): "yeticache", "pointcache"] representations = ["*"] + extension = {"*"} label = "Set frame range" order = 11 diff --git a/openpype/hosts/nuke/plugins/load/load_backdrop.py b/openpype/hosts/nuke/plugins/load/load_backdrop.py index d1fb763500..f227aa161a 100644 --- a/openpype/hosts/nuke/plugins/load/load_backdrop.py +++ b/openpype/hosts/nuke/plugins/load/load_backdrop.py @@ -25,8 +25,9 @@ from openpype.hosts.nuke.api import containerise, update_container class LoadBackdropNodes(load.LoaderPlugin): """Loading Published Backdrop nodes (workfile, nukenodes)""" - representations = ["nk"] families = ["workfile", "nukenodes"] + representations = ["*"] + extension = {"nk"} label = "Import Nuke Nodes" order = 0 diff --git a/openpype/hosts/nuke/plugins/load/load_camera_abc.py b/openpype/hosts/nuke/plugins/load/load_camera_abc.py index 9fef7424c8..11cc63d25c 100644 --- a/openpype/hosts/nuke/plugins/load/load_camera_abc.py +++ b/openpype/hosts/nuke/plugins/load/load_camera_abc.py @@ -25,7 +25,8 @@ class AlembicCameraLoader(load.LoaderPlugin): """ families = ["camera"] - representations = ["abc"] + representations = ["*"] + extension = {"abc"} label = "Load Alembic Camera" icon = "camera" diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index 18ecaf4d2b..d170276add 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -43,7 +43,6 @@ class LoadClip(plugin.NukeLoader): "review" ] representations = ["*"] - extensions = set( ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS) ) diff --git a/openpype/hosts/nuke/plugins/load/load_effects.py b/openpype/hosts/nuke/plugins/load/load_effects.py index cef4b0a5fc..d49f87a094 100644 --- a/openpype/hosts/nuke/plugins/load/load_effects.py +++ b/openpype/hosts/nuke/plugins/load/load_effects.py @@ -22,8 +22,9 @@ from openpype.hosts.nuke.api import ( class LoadEffects(load.LoaderPlugin): """Loading colorspace soft effect exported from nukestudio""" - representations = ["effectJson"] families = ["effect"] + representations = ["*"] + extension = {"json"} label = "Load Effects - nodes" order = 0 diff --git a/openpype/hosts/nuke/plugins/load/load_effects_ip.py b/openpype/hosts/nuke/plugins/load/load_effects_ip.py index 9bd40be816..bfe32c1ed9 100644 --- a/openpype/hosts/nuke/plugins/load/load_effects_ip.py +++ b/openpype/hosts/nuke/plugins/load/load_effects_ip.py @@ -23,8 +23,9 @@ from openpype.hosts.nuke.api import ( class LoadEffectsInputProcess(load.LoaderPlugin): """Loading colorspace soft effect exported from nukestudio""" - representations = ["effectJson"] families = ["effect"] + representations = ["*"] + extension = {"json"} label = "Load Effects - Input Process" order = 0 diff --git a/openpype/hosts/nuke/plugins/load/load_gizmo.py b/openpype/hosts/nuke/plugins/load/load_gizmo.py index 9a18eeef5c..2aa7c49723 100644 --- a/openpype/hosts/nuke/plugins/load/load_gizmo.py +++ b/openpype/hosts/nuke/plugins/load/load_gizmo.py @@ -24,8 +24,9 @@ from openpype.hosts.nuke.api import ( class LoadGizmo(load.LoaderPlugin): """Loading nuke Gizmo""" - representations = ["gizmo"] families = ["gizmo"] + representations = ["*"] + extension = {"gizmo"} label = "Load Gizmo" order = 0 diff --git a/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py b/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py index 2890dbfd2c..2514a28299 100644 --- a/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py +++ b/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py @@ -26,8 +26,9 @@ from openpype.hosts.nuke.api import ( class LoadGizmoInputProcess(load.LoaderPlugin): """Loading colorspace soft effect exported from nukestudio""" - representations = ["gizmo"] families = ["gizmo"] + representations = ["*"] + extension = {"gizmo"} label = "Load Gizmo - Input Process" order = 0 diff --git a/openpype/hosts/nuke/plugins/load/load_matchmove.py b/openpype/hosts/nuke/plugins/load/load_matchmove.py index f5a90706c7..a7d124d472 100644 --- a/openpype/hosts/nuke/plugins/load/load_matchmove.py +++ b/openpype/hosts/nuke/plugins/load/load_matchmove.py @@ -8,7 +8,9 @@ class MatchmoveLoader(load.LoaderPlugin): """ families = ["matchmove"] - representations = ["py"] + representations = ["*"] + extension = {"py"} + defaults = ["Camera", "Object"] label = "Run matchmove script" diff --git a/openpype/hosts/nuke/plugins/load/load_model.py b/openpype/hosts/nuke/plugins/load/load_model.py index ad985e83c6..f968da8475 100644 --- a/openpype/hosts/nuke/plugins/load/load_model.py +++ b/openpype/hosts/nuke/plugins/load/load_model.py @@ -23,7 +23,8 @@ class AlembicModelLoader(load.LoaderPlugin): """ families = ["model", "pointcache", "animation"] - representations = ["abc"] + representations = ["*"] + extension = {"abc"} label = "Load Alembic" icon = "cube" diff --git a/openpype/hosts/nuke/plugins/load/load_script_precomp.py b/openpype/hosts/nuke/plugins/load/load_script_precomp.py index f0972f85d2..90581c2f22 100644 --- a/openpype/hosts/nuke/plugins/load/load_script_precomp.py +++ b/openpype/hosts/nuke/plugins/load/load_script_precomp.py @@ -20,8 +20,9 @@ from openpype.hosts.nuke.api import ( class LinkAsGroup(load.LoaderPlugin): """Copy the published file to be pasted at the desired location""" - representations = ["nk"] families = ["workfile", "nukenodes"] + representations = ["*"] + extension = {"nk"} label = "Load Precomp" order = 0 From 1b479f7eb7919c114efa7537f4a9922eb9e01a4b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 Feb 2023 11:52:26 +0100 Subject: [PATCH 1093/1271] resolve: adding extensions to loader --- openpype/hosts/resolve/plugins/load/load_clip.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/resolve/plugins/load/load_clip.py b/openpype/hosts/resolve/plugins/load/load_clip.py index a0c78c182f..d30a7ea272 100644 --- a/openpype/hosts/resolve/plugins/load/load_clip.py +++ b/openpype/hosts/resolve/plugins/load/load_clip.py @@ -14,7 +14,10 @@ from openpype.hosts.resolve.api.pipeline import ( containerise, update_container, ) - +from openpype.lib.transcoding import ( + VIDEO_EXTENSIONS, + IMAGE_EXTENSIONS +) class LoadClip(plugin.TimelineItemLoader): """Load a subset to timeline as clip @@ -24,7 +27,11 @@ class LoadClip(plugin.TimelineItemLoader): """ families = ["render2d", "source", "plate", "render", "review"] - representations = ["exr", "dpx", "jpg", "jpeg", "png", "h264", "mov"] + + representations = ["*"] + extensions = set( + ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS) + ) label = "Load as clip" order = -10 From 339417c5055cdb8f77ee603a32a9caa630ab695d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 Feb 2023 15:14:31 +0100 Subject: [PATCH 1094/1271] hiero: adding extension to load plugins also removing representation from settings and ignoring settings for representation on plugin --- .../hosts/hiero/plugins/load/load_clip.py | 41 ++++++++++++++++++- .../hosts/hiero/plugins/load/load_effects.py | 3 +- .../defaults/project_settings/hiero.json | 10 ----- .../projects_schema/schema_project_hiero.json | 8 +--- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/openpype/hosts/hiero/plugins/load/load_clip.py b/openpype/hosts/hiero/plugins/load/load_clip.py index 2a7d1af41e..77844d2448 100644 --- a/openpype/hosts/hiero/plugins/load/load_clip.py +++ b/openpype/hosts/hiero/plugins/load/load_clip.py @@ -6,6 +6,10 @@ from openpype.pipeline import ( legacy_io, get_representation_path, ) +from openpype.lib.transcoding import ( + VIDEO_EXTENSIONS, + IMAGE_EXTENSIONS +) import openpype.hosts.hiero.api as phiero @@ -17,7 +21,10 @@ class LoadClip(phiero.SequenceLoader): """ families = ["render2d", "source", "plate", "render", "review"] - representations = ["exr", "dpx", "jpg", "jpeg", "png", "h264"] + representations = ["*"] + extensions = set( + ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS) + ) label = "Load as clip" order = -10 @@ -34,6 +41,38 @@ class LoadClip(phiero.SequenceLoader): clip_name_template = "{asset}_{subset}_{representation}" + def apply_settings(cls, project_settings, system_settings): + + plugin_type_settings = ( + project_settings + .get("hiero", {}) + .get("load", {}) + ) + + if not plugin_type_settings: + return + + plugin_name = cls.__name__ + + plugin_settings = None + # Look for plugin settings in host specific settings + if plugin_name in plugin_type_settings: + plugin_settings = plugin_type_settings[plugin_name] + + if not plugin_settings: + return + + print(">>> We have preset for {}".format(plugin_name)) + for option, value in plugin_settings.items(): + if option == "enabled" and value is False: + print(" - is disabled by preset") + elif option == "representations": + continue + else: + print(" - setting `{}`: `{}`".format(option, value)) + setattr(cls, option, value) + + def load(self, context, name, namespace, options): # add clip name template to options options.update({ diff --git a/openpype/hosts/hiero/plugins/load/load_effects.py b/openpype/hosts/hiero/plugins/load/load_effects.py index a3fcd63b5b..b61cca9731 100644 --- a/openpype/hosts/hiero/plugins/load/load_effects.py +++ b/openpype/hosts/hiero/plugins/load/load_effects.py @@ -19,8 +19,9 @@ from openpype.lib import Logger class LoadEffects(load.LoaderPlugin): """Loading colorspace soft effect exported from nukestudio""" - representations = ["effectJson"] families = ["effect"] + representations = ["*"] + extension = {"json"} label = "Load Effects" order = 0 diff --git a/openpype/settings/defaults/project_settings/hiero.json b/openpype/settings/defaults/project_settings/hiero.json index 0412967eaa..100c1f5b47 100644 --- a/openpype/settings/defaults/project_settings/hiero.json +++ b/openpype/settings/defaults/project_settings/hiero.json @@ -60,16 +60,6 @@ "render", "review" ], - "representations": [ - "exr", - "dpx", - "jpg", - "jpeg", - "png", - "h264", - "mov", - "mp4" - ], "clip_name_template": "{asset}_{subset}_{representation}" } }, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json index 03bfb56ad1..f44f92438c 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json @@ -266,12 +266,6 @@ "label": "Families", "object_type": "text" }, - { - "type": "list", - "key": "representations", - "label": "Representations", - "object_type": "text" - }, { "type": "text", "key": "clip_name_template", @@ -334,4 +328,4 @@ "name": "schema_scriptsmenu" } ] -} \ No newline at end of file +} From 6d43fa21902f0b9cfa2beee43bf9e9d8ff362eb0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 Feb 2023 15:19:50 +0100 Subject: [PATCH 1095/1271] flame: adding extensions to loaders also removing representations from settings and ignoring them in plugin settings loader --- openpype/hosts/flame/api/plugin.py | 31 +++++++++++++++++++ .../hosts/flame/plugins/load/load_clip.py | 9 +++++- .../flame/plugins/load/load_clip_batch.py | 10 ++++-- .../defaults/project_settings/flame.json | 22 ------------- .../projects_schema/schema_project_flame.json | 12 ------- 5 files changed, 47 insertions(+), 37 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index b1db612671..983d7486b3 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -702,6 +702,37 @@ class ClipLoader(LoaderPlugin): _mapping = None + def apply_settings(cls, project_settings, system_settings): + + plugin_type_settings = ( + project_settings + .get("flame", {}) + .get("load", {}) + ) + + if not plugin_type_settings: + return + + plugin_name = cls.__name__ + + plugin_settings = None + # Look for plugin settings in host specific settings + if plugin_name in plugin_type_settings: + plugin_settings = plugin_type_settings[plugin_name] + + if not plugin_settings: + return + + print(">>> We have preset for {}".format(plugin_name)) + for option, value in plugin_settings.items(): + if option == "enabled" and value is False: + print(" - is disabled by preset") + elif option == "representations": + continue + else: + print(" - setting `{}`: `{}`".format(option, value)) + setattr(cls, option, value) + def get_colorspace(self, context): """Get colorspace name diff --git a/openpype/hosts/flame/plugins/load/load_clip.py b/openpype/hosts/flame/plugins/load/load_clip.py index 6f47c23d57..25b31c94a3 100644 --- a/openpype/hosts/flame/plugins/load/load_clip.py +++ b/openpype/hosts/flame/plugins/load/load_clip.py @@ -4,6 +4,10 @@ import flame from pprint import pformat import openpype.hosts.flame.api as opfapi from openpype.lib import StringTemplate +from openpype.lib.transcoding import ( + VIDEO_EXTENSIONS, + IMAGE_EXTENSIONS +) class LoadClip(opfapi.ClipLoader): @@ -14,7 +18,10 @@ class LoadClip(opfapi.ClipLoader): """ families = ["render2d", "source", "plate", "render", "review"] - representations = ["exr", "dpx", "jpg", "jpeg", "png", "h264"] + representations = ["*"] + extensions = set( + ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS) + ) label = "Load as clip" order = -10 diff --git a/openpype/hosts/flame/plugins/load/load_clip_batch.py b/openpype/hosts/flame/plugins/load/load_clip_batch.py index 5975c6e42f..86bc0f8f1e 100644 --- a/openpype/hosts/flame/plugins/load/load_clip_batch.py +++ b/openpype/hosts/flame/plugins/load/load_clip_batch.py @@ -4,7 +4,10 @@ import flame from pprint import pformat import openpype.hosts.flame.api as opfapi from openpype.lib import StringTemplate - +from openpype.lib.transcoding import ( + VIDEO_EXTENSIONS, + IMAGE_EXTENSIONS +) class LoadClipBatch(opfapi.ClipLoader): """Load a subset to timeline as clip @@ -14,7 +17,10 @@ class LoadClipBatch(opfapi.ClipLoader): """ families = ["render2d", "source", "plate", "render", "review"] - representations = ["exr", "dpx", "jpg", "jpeg", "png", "h264"] + representations = ["*"] + extensions = set( + ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS) + ) label = "Load as clip to current batch" order = -10 diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index 3190bdb3bf..5a13d81384 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -114,17 +114,6 @@ "render", "review" ], - "representations": [ - "exr", - "dpx", - "jpg", - "jpeg", - "png", - "h264", - "mov", - "mp4", - "exr16fpdwaa" - ], "reel_group_name": "OpenPype_Reels", "reel_name": "Loaded", "clip_name_template": "{asset}_{subset}<_{output}>", @@ -143,17 +132,6 @@ "render", "review" ], - "representations": [ - "exr", - "dpx", - "jpg", - "jpeg", - "png", - "h264", - "mov", - "mp4", - "exr16fpdwaa" - ], "reel_name": "OP_LoadedReel", "clip_name_template": "{batch}_{asset}_{subset}<_{output}>", "layer_rename_template": "{asset}_{subset}<_{output}>", diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json index 0f20c0efbe..aab8f21d15 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -494,12 +494,6 @@ "label": "Families", "object_type": "text" }, - { - "type": "list", - "key": "representations", - "label": "Representations", - "object_type": "text" - }, { "type": "separator" }, @@ -552,12 +546,6 @@ "label": "Families", "object_type": "text" }, - { - "type": "list", - "key": "representations", - "label": "Representations", - "object_type": "text" - }, { "type": "separator" }, From a2d7ab24f60609096b58fbb8e6b891e743a1892e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 Feb 2023 15:24:22 +0100 Subject: [PATCH 1096/1271] fusion: adding extensions to loaders --- openpype/hosts/fusion/plugins/load/actions.py | 1 + openpype/hosts/fusion/plugins/load/load_alembic.py | 3 ++- openpype/hosts/fusion/plugins/load/load_fbx.py | 3 ++- openpype/hosts/fusion/plugins/load/load_sequence.py | 6 ++++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/plugins/load/actions.py b/openpype/hosts/fusion/plugins/load/actions.py index 819c9272fd..3b14f022e5 100644 --- a/openpype/hosts/fusion/plugins/load/actions.py +++ b/openpype/hosts/fusion/plugins/load/actions.py @@ -15,6 +15,7 @@ class FusionSetFrameRangeLoader(load.LoaderPlugin): "pointcache", "render"] representations = ["*"] + extensions = {"*"} label = "Set frame range" order = 11 diff --git a/openpype/hosts/fusion/plugins/load/load_alembic.py b/openpype/hosts/fusion/plugins/load/load_alembic.py index f8b8c2cb0a..11bf59af12 100644 --- a/openpype/hosts/fusion/plugins/load/load_alembic.py +++ b/openpype/hosts/fusion/plugins/load/load_alembic.py @@ -13,7 +13,8 @@ class FusionLoadAlembicMesh(load.LoaderPlugin): """Load Alembic mesh into Fusion""" families = ["pointcache", "model"] - representations = ["abc"] + representations = ["*"] + extensions = {"abc"} label = "Load alembic mesh" order = -10 diff --git a/openpype/hosts/fusion/plugins/load/load_fbx.py b/openpype/hosts/fusion/plugins/load/load_fbx.py index 70fe82ffef..b8f501ae7e 100644 --- a/openpype/hosts/fusion/plugins/load/load_fbx.py +++ b/openpype/hosts/fusion/plugins/load/load_fbx.py @@ -14,7 +14,8 @@ class FusionLoadFBXMesh(load.LoaderPlugin): """Load FBX mesh into Fusion""" families = ["*"] - representations = ["fbx"] + representations = ["*"] + extensions = {"fbx"} label = "Load FBX mesh" order = -10 diff --git a/openpype/hosts/fusion/plugins/load/load_sequence.py b/openpype/hosts/fusion/plugins/load/load_sequence.py index 6f44c61d1b..465df757b1 100644 --- a/openpype/hosts/fusion/plugins/load/load_sequence.py +++ b/openpype/hosts/fusion/plugins/load/load_sequence.py @@ -12,6 +12,9 @@ from openpype.hosts.fusion.api import ( get_current_comp, comp_lock_and_undo_chunk ) +from openpype.lib.transcoding import ( + IMAGE_EXTENSIONS +) comp = get_current_comp() @@ -129,6 +132,9 @@ class FusionLoadSequence(load.LoaderPlugin): families = ["imagesequence", "review", "render", "plate"] representations = ["*"] + extensions = set( + ext.lstrip(".") for ext in IMAGE_EXTENSIONS + ) label = "Load sequence" order = -10 From 3a02a03efad89fe50850c270a55ee028e3f6ec91 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 Feb 2023 16:38:34 +0100 Subject: [PATCH 1097/1271] fusion: adding video extensions to sequence loader --- openpype/hosts/fusion/plugins/load/load_sequence.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/plugins/load/load_sequence.py b/openpype/hosts/fusion/plugins/load/load_sequence.py index 465df757b1..542c3c1a51 100644 --- a/openpype/hosts/fusion/plugins/load/load_sequence.py +++ b/openpype/hosts/fusion/plugins/load/load_sequence.py @@ -13,7 +13,8 @@ from openpype.hosts.fusion.api import ( comp_lock_and_undo_chunk ) from openpype.lib.transcoding import ( - IMAGE_EXTENSIONS + IMAGE_EXTENSIONS, + VIDEO_EXTENSIONS ) comp = get_current_comp() @@ -133,7 +134,7 @@ class FusionLoadSequence(load.LoaderPlugin): families = ["imagesequence", "review", "render", "plate"] representations = ["*"] extensions = set( - ext.lstrip(".") for ext in IMAGE_EXTENSIONS + ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS) ) label = "Load sequence" From 42cc7152334bb59485470a6bb3a56d8eb76f8dde Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 24 Feb 2023 16:50:11 +0100 Subject: [PATCH 1098/1271] changed info to debug --- openpype/pipeline/load/plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index d35281e823..e380d65bbe 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -108,7 +108,7 @@ class LoaderPlugin(list): ) return False - cls.log.info("Using legacy source of extension from path.") + cls.log.debug("Using legacy source of extension from path.") ext = os.path.splitext(path)[-1].lstrip(".") # If representation does not have extension then can't be valid From 956fbb780badbd2bcbf052f4646d3014b5fe9df8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 28 Feb 2023 11:24:02 +0100 Subject: [PATCH 1099/1271] change Harmony loader to use extensions --- openpype/hosts/harmony/plugins/load/load_imagesequence.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/harmony/plugins/load/load_imagesequence.py b/openpype/hosts/harmony/plugins/load/load_imagesequence.py index 1b64aff595..7c3185fd75 100644 --- a/openpype/hosts/harmony/plugins/load/load_imagesequence.py +++ b/openpype/hosts/harmony/plugins/load/load_imagesequence.py @@ -20,8 +20,9 @@ class ImageSequenceLoader(load.LoaderPlugin): Stores the imported asset in a container named after the asset. """ - families = ["shot", "render", "image", "plate", "reference"] - representations = ["jpeg", "png", "jpg"] + families = ["shot", "render", "image", "plate", "reference", "review"] + representations = ["*"] + extensions = set(["jpeg", "png", "jpg"]) def load(self, context, name=None, namespace=None, data=None): """Plugin entry point. From 0765602ce3aabfc6751662d1026a7638385d67a8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 28 Feb 2023 11:27:13 +0100 Subject: [PATCH 1100/1271] simpler set initialization --- openpype/hosts/harmony/plugins/load/load_imagesequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/harmony/plugins/load/load_imagesequence.py b/openpype/hosts/harmony/plugins/load/load_imagesequence.py index 7c3185fd75..b95d25f507 100644 --- a/openpype/hosts/harmony/plugins/load/load_imagesequence.py +++ b/openpype/hosts/harmony/plugins/load/load_imagesequence.py @@ -22,7 +22,7 @@ class ImageSequenceLoader(load.LoaderPlugin): families = ["shot", "render", "image", "plate", "reference", "review"] representations = ["*"] - extensions = set(["jpeg", "png", "jpg"]) + extensions = {"jpeg", "png", "jpg"} def load(self, context, name=None, namespace=None, data=None): """Plugin entry point. From 9229ff9c0b229db2d9d25c8d91b6cb6f7b58de9b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 28 Feb 2023 13:58:01 +0100 Subject: [PATCH 1101/1271] hiero: fix effect item node class --- .../hosts/hiero/plugins/publish/collect_clip_effects.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py b/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py index 9489b1c4fb..95e4b09504 100644 --- a/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py +++ b/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py @@ -120,13 +120,10 @@ class CollectClipEffects(pyblish.api.InstancePlugin): track = sitem.parentTrack().name() # node serialization node = sitem.node() - node_serialized = self.node_serialisation(node) + node_serialized = self.node_serialization(node) node_name = sitem.name() + node_class = node.Class() - if "_" in node_name: - node_class = re.sub(r"(?:_)[_0-9]+", "", node_name) # more numbers - else: - node_class = re.sub(r"\d+", "", node_name) # one number # collect timelineIn/Out effect_t_in = int(sitem.timelineIn()) @@ -148,7 +145,7 @@ class CollectClipEffects(pyblish.api.InstancePlugin): "node": node_serialized }} - def node_serialisation(self, node): + def node_serialization(self, node): node_serialized = {} # adding ignoring knob keys From 2bd4e5c3c91b453bca3b21aed3c1cfd6cf19be37 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 28 Feb 2023 14:11:01 +0100 Subject: [PATCH 1102/1271] hound comments --- openpype/hosts/hiero/plugins/publish/collect_clip_effects.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py b/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py index 95e4b09504..d455ad4a4e 100644 --- a/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py +++ b/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py @@ -124,7 +124,6 @@ class CollectClipEffects(pyblish.api.InstancePlugin): node_name = sitem.name() node_class = node.Class() - # collect timelineIn/Out effect_t_in = int(sitem.timelineIn()) effect_t_out = int(sitem.timelineOut()) From 2ed1a97864ba7c9b557b6159c03a78056d804b6f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 28 Feb 2023 14:22:38 +0100 Subject: [PATCH 1103/1271] nuke: start new workfile from tempate wip --- openpype/hosts/nuke/api/lib.py | 12 ++++++++++- openpype/hosts/nuke/api/pipeline.py | 3 ++- .../nuke/api/workfile_template_builder.py | 20 +++++++++++++++++- .../workfile/workfile_template_builder.py | 21 +++++++------------ 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index c08db978d3..2b7aaa9d70 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -48,7 +48,6 @@ from openpype.pipeline.colorspace import ( get_imageio_config ) from openpype.pipeline.workfile import BuildWorkfile - from . import gizmo_menu from .constants import ASSIST @@ -2678,6 +2677,17 @@ def process_workfile_builder(): open_file(last_workfile_path) +def start_workfile_template_builder(): + from .workfile_template_builder import ( + build_workfile_template + ) + + # to avoid looping of the callback, remove it! + # nuke.removeOnCreate(start_workfile_template_builder, nodeClass="Root") + log.info("Starting workfile template builder...") + build_workfile_template() + + @deprecated def recreate_instance(origin_node, avalon_data=None): """Recreate input instance to different data diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 2496d66c1d..30270a4e5f 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -33,6 +33,7 @@ from .lib import ( add_publish_knob, WorkfileSettings, process_workfile_builder, + start_workfile_template_builder, launch_workfiles_app, check_inventory_versions, set_avalon_knob_data, @@ -48,7 +49,6 @@ from .workfile_template_builder import ( NukePlaceholderLoadPlugin, NukePlaceholderCreatePlugin, build_workfile_template, - update_workfile_template, create_placeholder, update_placeholder, ) @@ -155,6 +155,7 @@ def add_nuke_callbacks(): # Set context settings. nuke.addOnCreate( workfile_settings.set_context_settings, nodeClass="Root") + nuke.addOnCreate(start_workfile_template_builder, nodeClass="Root") nuke.addOnCreate(workfile_settings.set_favorites, nodeClass="Root") nuke.addOnCreate(process_workfile_builder, nodeClass="Root") diff --git a/openpype/hosts/nuke/api/workfile_template_builder.py b/openpype/hosts/nuke/api/workfile_template_builder.py index 739c10d56b..1c0a41456a 100644 --- a/openpype/hosts/nuke/api/workfile_template_builder.py +++ b/openpype/hosts/nuke/api/workfile_template_builder.py @@ -15,7 +15,7 @@ from openpype.pipeline.workfile.workfile_template_builder import ( from openpype.tools.workfile_template_build import ( WorkfileBuildPlaceholderDialog, ) - +from openpype.host import IWorkfileHost from .lib import ( find_free_space_to_paste_nodes, get_extreme_positions, @@ -56,6 +56,24 @@ class NukeTemplateBuilder(AbstractTemplateBuilder): return True + def create_first_workfile_version(self): + """ + Create first version of workfile. + + Should load the content of template into scene so + 'populate_scene_placeholders' can be started. + + Args: + template_path (str): Fullpath for current task and + host's template file. + """ + last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE") + # Save current scene, continue to open file + if isinstance(self.host, IWorkfileHost): + self.host.save_workfile(last_workfile_path) + else: + self.host.save_file(last_workfile_path) + class NukePlaceholderPlugin(PlaceholderPlugin): node_color = 4278190335 diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 6bbe5f5d13..1758c30a8b 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -505,19 +505,8 @@ class AbstractTemplateBuilder(object): """ Create first version of workfile. - Should load the content of template into scene so - 'populate_scene_placeholders' can be started. - - Args: - template_path (str): Fullpath for current task and - host's template file. """ - last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE") - # Save current scene, continue to open file - if isinstance(self.host, IWorkfileHost): - self.host.save_workfile(last_workfile_path) - else: - self.host.save_file(last_workfile_path) + pass def _prepare_placeholders(self, placeholders): """Run preparation part for placeholders on plugins. @@ -702,6 +691,8 @@ class AbstractTemplateBuilder(object): # switch to remove placeholders after they are used keep_placeholder = profile.get("keep_placeholder") + create_first_version = profile.get("create_first_version") + # backward compatibility, since default is True if keep_placeholder is None: keep_placeholder = True @@ -735,7 +726,8 @@ class AbstractTemplateBuilder(object): self.log.info("Found template at: '{}'".format(path)) return { "path": path, - "keep_placeholder": keep_placeholder + "keep_placeholder": keep_placeholder, + "create_first_version": create_first_version } solved_path = None @@ -764,7 +756,8 @@ class AbstractTemplateBuilder(object): return { "path": solved_path, - "keep_placeholder": keep_placeholder + "keep_placeholder": keep_placeholder, + "create_first_version": create_first_version } From 477d2e24143ef5caebe6801d094541ab012ffe39 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 27 Feb 2023 18:15:44 +0100 Subject: [PATCH 1104/1271] :bug: return AssetContainer to allow loading --- .../OpenPype/Private/AssetContainer.cpp | 114 ++++++++++++++++++ .../Private/AssetContainerFactory.cpp | 20 +++ .../Source/OpenPype/Public/AssetContainer.h | 37 ++++++ .../OpenPype/Public/AssetContainerFactory.h | 21 ++++ 4 files changed, 192 insertions(+) create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainer.cpp create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainerFactory.cpp create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/AssetContainer.h create mode 100644 openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/AssetContainerFactory.h diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainer.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainer.cpp new file mode 100644 index 0000000000..0bea9e3d78 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainer.cpp @@ -0,0 +1,114 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "AssetContainer.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "Misc/PackageName.h" +#include "Engine.h" +#include "Containers/UnrealString.h" + +UAssetContainer::UAssetContainer(const FObjectInitializer& ObjectInitializer) +: UAssetUserData(ObjectInitializer) +{ + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); + FString path = UAssetContainer::GetPathName(); + UE_LOG(LogTemp, Warning, TEXT("UAssetContainer %s"), *path); + FARFilter Filter; + Filter.PackagePaths.Add(FName(*path)); + + AssetRegistryModule.Get().OnAssetAdded().AddUObject(this, &UAssetContainer::OnAssetAdded); + AssetRegistryModule.Get().OnAssetRemoved().AddUObject(this, &UAssetContainer::OnAssetRemoved); + AssetRegistryModule.Get().OnAssetRenamed().AddUObject(this, &UAssetContainer::OnAssetRenamed); +} + +void UAssetContainer::OnAssetAdded(const FAssetData& AssetData) +{ + TArray split; + + // get directory of current container + FString selfFullPath = UAssetContainer::GetPathName(); + FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath); + + // get asset path and class + FString assetPath = AssetData.GetFullName(); + FString assetFName = AssetData.AssetClassPath.ToString(); + UE_LOG(LogTemp, Log, TEXT("asset name %s"), *assetFName); + // split path + assetPath.ParseIntoArray(split, TEXT(" "), true); + + FString assetDir = FPackageName::GetLongPackagePath(*split[1]); + + // take interest only in paths starting with path of current container + if (assetDir.StartsWith(*selfDir)) + { + // exclude self + if (assetFName != "AssetContainer") + { + assets.Add(assetPath); + assetsData.Add(AssetData); + UE_LOG(LogTemp, Log, TEXT("%s: asset added to %s"), *selfFullPath, *selfDir); + } + } +} + +void UAssetContainer::OnAssetRemoved(const FAssetData& AssetData) +{ + TArray split; + + // get directory of current container + FString selfFullPath = UAssetContainer::GetPathName(); + FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath); + + // get asset path and class + FString assetPath = AssetData.GetFullName(); + FString assetFName = AssetData.AssetClassPath.ToString(); + + // split path + assetPath.ParseIntoArray(split, TEXT(" "), true); + + FString assetDir = FPackageName::GetLongPackagePath(*split[1]); + + // take interest only in paths starting with path of current container + FString path = UAssetContainer::GetPathName(); + FString lpp = FPackageName::GetLongPackagePath(*path); + + if (assetDir.StartsWith(*selfDir)) + { + // exclude self + if (assetFName != "AssetContainer") + { + // UE_LOG(LogTemp, Warning, TEXT("%s: asset removed"), *lpp); + assets.Remove(assetPath); + assetsData.Remove(AssetData); + } + } +} + +void UAssetContainer::OnAssetRenamed(const FAssetData& AssetData, const FString& str) +{ + TArray split; + + // get directory of current container + FString selfFullPath = UAssetContainer::GetPathName(); + FString selfDir = FPackageName::GetLongPackagePath(*selfFullPath); + + // get asset path and class + FString assetPath = AssetData.GetFullName(); + FString assetFName = AssetData.AssetClassPath.ToString(); + + // split path + assetPath.ParseIntoArray(split, TEXT(" "), true); + + FString assetDir = FPackageName::GetLongPackagePath(*split[1]); + if (assetDir.StartsWith(*selfDir)) + { + // exclude self + if (assetFName != "AssetContainer") + { + + assets.Remove(str); + assets.Add(assetPath); + assetsData.Remove(AssetData); + // UE_LOG(LogTemp, Warning, TEXT("%s: asset renamed %s"), *lpp, *str); + } + } +} diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainerFactory.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainerFactory.cpp new file mode 100644 index 0000000000..b943150bdd --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainerFactory.cpp @@ -0,0 +1,20 @@ +#include "AssetContainerFactory.h" +#include "AssetContainer.h" + +UAssetContainerFactory::UAssetContainerFactory(const FObjectInitializer& ObjectInitializer) + : UFactory(ObjectInitializer) +{ + SupportedClass = UAssetContainer::StaticClass(); + bCreateNew = false; + bEditorImport = true; +} + +UObject* UAssetContainerFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + UAssetContainer* AssetContainer = NewObject(InParent, Class, Name, Flags); + return AssetContainer; +} + +bool UAssetContainerFactory::ShouldShowInNewMenu() const { + return false; +} diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/AssetContainer.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/AssetContainer.h new file mode 100644 index 0000000000..9157569c08 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/AssetContainer.h @@ -0,0 +1,37 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/NoExportTypes.h" +#include "Engine/AssetUserData.h" +#include "AssetRegistry/AssetData.h" +#include "AssetContainer.generated.h" + +/** + * + */ +UCLASS(Blueprintable) +class OPENPYPE_API UAssetContainer : public UAssetUserData +{ + GENERATED_BODY() + +public: + + UAssetContainer(const FObjectInitializer& ObjectInitalizer); + // ~UAssetContainer(); + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Assets") + TArray assets; + + // There seems to be no reflection option to expose array of FAssetData + /* + UPROPERTY(Transient, BlueprintReadOnly, Category = "Python", meta=(DisplayName="Assets Data")) + TArray assetsData; + */ +private: + TArray assetsData; + void OnAssetAdded(const FAssetData& AssetData); + void OnAssetRemoved(const FAssetData& AssetData); + void OnAssetRenamed(const FAssetData& AssetData, const FString& str); +}; diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/AssetContainerFactory.h b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/AssetContainerFactory.h new file mode 100644 index 0000000000..9095f8a3d7 --- /dev/null +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Public/AssetContainerFactory.h @@ -0,0 +1,21 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Factories/Factory.h" +#include "AssetContainerFactory.generated.h" + +/** + * + */ +UCLASS() +class OPENPYPE_API UAssetContainerFactory : public UFactory +{ + GENERATED_BODY() + +public: + UAssetContainerFactory(const FObjectInitializer& ObjectInitializer); + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; + virtual bool ShouldShowInNewMenu() const override; +}; From 1ef786afb8c7dacfd219bdf3733a52e777082c3f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 28 Feb 2023 14:39:12 +0100 Subject: [PATCH 1105/1271] added option to use new creating system in workfile template builder --- .../maya/api/workfile_template_builder.py | 2 + .../workfile/workfile_template_builder.py | 97 ++++++++++++++----- 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index 2f550e787a..90ab6e21e0 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -22,6 +22,8 @@ PLACEHOLDER_SET = "PLACEHOLDERS_SET" class MayaTemplateBuilder(AbstractTemplateBuilder): """Concrete implementation of AbstractTemplateBuilder for maya""" + use_legacy_creators = True + def import_template(self, path): """Import template into current scene. Block if a template is already loaded. diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 119e4aaeb7..0167224cb0 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -43,7 +43,8 @@ from openpype.pipeline.load import ( load_with_repre_context, ) from openpype.pipeline.create import ( - discover_legacy_creator_plugins + discover_legacy_creator_plugins, + CreateContext, ) @@ -91,6 +92,7 @@ class AbstractTemplateBuilder(object): """ _log = None + use_legacy_creators = False def __init__(self, host): # Get host name @@ -110,6 +112,7 @@ class AbstractTemplateBuilder(object): self._placeholder_plugins = None self._loaders_by_name = None self._creators_by_name = None + self._create_context = None self._system_settings = None self._project_settings = None @@ -171,6 +174,14 @@ class AbstractTemplateBuilder(object): .get("type") ) + @property + def create_context(self): + if self._create_context is None: + self._create_context = CreateContext( + self.host, discover_publish_plugins=False + ) + return self._create_context + def get_placeholder_plugin_classes(self): """Get placeholder plugin classes that can be used to build template. @@ -235,18 +246,29 @@ class AbstractTemplateBuilder(object): self._loaders_by_name = get_loaders_by_name() return self._loaders_by_name + def _collect_legacy_creators(self): + creators_by_name = {} + for creator in discover_legacy_creator_plugins(): + if not creator.enabled: + continue + creator_name = creator.__name__ + if creator_name in creators_by_name: + raise KeyError( + "Duplicated creator name {} !".format(creator_name) + ) + creators_by_name[creator_name] = creator + self._creators_by_name = creators_by_name + + def _collect_creators(self): + self._creators_by_name = dict(self.create_context.creators) + def get_creators_by_name(self): if self._creators_by_name is None: - self._creators_by_name = {} - for creator in discover_legacy_creator_plugins(): - if not creator.enabled: - continue - creator_name = creator.__name__ - if creator_name in self._creators_by_name: - raise KeyError( - "Duplicated creator name {} !".format(creator_name) - ) - self._creators_by_name[creator_name] = creator + if self.use_legacy_creators: + self._collect_legacy_creators() + else: + self._collect_creators() + return self._creators_by_name def get_shared_data(self, key): @@ -1579,6 +1601,8 @@ class PlaceholderCreateMixin(object): placeholder (PlaceholderItem): Placeholder item with information about requested publishable instance. """ + + legacy_create = self.builder.use_legacy_creators creator_name = placeholder.data["creator"] create_variant = placeholder.data["create_variant"] @@ -1589,17 +1613,28 @@ class PlaceholderCreateMixin(object): task_name = legacy_io.Session["AVALON_TASK"] asset_name = legacy_io.Session["AVALON_ASSET"] - # get asset id - asset_doc = get_asset_by_name(project_name, asset_name, fields=["_id"]) - assert asset_doc, "No current asset found in Session" - asset_id = asset_doc['_id'] + if legacy_create: + asset_doc = get_asset_by_name( + project_name, asset_name, fields=["_id"] + ) + assert asset_doc, "No current asset found in Session" + subset_name = creator_plugin.get_subset_name( + create_variant, + task_name, + asset_doc["_id"], + project_name + ) - subset_name = creator_plugin.get_subset_name( - create_variant, - task_name, - asset_id, - project_name - ) + else: + asset_doc = get_asset_by_name(project_name, asset_name) + assert asset_doc, "No current asset found in Session" + subset_name = creator_plugin.get_subset_name( + create_variant, + task_name, + asset_doc, + project_name, + self.builder.host_name + ) creator_data = { "creator_name": creator_name, @@ -1612,10 +1647,22 @@ class PlaceholderCreateMixin(object): # compile subset name from variant try: - creator_instance = creator_plugin( - subset_name, - asset_name - ).process() + if legacy_create: + creator_instance = creator_plugin( + subset_name, + asset_name + ).process() + else: + creator_instance = creator_plugin.create( + subset_name, + { + "asset": asset_doc["name"], + "task": task_name, + "family": creator_plugin.family, + "variant": create_variant + }, + {} + ) except Exception: failed = True From ee3e346c8df68515d72221bb2e3fe84ab92e9b0e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 28 Feb 2023 14:53:49 +0100 Subject: [PATCH 1106/1271] Global: refactory colormanaged exctractor into plugin mixin --- .../plugins/publish/extract_render_local.py | 3 +- openpype/pipeline/publish/__init__.py | 4 +- openpype/pipeline/publish/publish_plugins.py | 40 ++++++++++--------- .../publish/extract_colorspace_data.py | 3 +- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_render_local.py b/openpype/hosts/nuke/plugins/publish/extract_render_local.py index b99a7a9548..4d7ade9c7a 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_render_local.py +++ b/openpype/hosts/nuke/plugins/publish/extract_render_local.py @@ -9,7 +9,8 @@ from openpype.pipeline import publish from openpype.lib import collect_frames -class NukeRenderLocal(publish.ExtractorColormanaged): +class NukeRenderLocal(publish.Extractor, + publish.ColormanagedPyblishPluginMixin): """Render the current Nuke composition locally. Extract the result of savers by starting a comp render diff --git a/openpype/pipeline/publish/__init__.py b/openpype/pipeline/publish/__init__.py index 05ba1c9c33..36252c9f3d 100644 --- a/openpype/pipeline/publish/__init__.py +++ b/openpype/pipeline/publish/__init__.py @@ -19,7 +19,7 @@ from .publish_plugins import ( RepairContextAction, Extractor, - ExtractorColormanaged, + ColormanagedPyblishPluginMixin ) from .lib import ( @@ -64,7 +64,7 @@ __all__ = ( "RepairContextAction", "Extractor", - "ExtractorColormanaged", + "ColormanagedPyblishPluginMixin", "get_publish_template_name", diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index e2ae893aa9..0142919e76 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -3,7 +3,7 @@ from abc import ABCMeta from pprint import pformat import pyblish.api from pyblish.plugin import MetaPlugin, ExplicitMetaPlugin - +from openpype.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS from openpype.lib import BoolDef from .lib import ( @@ -288,24 +288,25 @@ class Extractor(pyblish.api.InstancePlugin): return get_instance_staging_dir(instance) -class ExtractorColormanaged(Extractor): - """Extractor base for color managed image data. - - Each Extractor intended to export pixel data representation - should inherit from this class to allow color managed data. - Class implements "get_colorspace_settings" and - "set_representation_colorspace" functions used - for injecting colorspace data to representation data for farther - integration into db document. +class ColormanagedPyblishPluginMixin(object): + """Mixin for colormanaged plugins. + This class is used to set colorspace data to a publishing + representation. It contains a static method, + get_colorspace_settings, which returns config and + file rules data for the host context. + It also contains a method, set_representation_colorspace, + which sets colorspace data to the representation. + The allowed file extensions are listed in the allowed_ext variable. + he method first checks if the file extension is in + the list of allowed extensions. If it is, it then gets the + colorspace settings from the host context and gets a + matching colorspace from rules. Finally, it infuses this + data into the representation. """ - - allowed_ext = [ - "cin", "dpx", "avi", "dv", "gif", "flv", "mkv", "mov", "mpg", "mpeg", - "mp4", "m4v", "mxf", "iff", "z", "ifl", "jpeg", "jpg", "jfif", "lut", - "1dl", "exr", "pic", "png", "ppm", "pnm", "pgm", "pbm", "rla", "rpf", - "sgi", "rgba", "rgb", "bw", "tga", "tiff", "tif", "img" - ] + allowed_ext = set( + ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS) + ) @staticmethod def get_colorspace_settings(context): @@ -375,7 +376,10 @@ class ExtractorColormanaged(Extractor): ext = representation["ext"] # check extension self.log.debug("__ ext: `{}`".format(ext)) - if ext.lower() not in self.allowed_ext: + + # check if ext in lower case is in self.allowed_ext + if ext.lstrip(".").lower() not in self.allowed_ext: + self.log.debug("Extension is not in allowed extensions.") return if colorspace_settings is None: diff --git a/openpype/plugins/publish/extract_colorspace_data.py b/openpype/plugins/publish/extract_colorspace_data.py index 611fb91cbb..363df28fb5 100644 --- a/openpype/plugins/publish/extract_colorspace_data.py +++ b/openpype/plugins/publish/extract_colorspace_data.py @@ -2,7 +2,8 @@ import pyblish.api from openpype.pipeline import publish -class ExtractColorspaceData(publish.ExtractorColormanaged): +class ExtractColorspaceData(publish.Extractor, + publish.ColormanagedPyblishPluginMixin): """ Inject Colorspace data to available representations. Input data: From 76f312a3ff026d04feda901b98bc0ad523e3b00b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 28 Feb 2023 15:04:54 +0100 Subject: [PATCH 1107/1271] Nuke: adding colorspace to representation when rendered mode --- .../hosts/nuke/plugins/publish/collect_writes.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/collect_writes.py b/openpype/hosts/nuke/plugins/publish/collect_writes.py index 3054e5a30c..2b741426e6 100644 --- a/openpype/hosts/nuke/plugins/publish/collect_writes.py +++ b/openpype/hosts/nuke/plugins/publish/collect_writes.py @@ -3,9 +3,10 @@ from pprint import pformat import nuke import pyblish.api from openpype.hosts.nuke import api as napi +from openpype.pipeline import publish - -class CollectNukeWrites(pyblish.api.InstancePlugin): +class CollectNukeWrites(pyblish.api.InstancePlugin, + publish.ColormanagedPyblishPluginMixin): """Collect all write nodes.""" order = pyblish.api.CollectorOrder - 0.48 @@ -128,6 +129,12 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): else: representation['files'] = collected_frames + # inject colorspace data + self.set_representation_colorspace( + representation, instance.context, + colorspace=colorspace + ) + instance.data["representations"].append(representation) self.log.info("Publishing rendered frames ...") @@ -147,6 +154,8 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): # get colorspace and add to version data colorspace = napi.get_colorspace_from_node(write_node) + + # TODO: remove this when we have proper colorspace support version_data = { "colorspace": colorspace } From 9bb36864be3911b25c37c873d68ae4871fdcf57a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 28 Feb 2023 15:05:16 +0100 Subject: [PATCH 1108/1271] Nuke: colorspace from node unified --- openpype/hosts/nuke/plugins/publish/extract_render_local.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_render_local.py b/openpype/hosts/nuke/plugins/publish/extract_render_local.py index 4d7ade9c7a..e5feda4cd8 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_render_local.py +++ b/openpype/hosts/nuke/plugins/publish/extract_render_local.py @@ -4,7 +4,7 @@ import shutil import pyblish.api import clique import nuke - +from openpype.hosts.nuke import api as napi from openpype.pipeline import publish from openpype.lib import collect_frames @@ -86,7 +86,7 @@ class NukeRenderLocal(publish.Extractor, ) ext = node["file_type"].value() - colorspace = node["colorspace"].value() + colorspace = napi.get_colorspace_from_node(node) if "representations" not in instance.data: instance.data["representations"] = [] From b7e99dacb8b5107a90cbdd933c6b5985dc5bbb79 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 28 Feb 2023 15:45:12 +0100 Subject: [PATCH 1109/1271] fix spaces --- openpype/pipeline/workfile/workfile_template_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 0167224cb0..7ef2e7378b 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -1653,7 +1653,7 @@ class PlaceholderCreateMixin(object): asset_name ).process() else: - creator_instance = creator_plugin.create( + creator_instance = creator_plugin.create( subset_name, { "asset": asset_doc["name"], From d68cc23d32aabb7773b40ff2896fb8ea6390cae7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 28 Feb 2023 14:37:41 +0100 Subject: [PATCH 1110/1271] hardcode Poetry to 1.3.2 temporarily New 1.4.0 breaks sphinxcontrib-applehelp. Change in poetry.lock would need new minor version. --- tools/create_env.ps1 | 2 +- tools/create_env.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/create_env.ps1 b/tools/create_env.ps1 index f79bc2a076..7d1f6635d0 100644 --- a/tools/create_env.ps1 +++ b/tools/create_env.ps1 @@ -68,7 +68,7 @@ function Install-Poetry() { } $env:POETRY_HOME="$openpype_root\.poetry" - # $env:POETRY_VERSION="1.1.15" + $env:POETRY_VERSION="1.3.2" (Invoke-WebRequest -Uri https://install.python-poetry.org/ -UseBasicParsing).Content | & $($python) - } diff --git a/tools/create_env.sh b/tools/create_env.sh index fbae69e56d..6915d3f000 100755 --- a/tools/create_env.sh +++ b/tools/create_env.sh @@ -109,7 +109,7 @@ detect_python () { install_poetry () { echo -e "${BIGreen}>>>${RST} Installing Poetry ..." export POETRY_HOME="$openpype_root/.poetry" - # export POETRY_VERSION="1.1.15" + export POETRY_VERSION="1.3.2" command -v curl >/dev/null 2>&1 || { echo -e "${BIRed}!!!${RST}${BIYellow} Missing ${RST}${BIBlue}curl${BIYellow} command.${RST}"; return 1; } curl -sSL https://install.python-poetry.org/ | python - } From 9f35ed2a63ce38f0d7c6b1c29fec305116896a04 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 28 Feb 2023 16:33:18 +0100 Subject: [PATCH 1111/1271] Fix - store target_colorspace as new colorspace (#4544) When transcoding into new colorspace, representation must carry this information instead original color space. --- openpype/plugins/publish/extract_color_transcode.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index a6fa710425..58e0350a2e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -129,11 +129,14 @@ class ExtractOIIOTranscode(publish.Extractor): colorspace_data.get("display")) # both could be already collected by DCC, - # but could be overwritten + # but could be overwritten when transcoding if view: new_repre["colorspaceData"]["view"] = view if display: new_repre["colorspaceData"]["display"] = display + if target_colorspace: + new_repre["colorspaceData"]["colorspace"] = \ + target_colorspace additional_command_args = (output_def["oiiotool_args"] ["additional_command_args"]) From 6b2c10da04cb4494d709d612ba06d9d5b7482bf4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 28 Feb 2023 16:51:18 +0100 Subject: [PATCH 1112/1271] use 'create' method on create context to trigger creation --- .../pipeline/workfile/workfile_template_builder.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 7ef2e7378b..26735d77d0 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -1653,15 +1653,11 @@ class PlaceholderCreateMixin(object): asset_name ).process() else: - creator_instance = creator_plugin.create( - subset_name, - { - "asset": asset_doc["name"], - "task": task_name, - "family": creator_plugin.family, - "variant": create_variant - }, - {} + creator_instance = self.create_context.create( + creator_plugin.identifier, + create_variant, + asset_doc, + task_name=task_name ) except Exception: From 9acf634d1363d08a580bc8341f45419f2effe721 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 28 Feb 2023 17:09:43 +0100 Subject: [PATCH 1113/1271] fix attribute access --- openpype/pipeline/workfile/workfile_template_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 26735d77d0..6ffe0116e5 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -1653,7 +1653,7 @@ class PlaceholderCreateMixin(object): asset_name ).process() else: - creator_instance = self.create_context.create( + creator_instance = self.builder.create_context.create( creator_plugin.identifier, create_variant, asset_doc, From 7c90b6616d50d8106dd6be0811af372d0e5b486c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 28 Feb 2023 17:32:49 +0100 Subject: [PATCH 1114/1271] adding headless to creators and workfile builder abstraction --- openpype/hosts/nuke/api/plugin.py | 6 +++++- openpype/hosts/nuke/plugins/create/create_write_image.py | 2 +- .../hosts/nuke/plugins/create/create_write_prerender.py | 2 +- openpype/hosts/nuke/plugins/create/create_write_render.py | 2 +- openpype/pipeline/workfile/workfile_template_builder.py | 6 ++++-- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 160ca820a4..9518598238 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -239,7 +239,11 @@ class NukeCreator(NewCreator): def get_pre_create_attr_defs(self): return [ - BoolDef("use_selection", label="Use selection") + BoolDef( + "use_selection", + default=not self.create_context.headless, + label="Use selection" + ) ] def get_creator_settings(self, project_settings, settings_key=None): diff --git a/openpype/hosts/nuke/plugins/create/create_write_image.py b/openpype/hosts/nuke/plugins/create/create_write_image.py index 1e23b3ad7f..d38253ab2f 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_image.py +++ b/openpype/hosts/nuke/plugins/create/create_write_image.py @@ -35,7 +35,7 @@ class CreateWriteImage(napi.NukeWriteCreator): attr_defs = [ BoolDef( "use_selection", - default=True, + default=not self.create_context.headless, label="Use selection" ), self._get_render_target_enum(), diff --git a/openpype/hosts/nuke/plugins/create/create_write_prerender.py b/openpype/hosts/nuke/plugins/create/create_write_prerender.py index a15f362dd1..d0d7f8edfd 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_prerender.py +++ b/openpype/hosts/nuke/plugins/create/create_write_prerender.py @@ -37,7 +37,7 @@ class CreateWritePrerender(napi.NukeWriteCreator): attr_defs = [ BoolDef( "use_selection", - default=True, + default=not self.create_context.headless, label="Use selection" ), self._get_render_target_enum() diff --git a/openpype/hosts/nuke/plugins/create/create_write_render.py b/openpype/hosts/nuke/plugins/create/create_write_render.py index 481d1d2201..4e0b42361d 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_render.py +++ b/openpype/hosts/nuke/plugins/create/create_write_render.py @@ -34,7 +34,7 @@ class CreateWriteRender(napi.NukeWriteCreator): attr_defs = [ BoolDef( "use_selection", - default=True, + default=not self.create_context.headless, label="Use selection" ), self._get_render_target_enum() diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 6ffe0116e5..6a99314f48 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -178,7 +178,9 @@ class AbstractTemplateBuilder(object): def create_context(self): if self._create_context is None: self._create_context = CreateContext( - self.host, discover_publish_plugins=False + self.host, + discover_publish_plugins=False, + headless=True ) return self._create_context @@ -1660,7 +1662,7 @@ class PlaceholderCreateMixin(object): task_name=task_name ) - except Exception: + except: failed = True self.create_failed(placeholder, creator_data) From 6885357c75246e4851aade1ae3261ea89f3a9658 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 28 Feb 2023 18:14:46 +0000 Subject: [PATCH 1115/1271] Fix broken review publishing. --- .../maya/plugins/publish/extract_playblast.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_playblast.py b/openpype/hosts/maya/plugins/publish/extract_playblast.py index 1966ad7b66..7b9e4214b9 100644 --- a/openpype/hosts/maya/plugins/publish/extract_playblast.py +++ b/openpype/hosts/maya/plugins/publish/extract_playblast.py @@ -118,7 +118,7 @@ class ExtractPlayblast(publish.Extractor): # Need to explicitly enable some viewport changes so the viewport is # refreshed ahead of playblasting. - panel = cmds.getPanel(withFocus=True) + panel = cmds.getPanel(withFocus=True) or "" keys = [ "useDefaultMaterial", "wireframeOnShaded", @@ -127,12 +127,13 @@ class ExtractPlayblast(publish.Extractor): "backfaceCulling" ] viewport_defaults = {} - for key in keys: - viewport_defaults[key] = cmds.modelEditor( - panel, query=True, **{key: True} - ) - if preset["viewport_options"][key]: - cmds.modelEditor(panel, edit=True, **{key: True}) + if panel and "modelPanel" in panel: + for key in keys: + viewport_defaults[key] = cmds.modelEditor( + panel, query=True, **{key: True} + ) + if preset["viewport_options"][key]: + cmds.modelEditor(panel, edit=True, **{key: True}) override_viewport_options = ( capture_presets['Viewport Options']['override_viewport_options'] @@ -163,7 +164,8 @@ class ExtractPlayblast(publish.Extractor): path = capture.capture(log=self.log, **preset) # Restoring viewport options. - cmds.modelEditor(panel, edit=True, **viewport_defaults) + if viewport_defaults: + cmds.modelEditor(panel, edit=True, **viewport_defaults) cmds.setAttr("{}.panZoomEnabled".format(preset["camera"]), pan_zoom) From b29b53af2024a4b849a4e96f3e77e24f5fba7be7 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 1 Mar 2023 03:32:10 +0000 Subject: [PATCH 1116/1271] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index bd1ba5309d..4d6f3d43e4 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.2-nightly.2" +__version__ = "3.15.2-nightly.3" From f4d22e6ac671245dab4c4f6831ea6f057ca088c6 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 24 Feb 2023 11:44:41 +0000 Subject: [PATCH 1117/1271] Options for VrayProxy output. --- .../maya/plugins/create/create_vrayproxy.py | 6 ++++ .../maya/plugins/publish/collect_vrayproxy.py | 8 ++++- .../plugins/publish/extract_pointcache.py | 2 +- .../maya/plugins/publish/extract_vrayproxy.py | 2 +- .../plugins/publish/validate_vrayproxy.py | 32 ++++++++++------- .../defaults/project_settings/maya.json | 16 +++++---- .../schemas/schema_maya_create.json | 34 ++++++++++++++++--- 7 files changed, 73 insertions(+), 27 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_vrayproxy.py b/openpype/hosts/maya/plugins/create/create_vrayproxy.py index 5c0365b495..d135073e82 100644 --- a/openpype/hosts/maya/plugins/create/create_vrayproxy.py +++ b/openpype/hosts/maya/plugins/create/create_vrayproxy.py @@ -9,6 +9,9 @@ class CreateVrayProxy(plugin.Creator): family = "vrayproxy" icon = "gears" + vrmesh = True + alembic = True + def __init__(self, *args, **kwargs): super(CreateVrayProxy, self).__init__(*args, **kwargs) @@ -18,3 +21,6 @@ class CreateVrayProxy(plugin.Creator): # Write vertex colors self.data["vertexColors"] = False + + self.data["vrmesh"] = self.vrmesh + self.data["alembic"] = self.alembic diff --git a/openpype/hosts/maya/plugins/publish/collect_vrayproxy.py b/openpype/hosts/maya/plugins/publish/collect_vrayproxy.py index 236797ca3c..24521a2f09 100644 --- a/openpype/hosts/maya/plugins/publish/collect_vrayproxy.py +++ b/openpype/hosts/maya/plugins/publish/collect_vrayproxy.py @@ -9,10 +9,16 @@ class CollectVrayProxy(pyblish.api.InstancePlugin): Add `pointcache` family for it. """ order = pyblish.api.CollectorOrder + 0.01 - label = 'Collect Vray Proxy' + label = "Collect Vray Proxy" families = ["vrayproxy"] def process(self, instance): """Collector entry point.""" if not instance.data.get('families'): instance.data["families"] = [] + + if instance.data.get("vrmesh"): + instance.data["families"].append("vrayproxy.vrmesh") + + if instance.data.get("alembic"): + instance.data["families"].append("vrayproxy.alembic") diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index a3b0560099..f44c13767c 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -23,7 +23,7 @@ class ExtractAlembic(publish.Extractor): label = "Extract Pointcache (Alembic)" hosts = ["maya"] - families = ["pointcache", "model", "vrayproxy"] + families = ["pointcache", "model", "vrayproxy.alembic"] targets = ["local", "remote"] def process(self, instance): diff --git a/openpype/hosts/maya/plugins/publish/extract_vrayproxy.py b/openpype/hosts/maya/plugins/publish/extract_vrayproxy.py index 38bf02245a..9b10d2737d 100644 --- a/openpype/hosts/maya/plugins/publish/extract_vrayproxy.py +++ b/openpype/hosts/maya/plugins/publish/extract_vrayproxy.py @@ -16,7 +16,7 @@ class ExtractVRayProxy(publish.Extractor): label = "VRay Proxy (.vrmesh)" hosts = ["maya"] - families = ["vrayproxy"] + families = ["vrayproxy.vrmesh"] def process(self, instance): diff --git a/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py b/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py index 3eceace76d..075eedc378 100644 --- a/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py +++ b/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py @@ -1,27 +1,33 @@ import pyblish.api +from openpype.pipeline import KnownPublishError + class ValidateVrayProxy(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder - label = 'VRay Proxy Settings' - hosts = ['maya'] - families = ['studio.vrayproxy'] + label = "VRay Proxy Settings" + hosts = ["maya"] + families = ["vrayproxy"] def process(self, instance): - - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError("'%s' has invalid settings for VRay Proxy " - "export!" % instance.name) - - @classmethod - def get_invalid(cls, instance): data = instance.data + self.log.info(data["family"]) + self.log.info(data["families"]) if not data["setMembers"]: - cls.log.error("'%s' is empty! This is a bug" % instance.name) + raise KnownPublishError( + "'%s' is empty! This is a bug" % instance.name + ) if data["animation"]: if data["frameEnd"] < data["frameStart"]: - cls.log.error("End frame is smaller than start frame") + raise KnownPublishError( + "End frame is smaller than start frame" + ) + + if not data["vrmesh"] and not data["alembic"]: + raise KnownPublishError( + "Both vrmesh and alembic are off. Needs at least one to" + " publish." + ) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 2559448900..90334a6644 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -199,6 +199,14 @@ "maskColor_manager": false, "maskOperator": false }, + "CreateVrayProxy": { + "enabled": true, + "vrmesh": true, + "alembic": true, + "defaults": [ + "Main" + ] + }, "CreateMultiverseUsd": { "enabled": true, "defaults": [ @@ -268,12 +276,6 @@ "Anim" ] }, - "CreateVrayProxy": { - "enabled": true, - "defaults": [ - "Main" - ] - }, "CreateVRayScene": { "enabled": true, "defaults": [ @@ -676,7 +678,7 @@ "families": [ "pointcache", "model", - "vrayproxy" + "vrayproxy.alembic" ] }, "ExtractObj": { 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 77a39f692f..49503cce83 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 @@ -332,6 +332,36 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "CreateVrayProxy", + "label": "Create VRay Proxy", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "vrmesh", + "label": "VrMesh" + }, + { + "type": "boolean", + "key": "alembic", + "label": "Alembic" + }, + { + "type": "list", + "key": "defaults", + "label": "Default Subsets", + "object_type": "text" + } + ] + }, { "type": "schema_template", "name": "template_create_plugin", @@ -380,10 +410,6 @@ "key": "CreateSetDress", "label": "Create Set Dress" }, - { - "key": "CreateVrayProxy", - "label": "Create VRay Proxy" - }, { "key": "CreateVRayScene", "label": "Create VRay Scene" From 65e0273f1c93dee1bd359d740d3206b4c4472548 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 28 Feb 2023 07:22:09 +0000 Subject: [PATCH 1118/1271] Update openpype/hosts/maya/plugins/publish/validate_vrayproxy.py --- openpype/hosts/maya/plugins/publish/validate_vrayproxy.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py b/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py index 075eedc378..a106b970b4 100644 --- a/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py +++ b/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py @@ -12,8 +12,6 @@ class ValidateVrayProxy(pyblish.api.InstancePlugin): def process(self, instance): data = instance.data - self.log.info(data["family"]) - self.log.info(data["families"]) if not data["setMembers"]: raise KnownPublishError( From ab22f503c9542d002d22f162b76489caa7fe90ca Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Mar 2023 08:06:05 +0000 Subject: [PATCH 1119/1271] Fix panel issues. --- .../maya/plugins/publish/collect_review.py | 3 +++ .../maya/plugins/publish/extract_playblast.py | 24 +++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_review.py b/openpype/hosts/maya/plugins/publish/collect_review.py index eb872c2935..65ff7cf0fe 100644 --- a/openpype/hosts/maya/plugins/publish/collect_review.py +++ b/openpype/hosts/maya/plugins/publish/collect_review.py @@ -23,6 +23,9 @@ class CollectReview(pyblish.api.InstancePlugin): task = legacy_io.Session["AVALON_TASK"] + # Get panel. + instance.data["panel"] = cmds.playblast(activeEditor=True) + # get cameras members = instance.data['setMembers'] cameras = cmds.ls(members, long=True, diff --git a/openpype/hosts/maya/plugins/publish/extract_playblast.py b/openpype/hosts/maya/plugins/publish/extract_playblast.py index 7b9e4214b9..94571ff731 100644 --- a/openpype/hosts/maya/plugins/publish/extract_playblast.py +++ b/openpype/hosts/maya/plugins/publish/extract_playblast.py @@ -118,7 +118,6 @@ class ExtractPlayblast(publish.Extractor): # Need to explicitly enable some viewport changes so the viewport is # refreshed ahead of playblasting. - panel = cmds.getPanel(withFocus=True) or "" keys = [ "useDefaultMaterial", "wireframeOnShaded", @@ -127,13 +126,14 @@ class ExtractPlayblast(publish.Extractor): "backfaceCulling" ] viewport_defaults = {} - if panel and "modelPanel" in panel: - for key in keys: - viewport_defaults[key] = cmds.modelEditor( - panel, query=True, **{key: True} + for key in keys: + viewport_defaults[key] = cmds.modelEditor( + instance.data["panel"], query=True, **{key: True} + ) + if preset["viewport_options"][key]: + cmds.modelEditor( + instance.data["panel"], edit=True, **{key: True} ) - if preset["viewport_options"][key]: - cmds.modelEditor(panel, edit=True, **{key: True}) override_viewport_options = ( capture_presets['Viewport Options']['override_viewport_options'] @@ -148,12 +148,10 @@ class ExtractPlayblast(publish.Extractor): # Update preset with current panel setting # if override_viewport_options is turned off - panel = cmds.getPanel(withFocus=True) or "" - if not override_viewport_options and "modelPanel" in panel: - panel_preset = capture.parse_active_view() + if not override_viewport_options: + panel_preset = capture.parse_view(instance.data["panel"]) panel_preset.pop("camera") preset.update(panel_preset) - cmds.setFocus(panel) self.log.info( "Using preset:\n{}".format( @@ -165,7 +163,9 @@ class ExtractPlayblast(publish.Extractor): # Restoring viewport options. if viewport_defaults: - cmds.modelEditor(panel, edit=True, **viewport_defaults) + cmds.modelEditor( + instance.data["panel"], edit=True, **viewport_defaults + ) cmds.setAttr("{}.panZoomEnabled".format(preset["camera"]), pan_zoom) From 4f94a4454ab254018c189c8aa53c21fb12b1392d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 1 Mar 2023 16:02:12 +0000 Subject: [PATCH 1120/1271] Only run Maya specific code in Maya. --- .../plugins/publish/submit_publish_job.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 5325715e38..29f6f406df 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -933,15 +933,18 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self.log.info(data.get("expectedFiles")) - additional_data = { - "renderProducts": instance.data["renderProducts"], - "colorspaceConfig": instance.data["colorspaceConfig"], - "display": instance.data["colorspaceDisplay"], - "view": instance.data["colorspaceView"], - "colorspaceTemplate": instance.data["colorspaceConfig"].replace( - str(context.data["anatomy"].roots["work"]), "{root[work]}" - ) - } + additional_data = {} + if pyblish.api.current_host() == "maya": + config = instance.data["colorspaceConfig"] + additional_data = { + "renderProducts": instance.data["renderProducts"], + "colorspaceConfig": instance.data["colorspaceConfig"], + "display": instance.data["colorspaceDisplay"], + "view": instance.data["colorspaceView"], + "colorspaceTemplate": config.replace( + str(context.data["anatomy"].roots["work"]), "{root[work]}" + ) + } if isinstance(data.get("expectedFiles")[0], dict): # we cannot attach AOVs to other subsets as we consider every From 491eb3e75010d2f5214385145d05120ce736f93f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 1 Mar 2023 17:27:16 +0100 Subject: [PATCH 1121/1271] create first workfile version function to global abstraction --- openpype/hosts/nuke/api/lib.py | 2 +- .../nuke/api/workfile_template_builder.py | 22 +---------- .../workfile/workfile_template_builder.py | 39 ++++++++++++++++--- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 2b7aaa9d70..cd31e42690 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -2685,7 +2685,7 @@ def start_workfile_template_builder(): # to avoid looping of the callback, remove it! # nuke.removeOnCreate(start_workfile_template_builder, nodeClass="Root") log.info("Starting workfile template builder...") - build_workfile_template() + build_workfile_template(run_from_callback=True) @deprecated diff --git a/openpype/hosts/nuke/api/workfile_template_builder.py b/openpype/hosts/nuke/api/workfile_template_builder.py index 1c0a41456a..80db0d160c 100644 --- a/openpype/hosts/nuke/api/workfile_template_builder.py +++ b/openpype/hosts/nuke/api/workfile_template_builder.py @@ -56,24 +56,6 @@ class NukeTemplateBuilder(AbstractTemplateBuilder): return True - def create_first_workfile_version(self): - """ - Create first version of workfile. - - Should load the content of template into scene so - 'populate_scene_placeholders' can be started. - - Args: - template_path (str): Fullpath for current task and - host's template file. - """ - last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE") - # Save current scene, continue to open file - if isinstance(self.host, IWorkfileHost): - self.host.save_workfile(last_workfile_path) - else: - self.host.save_file(last_workfile_path) - class NukePlaceholderPlugin(PlaceholderPlugin): node_color = 4278190335 @@ -966,9 +948,9 @@ class NukePlaceholderCreatePlugin( siblings_input.setInput(0, copy_output) -def build_workfile_template(*args): +def build_workfile_template(*args, **kwargs): builder = NukeTemplateBuilder(registered_host()) - builder.build_template() + builder.build_template(*args, **kwargs) def update_workfile_template(*args): diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 3dd02ea14d..d73168194e 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -442,7 +442,8 @@ class AbstractTemplateBuilder(object): template_path=None, level_limit=None, keep_placeholders=None, - create_first_version=None + create_first_version=None, + run_from_callback=False ): """Main callback for building workfile from template path. @@ -460,6 +461,9 @@ class AbstractTemplateBuilder(object): hosts to decide if they want to remove placeholder after it is used. create_first_version (bool): create first version of a workfile + run_from_callback (bool): If True, it might create first version + but ignore process if version is created + """ template_preset = self.get_template_preset() @@ -471,8 +475,14 @@ class AbstractTemplateBuilder(object): if create_first_version is None: create_first_version = template_preset["create_first_version"] - if create_first_version: - self.create_first_workfile_version() + # run creation of first version only if it is + # run from callback and no new version is created + first_creation = False + if create_first_version and run_from_callback: + first_creation = not self.create_first_workfile_version() + + if first_creation: + return self.import_template(template_path) self.populate_scene_placeholders( @@ -524,13 +534,32 @@ class AbstractTemplateBuilder(object): pass - @abstractmethod def create_first_workfile_version(self): """ Create first version of workfile. + Should load the content of template into scene so + 'populate_scene_placeholders' can be started. + + Args: + template_path (str): Fullpath for current task and + host's template file. """ - pass + last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE") + if os.path.exists(last_workfile_path): + # ignore in case workfile existence + self.log.info("Workfile already exists, skipping creation.") + return False + + # Save current scene, continue to open file + if isinstance(self.host, IWorkfileHost): + self.host.save_workfile(last_workfile_path) + else: + self.host.save_file(last_workfile_path) + + # Confirm creation of first version + return True + def _prepare_placeholders(self, placeholders): """Run preparation part for placeholders on plugins. From e1fa9f7c3133c359475dd15145b405406aada8f5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 1 Mar 2023 17:30:31 +0100 Subject: [PATCH 1122/1271] adding noqa for hound --- openpype/pipeline/workfile/workfile_template_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 6a99314f48..2d768d216f 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -1662,7 +1662,7 @@ class PlaceholderCreateMixin(object): task_name=task_name ) - except: + except: # noqa: E722 failed = True self.create_failed(placeholder, creator_data) From 2e83019efa4d2831e5299f33bb4052e263341949 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 1 Mar 2023 17:31:45 +0100 Subject: [PATCH 1123/1271] hound --- openpype/pipeline/workfile/workfile_template_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 2d768d216f..27214af79f 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -1662,7 +1662,7 @@ class PlaceholderCreateMixin(object): task_name=task_name ) - except: # noqa: E722 + except: # noqa: E722 failed = True self.create_failed(placeholder, creator_data) From a3508b14122d6ab884a4303d636bdf37b35ca973 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 2 Mar 2023 08:18:47 +0000 Subject: [PATCH 1124/1271] Fix _get_representations --- openpype/modules/deadline/plugins/publish/submit_publish_job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 29f6f406df..adfbcbded8 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -588,7 +588,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self.log.debug("instances:{}".format(instances)) return instances - def _get_representations(self, instance, exp_files, additional_data): + def _get_representations(self, instance, exp_files): """Create representations for file sequences. This will return representations of expected files if they are not From 5bb204cacbfd0f9769f2f4112e50f6e65b4a7f6e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 2 Mar 2023 11:30:42 +0100 Subject: [PATCH 1125/1271] nuke flip order --- openpype/hosts/nuke/plugins/publish/collect_writes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/collect_writes.py b/openpype/hosts/nuke/plugins/publish/collect_writes.py index 2b741426e6..f6acd24f99 100644 --- a/openpype/hosts/nuke/plugins/publish/collect_writes.py +++ b/openpype/hosts/nuke/plugins/publish/collect_writes.py @@ -67,6 +67,9 @@ class CollectNukeWrites(pyblish.api.InstancePlugin, write_file_path = nuke.filename(write_node) output_dir = os.path.dirname(write_file_path) + # get colorspace and add to version data + colorspace = napi.get_colorspace_from_node(write_node) + self.log.debug('output dir: {}'.format(output_dir)) if render_target == "frames": @@ -152,9 +155,6 @@ class CollectNukeWrites(pyblish.api.InstancePlugin, instance.data["farm"] = True self.log.info("Farm rendering ON ...") - # get colorspace and add to version data - colorspace = napi.get_colorspace_from_node(write_node) - # TODO: remove this when we have proper colorspace support version_data = { "colorspace": colorspace From f0997710818d3ca2f5ece87aed242ddf4c139a6c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 2 Mar 2023 11:36:17 +0100 Subject: [PATCH 1126/1271] hound --- openpype/hosts/nuke/plugins/publish/collect_writes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/nuke/plugins/publish/collect_writes.py b/openpype/hosts/nuke/plugins/publish/collect_writes.py index f6acd24f99..858fa79a4b 100644 --- a/openpype/hosts/nuke/plugins/publish/collect_writes.py +++ b/openpype/hosts/nuke/plugins/publish/collect_writes.py @@ -5,6 +5,7 @@ import pyblish.api from openpype.hosts.nuke import api as napi from openpype.pipeline import publish + class CollectNukeWrites(pyblish.api.InstancePlugin, publish.ColormanagedPyblishPluginMixin): """Collect all write nodes.""" From 91685e3d1fd3b43677fc33a537c3d93a5e8920cb Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 2 Mar 2023 11:06:47 +0000 Subject: [PATCH 1127/1271] Move AOV code to host agnostic. --- .../deadline/plugins/publish/submit_publish_job.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index adfbcbded8..31df4746ba 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -933,8 +933,10 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self.log.info(data.get("expectedFiles")) - additional_data = {} - if pyblish.api.current_host() == "maya": + if isinstance(data.get("expectedFiles")[0], dict): + # we cannot attach AOVs to other subsets as we consider every + # AOV subset of its own. + config = instance.data["colorspaceConfig"] additional_data = { "renderProducts": instance.data["renderProducts"], @@ -946,10 +948,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): ) } - if isinstance(data.get("expectedFiles")[0], dict): - # we cannot attach AOVs to other subsets as we consider every - # AOV subset of its own. - if len(data.get("attachTo")) > 0: assert len(data.get("expectedFiles")[0].keys()) == 1, ( "attaching multiple AOVs or renderable cameras to " From 59cea8c7d070bfe1b757d5f12be96c8f4d1e9a47 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Mar 2023 12:15:37 +0100 Subject: [PATCH 1128/1271] Catch for each instance whether the render succeeded or not --- .../fusion/plugins/publish/render_local.py | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index c22074d6c6..27bd312048 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -59,24 +59,27 @@ class Fusionlocal(pyblish.api.InstancePlugin): # to speed up the rendering. The check below makes sure that we only # execute the rendering once and not for each instance. key = f"__hasRun{self.__class__.__name__}" - if context.data.get(key, False): - return - context.data[key] = True + if key not in context.data: + # We initialize as false to indicate it wasn't successful yet + # so we can keep track of whether Fusion succeeded + context.data[key] = False - current_comp = context.data["currentComp"] - frame_start = context.data["frameStartHandle"] - frame_end = context.data["frameEndHandle"] + current_comp = context.data["currentComp"] + frame_start = context.data["frameStartHandle"] + frame_end = context.data["frameEndHandle"] + + self.log.info("Starting Fusion render") + self.log.info(f"Start frame: {frame_start}") + self.log.info(f"End frame: {frame_end}") + + with comp_lock_and_undo_chunk(current_comp): + result = current_comp.Render({ + "Start": frame_start, + "End": frame_end, + "Wait": True + }) + + context.data[key] = bool(result) - self.log.info("Starting render") - self.log.info(f"Start frame: {frame_start}") - self.log.info(f"End frame: {frame_end}") - - with comp_lock_and_undo_chunk(current_comp): - result = current_comp.Render({ - "Start": frame_start, - "End": frame_end, - "Wait": True - }) - - if not result: + if context.data[key] is False: raise RuntimeError("Comp render failed") From 185623ff702a3ddc58038a4368e69e5b3ce4cc94 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 24 Feb 2023 16:21:00 +0000 Subject: [PATCH 1129/1271] Set frame range with handles on review instance. --- openpype/hosts/maya/api/lib.py | 32 +++++++++++++------ .../maya/plugins/create/create_review.py | 6 ++-- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 4324d321dc..0d9733fcf7 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -4,7 +4,6 @@ import os import sys import platform import uuid -import math import re import json @@ -2064,13 +2063,8 @@ def set_scene_resolution(width, height, pixelAspect): cmds.setAttr("%s.pixelAspect" % control_node, pixelAspect) -def reset_frame_range(): - """Set frame range to current asset""" - - fps = convert_to_maya_fps( - float(legacy_io.Session.get("AVALON_FPS", 25)) - ) - set_scene_fps(fps) +def get_frame_range(): + """Get the current assets frame range and handles.""" # Set frame start/end project_name = legacy_io.active_project() @@ -2097,8 +2091,26 @@ def reset_frame_range(): if handle_end is None: handle_end = handles - frame_start -= int(handle_start) - frame_end += int(handle_end) + return { + "frameStart": frame_start, + "frameEnd": frame_end, + "handleStart": handle_start, + "handleEnd": handle_end + } + + +def reset_frame_range(): + """Set frame range to current asset""" + + fps = convert_to_maya_fps( + float(legacy_io.Session.get("AVALON_FPS", 25)) + ) + set_scene_fps(fps) + + frame_range = get_frame_range() + + frame_start = frame_range["frameStart"] - int(frame_range["handleStart"]) + frame_end = frame_range["frameEnd"] + int(frame_range["handleEnd"]) cmds.playbackOptions(minTime=frame_start) cmds.playbackOptions(maxTime=frame_end) diff --git a/openpype/hosts/maya/plugins/create/create_review.py b/openpype/hosts/maya/plugins/create/create_review.py index ba51ffa009..6e0bd2e4c3 100644 --- a/openpype/hosts/maya/plugins/create/create_review.py +++ b/openpype/hosts/maya/plugins/create/create_review.py @@ -28,13 +28,13 @@ class CreateReview(plugin.Creator): def __init__(self, *args, **kwargs): super(CreateReview, self).__init__(*args, **kwargs) + data = OrderedDict(**self.data) # get basic animation data : start / end / handles / steps - data = OrderedDict(**self.data) - animation_data = lib.collect_animation_data(fps=True) - for key, value in animation_data.items(): + for key, value in lib.get_frame_range().items(): data[key] = value + data["fps"] = lib.collect_animation_data(fps=True)["fps"] data["review_width"] = self.Width data["review_height"] = self.Height data["isolate"] = self.isolate From db0a3554b62afcfc03a8a33563e20a9a935bef22 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 24 Feb 2023 16:42:01 +0000 Subject: [PATCH 1130/1271] Validate frame range on instance to asset. - frame start - frame end - handle start - handle end --- .../plugins/publish/validate_frame_range.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_frame_range.py b/openpype/hosts/maya/plugins/publish/validate_frame_range.py index d86925184e..dbf856a30a 100644 --- a/openpype/hosts/maya/plugins/publish/validate_frame_range.py +++ b/openpype/hosts/maya/plugins/publish/validate_frame_range.py @@ -57,6 +57,10 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): inst_start = int(instance.data.get("frameStartHandle")) inst_end = int(instance.data.get("frameEndHandle")) + inst_frame_start = int(instance.data.get("frameStart")) + inst_frame_end = int(instance.data.get("frameEnd")) + inst_handle_start = int(instance.data.get("handleStart")) + inst_handle_end = int(instance.data.get("handleEnd")) # basic sanity checks assert frame_start_handle <= frame_end_handle, ( @@ -69,7 +73,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): if [ef for ef in self.exclude_families if instance.data["family"] in ef]: return - if(inst_start != frame_start_handle): + if (inst_start != frame_start_handle): errors.append("Instance start frame [ {} ] doesn't " "match the one set on instance [ {} ]: " "{}/{}/{}/{} (handle/start/end/handle)".format( @@ -78,7 +82,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): handle_start, frame_start, frame_end, handle_end )) - if(inst_end != frame_end_handle): + if (inst_end != frame_end_handle): errors.append("Instance end frame [ {} ] doesn't " "match the one set on instance [ {} ]: " "{}/{}/{}/{} (handle/start/end/handle)".format( @@ -87,6 +91,19 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): handle_start, frame_start, frame_end, handle_end )) + checks = { + "frame start": (frame_start, inst_frame_start), + "frame end": (frame_end, inst_frame_end), + "handle start": (handle_start, inst_handle_start), + "handle end": (handle_end, inst_handle_end) + } + for label, values in checks.items(): + if values[0] != values[1]: + errors.append( + "{} on instance ({}) does not match with the asset " + "({}).".format(label.title(), values[1], values[0]) + ) + for e in errors: self.log.error(e) From d33aa1cc7df5eb7ae73320693b3a9b00183b3d70 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 28 Feb 2023 07:26:50 +0000 Subject: [PATCH 1131/1271] Better error reports. --- openpype/hosts/maya/plugins/publish/validate_frame_range.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_frame_range.py b/openpype/hosts/maya/plugins/publish/validate_frame_range.py index dbf856a30a..59b06874b3 100644 --- a/openpype/hosts/maya/plugins/publish/validate_frame_range.py +++ b/openpype/hosts/maya/plugins/publish/validate_frame_range.py @@ -75,7 +75,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): return if (inst_start != frame_start_handle): errors.append("Instance start frame [ {} ] doesn't " - "match the one set on instance [ {} ]: " + "match the one set on asset [ {} ]: " "{}/{}/{}/{} (handle/start/end/handle)".format( inst_start, frame_start_handle, @@ -84,7 +84,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): if (inst_end != frame_end_handle): errors.append("Instance end frame [ {} ] doesn't " - "match the one set on instance [ {} ]: " + "match the one set on asset [ {} ]: " "{}/{}/{}/{} (handle/start/end/handle)".format( inst_end, frame_end_handle, From 7bbf5608711c865f3f50c82f1fbd34ddc679982a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 28 Feb 2023 07:42:58 +0000 Subject: [PATCH 1132/1271] Backwards compatibility --- .../maya/plugins/create/create_review.py | 8 +++-- .../defaults/project_settings/maya.json | 13 +++++---- .../schemas/schema_maya_create.json | 29 ++++++++++++++++--- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_review.py b/openpype/hosts/maya/plugins/create/create_review.py index 6e0bd2e4c3..f1b626c06b 100644 --- a/openpype/hosts/maya/plugins/create/create_review.py +++ b/openpype/hosts/maya/plugins/create/create_review.py @@ -25,13 +25,17 @@ class CreateReview(plugin.Creator): "depth peeling", "alpha cut" ] + useMayaTimeline = True def __init__(self, *args, **kwargs): super(CreateReview, self).__init__(*args, **kwargs) data = OrderedDict(**self.data) - # get basic animation data : start / end / handles / steps - for key, value in lib.get_frame_range().items(): + # Option for using Maya or asset frame range in settings. + frame_range = lib.get_frame_range() + if self.useMayaTimeline: + frame_range = lib.collect_animation_data(fps=True) + for key, value in frame_range.items(): data[key] = value data["fps"] = lib.collect_animation_data(fps=True)["fps"] diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 90334a6644..dca0b95293 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -179,6 +179,13 @@ "Main" ] }, + "CreateReview": { + "enabled": true, + "defaults": [ + "Main" + ], + "useMayaTimeline": true + }, "CreateAss": { "enabled": true, "defaults": [ @@ -255,12 +262,6 @@ "Main" ] }, - "CreateReview": { - "enabled": true, - "defaults": [ - "Main" - ] - }, "CreateRig": { "enabled": true, "defaults": [ 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 49503cce83..1598f90643 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 @@ -240,6 +240,31 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "CreateReview", + "label": "Create Review", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "defaults", + "label": "Default Subsets", + "object_type": "text" + }, + { + "type": "boolean", + "key": "useMayaTimeline", + "label": "Use Maya Timeline for Frame Range." + } + ] + }, { "type": "dict", "collapsible": true, @@ -398,10 +423,6 @@ "key": "CreateRenderSetup", "label": "Create Render Setup" }, - { - "key": "CreateReview", - "label": "Create Review" - }, { "key": "CreateRig", "label": "Create Rig" From db98f65b43517c1930ab3be11f56f0fa672e5c8d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 Feb 2023 16:23:21 +0000 Subject: [PATCH 1133/1271] Fix publish pool and secondary. --- .../modules/deadline/plugins/publish/submit_publish_job.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 31df4746ba..53c09ad22f 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -284,6 +284,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): args.append("--automatic-tests") # Generate the payload for Deadline submission + secondary_pool = ( + self.deadline_pool_secondary or instance.data.get("secondaryPool") + ) payload = { "JobInfo": { "Plugin": self.deadline_plugin, @@ -297,8 +300,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "Priority": priority, "Group": self.deadline_group, - "Pool": instance.data.get("primaryPool"), - "SecondaryPool": instance.data.get("secondaryPool"), + "Pool": self.deadline_pool or instance.data.get("primaryPool"), + "SecondaryPool": secondary_pool, # ensure the outputdirectory with correct slashes "OutputDirectory0": output_dir.replace("\\", "/") }, From d827ffa8fbabd6fb02dc03e123e22d69c5b87c24 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 Feb 2023 16:23:38 +0000 Subject: [PATCH 1134/1271] Use publish pool for tile jobs. --- .../deadline/plugins/publish/submit_maya_deadline.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 22b5c02296..15025e47f2 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -419,8 +419,13 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): assembly_job_info.Name += " - Tile Assembly Job" assembly_job_info.Frames = 1 assembly_job_info.MachineLimit = 1 - assembly_job_info.Priority = instance.data.get("tile_priority", - self.tile_priority) + assembly_job_info.Priority = instance.data.get( + "tile_priority", self.tile_priority + ) + + pool = instance.context.data["project_settings"]["deadline"] + pool = pool["publish"]["ProcessSubmittedJobOnFarm"]["deadline_pool"] + assembly_job_info.Pool = pool or instance.data.get("primaryPool", "") assembly_plugin_info = { "CleanupTiles": 1, From 9117a0d6329c5cf47aee68bf4fd2e57dac5fff6a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 Feb 2023 16:39:10 +0000 Subject: [PATCH 1135/1271] Documentation --- website/docs/module_deadline.md | 34 +++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/website/docs/module_deadline.md b/website/docs/module_deadline.md index c96da91909..c4b179b98d 100644 --- a/website/docs/module_deadline.md +++ b/website/docs/module_deadline.md @@ -28,16 +28,16 @@ For [AWS Thinkbox Deadline](https://www.awsthinkbox.com/deadline) support you ne OpenPype integration for Deadline consists of two parts: - The `OpenPype` Deadline Plug-in -- A `GlobalJobPreLoad` Deadline Script (this gets triggered for each deadline job) +- A `GlobalJobPreLoad` Deadline Script (this gets triggered for each deadline job) The `GlobalJobPreLoad` handles populating render and publish jobs with proper environment variables using settings from the `OpenPype` Deadline Plug-in. -The `OpenPype` Deadline Plug-in must be configured to point to a valid OpenPype executable location. The executable need to be installed to +The `OpenPype` Deadline Plug-in must be configured to point to a valid OpenPype executable location. The executable need to be installed to destinations accessible by DL process. Check permissions (must be executable and accessible by Deadline process) - Enable `Tools > Super User Mode` in Deadline Monitor -- Go to `Tools > Configure Plugins...`, find `OpenPype` in the list on the left side, find location of OpenPype +- Go to `Tools > Configure Plugins...`, find `OpenPype` in the list on the left side, find location of OpenPype executable. It is recommended to use the `openpype_console` executable as it provides a bit more logging. - In case of multi OS farms, provide multiple locations, each Deadline Worker goes through the list and tries to find the first accessible @@ -45,12 +45,22 @@ executable. It is recommended to use the `openpype_console` executable as it pro ![Configure plugin](assets/deadline_configure_plugin.png) +### Pools + +The main pools can be configured at `project_settings/deadline/publish/CollectDeadlinePools/primary_pool`, which is applied to the rendering jobs. + +The dependent publishing job's pool uses `project_settings/deadline/publish/ProcessSubmittedJobOnFarm/deadline_pool`. If nothing is specified the pool will fallback to the main pool above. + +:::note maya tile rendering +The logic for publishing job pool assignment applies to tiling jobs. +::: + ## Troubleshooting #### Publishing jobs fail directly in DCCs - Double check that all previously described steps were finished -- Check that `deadlinewebservice` is running on DL server +- Check that `deadlinewebservice` is running on DL server - Check that user's machine has access to deadline server on configured port #### Jobs are failing on DL side @@ -61,40 +71,40 @@ Each publishing from OpenPype consists of 2 jobs, first one is rendering, second - Jobs are failing with `OpenPype executable was not found` error - Check if OpenPype is installed on the Worker handling this job and ensure `OpenPype` Deadline Plug-in is properly [configured](#configuration) + Check if OpenPype is installed on the Worker handling this job and ensure `OpenPype` Deadline Plug-in is properly [configured](#configuration) - Publishing job is failing with `ffmpeg not installed` error - + OpenPype executable has to have access to `ffmpeg` executable, check OpenPype `Setting > General` ![FFmpeg setting](assets/ffmpeg_path.png) - Both jobs finished successfully, but there is no review on Ftrack - Make sure that you correctly set published family to be send to Ftrack. + Make sure that you correctly set published family to be send to Ftrack. ![Ftrack Family](assets/ftrack/ftrack-collect-main.png) Example: I want send to Ftrack review of rendered images from Harmony : - `Host names`: "harmony" - - `Families`: "render" + - `Families`: "render" - `Add Ftrack Family` to "Enabled" - + Make sure that you actually configured to create review for published subset in `project_settings/ftrack/publish/CollectFtrackFamily` ![Ftrack Family](assets/deadline_review.png) - Example: I want to create review for all reviewable subsets in Harmony : + Example: I want to create review for all reviewable subsets in Harmony : - Add "harmony" as a new key an ".*" as a value. - Rendering jobs are stuck in 'Queued' state or failing Make sure that your Deadline is not limiting specific jobs to be run only on specific machines. (Eg. only some machines have installed particular application.) - + Check `project_settings/deadline` - + ![Deadline group](assets/deadline_group.png) Example: I have separated machines with "Harmony" installed into "harmony" group on Deadline. I want rendering jobs published from Harmony to run only on those machines. From 6f5ba9ce2a98aed1777df57731ae02409cc474a2 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 23 Feb 2023 16:42:02 +0000 Subject: [PATCH 1136/1271] Docs --- website/docs/module_deadline.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/module_deadline.md b/website/docs/module_deadline.md index c4b179b98d..ab1016788d 100644 --- a/website/docs/module_deadline.md +++ b/website/docs/module_deadline.md @@ -49,7 +49,7 @@ executable. It is recommended to use the `openpype_console` executable as it pro The main pools can be configured at `project_settings/deadline/publish/CollectDeadlinePools/primary_pool`, which is applied to the rendering jobs. -The dependent publishing job's pool uses `project_settings/deadline/publish/ProcessSubmittedJobOnFarm/deadline_pool`. If nothing is specified the pool will fallback to the main pool above. +The dependent publishing job's pool uses `project_settings/deadline/publish/ProcessSubmittedJobOnFarm/deadline_pool`. If nothing is specified the pool will fallback to the primary pool above. :::note maya tile rendering The logic for publishing job pool assignment applies to tiling jobs. From b61f1ed1df5a5a6dcab162ffe320a4093b5efd12 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 28 Feb 2023 08:17:29 +0000 Subject: [PATCH 1137/1271] Validate against zero polygon mesh. --- .../plugins/publish/validate_mesh_empty.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 openpype/hosts/maya/plugins/publish/validate_mesh_empty.py diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_empty.py b/openpype/hosts/maya/plugins/publish/validate_mesh_empty.py new file mode 100644 index 0000000000..848d66c4ae --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_empty.py @@ -0,0 +1,54 @@ +from maya import cmds + +import pyblish.api +import openpype.hosts.maya.api.action +from openpype.pipeline.publish import ( + RepairAction, + ValidateMeshOrder +) + + +class ValidateMeshEmpty(pyblish.api.InstancePlugin): + """Validate meshes have some vertices. + + Its possible to have meshes without any vertices. To replicate + this issue, delete all faces/polygons then all edges. + """ + + order = ValidateMeshOrder + hosts = ["maya"] + families = ["model"] + label = "Mesh Empty" + actions = [ + openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction + ] + + @classmethod + def repair(cls, instance): + invalid = cls.get_invalid(instance) + for node in invalid: + cmds.delete(node) + + @classmethod + def get_invalid(cls, instance): + invalid = [] + + meshes = cmds.ls(instance, type="mesh", long=True) + for mesh in meshes: + num_vertices = cmds.polyEvaluate(mesh, vertex=True) + + if num_vertices == 0: + cls.log.warning( + "\"{}\" does not have any vertices.".format(mesh) + ) + invalid.append(mesh) + + return invalid + + def process(self, instance): + + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError( + "Meshes found in instance without any vertices: %s" % invalid + ) From 206bf55a4909e603e845d0ca5b3fc46896f1ca90 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 28 Feb 2023 08:18:16 +0000 Subject: [PATCH 1138/1271] Refactor `len_flattened` --- openpype/hosts/maya/api/lib.py | 31 +++++++++++++++++ .../plugins/publish/validate_mesh_has_uv.py | 32 +---------------- .../validate_mesh_vertices_have_edges.py | 34 +------------------ 3 files changed, 33 insertions(+), 64 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 0d9733fcf7..954576f02e 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3574,3 +3574,34 @@ def get_color_management_output_transform(): if preferences["output_transform_enabled"]: colorspace = preferences["output_transform"] return colorspace + + +def len_flattened(components): + """Return the length of the list as if it was flattened. + + Maya will return consecutive components as a single entry + when requesting with `maya.cmds.ls` without the `flatten` + flag. Though enabling `flatten` on a large list (e.g. millions) + will result in a slow result. This command will return the amount + of entries in a non-flattened list by parsing the result with + regex. + + Args: + components (list): The non-flattened components. + + Returns: + int: The amount of entries. + + """ + assert isinstance(components, (list, tuple)) + n = 0 + + pattern = re.compile(r"\[(\d+):(\d+)\]") + for c in components: + match = pattern.search(c) + if match: + start, end = match.groups() + n += int(end) - int(start) + 1 + else: + n += 1 + return n diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py b/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py index 0eece1014e..1775bd84c6 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py @@ -1,39 +1,9 @@ -import re - from maya import cmds import pyblish.api import openpype.hosts.maya.api.action from openpype.pipeline.publish import ValidateMeshOrder - - -def len_flattened(components): - """Return the length of the list as if it was flattened. - - Maya will return consecutive components as a single entry - when requesting with `maya.cmds.ls` without the `flatten` - flag. Though enabling `flatten` on a large list (e.g. millions) - will result in a slow result. This command will return the amount - of entries in a non-flattened list by parsing the result with - regex. - - Args: - components (list): The non-flattened components. - - Returns: - int: The amount of entries. - - """ - assert isinstance(components, (list, tuple)) - n = 0 - for c in components: - match = re.search("\[([0-9]+):([0-9]+)\]", c) - if match: - start, end = match.groups() - n += int(end) - int(start) + 1 - else: - n += 1 - return n +from openpype.hosts.maya.api.lib import len_flattened class ValidateMeshHasUVs(pyblish.api.InstancePlugin): diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py b/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py index 9ac7735501..51e1ddfc7f 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py @@ -1,5 +1,3 @@ -import re - from maya import cmds import pyblish.api @@ -8,37 +6,7 @@ from openpype.pipeline.publish import ( RepairAction, ValidateMeshOrder, ) - - -def len_flattened(components): - """Return the length of the list as if it was flattened. - - Maya will return consecutive components as a single entry - when requesting with `maya.cmds.ls` without the `flatten` - flag. Though enabling `flatten` on a large list (e.g. millions) - will result in a slow result. This command will return the amount - of entries in a non-flattened list by parsing the result with - regex. - - Args: - components (list): The non-flattened components. - - Returns: - int: The amount of entries. - - """ - assert isinstance(components, (list, tuple)) - n = 0 - - pattern = re.compile(r"\[(\d+):(\d+)\]") - for c in components: - match = pattern.search(c) - if match: - start, end = match.groups() - n += int(end) - int(start) + 1 - else: - n += 1 - return n +from openpype.hosts.maya.api.lib import len_flattened class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin): From 63177ca7e935cd87e716229831eaf1f456825e66 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 28 Feb 2023 08:18:50 +0000 Subject: [PATCH 1139/1271] Only mesh empty validator should fail. --- .../plugins/publish/validate_mesh_has_uv.py | 9 +++++++++ .../publish/validate_mesh_non_zero_edge.py | 20 +++++++++++++++++-- .../validate_mesh_vertices_have_edges.py | 7 +++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py b/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py index 1775bd84c6..b7836b3e92 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_has_uv.py @@ -27,6 +27,15 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin): invalid = [] for node in cmds.ls(instance, type='mesh'): + num_vertices = cmds.polyEvaluate(node, vertex=True) + + if num_vertices == 0: + cls.log.warning( + "Skipping \"{}\", cause it does not have any " + "vertices.".format(node) + ) + continue + uv = cmds.polyEvaluate(node, uv=True) if uv == 0: diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py b/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py index 78e844d201..b49ba85648 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_non_zero_edge.py @@ -28,7 +28,10 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin): @classmethod def get_invalid(cls, instance): """Return the invalid edges. - Also see: http://help.autodesk.com/view/MAYAUL/2015/ENU/?guid=Mesh__Cleanup + + Also see: + + http://help.autodesk.com/view/MAYAUL/2015/ENU/?guid=Mesh__Cleanup """ @@ -36,8 +39,21 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin): if not meshes: return list() + valid_meshes = [] + for mesh in meshes: + num_vertices = cmds.polyEvaluate(mesh, vertex=True) + + if num_vertices == 0: + cls.log.warning( + "Skipping \"{}\", cause it does not have any " + "vertices.".format(mesh) + ) + continue + + valid_meshes.append(mesh) + # Get all edges - edges = ['{0}.e[*]'.format(node) for node in meshes] + edges = ['{0}.e[*]'.format(node) for node in valid_meshes] # Filter by constraint on edge length invalid = lib.polyConstraint(edges, diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py b/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py index 51e1ddfc7f..d885158004 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py @@ -55,6 +55,13 @@ class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin): for mesh in meshes: num_vertices = cmds.polyEvaluate(mesh, vertex=True) + if num_vertices == 0: + cls.log.warning( + "Skipping \"{}\", cause it does not have any " + "vertices.".format(mesh) + ) + continue + # Vertices from all edges edges = "%s.e[*]" % mesh vertices = cmds.polyListComponentConversion(edges, toVertex=True) From ec7392433cc0386feaccca6c0c3adea47281f67c Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 2 Mar 2023 17:24:41 +0000 Subject: [PATCH 1140/1271] Maya: Validate missing instance attributes (#4559) * Validate missing instance attributes. * Plugins docs. --- .../maya/plugins/publish/collect_instances.py | 1 + .../publish/validate_instance_attributes.py | 60 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 openpype/hosts/maya/plugins/publish/validate_instance_attributes.py diff --git a/openpype/hosts/maya/plugins/publish/collect_instances.py b/openpype/hosts/maya/plugins/publish/collect_instances.py index 6c6819f0a2..c594626569 100644 --- a/openpype/hosts/maya/plugins/publish/collect_instances.py +++ b/openpype/hosts/maya/plugins/publish/collect_instances.py @@ -137,6 +137,7 @@ class CollectInstances(pyblish.api.ContextPlugin): # Create the instance instance = context.create_instance(objset) instance[:] = members_hierarchy + instance.data["objset"] = objset # Store the exact members of the object set instance.data["setMembers"] = members diff --git a/openpype/hosts/maya/plugins/publish/validate_instance_attributes.py b/openpype/hosts/maya/plugins/publish/validate_instance_attributes.py new file mode 100644 index 0000000000..f870c9f8c4 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_instance_attributes.py @@ -0,0 +1,60 @@ +from maya import cmds + +import pyblish.api +from openpype.pipeline.publish import ( + ValidateContentsOrder, PublishValidationError, RepairAction +) +from openpype.pipeline import discover_legacy_creator_plugins +from openpype.hosts.maya.api.lib import imprint + + +class ValidateInstanceAttributes(pyblish.api.InstancePlugin): + """Validate Instance Attributes. + + New attributes can be introduced as new features come in. Old instances + will need to be updated with these attributes for the documentation to make + sense, and users do not have to recreate the instances. + """ + + order = ValidateContentsOrder + hosts = ["maya"] + families = ["*"] + label = "Instance Attributes" + plugins_by_family = { + p.family: p for p in discover_legacy_creator_plugins() + } + actions = [RepairAction] + + @classmethod + def get_missing_attributes(self, instance): + plugin = self.plugins_by_family[instance.data["family"]] + subset = instance.data["subset"] + asset = instance.data["asset"] + objset = instance.data["objset"] + + missing_attributes = {} + for key, value in plugin(subset, asset).data.items(): + if not cmds.objExists("{}.{}".format(objset, key)): + missing_attributes[key] = value + + return missing_attributes + + def process(self, instance): + objset = instance.data.get("objset") + if objset is None: + self.log.debug( + "Skipping {} because no objectset found.".format(instance) + ) + return + + missing_attributes = self.get_missing_attributes(instance) + if missing_attributes: + raise PublishValidationError( + "Missing attributes on {}:\n{}".format( + objset, missing_attributes + ) + ) + + @classmethod + def repair(cls, instance): + imprint(instance.data["objset"], cls.get_missing_attributes(instance)) From 173ec225eba1262de024e30917673d4650feb1ee Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 3 Mar 2023 10:32:05 +0100 Subject: [PATCH 1141/1271] Set image format --- openpype/hosts/maya/api/lib_rendersettings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index f19deb0351..7d44d4eaa6 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -336,7 +336,8 @@ class RenderSettings(object): ) # Set render file format to exr - cmds.setAttr("{}.imageFormatStr".format(node), "exr", type="string") + ext = vray_render_presets["image_format"] + cmds.setAttr("{}.imageFormatStr".format(node), ext, type="string") # animType cmds.setAttr("{}.animType".format(node), 1) From 8fcf6de8c802d78effd5e5e696aa36707f84b473 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 3 Mar 2023 12:58:00 +0100 Subject: [PATCH 1142/1271] Fixed hound's comments --- .../fusion/plugins/publish/render_local.py | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 27bd312048..3eb4cbd868 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -17,7 +17,6 @@ class Fusionlocal(pyblish.api.InstancePlugin): families = ["render.local"] def process(self, instance): - context = instance.context # Start render @@ -35,10 +34,10 @@ class Fusionlocal(pyblish.api.InstancePlugin): for frame in range(frame_start, frame_end + 1) ] repre = { - 'name': ext[1:], - 'ext': ext[1:], - 'frameStart': f"%0{len(str(frame_end))}d" % frame_start, - 'files': files, + "name": ext[1:], + "ext": ext[1:], + "frameStart": f"%0{len(str(frame_end))}d" % frame_start, + "files": files, "stagingDir": output_dir, } @@ -67,18 +66,20 @@ class Fusionlocal(pyblish.api.InstancePlugin): current_comp = context.data["currentComp"] frame_start = context.data["frameStartHandle"] frame_end = context.data["frameEndHandle"] - + self.log.info("Starting Fusion render") self.log.info(f"Start frame: {frame_start}") self.log.info(f"End frame: {frame_end}") - + with comp_lock_and_undo_chunk(current_comp): - result = current_comp.Render({ - "Start": frame_start, - "End": frame_end, - "Wait": True - }) - + result = current_comp.Render( + { + "Start": frame_start, + "End": frame_end, + "Wait": True, + } + ) + context.data[key] = bool(result) if context.data[key] is False: From f3baace6682bebfbbbae273b54c4aaf38477f4a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Fri, 3 Mar 2023 13:17:46 +0100 Subject: [PATCH 1143/1271] Update openpype/pipeline/publish/publish_plugins.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fabià Serra Arrizabalaga --- 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 0142919e76..7da61fec5e 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -298,7 +298,7 @@ class ColormanagedPyblishPluginMixin(object): It also contains a method, set_representation_colorspace, which sets colorspace data to the representation. The allowed file extensions are listed in the allowed_ext variable. - he method first checks if the file extension is in + The method first checks if the file extension is in the list of allowed extensions. If it is, it then gets the colorspace settings from the host context and gets a matching colorspace from rules. Finally, it infuses this From 0c517a12a618076fdd3fc043fb1ce80d7e1f3327 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 3 Mar 2023 14:07:20 +0100 Subject: [PATCH 1144/1271] Nuke: fix the order of plugin to be after anatomy data collector also convert anatomy data with deepcopy --- openpype/hosts/nuke/plugins/publish/collect_writes.py | 2 +- openpype/pipeline/publish/publish_plugins.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/collect_writes.py b/openpype/hosts/nuke/plugins/publish/collect_writes.py index 858fa79a4b..304b3d8f32 100644 --- a/openpype/hosts/nuke/plugins/publish/collect_writes.py +++ b/openpype/hosts/nuke/plugins/publish/collect_writes.py @@ -10,7 +10,7 @@ class CollectNukeWrites(pyblish.api.InstancePlugin, publish.ColormanagedPyblishPluginMixin): """Collect all write nodes.""" - order = pyblish.api.CollectorOrder - 0.48 + order = pyblish.api.CollectorOrder + 0.0021 label = "Collect Writes" hosts = ["nuke", "nukeassist"] families = ["render", "prerender", "image"] diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index 7da61fec5e..2df98221ba 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -1,3 +1,4 @@ +from copy import deepcopy import inspect from abc import ABCMeta from pprint import pformat @@ -323,7 +324,7 @@ class ColormanagedPyblishPluginMixin(object): project_name = context.data["projectName"] host_name = context.data["hostName"] - anatomy_data = context.data["anatomyData"] + anatomy_data = deepcopy(context.data["anatomyData"]) project_settings_ = context.data["project_settings"] config_data = get_imageio_config( From efac55ba8961d75e3fa1fb9d2f2860790f1e58e8 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 3 Mar 2023 14:08:00 +0100 Subject: [PATCH 1145/1271] Added render log per instance --- openpype/hosts/fusion/plugins/publish/render_local.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 3eb4cbd868..86c283952c 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -22,6 +22,15 @@ class Fusionlocal(pyblish.api.InstancePlugin): # Start render self.render_once(context) + # Log render status + self.log.info( + "Rendered '{nm}' for asset '{ast}' under the task '{tsk}'".format( + nm=instance.data.name, + ast=instance.data.asset, + tsk=instance.data.task, + ) + ) + frame_start = context.data["frameStartHandle"] frame_end = context.data["frameEndHandle"] path = instance.data["path"] From a63872b54b4e4aaf42e4875a2b437642b345eb27 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 3 Mar 2023 16:07:29 +0100 Subject: [PATCH 1146/1271] Fixed dict data access --- openpype/hosts/fusion/plugins/publish/render_local.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 86c283952c..0eca7f6cdd 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -25,9 +25,9 @@ class Fusionlocal(pyblish.api.InstancePlugin): # Log render status self.log.info( "Rendered '{nm}' for asset '{ast}' under the task '{tsk}'".format( - nm=instance.data.name, - ast=instance.data.asset, - tsk=instance.data.task, + nm=instance.data["name"], + ast=instance.data["asset"], + tsk=instance.data["task"], ) ) From f2311c686638dd1e9d646333b5dd482129024869 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 28 Feb 2023 09:12:51 +0000 Subject: [PATCH 1147/1271] Fixes - missing platform extraction from settings - map function should be list comprehension - code cosmetics --- .../maya/plugins/publish/validate_model_name.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_model_name.py b/openpype/hosts/maya/plugins/publish/validate_model_name.py index 2dec9ba267..0e7adc640f 100644 --- a/openpype/hosts/maya/plugins/publish/validate_model_name.py +++ b/openpype/hosts/maya/plugins/publish/validate_model_name.py @@ -2,9 +2,11 @@ """Validate model nodes names.""" import os import re -from maya import cmds -import pyblish.api +import platform +from maya import cmds + +import pyblish.api from openpype.pipeline import legacy_io from openpype.pipeline.publish import ValidateContentsOrder import openpype.hosts.maya.api.action @@ -44,7 +46,7 @@ class ValidateModelName(pyblish.api.InstancePlugin): if not cmds.ls(child, transforms=True): return False return True - except: + except Exception: return False invalid = [] @@ -94,9 +96,10 @@ class ValidateModelName(pyblish.api.InstancePlugin): # load shader list file as utf-8 shaders = [] if not use_db: - if cls.material_file: - if os.path.isfile(cls.material_file): - shader_file = open(cls.material_file, "r") + material_file = cls.material_file[platform.system().lower()] + if material_file: + if os.path.isfile(material_file): + shader_file = open(material_file, "r") shaders = shader_file.readlines() shader_file.close() else: @@ -113,7 +116,7 @@ class ValidateModelName(pyblish.api.InstancePlugin): shader_file.close() # strip line endings from list - shaders = map(lambda s: s.rstrip(), shaders) + shaders = [s.rstrip() for s in shaders if s.rstrip()] # compile regex for testing names regex = cls.regex From 6f0cd7fc2f04f8ff13e7a756f932ab7068c29629 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Tue, 31 Jan 2023 17:30:27 +0100 Subject: [PATCH 1148/1271] add visualParent None to Shots and Assets Without this, tray-publisher throws an error and stops working. --- openpype/modules/kitsu/utils/update_op_with_zou.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 2d14b38bc4..5af3a61e81 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -389,6 +389,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): "data": { "root_of": r, "tasks": {}, + "visualParent": None, }, } for r in ["Assets", "Shots"] From 76273b7376c5b65423beb3a195c4a6376f0453be Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Wed, 1 Feb 2023 13:38:22 +0100 Subject: [PATCH 1149/1271] lowercase URL path to match in Kitsu Without this fix the menu-dropdown wouldn't show the correct current page in Kitsu --- openpype/modules/kitsu/actions/launcher_show_in_kitsu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py b/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py index c95079e042..4793d60fc3 100644 --- a/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py +++ b/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py @@ -100,7 +100,7 @@ class ShowInKitsu(LauncherAction): kitsu_url = kitsu_url[:-len("/api")] sub_url = f"/productions/{project_id}" - asset_type_url = "Shots" if asset_type in shots_url else "Assets" + asset_type_url = "shots" if asset_type in shots_url else "assets" if task_id: # Go to task page From 9795e7c9105777f0ae0c4d20376d5204b23b1b00 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Wed, 1 Feb 2023 15:52:56 +0100 Subject: [PATCH 1150/1271] Populate items with correct data from Kitsu or project defaults If data doesn't exist in Kitsu, use Projects default data. --- .../modules/kitsu/utils/update_op_with_zou.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 5af3a61e81..554d90f7a9 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -69,6 +69,7 @@ def set_op_project(dbcon: AvalonMongoDB, project_id: str): def update_op_assets( dbcon: AvalonMongoDB, + gazu_project: dict, project_doc: dict, entities_list: List[dict], asset_doc_ids: Dict[str, dict], @@ -119,21 +120,20 @@ def update_op_assets( # because of zou's legacy design frames_duration = int(item.get("nb_frames", 0)) except (TypeError, ValueError): - frames_duration = 0 + frames_duration = None # Frame out, fallback on frame_in + duration or project's value or 1001 frame_out = item_data.pop("frame_out", None) if not frame_out: - frame_out = frame_in + frames_duration - try: - frame_out = int(frame_out) - except (TypeError, ValueError): - frame_out = 1001 + if frames_duration: + frame_out = frame_in + frames_duration + else: + frame_out = project_doc["data"].get("frameEnd", 1001) item_data["frameEnd"] = frame_out # Fps, fallback to project's value or default value (25.0) try: - fps = float(item_data.get("fps", project_doc["data"].get("fps"))) + fps = float(item_data.get("fps")) except (TypeError, ValueError): - fps = 25.0 + fps = float(gazu_project.get("fps", project_doc["data"].get("fps", 25))) item_data["fps"] = fps # Tasks @@ -424,7 +424,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): [ UpdateOne({"_id": id}, update) for id, update in update_op_assets( - dbcon, project_doc, all_entities, zou_ids_and_asset_docs + dbcon, project, project_doc, all_entities, zou_ids_and_asset_docs ) ] ) From a005d108292821e9a062da8f3cef32f113ad8eca Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Wed, 1 Feb 2023 16:07:45 +0100 Subject: [PATCH 1151/1271] Add Resolution and Pixel Aspect to each item Without this info eg. Fusion would throw an error and stop working. --- openpype/modules/kitsu/utils/update_op_with_zou.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 554d90f7a9..cbf7afb413 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -135,6 +135,17 @@ def update_op_assets( except (TypeError, ValueError): fps = float(gazu_project.get("fps", project_doc["data"].get("fps", 25))) item_data["fps"] = fps + # Resolution, fall back to project default + match_res = re.match(r"(\d+)x(\d+)", item_data.get("resolution", gazu_project.get("resolution"))) + if match_res: + item_data["resolutionWidth"] = int(match_res.group(1)) + item_data["resolutionHeight"] = int(match_res.group(2)) + else: + item_data["resolutionWidth"] = project_doc["data"].get("resolutionWidth") + item_data["resolutionHeight"] = project_doc["data"].get("resolutionHeight") + # Properties that doesn't fully exist in Kitsu. Guessing the property name + # Pixel Aspect Ratio + item_data["pixelAspect"] = item_data.get("pixel_aspect", project_doc["data"].get("pixelAspect")) # Tasks tasks_list = [] From 3b4cdb13db5c3ae89b5c3e3bd6a1b0e73731fe3d Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Wed, 1 Feb 2023 20:13:00 +0100 Subject: [PATCH 1152/1271] Add parents key to assets and shots Without this you can't open the project in Tray Publisher --- openpype/modules/kitsu/utils/update_op_with_zou.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index cbf7afb413..1b191ccb1e 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -401,6 +401,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): "root_of": r, "tasks": {}, "visualParent": None, + "parents": [], }, } for r in ["Assets", "Shots"] From 5dee2b8ff6b8eedbaaf68af164b316874f413059 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Feb 2023 17:15:55 +0100 Subject: [PATCH 1153/1271] Add missing attributes to each asset Without them, Tray Publisher errors out. --- openpype/modules/kitsu/utils/update_op_with_zou.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 1b191ccb1e..d88198eace 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -146,6 +146,14 @@ def update_op_assets( # Properties that doesn't fully exist in Kitsu. Guessing the property name # Pixel Aspect Ratio item_data["pixelAspect"] = item_data.get("pixel_aspect", project_doc["data"].get("pixelAspect")) + # Handle Start + item_data["handleStart"] = item_data.get("handle_start", project_doc["data"].get("handleStart")) + # Handle End + item_data["handleEnd"] = item_data.get("handle_end", project_doc["data"].get("handleEnd")) + # Clip In + item_data["clipIn"] = item_data.get("clip_in", project_doc["data"].get("clipIn")) + # Clip Out + item_data["clipOut"] = item_data.get("clip_out", project_doc["data"].get("clipOut")) # Tasks tasks_list = [] From 785825751a0e5dacdc6e7bf2825e8360456d906c Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Feb 2023 17:18:16 +0100 Subject: [PATCH 1154/1271] Add render to families to make sure a task exists to process --- openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py | 2 +- .../modules/kitsu/plugins/publish/integrate_kitsu_review.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py index ea98e0b7cc..b801e0e4d4 100644 --- a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py +++ b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py @@ -8,7 +8,7 @@ class IntegrateKitsuNote(pyblish.api.ContextPlugin): order = pyblish.api.IntegratorOrder label = "Kitsu Note and Status" - # families = ["kitsu"] + families = ["render", "kitsu"] set_status_note = False note_status_shortname = "wfa" diff --git a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py index e5e6439439..9e9eaadc27 100644 --- a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py +++ b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py @@ -8,7 +8,7 @@ class IntegrateKitsuReview(pyblish.api.InstancePlugin): order = pyblish.api.IntegratorOrder + 0.01 label = "Kitsu Review" - # families = ["kitsu"] + families = ["render", "kitsu"] optional = True def process(self, instance): From 27c8a1f36099347f777c538d72c67eb5dcb9dc80 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Feb 2023 19:31:06 +0100 Subject: [PATCH 1155/1271] Fixed so correct review file gets uploaded to Kitsu --- .../modules/kitsu/plugins/publish/integrate_kitsu_review.py | 2 +- .../projects_schema/schemas/schema_representation_tags.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py index 9e9eaadc27..94897b2553 100644 --- a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py +++ b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py @@ -27,7 +27,7 @@ class IntegrateKitsuReview(pyblish.api.InstancePlugin): # Add review representations as preview of comment for representation in instance.data.get("representations", []): # Skip if not tagged as review - if "review" not in representation.get("tags", []): + if "kitsureview" not in representation.get("tags", []): continue review_path = representation.get("published_path") diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_representation_tags.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_representation_tags.json index a4b28f47bc..7046952eef 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_representation_tags.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_representation_tags.json @@ -16,6 +16,9 @@ { "shotgridreview": "Add review to Shotgrid" }, + { + "kitsureview": "Add review to Kitsu" + }, { "delete": "Delete output" }, From 72270005edc887e8e708a8d078f8a3d7ac1ec287 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Feb 2023 23:33:57 +0100 Subject: [PATCH 1156/1271] Update Kitsu plugin to work without assetEntry --- .../plugins/publish/collect_kitsu_entities.py | 83 +++++++++++-------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py index c9e78b59eb..38c67898ef 100644 --- a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py +++ b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py @@ -4,6 +4,11 @@ import os import gazu import pyblish.api +from openpype.client import ( + get_projects, + get_project, + get_assets, +) class CollectKitsuEntities(pyblish.api.ContextPlugin): """Collect Kitsu entities according to the current context""" @@ -12,20 +17,34 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): label = "Kitsu entities" def process(self, context): + + # Get all needed names + project_name = context.data.get("projectName") + asset_name = context.data.get("asset") + task_name = context.data.get("task") + # If asset and task name doesn't exist in context, look in instance + for instance in context: + if not asset_name: + asset_name = instance.data.get("asset") + if not task_name: + task_name = instance.data.get("task") - asset_data = context.data["assetEntity"]["data"] - zou_asset_data = asset_data.get("zou") + # Get all assets of the local project + asset_docs = { + asset_doc["name"]: asset_doc + for asset_doc in get_assets(project_name) + } + + # Get asset object + asset = asset_docs.get(asset_name) + if not asset: + raise AssertionError("{} not found in DB".format(asset_name)) + + zou_asset_data = asset["data"].get("zou") if not zou_asset_data: raise AssertionError("Zou asset data not found in OpenPype!") self.log.debug("Collected zou asset data: {}".format(zou_asset_data)) - zou_task_data = asset_data["tasks"][os.environ["AVALON_TASK"]].get( - "zou" - ) - if not zou_task_data: - self.log.warning("Zou task data not found in OpenPype!") - self.log.debug("Collected zou task data: {}".format(zou_task_data)) - kitsu_project = gazu.project.get_project(zou_asset_data["project_id"]) if not kitsu_project: raise AssertionError("Project not found in kitsu!") @@ -37,37 +56,33 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): kitsu_entity = gazu.shot.get_shot(zou_asset_data["id"]) else: kitsu_entity = gazu.asset.get_asset(zou_asset_data["id"]) - if not kitsu_entity: raise AssertionError("{} not found in kitsu!".format(entity_type)) - context.data["kitsu_entity"] = kitsu_entity self.log.debug( "Collect kitsu {}: {}".format(entity_type, kitsu_entity) ) - if zou_task_data: - kitsu_task = gazu.task.get_task(zou_task_data["id"]) - if not kitsu_task: - raise AssertionError("Task not found in kitsu!") - context.data["kitsu_task"] = kitsu_task - self.log.debug("Collect kitsu task: {}".format(kitsu_task)) - - else: - kitsu_task_type = gazu.task.get_task_type_by_name( - os.environ["AVALON_TASK"] - ) - if not kitsu_task_type: - raise AssertionError( - "Task type {} not found in Kitsu!".format( - os.environ["AVALON_TASK"] + if task_name: + zou_task_data = asset["data"]["tasks"][task_name].get("zou") + self.log.debug("Collected zou task data: {}".format(zou_task_data)) + if zou_task_data: + kitsu_task = gazu.task.get_task(zou_task_data["id"]) + if not kitsu_task: + raise AssertionError("Task not found in kitsu!") + context.data["kitsu_task"] = kitsu_task + self.log.debug("Collect kitsu task: {}".format(kitsu_task)) + else: + kitsu_task_type = gazu.task.get_task_type_by_name(task_name) + if not kitsu_task_type: + raise AssertionError( + "Task type {} not found in Kitsu!".format(task_name) ) - ) - kitsu_task = gazu.task.get_task_by_name( - kitsu_entity, kitsu_task_type - ) - if not kitsu_task: - raise AssertionError("Task not found in kitsu!") - context.data["kitsu_task"] = kitsu_task - self.log.debug("Collect kitsu task: {}".format(kitsu_task)) + kitsu_task = gazu.task.get_task_by_name( + kitsu_entity, kitsu_task_type + ) + if not kitsu_task: + raise AssertionError("Task not found in kitsu!") + context.data["kitsu_task"] = kitsu_task + self.log.debug("Collect kitsu task: {}".format(kitsu_task)) \ No newline at end of file From 62c111e9486e532255d213d0508f00281f40e86c Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 3 Feb 2023 00:55:01 +0100 Subject: [PATCH 1157/1271] Add kitsureview to default burnin tags --- openpype/settings/defaults/project_settings/global.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index a5e2d25a88..aad17d54da 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -139,7 +139,8 @@ "ext": "mp4", "tags": [ "burnin", - "ftrackreview" + "ftrackreview", + "kitsureview" ], "burnins": [], "ffmpeg_args": { From dc2b519fd5689389a482c4772289af8ba9154176 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 3 Feb 2023 01:07:48 +0100 Subject: [PATCH 1158/1271] Fixed hound-bots comments --- .../plugins/publish/collect_kitsu_entities.py | 25 ++++++++--------- .../modules/kitsu/utils/update_op_with_zou.py | 27 ++++++++++++------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py index 38c67898ef..92c8c1823d 100644 --- a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py +++ b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py @@ -5,11 +5,10 @@ import gazu import pyblish.api from openpype.client import ( - get_projects, - get_project, get_assets, ) + class CollectKitsuEntities(pyblish.api.ContextPlugin): """Collect Kitsu entities according to the current context""" @@ -17,7 +16,7 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): label = "Kitsu entities" def process(self, context): - + # Get all needed names project_name = context.data.get("projectName") asset_name = context.data.get("asset") @@ -38,18 +37,18 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): # Get asset object asset = asset_docs.get(asset_name) if not asset: - raise AssertionError("{} not found in DB".format(asset_name)) + raise AssertionError(f"{asset_name} not found in DB") zou_asset_data = asset["data"].get("zou") if not zou_asset_data: raise AssertionError("Zou asset data not found in OpenPype!") - self.log.debug("Collected zou asset data: {}".format(zou_asset_data)) + self.log.debug(f"Collected zou asset data: {zou_asset_data}") kitsu_project = gazu.project.get_project(zou_asset_data["project_id"]) if not kitsu_project: raise AssertionError("Project not found in kitsu!") context.data["kitsu_project"] = kitsu_project - self.log.debug("Collect kitsu project: {}".format(kitsu_project)) + self.log.debug(f"Collect kitsu project: {kitsu_project}") entity_type = zou_asset_data["type"] if entity_type == "Shot": @@ -57,26 +56,24 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): else: kitsu_entity = gazu.asset.get_asset(zou_asset_data["id"]) if not kitsu_entity: - raise AssertionError("{} not found in kitsu!".format(entity_type)) + raise AssertionError(f"{entity_type} not found in kitsu!") context.data["kitsu_entity"] = kitsu_entity - self.log.debug( - "Collect kitsu {}: {}".format(entity_type, kitsu_entity) - ) + self.log.debug(f"Collect kitsu {entity_type}: {kitsu_entity}") if task_name: zou_task_data = asset["data"]["tasks"][task_name].get("zou") - self.log.debug("Collected zou task data: {}".format(zou_task_data)) + self.log.debug(f"Collected zou task data: {zou_task_data}") if zou_task_data: kitsu_task = gazu.task.get_task(zou_task_data["id"]) if not kitsu_task: raise AssertionError("Task not found in kitsu!") context.data["kitsu_task"] = kitsu_task - self.log.debug("Collect kitsu task: {}".format(kitsu_task)) + self.log.debug(f"Collect kitsu task: {kitsu_task}") else: kitsu_task_type = gazu.task.get_task_type_by_name(task_name) if not kitsu_task_type: raise AssertionError( - "Task type {} not found in Kitsu!".format(task_name) + f"Task type {task_name} not found in Kitsu!" ) kitsu_task = gazu.task.get_task_by_name( @@ -85,4 +82,4 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): if not kitsu_task: raise AssertionError("Task not found in kitsu!") context.data["kitsu_task"] = kitsu_task - self.log.debug("Collect kitsu task: {}".format(kitsu_task)) \ No newline at end of file + self.log.debug(f"Collect kitsu task: {kitsu_task}") diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index d88198eace..a079fd5529 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -133,27 +133,36 @@ def update_op_assets( try: fps = float(item_data.get("fps")) except (TypeError, ValueError): - fps = float(gazu_project.get("fps", project_doc["data"].get("fps", 25))) + fps = float(gazu_project.get( + "fps", project_doc["data"].get("fps", 25))) item_data["fps"] = fps # Resolution, fall back to project default - match_res = re.match(r"(\d+)x(\d+)", item_data.get("resolution", gazu_project.get("resolution"))) + match_res = re.match( + r"(\d+)x(\d+)", item_data.get("resolution", gazu_project.get("resolution"))) if match_res: item_data["resolutionWidth"] = int(match_res.group(1)) item_data["resolutionHeight"] = int(match_res.group(2)) else: - item_data["resolutionWidth"] = project_doc["data"].get("resolutionWidth") - item_data["resolutionHeight"] = project_doc["data"].get("resolutionHeight") + item_data["resolutionWidth"] = project_doc["data"].get( + "resolutionWidth") + item_data["resolutionHeight"] = project_doc["data"].get( + "resolutionHeight") # Properties that doesn't fully exist in Kitsu. Guessing the property name # Pixel Aspect Ratio - item_data["pixelAspect"] = item_data.get("pixel_aspect", project_doc["data"].get("pixelAspect")) + item_data["pixelAspect"] = item_data.get( + "pixel_aspect", project_doc["data"].get("pixelAspect")) # Handle Start - item_data["handleStart"] = item_data.get("handle_start", project_doc["data"].get("handleStart")) + item_data["handleStart"] = item_data.get( + "handle_start", project_doc["data"].get("handleStart")) # Handle End - item_data["handleEnd"] = item_data.get("handle_end", project_doc["data"].get("handleEnd")) + item_data["handleEnd"] = item_data.get( + "handle_end", project_doc["data"].get("handleEnd")) # Clip In - item_data["clipIn"] = item_data.get("clip_in", project_doc["data"].get("clipIn")) + item_data["clipIn"] = item_data.get( + "clip_in", project_doc["data"].get("clipIn")) # Clip Out - item_data["clipOut"] = item_data.get("clip_out", project_doc["data"].get("clipOut")) + item_data["clipOut"] = item_data.get( + "clip_out", project_doc["data"].get("clipOut")) # Tasks tasks_list = [] From bb61d43c27286bda8b319d5e49da8166c1472ae8 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 3 Feb 2023 11:52:37 +0100 Subject: [PATCH 1159/1271] Shortened length of line 141 --- openpype/modules/kitsu/utils/update_op_with_zou.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index a079fd5529..15e88947a1 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -138,7 +138,9 @@ def update_op_assets( item_data["fps"] = fps # Resolution, fall back to project default match_res = re.match( - r"(\d+)x(\d+)", item_data.get("resolution", gazu_project.get("resolution"))) + r"(\d+)x(\d+)", + item_data.get("resolution", gazu_project.get("resolution")) + ) if match_res: item_data["resolutionWidth"] = int(match_res.group(1)) item_data["resolutionHeight"] = int(match_res.group(2)) From 47caa760305dcb46639d94ae7c345d8682e724fc Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 10 Feb 2023 13:32:03 +0100 Subject: [PATCH 1160/1271] Updated Kitsu Sync module to fully work with all events Manually tried all events, added logging for events and cleaned up the code some. --- openpype/modules/kitsu/utils/sync_service.py | 318 +++++++++++++----- .../modules/kitsu/utils/update_op_with_zou.py | 17 +- 2 files changed, 248 insertions(+), 87 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 237746bea0..e371c1a9bb 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -1,3 +1,15 @@ +""" +Bugs: + * Error when adding task type to anything that isn't Shot or Assets + * Assets don't get added under an episode if TV show + * Assets added under Main Pack throws error. Can't get the name of Main Pack? + +Features ToDo: + * Select in settings what types you wish to sync + * Print what's updated on entity-update + * Add listener for Edits +""" + import os import threading @@ -5,6 +17,7 @@ import gazu from openpype.client import get_project, get_assets, get_asset_by_name from openpype.pipeline import AvalonMongoDB +from openpype.lib import Logger from .credentials import validate_credentials from .update_op_with_zou import ( create_op_asset, @@ -14,6 +27,8 @@ from .update_op_with_zou import ( update_op_assets, ) +log = Logger.get_logger(__name__) + class Listener: """Host Kitsu listener.""" @@ -33,7 +48,7 @@ class Listener: self.dbcon = AvalonMongoDB() self.dbcon.install() - gazu.client.set_host(os.environ["KITSU_SERVER"]) + gazu.client.set_host(os.environ['KITSU_SERVER']) # Authenticate if not validate_credentials(login, password): @@ -42,7 +57,7 @@ class Listener: ) gazu.set_event_host( - os.environ["KITSU_SERVER"].replace("api", "socket.io") + os.environ['KITSU_SERVER'].replace("api", "socket.io") ) self.event_client = gazu.events.init() @@ -103,6 +118,8 @@ class Listener: ) def start(self): + """Start listening for events.""" + log.info("Listening to Kitsu events...") gazu.events.run_client(self.event_client) # == Project == @@ -112,36 +129,49 @@ class Listener: # Use update process to avoid duplicating code self._update_project(data) + # Print message + ## Happens in write_project_to_op() + def _update_project(self, data): """Update project into OP DB.""" # Get project entity - project = gazu.project.get_project(data["project_id"]) - project_name = project["name"] + project = gazu.project.get_project(data['project_id']) update_project = write_project_to_op(project, self.dbcon) # Write into DB if update_project: - self.dbcon.Session["AVALON_PROJECT"] = project_name + self.dbcon.Session['AVALON_PROJECT'] = get_kitsu_project_name( + data['project_id']) self.dbcon.bulk_write([update_project]) def _delete_project(self, data): """Delete project.""" - project_name = get_kitsu_project_name(data["project_id"]) + collections = self.dbcon.database.list_collection_names() + project_name = None + for collection in collections: + post = self.dbcon.database[collection].find_one( + {"data.zou_id": data['project_id']}) + if post: + project_name = post['name'] + break - # Delete project collection - self.dbcon.database[project_name].drop() + if project_name: + # Delete project collection + self.dbcon.database[project_name].drop() + + # Print message + log.info(f"Project deleted: {project_name}") # == Asset == - def _new_asset(self, data): """Create new asset into OP DB.""" # Get project entity - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) - # Get gazu entity - asset = gazu.asset.get_asset(data["asset_id"]) + # Get asset entity + asset = gazu.asset.get_asset(data['asset_id']) # Insert doc in DB self.dbcon.insert_one(create_op_asset(asset)) @@ -149,27 +179,43 @@ class Listener: # Update self._update_asset(data) + # Print message + episode = None + ep_id = asset['episode_id'] + if ep_id and ep_id != "": + episode = gazu.asset.get_episode(ep_id) + + msg = "Asset created: " + msg = msg + f"{asset['project_name']} - " + if episode is not None: + msg = msg + f"{episode['name']}_" + msg = msg + f"{asset['asset_type_name']}_" + msg = msg + f"{asset['name']}" + log.info(msg) + def _update_asset(self, data): """Update asset into OP DB.""" - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) project_name = self.dbcon.active_project() project_doc = get_project(project_name) # Get gazu entity - asset = gazu.asset.get_asset(data["asset_id"]) + asset = gazu.asset.get_asset(data['asset_id']) # Find asset doc # Query all assets of the local project zou_ids_and_asset_docs = { - asset_doc["data"]["zou"]["id"]: asset_doc + asset_doc['data']['zou']['id']: asset_doc for asset_doc in get_assets(project_name) - if asset_doc["data"].get("zou", {}).get("id") + if asset_doc['data'].get("zou", {}).get("id") } - zou_ids_and_asset_docs[asset["project_id"]] = project_doc + zou_ids_and_asset_docs[asset['project_id']] = project_doc + gazu_project = gazu.project.get_project(asset['project_id']) # Update update_op_result = update_op_assets( - self.dbcon, project_doc, [asset], zou_ids_and_asset_docs + self.dbcon, gazu_project, project_doc, + [asset], zou_ids_and_asset_docs ) if update_op_result: asset_doc_id, asset_update = update_op_result[0] @@ -177,21 +223,37 @@ class Listener: def _delete_asset(self, data): """Delete asset of OP DB.""" - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) - # Delete - self.dbcon.delete_one( - {"type": "asset", "data.zou.id": data["asset_id"]} - ) + asset = self.dbcon.find_one({"data.zou.id": data['asset_id']}) + if asset: + # Delete + self.dbcon.delete_one( + {"type": "asset", "data.zou.id": data['asset_id']} + ) + + # Print message + episode = None + ep_id = asset['data']['zou']['episode_id'] + if ep_id and ep_id != "": + episode = gazu.asset.get_episode(ep_id) + + msg = "Asset deleted: " + msg = msg + f"{asset['data']['zou']['project_name']} - " + if episode is not None: + msg = msg + f"{episode['name']}_" + msg = msg + f"{asset['data']['zou']['asset_type_name']}_" + msg = msg + f"'{asset['name']}" + log.info(msg) # == Episode == def _new_episode(self, data): """Create new episode into OP DB.""" # Get project entity - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) # Get gazu entity - episode = gazu.shot.get_episode(data["episode_id"]) + episode = gazu.shot.get_episode(data['episode_id']) # Insert doc in DB self.dbcon.insert_one(create_op_asset(episode)) @@ -199,27 +261,34 @@ class Listener: # Update self._update_episode(data) + # Print message + msg = "Episode created: " + msg = msg + f"{episode['project_name']} - " + msg = msg + f"{episode['name']}" + def _update_episode(self, data): """Update episode into OP DB.""" - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) project_name = self.dbcon.active_project() project_doc = get_project(project_name) # Get gazu entity - episode = gazu.shot.get_episode(data["episode_id"]) + episode = gazu.shot.get_episode(data['episode_id']) # Find asset doc # Query all assets of the local project zou_ids_and_asset_docs = { - asset_doc["data"]["zou"]["id"]: asset_doc + asset_doc['data']['zou']['id']: asset_doc for asset_doc in get_assets(project_name) - if asset_doc["data"].get("zou", {}).get("id") + if asset_doc['data'].get("zou", {}).get("id") } - zou_ids_and_asset_docs[episode["project_id"]] = project_doc + zou_ids_and_asset_docs[episode['project_id']] = project_doc + gazu_project = gazu.project.get_project(episode['project_id']) # Update update_op_result = update_op_assets( - self.dbcon, project_doc, [episode], zou_ids_and_asset_docs + self.dbcon, gazu_project, project_doc, [ + episode], zou_ids_and_asset_docs ) if update_op_result: asset_doc_id, asset_update = update_op_result[0] @@ -227,22 +296,31 @@ class Listener: def _delete_episode(self, data): """Delete shot of OP DB.""" - set_op_project(self.dbcon, data["project_id"]) - print("delete episode") # TODO check bugfix + set_op_project(self.dbcon, data['project_id']) - # Delete - self.dbcon.delete_one( - {"type": "asset", "data.zou.id": data["episode_id"]} - ) + episode = self.dbcon.find_one({"data.zou.id": data['episode_id']}) + if episode: + # Delete + self.dbcon.delete_one( + {"type": "asset", "data.zou.id": data['episode_id']} + ) + + # Print message + project = gazu.project.get_project( + episode['data']['zou']['project_id']) + + msg = "Episode deleted: " + msg = msg + f"{project['name']} - " + msg = msg + f"{episode['name']}" # == Sequence == def _new_sequence(self, data): """Create new sequnce into OP DB.""" # Get project entity - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) # Get gazu entity - sequence = gazu.shot.get_sequence(data["sequence_id"]) + sequence = gazu.shot.get_sequence(data['sequence_id']) # Insert doc in DB self.dbcon.insert_one(create_op_asset(sequence)) @@ -250,27 +328,43 @@ class Listener: # Update self._update_sequence(data) + # Print message + + episode = None + ep_id = sequence['episode_id'] + if ep_id and ep_id != "": + episode = gazu.asset.get_episode(ep_id) + + msg = "Sequence created: " + msg = msg + f"{sequence['project_name']} - " + if episode is not None: + msg = msg + f"{episode['name']}_" + msg = msg + f"{sequence['name']}" + log.info(msg) + def _update_sequence(self, data): """Update sequence into OP DB.""" - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) project_name = self.dbcon.active_project() project_doc = get_project(project_name) # Get gazu entity - sequence = gazu.shot.get_sequence(data["sequence_id"]) + sequence = gazu.shot.get_sequence(data['sequence_id']) # Find asset doc # Query all assets of the local project zou_ids_and_asset_docs = { - asset_doc["data"]["zou"]["id"]: asset_doc + asset_doc['data']['zou']['id']: asset_doc for asset_doc in get_assets(project_name) - if asset_doc["data"].get("zou", {}).get("id") + if asset_doc['data'].get("zou", {}).get("id") } - zou_ids_and_asset_docs[sequence["project_id"]] = project_doc + zou_ids_and_asset_docs[sequence['project_id']] = project_doc + gazu_project = gazu.project.get_project(sequence['project_id']) # Update update_op_result = update_op_assets( - self.dbcon, project_doc, [sequence], zou_ids_and_asset_docs + self.dbcon, gazu_project, project_doc, + [sequence], zou_ids_and_asset_docs ) if update_op_result: asset_doc_id, asset_update = update_op_result[0] @@ -278,22 +372,30 @@ class Listener: def _delete_sequence(self, data): """Delete sequence of OP DB.""" - set_op_project(self.dbcon, data["project_id"]) - print("delete sequence") # TODO check bugfix + set_op_project(self.dbcon, data['project_id']) + sequence = self.dbcon.find_one({"data.zou.id": data['sequence_id']}) + if sequence: + # Delete + self.dbcon.delete_one( + {"type": "asset", "data.zou.id": data['sequence_id']} + ) - # Delete - self.dbcon.delete_one( - {"type": "asset", "data.zou.id": data["sequence_id"]} - ) + # Print message + gazu_project = gazu.project.get_project( + sequence['data']['zou']['project_id']) + msg = f"Sequence deleted: " + msg = msg + f"{gazu_project['name']} - " + msg = msg + f"{sequence['name']}" + log.info(msg) # == Shot == def _new_shot(self, data): """Create new shot into OP DB.""" # Get project entity - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) # Get gazu entity - shot = gazu.shot.get_shot(data["shot_id"]) + shot = gazu.shot.get_shot(data['shot_id']) # Insert doc in DB self.dbcon.insert_one(create_op_asset(shot)) @@ -301,89 +403,151 @@ class Listener: # Update self._update_shot(data) + # Print message + episode = None + if shot['episode_id'] and shot['episode_id'] != "": + episode = gazu.asset.get_episode(shot['episode_id']) + + msg = "Shot created: " + msg = msg + f"{shot['project_name']} - " + if episode is not None: + msg = msg + f"{episode['name']}_" + msg = msg + f"{shot['sequence_name']}_" + msg = msg + f"{shot['name']}" + log.info(msg) + def _update_shot(self, data): """Update shot into OP DB.""" - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) project_name = self.dbcon.active_project() project_doc = get_project(project_name) # Get gazu entity - shot = gazu.shot.get_shot(data["shot_id"]) + shot = gazu.shot.get_shot(data['shot_id']) # Find asset doc # Query all assets of the local project zou_ids_and_asset_docs = { - asset_doc["data"]["zou"]["id"]: asset_doc + asset_doc['data']['zou']['id']: asset_doc for asset_doc in get_assets(project_name) - if asset_doc["data"].get("zou", {}).get("id") + if asset_doc['data'].get("zou", {}).get("id") } - zou_ids_and_asset_docs[shot["project_id"]] = project_doc + zou_ids_and_asset_docs[shot['project_id']] = project_doc + gazu_project = gazu.project.get_project(shot['project_id']) # Update update_op_result = update_op_assets( - self.dbcon, project_doc, [shot], zou_ids_and_asset_docs + self.dbcon, gazu_project, project_doc, + [shot], zou_ids_and_asset_docs ) + if update_op_result: asset_doc_id, asset_update = update_op_result[0] self.dbcon.update_one({"_id": asset_doc_id}, asset_update) def _delete_shot(self, data): """Delete shot of OP DB.""" - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) + shot = self.dbcon.find_one({"data.zou.id": data['shot_id']}) - # Delete - self.dbcon.delete_one( - {"type": "asset", "data.zou.id": data["shot_id"]} - ) + if shot: + # Delete + self.dbcon.delete_one( + {"type": "asset", "data.zou.id": data['shot_id']} + ) + + # Print message + gazu_project = gazu.project.get_project( + shot['data']['zou']['project_id']) + + msg = "Shot deleted: " + msg = msg + f"{gazu_project['name']} - " + msg = msg + f"{shot['name']}" + log.info(msg) # == Task == def _new_task(self, data): """Create new task into OP DB.""" # Get project entity - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) project_name = self.dbcon.active_project() # Get gazu entity - task = gazu.task.get_task(data["task_id"]) + task = gazu.task.get_task(data['task_id']) # Find asset doc - parent_name = task["entity"]["name"] + episode = None + ep_id = task['episode_id'] + if ep_id and ep_id != "": + episode = gazu.asset.get_episode(ep_id) + + parent_name = "" + if episode is not None: + parent_name = episode['name'] + "_" + parent_name = parent_name + \ + task['sequence']['name'] + "_" + task['entity']['name'] asset_doc = get_asset_by_name(project_name, parent_name) # Update asset tasks with new one - asset_tasks = asset_doc["data"].get("tasks") - task_type_name = task["task_type"]["name"] + asset_tasks = asset_doc['data'].get("tasks") + task_type_name = task['task_type']['name'] asset_tasks[task_type_name] = {"type": task_type_name, "zou": task} self.dbcon.update_one( - {"_id": asset_doc["_id"]}, {"$set": {"data.tasks": asset_tasks}} + {"_id": asset_doc['_id']}, {"$set": {"data.tasks": asset_tasks}} ) + # Print message + msg = "Task created: " + msg = msg + f"{task['project']['name']} - " + if episode is not None: + msg = msg + f"{episode['name']}_" + msg = msg + f"{task['sequence']['name']}_" + msg = msg + f"{task['entity']['name']} - " + msg = msg + f"{task['task_type']['name']}" + log.info(msg) + def _update_task(self, data): """Update task into OP DB.""" # TODO is it necessary? - pass def _delete_task(self, data): """Delete task of OP DB.""" - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data['project_id']) project_name = self.dbcon.active_project() # Find asset doc asset_docs = list(get_assets(project_name)) for doc in asset_docs: # Match task - for name, task in doc["data"]["tasks"].items(): - if task.get("zou") and data["task_id"] == task["zou"]["id"]: + for name, task in doc['data']['tasks'].items(): + if task.get("zou") and data['task_id'] == task['zou']['id']: # Pop task - asset_tasks = doc["data"].get("tasks", {}) + asset_tasks = doc['data'].get("tasks", {}) asset_tasks.pop(name) # Delete task in DB self.dbcon.update_one( - {"_id": doc["_id"]}, + {"_id": doc['_id']}, {"$set": {"data.tasks": asset_tasks}}, ) + + # Print message + shot = gazu.shot.get_shot(task['zou']['entity_id']) + + episode = None + ep_id = shot['episode_id'] + if ep_id and ep_id != "": + episode = gazu.asset.get_episode(ep_id) + + msg = "Task deleted: " + msg = msg + f"{shot['project_name']} - " + if episode is not None: + msg = msg + f"{episode['name']}_" + msg = msg + f"{shot['sequence_name']}_" + msg = msg + f"{shot['name']} - " + msg = msg + f"{task['type']}" + log.info(msg) return @@ -396,7 +560,7 @@ def start_listeners(login: str, password: str): """ # Refresh token every week def refresh_token_every_week(): - print("Refreshing token...") + log.info("Refreshing token...") gazu.refresh_token() threading.Timer(7 * 3600 * 24, refresh_token_every_week).start() diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 15e88947a1..0a59724393 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -5,10 +5,6 @@ from typing import Dict, List from pymongo import DeleteOne, UpdateOne import gazu -from gazu.task import ( - all_tasks_for_asset, - all_tasks_for_shot, -) from openpype.client import ( get_project, @@ -18,7 +14,6 @@ from openpype.client import ( create_project, ) from openpype.pipeline import AvalonMongoDB -from openpype.settings import get_project_settings from openpype.modules.kitsu.utils.credentials import validate_credentials from openpype.lib import Logger @@ -85,8 +80,10 @@ def update_op_assets( Returns: List[Dict[str, dict]]: List of (doc_id, update_dict) tuples """ + if not project_doc: + return + project_name = project_doc["name"] - project_module_settings = get_project_settings(project_name)["kitsu"] assets_with_update = [] for item in entities_list: @@ -170,9 +167,9 @@ def update_op_assets( tasks_list = [] item_type = item["type"] if item_type == "Asset": - tasks_list = all_tasks_for_asset(item) + tasks_list = gazu.task.all_tasks_for_asset(item) elif item_type == "Shot": - tasks_list = all_tasks_for_shot(item) + tasks_list = gazu.task.all_tasks_for_shot(item) item_data["tasks"] = { t["task_type_name"]: {"type": t["task_type_name"], "zou": t} for t in tasks_list @@ -207,7 +204,7 @@ def update_op_assets( # Root parent folder if exist visual_parent_doc_id = ( - asset_doc_ids[parent_zou_id]["_id"] if parent_zou_id else None + asset_doc_ids[parent_zou_id].get("_id") if parent_zou_id else None ) if visual_parent_doc_id is None: # Find root folder doc ("Assets" or "Shots") @@ -282,7 +279,7 @@ def write_project_to_op(project: dict, dbcon: AvalonMongoDB) -> UpdateOne: project_name = project["name"] project_doc = get_project(project_name) if not project_doc: - log.info(f"Creating project '{project_name}'") + log.info(f"Project created: {project_name}") project_doc = create_project(project_name, project_name) # Project data and tasks From c44c5c9ddbada1264691a32c78cf1765b0b9bce1 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 10 Feb 2023 14:08:33 +0100 Subject: [PATCH 1161/1271] Get asset and task names from instance only --- .../kitsu/plugins/publish/collect_kitsu_entities.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py index 92c8c1823d..5499b1782a 100644 --- a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py +++ b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py @@ -19,14 +19,12 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): # Get all needed names project_name = context.data.get("projectName") - asset_name = context.data.get("asset") - task_name = context.data.get("task") + asset_name = None + task_name = None # If asset and task name doesn't exist in context, look in instance for instance in context: - if not asset_name: - asset_name = instance.data.get("asset") - if not task_name: - task_name = instance.data.get("task") + asset_name = instance.data.get("asset") + task_name = instance.data.get("task") # Get all assets of the local project asset_docs = { From 4bc67437b61326540a2a62f141af5169296ee051 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 10 Feb 2023 14:36:17 +0100 Subject: [PATCH 1162/1271] Fixed line too long and too many '#' for comments --- openpype/modules/kitsu/utils/sync_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index e371c1a9bb..00c8c4eafa 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -2,7 +2,7 @@ Bugs: * Error when adding task type to anything that isn't Shot or Assets * Assets don't get added under an episode if TV show - * Assets added under Main Pack throws error. Can't get the name of Main Pack? + * Assets added under Main Pack throws error. No Main Pack name in dict Features ToDo: * Select in settings what types you wish to sync @@ -130,7 +130,7 @@ class Listener: self._update_project(data) # Print message - ## Happens in write_project_to_op() + # - Happens in write_project_to_op() def _update_project(self, data): """Update project into OP DB.""" From 8652dab47898e30cbdeb9c3ed9ced2f841c4ec4c Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 13:14:14 +0100 Subject: [PATCH 1163/1271] Fixed 'instance' code --- .../plugins/publish/collect_kitsu_entities.py | 104 ++++++++---------- .../plugins/publish/integrate_kitsu_note.py | 48 ++++---- .../plugins/publish/integrate_kitsu_review.py | 6 +- 3 files changed, 73 insertions(+), 85 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py index 5499b1782a..fe6854218d 100644 --- a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py +++ b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py @@ -1,13 +1,7 @@ # -*- coding: utf-8 -*- -import os - import gazu import pyblish.api -from openpype.client import ( - get_assets, -) - class CollectKitsuEntities(pyblish.api.ContextPlugin): """Collect Kitsu entities according to the current context""" @@ -17,67 +11,61 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): def process(self, context): - # Get all needed names - project_name = context.data.get("projectName") - asset_name = None - task_name = None - # If asset and task name doesn't exist in context, look in instance + kitsu_project = None + + kitsu_entities_by_id = {} for instance in context: - asset_name = instance.data.get("asset") + asset_doc = instance.data.get("assetEntity") task_name = instance.data.get("task") + if not asset_doc: + continue - # Get all assets of the local project - asset_docs = { - asset_doc["name"]: asset_doc - for asset_doc in get_assets(project_name) - } + zou_asset_data = asset_doc["data"].get("zou") + if not zou_asset_data: + raise AssertionError("Zou asset data not found in OpenPype!") - # Get asset object - asset = asset_docs.get(asset_name) - if not asset: - raise AssertionError(f"{asset_name} not found in DB") + if kitsu_project is None: + kitsu_project = gazu.project.get_project( + zou_asset_data["project_id"]) + if not kitsu_project: + raise AssertionError("Project not found in kitsu!") - zou_asset_data = asset["data"].get("zou") - if not zou_asset_data: - raise AssertionError("Zou asset data not found in OpenPype!") - self.log.debug(f"Collected zou asset data: {zou_asset_data}") + entity_type = zou_asset_data["type"] + kitsu_id = zou_asset_data["id"] + kitsu_entity = kitsu_entities_by_id.get(kitsu_id) + if not kitsu_entity: + if entity_type == "Shot": + kitsu_entity = gazu.shot.get_shot(kitsu_id) + else: + kitsu_entity = gazu.asset.get_asset(kitsu_id) + kitsu_entities_by_id[kitsu_id] = kitsu_entity - kitsu_project = gazu.project.get_project(zou_asset_data["project_id"]) - if not kitsu_project: - raise AssertionError("Project not found in kitsu!") - context.data["kitsu_project"] = kitsu_project - self.log.debug(f"Collect kitsu project: {kitsu_project}") + if not kitsu_entity: + raise AssertionError( + "{} not found in kitsu!".format(entity_type)) + instance.data["kitsu_entity"] = kitsu_entity - entity_type = zou_asset_data["type"] - if entity_type == "Shot": - kitsu_entity = gazu.shot.get_shot(zou_asset_data["id"]) - else: - kitsu_entity = gazu.asset.get_asset(zou_asset_data["id"]) - if not kitsu_entity: - raise AssertionError(f"{entity_type} not found in kitsu!") - context.data["kitsu_entity"] = kitsu_entity - self.log.debug(f"Collect kitsu {entity_type}: {kitsu_entity}") - - if task_name: - zou_task_data = asset["data"]["tasks"][task_name].get("zou") - self.log.debug(f"Collected zou task data: {zou_task_data}") - if zou_task_data: - kitsu_task = gazu.task.get_task(zou_task_data["id"]) - if not kitsu_task: - raise AssertionError("Task not found in kitsu!") - context.data["kitsu_task"] = kitsu_task - self.log.debug(f"Collect kitsu task: {kitsu_task}") - else: + if not task_name: + continue + zou_task_data = asset_doc["data"]["tasks"][task_name].get("zou") + self.log.debug( + "Collected zou task data: {}".format(zou_task_data)) + if not zou_task_data: kitsu_task_type = gazu.task.get_task_type_by_name(task_name) if not kitsu_task_type: raise AssertionError( - f"Task type {task_name} not found in Kitsu!" + "Task type {} not found in Kitsu!".format(task_name) ) + continue + kitsu_task_id = zou_task_data["id"] + kitsu_task = kitsu_entities_by_id.get(kitsu_task_id) + if not kitsu_task: + kitsu_task = gazu.task.get_task(zou_task_data["id"]) + kitsu_entities_by_id[kitsu_task_id] = kitsu_task - kitsu_task = gazu.task.get_task_by_name( - kitsu_entity, kitsu_task_type - ) - if not kitsu_task: - raise AssertionError("Task not found in kitsu!") - context.data["kitsu_task"] = kitsu_task - self.log.debug(f"Collect kitsu task: {kitsu_task}") + if not kitsu_task: + raise AssertionError("Task not found in kitsu!") + instance.data["kitsu_task"] = kitsu_task + self.log.debug("Collect kitsu task: {}".format(kitsu_task)) + + context.data["kitsu_project"] = kitsu_project diff --git a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py index b801e0e4d4..aeec2481e0 100644 --- a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py +++ b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py @@ -21,30 +21,32 @@ class IntegrateKitsuNote(pyblish.api.ContextPlugin): self.log.debug("Comment is `{}`".format(publish_comment)) - # Get note status, by default uses the task status for the note - # if it is not specified in the configuration - note_status = context.data["kitsu_task"]["task_status_id"] - if self.set_status_note: - kitsu_status = gazu.task.get_task_status_by_short_name( - self.note_status_shortname - ) - if kitsu_status: - note_status = kitsu_status - self.log.info("Note Kitsu status: {}".format(note_status)) - else: - self.log.info( - "Cannot find {} status. The status will not be " - "changed!".format(self.note_status_shortname) + for instance in context: + + # Get note status, by default uses the task status for the note + # if it is not specified in the configuration + note_status = instance.data["kitsu_task"]["task_status"]["id"] + + if self.set_status_note: + kitsu_status = gazu.task.get_task_status_by_short_name( + self.note_status_shortname ) + if kitsu_status: + note_status = kitsu_status + self.log.info("Note Kitsu status: {}".format(note_status)) + else: + self.log.info( + "Cannot find {} status. The status will not be " + "changed!".format(self.note_status_shortname) + ) - # Add comment to kitsu task - self.log.debug( - "Add new note in taks id {}".format( - context.data["kitsu_task"]["id"] + # Add comment to kitsu task + task = instance.data["kitsu_task"]["id"] + self.log.debug( + "Add new note in taks id {}".format(task) + ) + kitsu_comment = gazu.task.add_comment( + task, note_status, comment=publish_comment ) - ) - kitsu_comment = gazu.task.add_comment( - context.data["kitsu_task"], note_status, comment=publish_comment - ) - context.data["kitsu_comment"] = kitsu_comment + instance.data["kitsu_comment"] = kitsu_comment diff --git a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py index 94897b2553..d8f6cb7ac8 100644 --- a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py +++ b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py @@ -13,9 +13,8 @@ class IntegrateKitsuReview(pyblish.api.InstancePlugin): def process(self, instance): - context = instance.context - task = context.data["kitsu_task"] - comment = context.data.get("kitsu_comment") + task = instance.data["kitsu_task"]["id"] + comment = instance.data["kitsu_comment"]["id"] # Check comment has been created if not comment: @@ -29,7 +28,6 @@ class IntegrateKitsuReview(pyblish.api.InstancePlugin): # Skip if not tagged as review if "kitsureview" not in representation.get("tags", []): continue - review_path = representation.get("published_path") self.log.debug("Found review at: {}".format(review_path)) From 290709705e2811c1a87a5f18b859b5880bd09772 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 15:02:31 +0100 Subject: [PATCH 1164/1271] Changed logging from f-string to .format() --- .../modules/kitsu/actions/launcher_show_in_kitsu.py | 12 ++++++------ openpype/modules/kitsu/utils/credentials.py | 2 +- openpype/modules/kitsu/utils/sync_service.py | 2 +- openpype/modules/kitsu/utils/update_op_with_zou.py | 4 ++-- openpype/modules/kitsu/utils/update_zou_with_op.py | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py b/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py index 4793d60fc3..e3676afc4c 100644 --- a/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py +++ b/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py @@ -32,12 +32,12 @@ class ShowInKitsu(LauncherAction): project = get_project(project_name=project_name, fields=["data.zou_id"]) if not project: - raise RuntimeError(f"Project {project_name} not found.") + raise RuntimeError("Project {} not found.".format(project_name)) project_zou_id = project["data"].get("zou_id") if not project_zou_id: - raise RuntimeError(f"Project {project_name} has no " - f"connected kitsu id.") + raise RuntimeError( + "Project {} has no connected kitsu id.".format(project_name)) asset_zou_name = None asset_zou_id = None @@ -48,7 +48,7 @@ class ShowInKitsu(LauncherAction): asset_zou_name = asset_name asset_fields = ["data.zou.id", "data.zou.type"] if task_name: - asset_fields.append(f"data.tasks.{task_name}.zou.id") + asset_fields.append("data.tasks.{}.zou.id".format(task_name)) asset = get_asset_by_name(project_name, asset_name=asset_name, @@ -67,7 +67,7 @@ class ShowInKitsu(LauncherAction): task_data = asset["data"]["tasks"][task_name] task_zou_data = task_data.get("zou", {}) if not task_zou_data: - self.log.debug(f"No zou task data for task: {task_name}") + self.log.debug("No zou task data for task: {}".format(task_name)) task_zou_id = task_zou_data["id"] # Define URL @@ -78,7 +78,7 @@ class ShowInKitsu(LauncherAction): task_id=task_zou_id) # Open URL in webbrowser - self.log.info(f"Opening URL: {url}") + self.log.info("Opening URL: {}".format(url)) webbrowser.open(url, # Try in new tab new=2) diff --git a/openpype/modules/kitsu/utils/credentials.py b/openpype/modules/kitsu/utils/credentials.py index adcfb07cd5..1731e1ca4f 100644 --- a/openpype/modules/kitsu/utils/credentials.py +++ b/openpype/modules/kitsu/utils/credentials.py @@ -54,7 +54,7 @@ def validate_host(kitsu_url: str) -> bool: if gazu.client.host_is_valid(): return True else: - raise gazu.exception.HostException(f"Host '{kitsu_url}' is invalid.") + raise gazu.exception.HostException("Host '{}' is invalid.".format(kitsu_url)) def clear_credentials(): diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 00c8c4eafa..d4ef5ce63a 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -162,7 +162,7 @@ class Listener: self.dbcon.database[project_name].drop() # Print message - log.info(f"Project deleted: {project_name}") + log.info("Project deleted: {}".format(project_name)) # == Asset == def _new_asset(self, data): diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 0a59724393..15e6dd70d9 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -279,7 +279,7 @@ def write_project_to_op(project: dict, dbcon: AvalonMongoDB) -> UpdateOne: project_name = project["name"] project_doc = get_project(project_name) if not project_doc: - log.info(f"Project created: {project_name}") + log.info("Project created: {}".format(project_name)) project_doc = create_project(project_name, project_name) # Project data and tasks @@ -373,7 +373,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): if not project: project = gazu.project.get_project_by_name(project["name"]) - log.info(f"Synchronizing {project['name']}...") + log.info("Synchronizing {}...".format(project['name'])) # Get all assets from zou all_assets = gazu.asset.all_assets_for_project(project) diff --git a/openpype/modules/kitsu/utils/update_zou_with_op.py b/openpype/modules/kitsu/utils/update_zou_with_op.py index 39baf31b93..b13c2dd4c6 100644 --- a/openpype/modules/kitsu/utils/update_zou_with_op.py +++ b/openpype/modules/kitsu/utils/update_zou_with_op.py @@ -61,7 +61,7 @@ def sync_zou_from_op_project( project_doc = get_project(project_name) # Get all entities from zou - print(f"Synchronizing {project_name}...") + print("Synchronizing {}...".format(project_name)) zou_project = gazu.project.get_project_by_name(project_name) # Create project @@ -258,7 +258,7 @@ def sync_zou_from_op_project( for asset_doc in asset_docs.values() } for entity_id in deleted_entities: - gazu.raw.delete(f"data/entities/{entity_id}") + gazu.raw.delete("data/entities/{}".format(entity_id)) # Write into DB if bulk_writes: From 793af30caae9782ab9cb328f7b546231f0c727df Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 15:02:53 +0100 Subject: [PATCH 1165/1271] Changed AssertionError to ValueError --- .../kitsu/plugins/publish/collect_kitsu_entities.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py index fe6854218d..dc7048cf2a 100644 --- a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py +++ b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py @@ -22,13 +22,13 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): zou_asset_data = asset_doc["data"].get("zou") if not zou_asset_data: - raise AssertionError("Zou asset data not found in OpenPype!") + raise ValueError("Zou asset data not found in OpenPype!") if kitsu_project is None: kitsu_project = gazu.project.get_project( zou_asset_data["project_id"]) if not kitsu_project: - raise AssertionError("Project not found in kitsu!") + raise ValueError("Project not found in kitsu!") entity_type = zou_asset_data["type"] kitsu_id = zou_asset_data["id"] @@ -41,7 +41,7 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): kitsu_entities_by_id[kitsu_id] = kitsu_entity if not kitsu_entity: - raise AssertionError( + raise ValueError( "{} not found in kitsu!".format(entity_type)) instance.data["kitsu_entity"] = kitsu_entity @@ -53,7 +53,7 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): if not zou_task_data: kitsu_task_type = gazu.task.get_task_type_by_name(task_name) if not kitsu_task_type: - raise AssertionError( + raise ValueError( "Task type {} not found in Kitsu!".format(task_name) ) continue @@ -64,7 +64,7 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): kitsu_entities_by_id[kitsu_task_id] = kitsu_task if not kitsu_task: - raise AssertionError("Task not found in kitsu!") + raise ValueError("Task not found in kitsu!") instance.data["kitsu_task"] = kitsu_task self.log.debug("Collect kitsu task: {}".format(kitsu_task)) From 7e6c47967f42f26fd6d0f56c33a14a0e08e77906 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 15:04:05 +0100 Subject: [PATCH 1166/1271] Get episode_id using get() as ep_id might not always exist --- openpype/modules/kitsu/utils/sync_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index d4ef5ce63a..a449bf8c06 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -477,7 +477,7 @@ class Listener: # Find asset doc episode = None - ep_id = task['episode_id'] + ep_id = task.get('episode_id') if ep_id and ep_id != "": episode = gazu.asset.get_episode(ep_id) From 1045ee0c1ed5bb10c3c3d8f4e17f399a5f33c754 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 15:04:55 +0100 Subject: [PATCH 1167/1271] Check if asset_doc exist before processing it --- openpype/modules/kitsu/utils/sync_service.py | 34 ++++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index a449bf8c06..eed259cda6 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -487,25 +487,25 @@ class Listener: parent_name = parent_name + \ task['sequence']['name'] + "_" + task['entity']['name'] - asset_doc = get_asset_by_name(project_name, parent_name) - # Update asset tasks with new one - asset_tasks = asset_doc['data'].get("tasks") - task_type_name = task['task_type']['name'] - asset_tasks[task_type_name] = {"type": task_type_name, "zou": task} - self.dbcon.update_one( - {"_id": asset_doc['_id']}, {"$set": {"data.tasks": asset_tasks}} - ) + asset_doc = get_asset_by_name(project_name, parent_name) + if asset_doc: + asset_tasks = asset_doc['data'].get("tasks") + task_type_name = task['task_type']['name'] + asset_tasks[task_type_name] = {"type": task_type_name, "zou": task} + self.dbcon.update_one( + {"_id": asset_doc['_id']}, {"$set": {"data.tasks": asset_tasks}} + ) - # Print message - msg = "Task created: " - msg = msg + f"{task['project']['name']} - " - if episode is not None: - msg = msg + f"{episode['name']}_" - msg = msg + f"{task['sequence']['name']}_" - msg = msg + f"{task['entity']['name']} - " - msg = msg + f"{task['task_type']['name']}" - log.info(msg) + # Print message + msg = "Task created: " + msg = msg + f"{task['project']['name']} - " + if episode is not None: + msg = msg + f"{episode['name']}_" + msg = msg + f"{task['sequence']['name']}_" + msg = msg + f"{task['entity']['name']} - " + msg = msg + f"{task['task_type']['name']}" + log.info(msg) def _update_task(self, data): """Update task into OP DB.""" From ef698b4aface5ceff0ba1016a242a318853eaa2a Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 15:11:07 +0100 Subject: [PATCH 1168/1271] Fixed 1 extra frame at frameEnd Same as #4466 --- openpype/modules/kitsu/utils/update_op_with_zou.py | 6 +++--- openpype/modules/kitsu/utils/update_zou_with_op.py | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 15e6dd70d9..93d0d5e3fb 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -111,18 +111,18 @@ def update_op_assets( except (TypeError, ValueError): frame_in = 1001 item_data["frameStart"] = frame_in - # Frames duration, fallback on 0 + # Frames duration, fallback on 1 try: # NOTE nb_frames is stored directly in item # because of zou's legacy design - frames_duration = int(item.get("nb_frames", 0)) + frames_duration = int(item.get("nb_frames", 1)) except (TypeError, ValueError): frames_duration = None # Frame out, fallback on frame_in + duration or project's value or 1001 frame_out = item_data.pop("frame_out", None) if not frame_out: if frames_duration: - frame_out = frame_in + frames_duration + frame_out = frame_in + frames_duration - 1 else: frame_out = project_doc["data"].get("frameEnd", 1001) item_data["frameEnd"] = frame_out diff --git a/openpype/modules/kitsu/utils/update_zou_with_op.py b/openpype/modules/kitsu/utils/update_zou_with_op.py index b13c2dd4c6..b1a9b8b82c 100644 --- a/openpype/modules/kitsu/utils/update_zou_with_op.py +++ b/openpype/modules/kitsu/utils/update_zou_with_op.py @@ -174,7 +174,8 @@ def sync_zou_from_op_project( doc["name"], frame_in=doc["data"]["frameStart"], frame_out=doc["data"]["frameEnd"], - nb_frames=doc["data"]["frameEnd"] - doc["data"]["frameStart"], + nb_frames=( + doc["data"]["frameEnd"] - doc["data"]["frameStart"] + 1), ) elif match.group(2): # Sequence @@ -229,7 +230,7 @@ def sync_zou_from_op_project( "frame_in": frame_in, "frame_out": frame_out, }, - "nb_frames": frame_out - frame_in, + "nb_frames": frame_out - frame_in + 1, } ) entity = gazu.raw.update("entities", zou_id, entity_data) From c4f1a1f452d8e0f70d6a629db7925642d3acbc8d Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 15:13:51 +0100 Subject: [PATCH 1169/1271] Fixed hound's "line too long" comments --- openpype/modules/kitsu/actions/launcher_show_in_kitsu.py | 3 ++- openpype/modules/kitsu/utils/credentials.py | 3 ++- openpype/modules/kitsu/utils/sync_service.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py b/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py index e3676afc4c..11224f6e52 100644 --- a/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py +++ b/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py @@ -67,7 +67,8 @@ class ShowInKitsu(LauncherAction): task_data = asset["data"]["tasks"][task_name] task_zou_data = task_data.get("zou", {}) if not task_zou_data: - self.log.debug("No zou task data for task: {}".format(task_name)) + self.log.debug( + "No zou task data for task: {}".format(task_name)) task_zou_id = task_zou_data["id"] # Define URL diff --git a/openpype/modules/kitsu/utils/credentials.py b/openpype/modules/kitsu/utils/credentials.py index 1731e1ca4f..941343cc8d 100644 --- a/openpype/modules/kitsu/utils/credentials.py +++ b/openpype/modules/kitsu/utils/credentials.py @@ -54,7 +54,8 @@ def validate_host(kitsu_url: str) -> bool: if gazu.client.host_is_valid(): return True else: - raise gazu.exception.HostException("Host '{}' is invalid.".format(kitsu_url)) + raise gazu.exception.HostException( + "Host '{}' is invalid.".format(kitsu_url)) def clear_credentials(): diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index eed259cda6..498c8de71e 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -494,7 +494,8 @@ class Listener: task_type_name = task['task_type']['name'] asset_tasks[task_type_name] = {"type": task_type_name, "zou": task} self.dbcon.update_one( - {"_id": asset_doc['_id']}, {"$set": {"data.tasks": asset_tasks}} + {"_id": asset_doc['_id']}, + {"$set": {"data.tasks": asset_tasks}} ) # Print message From 07ac3d8d4db54b111fd0117bed4a5cbc5c9b19d7 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 15:58:49 +0100 Subject: [PATCH 1170/1271] If no task in instance, continue fix fore https://github.com/ynput/OpenPype/pull/4425#discussion_r1108582918 --- .../modules/kitsu/plugins/publish/integrate_kitsu_note.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py index aeec2481e0..54fb6a4678 100644 --- a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py +++ b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py @@ -22,10 +22,13 @@ class IntegrateKitsuNote(pyblish.api.ContextPlugin): self.log.debug("Comment is `{}`".format(publish_comment)) for instance in context: + kitsu_task = instance.data.get("kitsu_task") + if kitsu_task is None: + continue # Get note status, by default uses the task status for the note # if it is not specified in the configuration - note_status = instance.data["kitsu_task"]["task_status"]["id"] + note_status = kitsu_task["task_status"]["id"] if self.set_status_note: kitsu_status = gazu.task.get_task_status_by_short_name( @@ -41,7 +44,7 @@ class IntegrateKitsuNote(pyblish.api.ContextPlugin): ) # Add comment to kitsu task - task = instance.data["kitsu_task"]["id"] + task = kitsu_task["id"] self.log.debug( "Add new note in taks id {}".format(task) ) From 915d11040493e1e59d2389e9d5f86f678ef4b9ca Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 16 Feb 2023 16:02:53 +0100 Subject: [PATCH 1171/1271] Set frame_out to frame_in if no duration exists fix for https://github.com/ynput/OpenPype/pull/4425#discussion_r1108593566 --- openpype/modules/kitsu/utils/update_op_with_zou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 93d0d5e3fb..265c3638cd 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -124,7 +124,7 @@ def update_op_assets( if frames_duration: frame_out = frame_in + frames_duration - 1 else: - frame_out = project_doc["data"].get("frameEnd", 1001) + frame_out = project_doc["data"].get("frameEnd", frame_in) item_data["frameEnd"] = frame_out # Fps, fallback to project's value or default value (25.0) try: From 4bcef4406803f0210ed3a741ad99de8aa56c80a7 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Sat, 18 Feb 2023 20:52:20 +0100 Subject: [PATCH 1172/1271] Fixed hound's max-length note --- openpype/modules/kitsu/utils/update_op_with_zou.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 265c3638cd..898cf076c8 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -146,7 +146,8 @@ def update_op_assets( "resolutionWidth") item_data["resolutionHeight"] = project_doc["data"].get( "resolutionHeight") - # Properties that doesn't fully exist in Kitsu. Guessing the property name + # Properties that doesn't fully exist in Kitsu. + # Guessing those property names below: # Pixel Aspect Ratio item_data["pixelAspect"] = item_data.get( "pixel_aspect", project_doc["data"].get("pixelAspect")) @@ -452,7 +453,8 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): [ UpdateOne({"_id": id}, update) for id, update in update_op_assets( - dbcon, project, project_doc, all_entities, zou_ids_and_asset_docs + dbcon, project, project_doc, + all_entities, zou_ids_and_asset_docs ) ] ) From 3fc2180e51357cc74a82a76f2fcd21fa11752ecc Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Tue, 28 Feb 2023 23:06:52 +0100 Subject: [PATCH 1173/1271] Fixed all quotes types so they now match --- openpype/modules/kitsu/utils/sync_service.py | 133 ++++++++++--------- 1 file changed, 67 insertions(+), 66 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 498c8de71e..1af0b6edc4 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -48,16 +48,16 @@ class Listener: self.dbcon = AvalonMongoDB() self.dbcon.install() - gazu.client.set_host(os.environ['KITSU_SERVER']) + gazu.client.set_host(os.environ["KITSU_SERVER"]) # Authenticate if not validate_credentials(login, password): raise gazu.exception.AuthFailedException( - f"Kitsu authentication failed for login: '{login}'..." + 'Kitsu authentication failed for login: "{}"...'.format(login) ) gazu.set_event_host( - os.environ['KITSU_SERVER'].replace("api", "socket.io") + os.environ["KITSU_SERVER"].replace("api", "socket.io") ) self.event_client = gazu.events.init() @@ -135,14 +135,14 @@ class Listener: def _update_project(self, data): """Update project into OP DB.""" # Get project entity - project = gazu.project.get_project(data['project_id']) + project = gazu.project.get_project(data["project_id"]) update_project = write_project_to_op(project, self.dbcon) # Write into DB if update_project: - self.dbcon.Session['AVALON_PROJECT'] = get_kitsu_project_name( - data['project_id']) + self.dbcon.Session["AVALON_PROJECT"] = get_kitsu_project_name( + data["project_id"]) self.dbcon.bulk_write([update_project]) def _delete_project(self, data): @@ -168,10 +168,10 @@ class Listener: def _new_asset(self, data): """Create new asset into OP DB.""" # Get project entity - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) # Get asset entity - asset = gazu.asset.get_asset(data['asset_id']) + asset = gazu.asset.get_asset(data["asset_id"]) # Insert doc in DB self.dbcon.insert_one(create_op_asset(asset)) @@ -181,7 +181,7 @@ class Listener: # Print message episode = None - ep_id = asset['episode_id'] + ep_id = asset.get("episode_id") if ep_id and ep_id != "": episode = gazu.asset.get_episode(ep_id) @@ -195,22 +195,22 @@ class Listener: def _update_asset(self, data): """Update asset into OP DB.""" - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) project_name = self.dbcon.active_project() project_doc = get_project(project_name) # Get gazu entity - asset = gazu.asset.get_asset(data['asset_id']) + asset = gazu.asset.get_asset(data["asset_id"]) # Find asset doc # Query all assets of the local project zou_ids_and_asset_docs = { - asset_doc['data']['zou']['id']: asset_doc + asset_doc["data"]["zou"]["id"]: asset_doc for asset_doc in get_assets(project_name) - if asset_doc['data'].get("zou", {}).get("id") + if asset_doc["data"].get("zou", {}).get("id") } - zou_ids_and_asset_docs[asset['project_id']] = project_doc - gazu_project = gazu.project.get_project(asset['project_id']) + zou_ids_and_asset_docs[asset["project_id"]] = project_doc + gazu_project = gazu.project.get_project(asset["project_id"]) # Update update_op_result = update_op_assets( @@ -223,18 +223,18 @@ class Listener: def _delete_asset(self, data): """Delete asset of OP DB.""" - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) - asset = self.dbcon.find_one({"data.zou.id": data['asset_id']}) + asset = self.dbcon.find_one({"data.zou.id": data["asset_id"]}) if asset: # Delete self.dbcon.delete_one( - {"type": "asset", "data.zou.id": data['asset_id']} + {"type": "asset", "data.zou.id": data["asset_id"]} ) # Print message episode = None - ep_id = asset['data']['zou']['episode_id'] + ep_id = asset["data"]["zou"].get("episode_id") if ep_id and ep_id != "": episode = gazu.asset.get_episode(ep_id) @@ -250,10 +250,10 @@ class Listener: def _new_episode(self, data): """Create new episode into OP DB.""" # Get project entity - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) # Get gazu entity - episode = gazu.shot.get_episode(data['episode_id']) + ep = gazu.shot.get_episode(data["episode_id"]) # Insert doc in DB self.dbcon.insert_one(create_op_asset(episode)) @@ -268,22 +268,22 @@ class Listener: def _update_episode(self, data): """Update episode into OP DB.""" - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) project_name = self.dbcon.active_project() project_doc = get_project(project_name) # Get gazu entity - episode = gazu.shot.get_episode(data['episode_id']) + ep = gazu.shot.get_episode(data["episode_id"]) # Find asset doc # Query all assets of the local project zou_ids_and_asset_docs = { - asset_doc['data']['zou']['id']: asset_doc + asset_doc["data"]["zou"]["id"]: asset_doc for asset_doc in get_assets(project_name) - if asset_doc['data'].get("zou", {}).get("id") + if asset_doc["data"].get("zou", {}).get("id") } - zou_ids_and_asset_docs[episode['project_id']] = project_doc - gazu_project = gazu.project.get_project(episode['project_id']) + zou_ids_and_asset_docs[ep["project_id"]] = project_doc + gazu_project = gazu.project.get_project(ep["project_id"]) # Update update_op_result = update_op_assets( @@ -296,7 +296,7 @@ class Listener: def _delete_episode(self, data): """Delete shot of OP DB.""" - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) episode = self.dbcon.find_one({"data.zou.id": data['episode_id']}) if episode: @@ -317,10 +317,10 @@ class Listener: def _new_sequence(self, data): """Create new sequnce into OP DB.""" # Get project entity - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) # Get gazu entity - sequence = gazu.shot.get_sequence(data['sequence_id']) + sequence = gazu.shot.get_sequence(data["sequence_id"]) # Insert doc in DB self.dbcon.insert_one(create_op_asset(sequence)) @@ -331,7 +331,7 @@ class Listener: # Print message episode = None - ep_id = sequence['episode_id'] + ep_id = sequence.get("episode_id") if ep_id and ep_id != "": episode = gazu.asset.get_episode(ep_id) @@ -344,22 +344,22 @@ class Listener: def _update_sequence(self, data): """Update sequence into OP DB.""" - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) project_name = self.dbcon.active_project() project_doc = get_project(project_name) # Get gazu entity - sequence = gazu.shot.get_sequence(data['sequence_id']) + sequence = gazu.shot.get_sequence(data["sequence_id"]) # Find asset doc # Query all assets of the local project zou_ids_and_asset_docs = { - asset_doc['data']['zou']['id']: asset_doc + asset_doc["data"]["zou"]["id"]: asset_doc for asset_doc in get_assets(project_name) - if asset_doc['data'].get("zou", {}).get("id") + if asset_doc["data"].get("zou", {}).get("id") } - zou_ids_and_asset_docs[sequence['project_id']] = project_doc - gazu_project = gazu.project.get_project(sequence['project_id']) + zou_ids_and_asset_docs[sequence["project_id"]] = project_doc + gazu_project = gazu.project.get_project(sequence["project_id"]) # Update update_op_result = update_op_assets( @@ -372,15 +372,16 @@ class Listener: def _delete_sequence(self, data): """Delete sequence of OP DB.""" - set_op_project(self.dbcon, data['project_id']) - sequence = self.dbcon.find_one({"data.zou.id": data['sequence_id']}) + set_op_project(self.dbcon, data["project_id"]) + sequence = self.dbcon.find_one({"data.zou.id": data["sequence_id"]}) if sequence: # Delete self.dbcon.delete_one( - {"type": "asset", "data.zou.id": data['sequence_id']} + {"type": "asset", "data.zou.id": data["sequence_id"]} ) # Print message + ep_id = sequence["data"]["zou"].get("episode_id") gazu_project = gazu.project.get_project( sequence['data']['zou']['project_id']) msg = f"Sequence deleted: " @@ -392,10 +393,10 @@ class Listener: def _new_shot(self, data): """Create new shot into OP DB.""" # Get project entity - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) # Get gazu entity - shot = gazu.shot.get_shot(data['shot_id']) + shot = gazu.shot.get_shot(data["shot_id"]) # Insert doc in DB self.dbcon.insert_one(create_op_asset(shot)) @@ -405,7 +406,7 @@ class Listener: # Print message episode = None - if shot['episode_id'] and shot['episode_id'] != "": + if shot["episode_id"] and shot["episode_id"] != "": episode = gazu.asset.get_episode(shot['episode_id']) msg = "Shot created: " @@ -418,21 +419,21 @@ class Listener: def _update_shot(self, data): """Update shot into OP DB.""" - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) project_name = self.dbcon.active_project() project_doc = get_project(project_name) # Get gazu entity - shot = gazu.shot.get_shot(data['shot_id']) + shot = gazu.shot.get_shot(data["shot_id"]) # Find asset doc # Query all assets of the local project zou_ids_and_asset_docs = { - asset_doc['data']['zou']['id']: asset_doc + asset_doc["data"]["zou"]["id"]: asset_doc for asset_doc in get_assets(project_name) - if asset_doc['data'].get("zou", {}).get("id") - } - zou_ids_and_asset_docs[shot['project_id']] = project_doc + if asset_doc["data"].get("zou", {}).get("id")} + zou_ids_and_asset_docs[shot["project_id"]] = project_doc + gazu_project = gazu.project.get_project(shot["project_id"]) gazu_project = gazu.project.get_project(shot['project_id']) # Update @@ -447,18 +448,18 @@ class Listener: def _delete_shot(self, data): """Delete shot of OP DB.""" - set_op_project(self.dbcon, data['project_id']) - shot = self.dbcon.find_one({"data.zou.id": data['shot_id']}) + set_op_project(self.dbcon, data["project_id"]) + shot = self.dbcon.find_one({"data.zou.id": data["shot_id"]}) if shot: # Delete self.dbcon.delete_one( - {"type": "asset", "data.zou.id": data['shot_id']} + {"type": "asset", "data.zou.id": data["shot_id"]} ) # Print message gazu_project = gazu.project.get_project( - shot['data']['zou']['project_id']) + ep_id = shot["data"]["zou"].get("episode_id") msg = "Shot deleted: " msg = msg + f"{gazu_project['name']} - " @@ -469,15 +470,15 @@ class Listener: def _new_task(self, data): """Create new task into OP DB.""" # Get project entity - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) project_name = self.dbcon.active_project() # Get gazu entity - task = gazu.task.get_task(data['task_id']) + task = gazu.task.get_task(data["task_id"]) # Find asset doc episode = None - ep_id = task.get('episode_id') + ep_id = task.get("episode_id") if ep_id and ep_id != "": episode = gazu.asset.get_episode(ep_id) @@ -490,11 +491,11 @@ class Listener: # Update asset tasks with new one asset_doc = get_asset_by_name(project_name, parent_name) if asset_doc: - asset_tasks = asset_doc['data'].get("tasks") - task_type_name = task['task_type']['name'] + asset_tasks = asset_doc["data"].get("tasks") + task_type_name = task["task_type"]["name"] asset_tasks[task_type_name] = {"type": task_type_name, "zou": task} self.dbcon.update_one( - {"_id": asset_doc['_id']}, + {"_id": asset_doc["_id"]}, {"$set": {"data.tasks": asset_tasks}} ) @@ -515,29 +516,29 @@ class Listener: def _delete_task(self, data): """Delete task of OP DB.""" - set_op_project(self.dbcon, data['project_id']) + set_op_project(self.dbcon, data["project_id"]) project_name = self.dbcon.active_project() # Find asset doc asset_docs = list(get_assets(project_name)) for doc in asset_docs: # Match task - for name, task in doc['data']['tasks'].items(): - if task.get("zou") and data['task_id'] == task['zou']['id']: + for name, task in doc["data"]["tasks"].items(): + if task.get("zou") and data["task_id"] == task["zou"]["id"]: # Pop task - asset_tasks = doc['data'].get("tasks", {}) + asset_tasks = doc["data"].get("tasks", {}) asset_tasks.pop(name) # Delete task in DB self.dbcon.update_one( - {"_id": doc['_id']}, + {"_id": doc["_id"]}, {"$set": {"data.tasks": asset_tasks}}, ) # Print message - shot = gazu.shot.get_shot(task['zou']['entity_id']) + entity = gazu.entity.get_entity(task["zou"]["entity_id"]) episode = None - ep_id = shot['episode_id'] + ep_id = entity.get("episode_id") if ep_id and ep_id != "": episode = gazu.asset.get_episode(ep_id) From f9137bdb041690f2ef6cc0a3d26878ee70b321bf Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Tue, 28 Feb 2023 23:08:40 +0100 Subject: [PATCH 1174/1271] Added docstring and changed `doc` to `dict` in var-names --- .../modules/kitsu/utils/update_op_with_zou.py | 78 +++++++++++-------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 898cf076c8..9368848532 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -65,30 +65,32 @@ def set_op_project(dbcon: AvalonMongoDB, project_id: str): def update_op_assets( dbcon: AvalonMongoDB, gazu_project: dict, - project_doc: dict, + project_dict: dict, entities_list: List[dict], - asset_doc_ids: Dict[str, dict], + asset_dict_ids: Dict[str, dict], ) -> List[Dict[str, dict]]: """Update OpenPype assets. Set 'data' and 'parent' fields. Args: dbcon (AvalonMongoDB): Connection to DB + gazu_project dict): Dict of gazu, + project_dict dict): Dict of project, entities_list (List[dict]): List of zou entities to update - asset_doc_ids (Dict[str, dict]): Dicts of [{zou_id: asset_doc}, ...] + asset_dict_ids (Dict[str, dict]): Dicts of [{zou_id: asset_doc}, ...] Returns: List[Dict[str, dict]]: List of (doc_id, update_dict) tuples """ - if not project_doc: + if not project_dict: return - project_name = project_doc["name"] + project_name = project_dict["name"] assets_with_update = [] for item in entities_list: # Check asset exists - item_doc = asset_doc_ids.get(item["id"]) + item_doc = asset_dict_ids.get(item["id"]) if not item_doc: # Create asset op_asset = create_op_asset(item) insert_result = dbcon.insert_one(op_asset) @@ -105,7 +107,7 @@ def update_op_assets( try: frame_in = int( item_data.pop( - "frame_in", project_doc["data"].get("frameStart") + "frame_in", project_dict["data"].get("frameStart") ) ) except (TypeError, ValueError): @@ -124,14 +126,14 @@ def update_op_assets( if frames_duration: frame_out = frame_in + frames_duration - 1 else: - frame_out = project_doc["data"].get("frameEnd", frame_in) + frame_out = project_dict["data"].get("frameEnd", frame_in) item_data["frameEnd"] = frame_out # Fps, fallback to project's value or default value (25.0) try: fps = float(item_data.get("fps")) except (TypeError, ValueError): fps = float(gazu_project.get( - "fps", project_doc["data"].get("fps", 25))) + "fps", project_dict["data"].get("fps", 25))) item_data["fps"] = fps # Resolution, fall back to project default match_res = re.match( @@ -142,27 +144,27 @@ def update_op_assets( item_data["resolutionWidth"] = int(match_res.group(1)) item_data["resolutionHeight"] = int(match_res.group(2)) else: - item_data["resolutionWidth"] = project_doc["data"].get( + item_data["resolutionWidth"] = project_dict["data"].get( "resolutionWidth") - item_data["resolutionHeight"] = project_doc["data"].get( + item_data["resolutionHeight"] = project_dict["data"].get( "resolutionHeight") # Properties that doesn't fully exist in Kitsu. # Guessing those property names below: # Pixel Aspect Ratio item_data["pixelAspect"] = item_data.get( - "pixel_aspect", project_doc["data"].get("pixelAspect")) + "pixel_aspect", project_dict["data"].get("pixelAspect")) # Handle Start item_data["handleStart"] = item_data.get( - "handle_start", project_doc["data"].get("handleStart")) + "handle_start", project_dict["data"].get("handleStart")) # Handle End item_data["handleEnd"] = item_data.get( - "handle_end", project_doc["data"].get("handleEnd")) + "handle_end", project_dict["data"].get("handleEnd")) # Clip In item_data["clipIn"] = item_data.get( - "clip_in", project_doc["data"].get("clipIn")) + "clip_in", project_dict["data"].get("clipIn")) # Clip Out item_data["clipOut"] = item_data.get( - "clip_out", project_doc["data"].get("clipOut")) + "clip_out", project_dict["data"].get("clipOut")) # Tasks tasks_list = [] @@ -204,9 +206,14 @@ def update_op_assets( entity_root_asset_name = "Shots" # Root parent folder if exist - visual_parent_doc_id = ( - asset_doc_ids[parent_zou_id].get("_id") if parent_zou_id else None - ) + visual_parent_doc_id = None + if parent_zou_id is not None: + parent_zou_id_dict = asset_dict_ids.get(parent_zou_id) + if parent_zou_id_dict is not None: + visual_parent_doc_id = ( + parent_zou_id_dict.get("_id") + if parent_zou_id_dict else None) + if visual_parent_doc_id is None: # Find root folder doc ("Assets" or "Shots") root_folder_doc = get_asset_by_name( @@ -225,12 +232,15 @@ def update_op_assets( item_data["parents"] = [] ancestor_id = parent_zou_id while ancestor_id is not None: - parent_doc = asset_doc_ids[ancestor_id] - item_data["parents"].insert(0, parent_doc["name"]) + parent_doc = asset_dict_ids.get(ancestor_id) + if parent_doc is not None: + item_data["parents"].insert(0, parent_doc["name"]) - # Get parent entity - parent_entity = parent_doc["data"]["zou"] - ancestor_id = parent_entity.get("parent_id") + # Get parent entity + parent_entity = parent_doc["data"]["zou"] + ancestor_id = parent_entity.get("parent_id") + else: + ancestor_id = None # Build OpenPype compatible name if item_type in ["Shot", "Sequence"] and parent_zou_id is not None: @@ -239,7 +249,7 @@ def update_op_assets( item_name = f"{item_data['parents'][-1]}_{item['name']}" # Update doc name - asset_doc_ids[item["id"]]["name"] = item_name + asset_dict_ids[item["id"]]["name"] = item_name else: item_name = item["name"] @@ -258,7 +268,7 @@ def update_op_assets( "$set": { "name": item_name, "data": item_data, - "parent": project_doc["_id"], + "parent": project_dict["_id"], } }, ) @@ -278,13 +288,13 @@ def write_project_to_op(project: dict, dbcon: AvalonMongoDB) -> UpdateOne: UpdateOne: Update instance for the project """ project_name = project["name"] - project_doc = get_project(project_name) - if not project_doc: + project_dict = get_project(project_name) + if not project_dict: log.info("Project created: {}".format(project_name)) - project_doc = create_project(project_name, project_name) + project_dict = create_project(project_name, project_name) # Project data and tasks - project_data = project_doc["data"] or {} + project_data = project_dict["data"] or {} # Build project code and update Kitsu project_code = project.get("code") @@ -315,7 +325,7 @@ def write_project_to_op(project: dict, dbcon: AvalonMongoDB) -> UpdateOne: ) return UpdateOne( - {"_id": project_doc["_id"]}, + {"_id": project_dict["_id"]}, { "$set": { "config.tasks": { @@ -398,7 +408,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): # Try to find project document project_name = project["name"] dbcon.Session["AVALON_PROJECT"] = project_name - project_doc = get_project(project_name) + project_dict = get_project(project_name) # Query all assets of the local project zou_ids_and_asset_docs = { @@ -406,7 +416,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): for asset_doc in get_assets(project_name) if asset_doc["data"].get("zou", {}).get("id") } - zou_ids_and_asset_docs[project["id"]] = project_doc + zou_ids_and_asset_docs[project["id"]] = project_dict # Create entities root folders to_insert = [ @@ -453,7 +463,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): [ UpdateOne({"_id": id}, update) for id, update in update_op_assets( - dbcon, project, project_doc, + dbcon, project, project_dict, all_entities, zou_ids_and_asset_docs ) ] From 0f76d3a44e4974b5c0ec81f166a19289d9cb4fd6 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Tue, 28 Feb 2023 23:13:33 +0100 Subject: [PATCH 1175/1271] Cleaned up the fetching of the entity_id Also changed the name kitsu_id to entity_id and kitsu_entity to just entity as that's what it is. --- .../plugins/publish/collect_kitsu_entities.py | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py index dc7048cf2a..1531c80e04 100644 --- a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py +++ b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py @@ -16,7 +16,6 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): kitsu_entities_by_id = {} for instance in context: asset_doc = instance.data.get("assetEntity") - task_name = instance.data.get("task") if not asset_doc: continue @@ -24,27 +23,24 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): if not zou_asset_data: raise ValueError("Zou asset data not found in OpenPype!") - if kitsu_project is None: - kitsu_project = gazu.project.get_project( - zou_asset_data["project_id"]) - if not kitsu_project: - raise ValueError("Project not found in kitsu!") + kitsu_project = gazu.project.get_project( + zou_asset_data["project_id"]) + if not kitsu_project: + raise ValueError("Project not found in kitsu!") - entity_type = zou_asset_data["type"] - kitsu_id = zou_asset_data["id"] - kitsu_entity = kitsu_entities_by_id.get(kitsu_id) - if not kitsu_entity: - if entity_type == "Shot": - kitsu_entity = gazu.shot.get_shot(kitsu_id) - else: - kitsu_entity = gazu.asset.get_asset(kitsu_id) - kitsu_entities_by_id[kitsu_id] = kitsu_entity + entity_id = zou_asset_data["id"] + entity = kitsu_entities_by_id.get(entity_id) + if not entity: + entity = gazu.entity.get_entity(entity_id) + if not entity: + raise ValueError( + "{} was not found in kitsu!".format( + zou_asset_data["name"])) - if not kitsu_entity: - raise ValueError( - "{} not found in kitsu!".format(entity_type)) - instance.data["kitsu_entity"] = kitsu_entity + kitsu_entities_by_id[entity_id] = entity + instance.data["entity"] = entity + task_name = instance.data.get("task") if not task_name: continue zou_task_data = asset_doc["data"]["tasks"][task_name].get("zou") From 13a4c7201e5fd410e3546b4cd898ab8f0068b4ab Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Tue, 28 Feb 2023 23:18:43 +0100 Subject: [PATCH 1176/1271] change task to task_id --- .../modules/kitsu/plugins/publish/integrate_kitsu_note.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py index 54fb6a4678..006f0bc6d0 100644 --- a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py +++ b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py @@ -44,12 +44,12 @@ class IntegrateKitsuNote(pyblish.api.ContextPlugin): ) # Add comment to kitsu task - task = kitsu_task["id"] + task_id = kitsu_task["id"] self.log.debug( - "Add new note in taks id {}".format(task) + "Add new note in taks id {}".format(task_id) ) kitsu_comment = gazu.task.add_comment( - task, note_status, comment=publish_comment + task_id, note_status, comment=publish_comment ) instance.data["kitsu_comment"] = kitsu_comment From bcea2c70a269559a2fa370185393d6c4b675a38b Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Tue, 28 Feb 2023 23:20:32 +0100 Subject: [PATCH 1177/1271] Cleaned up project deletion code --- openpype/modules/kitsu/utils/sync_service.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 1af0b6edc4..6155b396aa 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -149,20 +149,16 @@ class Listener: """Delete project.""" collections = self.dbcon.database.list_collection_names() - project_name = None for collection in collections: - post = self.dbcon.database[collection].find_one( - {"data.zou_id": data['project_id']}) - if post: - project_name = post['name'] - break + project = self.dbcon.database[collection].find_one( + {"data.zou_id": data["project_id"]}) + if project: + # Delete project collection + self.dbcon.database[project["name"]].drop() - if project_name: - # Delete project collection - self.dbcon.database[project_name].drop() - - # Print message - log.info("Project deleted: {}".format(project_name)) + # Print message + log.info("Project deleted: {}".format(project["name"])) + return # == Asset == def _new_asset(self, data): From d9ac1ee95255c2d663cffaa00e63ad99a74188af Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Tue, 28 Feb 2023 23:24:55 +0100 Subject: [PATCH 1178/1271] Cleaned up log.info() message creation --- openpype/modules/kitsu/utils/sync_service.py | 171 +++++++++++-------- 1 file changed, 96 insertions(+), 75 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 6155b396aa..b389d25c4f 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -176,17 +176,18 @@ class Listener: self._update_asset(data) # Print message - episode = None + ep = None ep_id = asset.get("episode_id") if ep_id and ep_id != "": - episode = gazu.asset.get_episode(ep_id) + ep = gazu.asset.get_episode(ep_id) - msg = "Asset created: " - msg = msg + f"{asset['project_name']} - " - if episode is not None: - msg = msg + f"{episode['name']}_" - msg = msg + f"{asset['asset_type_name']}_" - msg = msg + f"{asset['name']}" + msg = "Asset created: {proj_name} - {ep_name}" \ + "{asset_type_name} - {asset_name}".format( + proj_name=asset["project_name"], + ep_name=ep["name"] + " - " if ep is not None else "", + asset_type_name=asset["asset_type_name"], + asset_name=asset["name"] + ) log.info(msg) def _update_asset(self, data): @@ -229,17 +230,18 @@ class Listener: ) # Print message - episode = None + ep = None ep_id = asset["data"]["zou"].get("episode_id") if ep_id and ep_id != "": - episode = gazu.asset.get_episode(ep_id) + ep = gazu.asset.get_episode(ep_id) - msg = "Asset deleted: " - msg = msg + f"{asset['data']['zou']['project_name']} - " - if episode is not None: - msg = msg + f"{episode['name']}_" - msg = msg + f"{asset['data']['zou']['asset_type_name']}_" - msg = msg + f"'{asset['name']}" + msg = "Asset deleted: {proj_name} - {ep_name}" \ + "{asset_type_name} - {asset_name}".format( + proj_name=asset["data"]["zou"]["project_name"], + ep_name=ep["name"] + " - " if ep is not None else "", + asset_type_name=asset["data"]["zou"]["asset_type_name"], + asset_name=asset["name"] + ) log.info(msg) # == Episode == @@ -252,15 +254,17 @@ class Listener: ep = gazu.shot.get_episode(data["episode_id"]) # Insert doc in DB - self.dbcon.insert_one(create_op_asset(episode)) + self.dbcon.insert_one(create_op_asset(ep)) # Update self._update_episode(data) # Print message - msg = "Episode created: " - msg = msg + f"{episode['project_name']} - " - msg = msg + f"{episode['name']}" + msg = "Episode created: {proj_name} - {ep_name}".format( + proj_name=ep["project_name"], + ep_name=ep["name"] + ) + log.info(msg) def _update_episode(self, data): """Update episode into OP DB.""" @@ -283,8 +287,8 @@ class Listener: # Update update_op_result = update_op_assets( - self.dbcon, gazu_project, project_doc, [ - episode], zou_ids_and_asset_docs + self.dbcon, gazu_project, project_doc, + [ep], zou_ids_and_asset_docs ) if update_op_result: asset_doc_id, asset_update = update_op_result[0] @@ -294,20 +298,22 @@ class Listener: """Delete shot of OP DB.""" set_op_project(self.dbcon, data["project_id"]) - episode = self.dbcon.find_one({"data.zou.id": data['episode_id']}) - if episode: + ep = self.dbcon.find_one({"data.zou.id": data["episode_id"]}) + if ep: # Delete self.dbcon.delete_one( - {"type": "asset", "data.zou.id": data['episode_id']} + {"type": "asset", "data.zou.id": data["episode_id"]} ) # Print message project = gazu.project.get_project( - episode['data']['zou']['project_id']) + ep["data"]["zou"]["project_id"]) - msg = "Episode deleted: " - msg = msg + f"{project['name']} - " - msg = msg + f"{episode['name']}" + msg = "Episode deleted: {proj_name} - {ep_name}".format( + proj_name=project["name"], + ep_name=ep["name"] + ) + log.info(msg) # == Sequence == def _new_sequence(self, data): @@ -325,17 +331,17 @@ class Listener: self._update_sequence(data) # Print message - - episode = None + ep = None ep_id = sequence.get("episode_id") if ep_id and ep_id != "": - episode = gazu.asset.get_episode(ep_id) + ep = gazu.asset.get_episode(ep_id) - msg = "Sequence created: " - msg = msg + f"{sequence['project_name']} - " - if episode is not None: - msg = msg + f"{episode['name']}_" - msg = msg + f"{sequence['name']}" + msg = "Sequence created: {proj_name} - {ep_name}" \ + "{sequence_name}".format( + proj_name=sequence["project_name"], + ep_name=ep["name"] + " - " if ep is not None else "", + sequence_name=sequence["name"] + ) log.info(msg) def _update_sequence(self, data): @@ -377,12 +383,20 @@ class Listener: ) # Print message + ep = None ep_id = sequence["data"]["zou"].get("episode_id") + if ep_id and ep_id != "": + ep = gazu.asset.get_episode(ep_id) + gazu_project = gazu.project.get_project( - sequence['data']['zou']['project_id']) - msg = f"Sequence deleted: " - msg = msg + f"{gazu_project['name']} - " - msg = msg + f"{sequence['name']}" + sequence["data"]["zou"]["project_id"]) + + msg = "Sequence created: {proj_name} - {ep_name}" \ + "{sequence_name}".format( + proj_name=gazu_project["name"], + ep_name=ep["name"] + " - " if ep is not None else "", + sequence_name=sequence["name"] + ) log.info(msg) # == Shot == @@ -401,16 +415,17 @@ class Listener: self._update_shot(data) # Print message - episode = None + ep = None if shot["episode_id"] and shot["episode_id"] != "": - episode = gazu.asset.get_episode(shot['episode_id']) + ep = gazu.asset.get_episode(shot["episode_id"]) - msg = "Shot created: " - msg = msg + f"{shot['project_name']} - " - if episode is not None: - msg = msg + f"{episode['name']}_" - msg = msg + f"{shot['sequence_name']}_" - msg = msg + f"{shot['name']}" + msg = "Shot created: {proj_name} - {ep_name}" \ + "{sequence_name} - {shot_name}".format( + proj_name=shot["project_name"], + ep_name=ep["name"] + " - " if ep is not None else "", + sequence_name=shot["sequence_name"], + shot_name=shot["name"] + ) log.info(msg) def _update_shot(self, data): @@ -430,7 +445,6 @@ class Listener: if asset_doc["data"].get("zou", {}).get("id")} zou_ids_and_asset_docs[shot["project_id"]] = project_doc gazu_project = gazu.project.get_project(shot["project_id"]) - gazu_project = gazu.project.get_project(shot['project_id']) # Update update_op_result = update_op_assets( @@ -454,12 +468,18 @@ class Listener: ) # Print message - gazu_project = gazu.project.get_project( + ep = None ep_id = shot["data"]["zou"].get("episode_id") + if ep_id and ep_id != "": + ep = gazu.asset.get_episode(ep_id) - msg = "Shot deleted: " - msg = msg + f"{gazu_project['name']} - " - msg = msg + f"{shot['name']}" + msg = "Shot deleted: {proj_name} - {ep_name}" \ + "{sequence_name} - {shot_name}".format( + proj_name=shot["data"]["zou"]["project_name"], + ep_name=ep["name"] + " - " if ep is not None else "", + sequence_name=shot["data"]["zou"]["sequence_name"], + shot_name=shot["name"] + ) log.info(msg) # == Task == @@ -472,14 +492,14 @@ class Listener: # Get gazu entity task = gazu.task.get_task(data["task_id"]) - # Find asset doc - episode = None + # Print message + ep = None ep_id = task.get("episode_id") if ep_id and ep_id != "": - episode = gazu.asset.get_episode(ep_id) + ep = gazu.asset.get_episode(ep_id) - parent_name = "" - if episode is not None: + parent_name = None + entity_type = None parent_name = episode['name'] + "_" parent_name = parent_name + \ task['sequence']['name'] + "_" + task['entity']['name'] @@ -496,13 +516,13 @@ class Listener: ) # Print message - msg = "Task created: " - msg = msg + f"{task['project']['name']} - " - if episode is not None: - msg = msg + f"{episode['name']}_" - msg = msg + f"{task['sequence']['name']}_" - msg = msg + f"{task['entity']['name']} - " - msg = msg + f"{task['task_type']['name']}" + msg = "Task created: {proj_name} - {entity_type}{parent_name}" \ + " - {task_name}".format( + proj_name=task["project"]["name"], + entity_type=entity_type + " - " if entity_type is not None else "", + parent_name=parent_name, + task_name=task["task_type"]["name"] + ) log.info(msg) def _update_task(self, data): @@ -533,19 +553,20 @@ class Listener: # Print message entity = gazu.entity.get_entity(task["zou"]["entity_id"]) - episode = None + ep = None ep_id = entity.get("episode_id") if ep_id and ep_id != "": - episode = gazu.asset.get_episode(ep_id) + ep = gazu.asset.get_episode(ep_id) - msg = "Task deleted: " - msg = msg + f"{shot['project_name']} - " - if episode is not None: - msg = msg + f"{episode['name']}_" - msg = msg + f"{shot['sequence_name']}_" - msg = msg + f"{shot['name']} - " - msg = msg + f"{task['type']}" + msg = "Task deleted: {proj_name} - {entity_type}{parent_name}" \ + " - {task_name}".format( + proj_name=task["zou"]["project"]["name"], + entity_type=entity_type + " - " if entity_type is not None else "", + parent_name=parent_name, + task_name=task["type"] + ) log.info(msg) + return From d153e6c224a6d2ea21b11a1c33e88b2c19049123 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Tue, 28 Feb 2023 23:26:10 +0100 Subject: [PATCH 1179/1271] Split up the difference in name and type Assets/Shots generate Before it was only working for shots. Now it also works for Assets. I'm ding an elif as Kitsu now also have tasks for sequences, edits and other things. Will try and add those in at a later stage. --- openpype/modules/kitsu/utils/sync_service.py | 24 +++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index b389d25c4f..7e7f3f557c 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -500,9 +500,15 @@ class Listener: parent_name = None entity_type = None - parent_name = episode['name'] + "_" - parent_name = parent_name + \ - task['sequence']['name'] + "_" + task['entity']['name'] + if task["task_type"]["for_entity"] == "Asset": + parent_name = task["entity"]["name"] + entity_type = task["entity_type"]["name"] + elif task["task_type"]["for_entity"] == "Shot": + parent_name = "{ep_name}{sequence_name} - {shot_name}".format( + ep_name=ep["name"] + " - " if ep is not None else "", + sequence_name=task["sequence"]["name"], + shot_name=task["entity"]["name"] + ) # Update asset tasks with new one asset_doc = get_asset_by_name(project_name, parent_name) @@ -558,6 +564,18 @@ class Listener: if ep_id and ep_id != "": ep = gazu.asset.get_episode(ep_id) + parent_name = None + entity_type = None + if task["task_type"]["for_entity"] == "Asset": + parent_name = task["entity"]["name"] + entity_type = task["entity_type"]["name"] + elif task["task_type"]["for_entity"] == "Shot": + parent_name = "{ep_name}{sequence_name} - {shot_name}".format( + ep_name=ep["name"] + " - " if ep is not None else "", + sequence_name=task["sequence"]["name"], + shot_name=task["entity"]["name"] + ) + msg = "Task deleted: {proj_name} - {entity_type}{parent_name}" \ " - {task_name}".format( proj_name=task["zou"]["project"]["name"], From 7965b91dafd59e0a75b498961d94aa8fdaa14467 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Wed, 1 Mar 2023 11:02:25 +0100 Subject: [PATCH 1180/1271] Moved kitsu_project out of context loop --- .../plugins/publish/collect_kitsu_entities.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py index 1531c80e04..f68226a4a5 100644 --- a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py +++ b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py @@ -11,7 +11,13 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): def process(self, context): - kitsu_project = None + kitsu_project = gazu.project.get_project_by_name( + context.data["projectName"]) + if not kitsu_project: + raise ValueError("Project not found in kitsu!") + + context.data["kitsu_project"] = kitsu_project + self.log.debug("Collect kitsu project: {}".format(kitsu_project)) kitsu_entities_by_id = {} for instance in context: @@ -23,10 +29,10 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): if not zou_asset_data: raise ValueError("Zou asset data not found in OpenPype!") - kitsu_project = gazu.project.get_project( - zou_asset_data["project_id"]) - if not kitsu_project: - raise ValueError("Project not found in kitsu!") + task_name = instance.data.get("task") + if not task_name: + continue + entity_id = zou_asset_data["id"] entity = kitsu_entities_by_id.get(entity_id) @@ -63,5 +69,3 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): raise ValueError("Task not found in kitsu!") instance.data["kitsu_task"] = kitsu_task self.log.debug("Collect kitsu task: {}".format(kitsu_task)) - - context.data["kitsu_project"] = kitsu_project From 962d0783b06d804cacf1ce628d17e1d0836951b2 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Wed, 1 Mar 2023 11:05:38 +0100 Subject: [PATCH 1181/1271] Fixed fetching of kitsu_task + moved data checks to the top of loop --- .../plugins/publish/collect_kitsu_entities.py | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py index f68226a4a5..9b34bd15a9 100644 --- a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py +++ b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py @@ -33,6 +33,9 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): if not task_name: continue + zou_task_data = asset_doc["data"]["tasks"][task_name].get("zou") + self.log.debug( + "Collected zou task data: {}".format(zou_task_data)) entity_id = zou_asset_data["id"] entity = kitsu_entities_by_id.get(entity_id) @@ -45,25 +48,26 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): kitsu_entities_by_id[entity_id] = entity instance.data["entity"] = entity - - task_name = instance.data.get("task") - if not task_name: - continue - zou_task_data = asset_doc["data"]["tasks"][task_name].get("zou") self.log.debug( - "Collected zou task data: {}".format(zou_task_data)) - if not zou_task_data: + "Collect kitsu {}: {}".format(zou_asset_data["type"], entity) + ) + + if zou_task_data: + kitsu_task_id = zou_task_data["id"] + kitsu_task = kitsu_entities_by_id.get(kitsu_task_id) + if not kitsu_task: + kitsu_task = gazu.task.get_task(zou_task_data["id"]) + kitsu_entities_by_id[kitsu_task_id] = kitsu_task + else: kitsu_task_type = gazu.task.get_task_type_by_name(task_name) if not kitsu_task_type: raise ValueError( "Task type {} not found in Kitsu!".format(task_name) ) - continue - kitsu_task_id = zou_task_data["id"] - kitsu_task = kitsu_entities_by_id.get(kitsu_task_id) - if not kitsu_task: - kitsu_task = gazu.task.get_task(zou_task_data["id"]) - kitsu_entities_by_id[kitsu_task_id] = kitsu_task + + kitsu_task = gazu.task.get_task_by_name( + entity, kitsu_task_type + ) if not kitsu_task: raise ValueError("Task not found in kitsu!") From 8bf970e8b96b80c2cae601530d076b27d3a6d8f7 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Wed, 1 Mar 2023 11:16:05 +0100 Subject: [PATCH 1182/1271] fixed hound's comments --- .../kitsu/plugins/publish/collect_kitsu_entities.py | 2 +- openpype/modules/kitsu/utils/sync_service.py | 9 ++++++--- openpype/modules/kitsu/utils/update_op_with_zou.py | 6 ++++-- openpype/modules/kitsu/utils/update_zou_with_op.py | 3 ++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py index 9b34bd15a9..71ed563580 100644 --- a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py +++ b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py @@ -64,7 +64,7 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): raise ValueError( "Task type {} not found in Kitsu!".format(task_name) ) - + kitsu_task = gazu.task.get_task_by_name( entity, kitsu_task_type ) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 7e7f3f557c..9c5c9e24ec 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -101,7 +101,8 @@ class Listener: self.event_client, "sequence:delete", self._delete_sequence ) - gazu.events.add_listener(self.event_client, "shot:new", self._new_shot) + gazu.events.add_listener( + self.event_client, "shot:new", self._new_shot) gazu.events.add_listener( self.event_client, "shot:update", self._update_shot ) @@ -109,7 +110,8 @@ class Listener: self.event_client, "shot:delete", self._delete_shot ) - gazu.events.add_listener(self.event_client, "task:new", self._new_task) + gazu.events.add_listener( + self.event_client, "task:new", self._new_task) gazu.events.add_listener( self.event_client, "task:update", self._update_task ) @@ -515,7 +517,8 @@ class Listener: if asset_doc: asset_tasks = asset_doc["data"].get("tasks") task_type_name = task["task_type"]["name"] - asset_tasks[task_type_name] = {"type": task_type_name, "zou": task} + asset_tasks[task_type_name] = { + "type": task_type_name, "zou": task} self.dbcon.update_one( {"_id": asset_doc["_id"]}, {"$set": {"data.tasks": asset_tasks}} diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 9368848532..6590d05a82 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -94,7 +94,8 @@ def update_op_assets( if not item_doc: # Create asset op_asset = create_op_asset(item) insert_result = dbcon.insert_one(op_asset) - item_doc = get_asset_by_id(project_name, insert_result.inserted_id) + item_doc = get_asset_by_id( + project_name, insert_result.inserted_id) # Update asset item_data = deepcopy(item_doc["data"]) @@ -339,7 +340,8 @@ def write_project_to_op(project: dict, dbcon: AvalonMongoDB) -> UpdateOne: ) -def sync_all_projects(login: str, password: str, ignore_projects: list = None): +def sync_all_projects( + login: str, password: str, ignore_projects: list = None): """Update all OP projects in DB with Zou data. Args: diff --git a/openpype/modules/kitsu/utils/update_zou_with_op.py b/openpype/modules/kitsu/utils/update_zou_with_op.py index b1a9b8b82c..617f037c1e 100644 --- a/openpype/modules/kitsu/utils/update_zou_with_op.py +++ b/openpype/modules/kitsu/utils/update_zou_with_op.py @@ -82,7 +82,8 @@ def sync_zou_from_op_project( f"x{project_doc['data']['resolutionHeight']}", } ) - gazu.project.update_project_data(zou_project, data=project_doc["data"]) + gazu.project.update_project_data( + zou_project, data=project_doc["data"]) gazu.project.update_project(zou_project) asset_types = gazu.asset.all_asset_types() From 0d981a61291d78f25c53425de4161d3f109f1505 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Wed, 1 Mar 2023 11:42:18 +0100 Subject: [PATCH 1183/1271] Fixed hound's comments --- openpype/modules/kitsu/utils/sync_service.py | 41 ++++++++++---------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 9c5c9e24ec..da81a23495 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -501,10 +501,10 @@ class Listener: ep = gazu.asset.get_episode(ep_id) parent_name = None - entity_type = None + ent_type = None if task["task_type"]["for_entity"] == "Asset": parent_name = task["entity"]["name"] - entity_type = task["entity_type"]["name"] + ent_type = task["entity_type"]["name"] elif task["task_type"]["for_entity"] == "Shot": parent_name = "{ep_name}{sequence_name} - {shot_name}".format( ep_name=ep["name"] + " - " if ep is not None else "", @@ -525,12 +525,12 @@ class Listener: ) # Print message - msg = "Task created: {proj_name} - {entity_type}{parent_name}" \ - " - {task_name}".format( - proj_name=task["project"]["name"], - entity_type=entity_type + " - " if entity_type is not None else "", - parent_name=parent_name, - task_name=task["task_type"]["name"] + msg = "Task created: {proj} - {ent_type}{parent}" \ + " - {task}".format( + proj=task["project"]["name"], + ent_type=ent_type + " - " if ent_type is not None else "", + parent=parent_name, + task=task["task_type"]["name"] ) log.info(msg) @@ -568,23 +568,24 @@ class Listener: ep = gazu.asset.get_episode(ep_id) parent_name = None - entity_type = None + ent_type = None if task["task_type"]["for_entity"] == "Asset": parent_name = task["entity"]["name"] - entity_type = task["entity_type"]["name"] + ent_type = task["entity_type"]["name"] elif task["task_type"]["for_entity"] == "Shot": - parent_name = "{ep_name}{sequence_name} - {shot_name}".format( - ep_name=ep["name"] + " - " if ep is not None else "", - sequence_name=task["sequence"]["name"], - shot_name=task["entity"]["name"] + parent_name = "{ep}{sequence} - {shot}".format( + ep=ep["name"] + " - " if ep is not None else "", + sequence=task["sequence"]["name"], + shot=task["entity"]["name"] ) - msg = "Task deleted: {proj_name} - {entity_type}{parent_name}" \ - " - {task_name}".format( - proj_name=task["zou"]["project"]["name"], - entity_type=entity_type + " - " if entity_type is not None else "", - parent_name=parent_name, - task_name=task["type"] + ent_type=ent_type + " - " if ent_type is not None else "", + msg = "Task deleted: {proj} - {ent_type}{parent}" \ + " - {task}".format( + proj=task["zou"]["project"]["name"], + ent_type=ent_type, + parent=parent_name, + task=task["type"] ) log.info(msg) From 0beec8c3a710e7c3e950678a4c0b6606f4f079d7 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Wed, 1 Mar 2023 11:44:55 +0100 Subject: [PATCH 1184/1271] Fixed hound's comments --- openpype/modules/kitsu/utils/sync_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index da81a23495..91ce84637d 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -579,7 +579,7 @@ class Listener: shot=task["entity"]["name"] ) - ent_type=ent_type + " - " if ent_type is not None else "", + ent_type=ent_type + " - " if ent_type is not None else "" msg = "Task deleted: {proj} - {ent_type}{parent}" \ " - {task}".format( proj=task["zou"]["project"]["name"], From 247778575f2e5c03cf055da288b444034bca4475 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Mar 2023 12:30:19 +0100 Subject: [PATCH 1185/1271] Project creation logs happens outside of write_project_to_op() function --- openpype/modules/kitsu/utils/sync_service.py | 12 ++++++------ openpype/modules/kitsu/utils/update_op_with_zou.py | 9 ++++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 91ce84637d..172f7555ac 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -129,12 +129,9 @@ class Listener: """Create new project into OP DB.""" # Use update process to avoid duplicating code - self._update_project(data) + self._update_project(data, new_project=True) - # Print message - # - Happens in write_project_to_op() - - def _update_project(self, data): + def _update_project(self, data, new_project=False): """Update project into OP DB.""" # Get project entity project = gazu.project.get_project(data["project_id"]) @@ -147,6 +144,9 @@ class Listener: data["project_id"]) self.dbcon.bulk_write([update_project]) + if new_project: + log.info("Project created: {}".format(project["name"])) + def _delete_project(self, data): """Delete project.""" @@ -579,7 +579,7 @@ class Listener: shot=task["entity"]["name"] ) - ent_type=ent_type + " - " if ent_type is not None else "" + ent_type = ent_type + " - " if ent_type is not None else "" msg = "Task deleted: {proj} - {ent_type}{parent}" \ " - {task}".format( proj=task["zou"]["project"]["name"], diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 6590d05a82..a559d8a19f 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -291,7 +291,6 @@ def write_project_to_op(project: dict, dbcon: AvalonMongoDB) -> UpdateOne: project_name = project["name"] project_dict = get_project(project_name) if not project_dict: - log.info("Project created: {}".format(project_name)) project_dict = create_project(project_name, project_name) # Project data and tasks @@ -405,12 +404,16 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): ] # Sync project. Create if doesn't exist + project_name = project["name"] + project_dict = get_project(project_name) + if not project_dict: + log.info("Project created: {}".format(project_name)) bulk_writes.append(write_project_to_op(project, dbcon)) # Try to find project document - project_name = project["name"] + if not project_dict: + project_dict = get_project(project_name) dbcon.Session["AVALON_PROJECT"] = project_name - project_dict = get_project(project_name) # Query all assets of the local project zou_ids_and_asset_docs = { From 5a9ea0d130ba6009804ac479e3ebe6ef4ff46906 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Mar 2023 12:36:57 +0100 Subject: [PATCH 1186/1271] Changed back from dict to doc for var names --- .../modules/kitsu/utils/update_op_with_zou.py | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index a559d8a19f..c215126dac 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -65,32 +65,32 @@ def set_op_project(dbcon: AvalonMongoDB, project_id: str): def update_op_assets( dbcon: AvalonMongoDB, gazu_project: dict, - project_dict: dict, + project_doc: dict, entities_list: List[dict], - asset_dict_ids: Dict[str, dict], + asset_doc_ids: Dict[str, dict], ) -> List[Dict[str, dict]]: """Update OpenPype assets. Set 'data' and 'parent' fields. Args: dbcon (AvalonMongoDB): Connection to DB - gazu_project dict): Dict of gazu, - project_dict dict): Dict of project, + gazu_project (dict): Dict of gazu, + project_doc (dict): Dict of project, entities_list (List[dict]): List of zou entities to update - asset_dict_ids (Dict[str, dict]): Dicts of [{zou_id: asset_doc}, ...] + asset_doc_ids (Dict[str, dict]): Dicts of [{zou_id: asset_doc}, ...] Returns: List[Dict[str, dict]]: List of (doc_id, update_dict) tuples """ - if not project_dict: + if not project_doc: return - project_name = project_dict["name"] + project_name = project_doc["name"] assets_with_update = [] for item in entities_list: # Check asset exists - item_doc = asset_dict_ids.get(item["id"]) + item_doc = asset_doc_ids.get(item["id"]) if not item_doc: # Create asset op_asset = create_op_asset(item) insert_result = dbcon.insert_one(op_asset) @@ -108,7 +108,7 @@ def update_op_assets( try: frame_in = int( item_data.pop( - "frame_in", project_dict["data"].get("frameStart") + "frame_in", project_doc["data"].get("frameStart") ) ) except (TypeError, ValueError): @@ -127,14 +127,14 @@ def update_op_assets( if frames_duration: frame_out = frame_in + frames_duration - 1 else: - frame_out = project_dict["data"].get("frameEnd", frame_in) + frame_out = project_doc["data"].get("frameEnd", frame_in) item_data["frameEnd"] = frame_out # Fps, fallback to project's value or default value (25.0) try: fps = float(item_data.get("fps")) except (TypeError, ValueError): fps = float(gazu_project.get( - "fps", project_dict["data"].get("fps", 25))) + "fps", project_doc["data"].get("fps", 25))) item_data["fps"] = fps # Resolution, fall back to project default match_res = re.match( @@ -145,27 +145,27 @@ def update_op_assets( item_data["resolutionWidth"] = int(match_res.group(1)) item_data["resolutionHeight"] = int(match_res.group(2)) else: - item_data["resolutionWidth"] = project_dict["data"].get( + item_data["resolutionWidth"] = project_doc["data"].get( "resolutionWidth") - item_data["resolutionHeight"] = project_dict["data"].get( + item_data["resolutionHeight"] = project_doc["data"].get( "resolutionHeight") # Properties that doesn't fully exist in Kitsu. # Guessing those property names below: # Pixel Aspect Ratio item_data["pixelAspect"] = item_data.get( - "pixel_aspect", project_dict["data"].get("pixelAspect")) + "pixel_aspect", project_doc["data"].get("pixelAspect")) # Handle Start item_data["handleStart"] = item_data.get( - "handle_start", project_dict["data"].get("handleStart")) + "handle_start", project_doc["data"].get("handleStart")) # Handle End item_data["handleEnd"] = item_data.get( - "handle_end", project_dict["data"].get("handleEnd")) + "handle_end", project_doc["data"].get("handleEnd")) # Clip In item_data["clipIn"] = item_data.get( - "clip_in", project_dict["data"].get("clipIn")) + "clip_in", project_doc["data"].get("clipIn")) # Clip Out item_data["clipOut"] = item_data.get( - "clip_out", project_dict["data"].get("clipOut")) + "clip_out", project_doc["data"].get("clipOut")) # Tasks tasks_list = [] @@ -209,7 +209,7 @@ def update_op_assets( # Root parent folder if exist visual_parent_doc_id = None if parent_zou_id is not None: - parent_zou_id_dict = asset_dict_ids.get(parent_zou_id) + parent_zou_id_dict = asset_doc_ids.get(parent_zou_id) if parent_zou_id_dict is not None: visual_parent_doc_id = ( parent_zou_id_dict.get("_id") @@ -233,7 +233,7 @@ def update_op_assets( item_data["parents"] = [] ancestor_id = parent_zou_id while ancestor_id is not None: - parent_doc = asset_dict_ids.get(ancestor_id) + parent_doc = asset_doc_ids.get(ancestor_id) if parent_doc is not None: item_data["parents"].insert(0, parent_doc["name"]) @@ -250,7 +250,7 @@ def update_op_assets( item_name = f"{item_data['parents'][-1]}_{item['name']}" # Update doc name - asset_dict_ids[item["id"]]["name"] = item_name + asset_doc_ids[item["id"]]["name"] = item_name else: item_name = item["name"] @@ -269,7 +269,7 @@ def update_op_assets( "$set": { "name": item_name, "data": item_data, - "parent": project_dict["_id"], + "parent": project_doc["_id"], } }, ) From 93eb9fce8609b0d6543a1f91ddb84907f89b63a3 Mon Sep 17 00:00:00 2001 From: Ember Light <49758407+EmberLightVFX@users.noreply.github.com> Date: Thu, 2 Mar 2023 12:38:37 +0100 Subject: [PATCH 1187/1271] Update openpype/modules/kitsu/utils/sync_service.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Félix David --- openpype/modules/kitsu/utils/sync_service.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 172f7555ac..1efebb2d47 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -178,10 +178,11 @@ class Listener: self._update_asset(data) # Print message - ep = None ep_id = asset.get("episode_id") if ep_id and ep_id != "": ep = gazu.asset.get_episode(ep_id) + else: + ep = None msg = "Asset created: {proj_name} - {ep_name}" \ "{asset_type_name} - {asset_name}".format( From 95e1f95bc1896547bde026f5c2a2517103e9e8e8 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Mar 2023 14:05:36 +0100 Subject: [PATCH 1188/1271] Moved all ep_dict code into one function --- openpype/modules/kitsu/utils/sync_service.py | 35 ++++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 1efebb2d47..d6bdb5391e 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -124,6 +124,11 @@ class Listener: log.info("Listening to Kitsu events...") gazu.events.run_client(self.event_client) + def get_ep_dict(self, ep_id): + if ep_id and ep_id != "": + return gazu.entity.get_entity(ep_id) + return + # == Project == def _new_project(self, data): """Create new project into OP DB.""" @@ -179,10 +184,7 @@ class Listener: # Print message ep_id = asset.get("episode_id") - if ep_id and ep_id != "": - ep = gazu.asset.get_episode(ep_id) - else: - ep = None + ep = self.get_ep_dict(ep_id) msg = "Asset created: {proj_name} - {ep_name}" \ "{asset_type_name} - {asset_name}".format( @@ -233,10 +235,8 @@ class Listener: ) # Print message - ep = None ep_id = asset["data"]["zou"].get("episode_id") - if ep_id and ep_id != "": - ep = gazu.asset.get_episode(ep_id) + ep = self.get_ep_dict(ep_id) msg = "Asset deleted: {proj_name} - {ep_name}" \ "{asset_type_name} - {asset_name}".format( @@ -334,10 +334,8 @@ class Listener: self._update_sequence(data) # Print message - ep = None ep_id = sequence.get("episode_id") - if ep_id and ep_id != "": - ep = gazu.asset.get_episode(ep_id) + ep = self.get_ep_dict(ep_id) msg = "Sequence created: {proj_name} - {ep_name}" \ "{sequence_name}".format( @@ -386,10 +384,8 @@ class Listener: ) # Print message - ep = None ep_id = sequence["data"]["zou"].get("episode_id") - if ep_id and ep_id != "": - ep = gazu.asset.get_episode(ep_id) + ep = self.get_ep_dict(ep_id) gazu_project = gazu.project.get_project( sequence["data"]["zou"]["project_id"]) @@ -418,9 +414,8 @@ class Listener: self._update_shot(data) # Print message - ep = None - if shot["episode_id"] and shot["episode_id"] != "": - ep = gazu.asset.get_episode(shot["episode_id"]) + ep_id = shot["episode_id"] + ep = self.get_ep_dict(ep_id) msg = "Shot created: {proj_name} - {ep_name}" \ "{sequence_name} - {shot_name}".format( @@ -471,10 +466,8 @@ class Listener: ) # Print message - ep = None ep_id = shot["data"]["zou"].get("episode_id") - if ep_id and ep_id != "": - ep = gazu.asset.get_episode(ep_id) + ep = self.get_ep_dict(ep_id) msg = "Shot deleted: {proj_name} - {ep_name}" \ "{sequence_name} - {shot_name}".format( @@ -496,10 +489,8 @@ class Listener: task = gazu.task.get_task(data["task_id"]) # Print message - ep = None ep_id = task.get("episode_id") - if ep_id and ep_id != "": - ep = gazu.asset.get_episode(ep_id) + ep = self.get_ep_dict(ep_id) parent_name = None ent_type = None From 966ba0166e349b2882cb5db1f686b6235abbd44b Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Mar 2023 14:07:14 +0100 Subject: [PATCH 1189/1271] Fixed delete_task msg creation to work with assets and episodes --- openpype/modules/kitsu/utils/sync_service.py | 41 ++++++++++---------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index d6bdb5391e..893d6a8b5e 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -554,31 +554,32 @@ class Listener: # Print message entity = gazu.entity.get_entity(task["zou"]["entity_id"]) - ep = None - ep_id = entity.get("episode_id") - if ep_id and ep_id != "": - ep = gazu.asset.get_episode(ep_id) + if entity["type"] == "Asset": + ep = self.get_ep_dict(entity["source_id"]) + + parent_name = "{ep}{entity_type} - {entity}".format( + ep=ep["name"] + " - " if ep is not None else "", + entity_type=task["zou"]["entity_type_name"], + entity=task["zou"]["entity_name"] + ) + elif entity["type"] == "Shot": + shot_dict = gazu.entity.get_entity( + task["zou"]["entity_id"]) + seq_dict = gazu.entity.get_entity( + shot_dict["parent_id"]) + ep = self.get_ep_dict(seq_dict["parent_id"]) - parent_name = None - ent_type = None - if task["task_type"]["for_entity"] == "Asset": - parent_name = task["entity"]["name"] - ent_type = task["entity_type"]["name"] - elif task["task_type"]["for_entity"] == "Shot": parent_name = "{ep}{sequence} - {shot}".format( ep=ep["name"] + " - " if ep is not None else "", - sequence=task["sequence"]["name"], - shot=task["entity"]["name"] + sequence=seq_dict["name"], + shot=shot_dict["name"] ) - ent_type = ent_type + " - " if ent_type is not None else "" - msg = "Task deleted: {proj} - {ent_type}{parent}" \ - " - {task}".format( - proj=task["zou"]["project"]["name"], - ent_type=ent_type, - parent=parent_name, - task=task["type"] - ) + msg = "Task deleted: {proj} - {parent} - {task}".format( + proj=task["zou"]["project_name"], + parent=parent_name, + task=task["zou"]["task_type_name"] + ) log.info(msg) return From 804cdcafd6268d84a3c7d2e887c9e05e5798dec4 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Mar 2023 19:33:11 +0100 Subject: [PATCH 1190/1271] Store the gazu asset data in OPs DB as sync_service does This isn't the most optimal way to do it but it makes sure the data is consistent through out the code until we can revision sync_service to only use the ID from the dict. --- openpype/modules/kitsu/utils/update_op_with_zou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index c215126dac..6797df6344 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -175,7 +175,7 @@ def update_op_assets( elif item_type == "Shot": tasks_list = gazu.task.all_tasks_for_shot(item) item_data["tasks"] = { - t["task_type_name"]: {"type": t["task_type_name"], "zou": t} + t["task_type_name"]: {"type": t["task_type_name"], "zou": gazu.task.get_task(t["id"])} for t in tasks_list } From 7176be9f92e71ee6942971e25ffc72199c1eecf8 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Thu, 2 Mar 2023 19:35:07 +0100 Subject: [PATCH 1191/1271] Log msg for new_task now work for both shot and assets --- openpype/modules/kitsu/utils/sync_service.py | 37 ++++++++++---------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 893d6a8b5e..1f12217d44 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -239,10 +239,10 @@ class Listener: ep = self.get_ep_dict(ep_id) msg = "Asset deleted: {proj_name} - {ep_name}" \ - "{asset_type_name} - {asset_name}".format( + "{type_name} - {asset_name}".format( proj_name=asset["data"]["zou"]["project_name"], ep_name=ep["name"] + " - " if ep is not None else "", - asset_type_name=asset["data"]["zou"]["asset_type_name"], + type_name=asset["data"]["zou"]["asset_type_name"], asset_name=asset["name"] ) log.info(msg) @@ -390,7 +390,7 @@ class Listener: gazu_project = gazu.project.get_project( sequence["data"]["zou"]["project_id"]) - msg = "Sequence created: {proj_name} - {ep_name}" \ + msg = "Sequence deleted: {proj_name} - {ep_name}" \ "{sequence_name}".format( proj_name=gazu_project["name"], ep_name=ep["name"] + " - " if ep is not None else "", @@ -493,9 +493,12 @@ class Listener: ep = self.get_ep_dict(ep_id) parent_name = None + asset_name = None ent_type = None + if task["task_type"]["for_entity"] == "Asset": parent_name = task["entity"]["name"] + asset_name = task["entity"]["name"] ent_type = task["entity_type"]["name"] elif task["task_type"]["for_entity"] == "Shot": parent_name = "{ep_name}{sequence_name} - {shot_name}".format( @@ -503,9 +506,14 @@ class Listener: sequence_name=task["sequence"]["name"], shot_name=task["entity"]["name"] ) + asset_name = "{ep_name}{sequence_name}_{shot_name}".format( + ep_name=ep["name"] + "_" if ep is not None else "", + sequence_name=task["sequence"]["name"], + shot_name=task["entity"]["name"] + ) # Update asset tasks with new one - asset_doc = get_asset_by_name(project_name, parent_name) + asset_doc = get_asset_by_name(project_name, asset_name) if asset_doc: asset_tasks = asset_doc["data"].get("tasks") task_type_name = task["task_type"]["name"] @@ -553,32 +561,25 @@ class Listener: # Print message entity = gazu.entity.get_entity(task["zou"]["entity_id"]) + ep = self.get_ep_dict(entity["source_id"]) if entity["type"] == "Asset": - ep = self.get_ep_dict(entity["source_id"]) - parent_name = "{ep}{entity_type} - {entity}".format( ep=ep["name"] + " - " if ep is not None else "", - entity_type=task["zou"]["entity_type_name"], - entity=task["zou"]["entity_name"] + entity_type=task["zou"]["entity_type"]["name"], + entity=task["zou"]["entity"]["name"] ) elif entity["type"] == "Shot": - shot_dict = gazu.entity.get_entity( - task["zou"]["entity_id"]) - seq_dict = gazu.entity.get_entity( - shot_dict["parent_id"]) - ep = self.get_ep_dict(seq_dict["parent_id"]) - parent_name = "{ep}{sequence} - {shot}".format( ep=ep["name"] + " - " if ep is not None else "", - sequence=seq_dict["name"], - shot=shot_dict["name"] + sequence=task["zou"]["sequence"]["name"], + shot=task["zou"]["entity"]["name"] ) msg = "Task deleted: {proj} - {parent} - {task}".format( - proj=task["zou"]["project_name"], + proj=task["zou"]["project"]["name"], parent=parent_name, - task=task["zou"]["task_type_name"] + task=name ) log.info(msg) From c50678bcb8c0a1cb2696fbb526d61cbe4261a361 Mon Sep 17 00:00:00 2001 From: Ember Light <49758407+EmberLightVFX@users.noreply.github.com> Date: Fri, 3 Mar 2023 10:34:17 +0100 Subject: [PATCH 1192/1271] Update openpype/modules/kitsu/utils/update_op_with_zou.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Félix David --- openpype/modules/kitsu/utils/update_op_with_zou.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 6797df6344..73b7a4249d 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -175,7 +175,12 @@ def update_op_assets( elif item_type == "Shot": tasks_list = gazu.task.all_tasks_for_shot(item) item_data["tasks"] = { - t["task_type_name"]: {"type": t["task_type_name"], "zou": gazu.task.get_task(t["id"])} + item_data["tasks"] = { + t["task_type_name"]: { + "type": t["task_type_name"], + "zou": gazu.task.get_task(t["id"]), + } + } for t in tasks_list } From 8fc6978ea2f56778d794e213c541f89888b24795 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 3 Mar 2023 11:10:12 +0100 Subject: [PATCH 1193/1271] Formatted with Black --- .../kitsu/actions/launcher_show_in_kitsu.py | 65 +++++----- .../publish/collect_kitsu_credential.py | 1 - .../plugins/publish/collect_kitsu_entities.py | 11 +- .../plugins/publish/integrate_kitsu_note.py | 5 +- .../plugins/publish/integrate_kitsu_review.py | 1 - openpype/modules/kitsu/utils/sync_service.py | 114 ++++++++++++------ .../modules/kitsu/utils/update_zou_with_op.py | 9 +- 7 files changed, 123 insertions(+), 83 deletions(-) diff --git a/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py b/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py index 11224f6e52..81d98cfffb 100644 --- a/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py +++ b/openpype/modules/kitsu/actions/launcher_show_in_kitsu.py @@ -23,36 +23,37 @@ class ShowInKitsu(LauncherAction): return True def process(self, session, **kwargs): - # Context inputs project_name = session["AVALON_PROJECT"] asset_name = session.get("AVALON_ASSET", None) task_name = session.get("AVALON_TASK", None) - project = get_project(project_name=project_name, - fields=["data.zou_id"]) + project = get_project( + project_name=project_name, fields=["data.zou_id"] + ) if not project: raise RuntimeError("Project {} not found.".format(project_name)) project_zou_id = project["data"].get("zou_id") if not project_zou_id: raise RuntimeError( - "Project {} has no connected kitsu id.".format(project_name)) + "Project {} has no connected kitsu id.".format(project_name) + ) asset_zou_name = None asset_zou_id = None - asset_zou_type = 'Assets' + asset_zou_type = "Assets" task_zou_id = None - zou_sub_type = ['AssetType', 'Sequence'] + zou_sub_type = ["AssetType", "Sequence"] if asset_name: asset_zou_name = asset_name asset_fields = ["data.zou.id", "data.zou.type"] if task_name: asset_fields.append("data.tasks.{}.zou.id".format(task_name)) - asset = get_asset_by_name(project_name, - asset_name=asset_name, - fields=asset_fields) + asset = get_asset_by_name( + project_name, asset_name=asset_name, fields=asset_fields + ) asset_zou_data = asset["data"].get("zou") @@ -68,37 +69,43 @@ class ShowInKitsu(LauncherAction): task_zou_data = task_data.get("zou", {}) if not task_zou_data: self.log.debug( - "No zou task data for task: {}".format(task_name)) + "No zou task data for task: {}".format(task_name) + ) task_zou_id = task_zou_data["id"] # Define URL - url = self.get_url(project_id=project_zou_id, - asset_name=asset_zou_name, - asset_id=asset_zou_id, - asset_type=asset_zou_type, - task_id=task_zou_id) + url = self.get_url( + project_id=project_zou_id, + asset_name=asset_zou_name, + asset_id=asset_zou_id, + asset_type=asset_zou_type, + task_id=task_zou_id, + ) # Open URL in webbrowser self.log.info("Opening URL: {}".format(url)) - webbrowser.open(url, - # Try in new tab - new=2) + webbrowser.open( + url, + # Try in new tab + new=2, + ) - def get_url(self, - project_id, - asset_name=None, - asset_id=None, - asset_type=None, - task_id=None): - - shots_url = {'Shots', 'Sequence', 'Shot'} - sub_type = {'AssetType', 'Sequence'} + def get_url( + self, + project_id, + asset_name=None, + asset_id=None, + asset_type=None, + task_id=None, + ): + shots_url = {"Shots", "Sequence", "Shot"} + sub_type = {"AssetType", "Sequence"} kitsu_module = self.get_kitsu_module() # Get kitsu url with /api stripped kitsu_url = kitsu_module.server_url if kitsu_url.endswith("/api"): - kitsu_url = kitsu_url[:-len("/api")] + kitsu_url = kitsu_url[: -len("/api")] sub_url = f"/productions/{project_id}" asset_type_url = "shots" if asset_type in shots_url else "assets" @@ -121,6 +128,6 @@ class ShowInKitsu(LauncherAction): # Add search method if is a sub_type sub_url += f"/{asset_type_url}" if asset_type in sub_type: - sub_url += f'?search={asset_name}' + sub_url += f"?search={asset_name}" return f"{kitsu_url}{sub_url}" diff --git a/openpype/modules/kitsu/plugins/publish/collect_kitsu_credential.py b/openpype/modules/kitsu/plugins/publish/collect_kitsu_credential.py index b7f6f67a40..ac501dd47d 100644 --- a/openpype/modules/kitsu/plugins/publish/collect_kitsu_credential.py +++ b/openpype/modules/kitsu/plugins/publish/collect_kitsu_credential.py @@ -13,6 +13,5 @@ class CollectKitsuSession(pyblish.api.ContextPlugin): # rename log in # families = ["kitsu"] def process(self, context): - gazu.client.set_host(os.environ["KITSU_SERVER"]) gazu.log_in(os.environ["KITSU_LOGIN"], os.environ["KITSU_PWD"]) diff --git a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py index 71ed563580..a0bd2b305b 100644 --- a/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py +++ b/openpype/modules/kitsu/plugins/publish/collect_kitsu_entities.py @@ -10,9 +10,9 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): label = "Kitsu entities" def process(self, context): - kitsu_project = gazu.project.get_project_by_name( - context.data["projectName"]) + context.data["projectName"] + ) if not kitsu_project: raise ValueError("Project not found in kitsu!") @@ -35,7 +35,8 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): zou_task_data = asset_doc["data"]["tasks"][task_name].get("zou") self.log.debug( - "Collected zou task data: {}".format(zou_task_data)) + "Collected zou task data: {}".format(zou_task_data) + ) entity_id = zou_asset_data["id"] entity = kitsu_entities_by_id.get(entity_id) @@ -44,7 +45,9 @@ class CollectKitsuEntities(pyblish.api.ContextPlugin): if not entity: raise ValueError( "{} was not found in kitsu!".format( - zou_asset_data["name"])) + zou_asset_data["name"] + ) + ) kitsu_entities_by_id[entity_id] = entity instance.data["entity"] = entity diff --git a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py index 006f0bc6d0..6702cbe7aa 100644 --- a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py +++ b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_note.py @@ -13,7 +13,6 @@ class IntegrateKitsuNote(pyblish.api.ContextPlugin): note_status_shortname = "wfa" def process(self, context): - # Get comment text body publish_comment = context.data.get("comment") if not publish_comment: @@ -45,9 +44,7 @@ class IntegrateKitsuNote(pyblish.api.ContextPlugin): # Add comment to kitsu task task_id = kitsu_task["id"] - self.log.debug( - "Add new note in taks id {}".format(task_id) - ) + self.log.debug("Add new note in taks id {}".format(task_id)) kitsu_comment = gazu.task.add_comment( task_id, note_status, comment=publish_comment ) diff --git a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py index d8f6cb7ac8..12482b5657 100644 --- a/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py +++ b/openpype/modules/kitsu/plugins/publish/integrate_kitsu_review.py @@ -12,7 +12,6 @@ class IntegrateKitsuReview(pyblish.api.InstancePlugin): optional = True def process(self, instance): - task = instance.data["kitsu_task"]["id"] comment = instance.data["kitsu_comment"]["id"] diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 1f12217d44..34714fa4b3 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -102,7 +102,8 @@ class Listener: ) gazu.events.add_listener( - self.event_client, "shot:new", self._new_shot) + self.event_client, "shot:new", self._new_shot + ) gazu.events.add_listener( self.event_client, "shot:update", self._update_shot ) @@ -111,7 +112,8 @@ class Listener: ) gazu.events.add_listener( - self.event_client, "task:new", self._new_task) + self.event_client, "task:new", self._new_task + ) gazu.events.add_listener( self.event_client, "task:update", self._update_task ) @@ -146,7 +148,8 @@ class Listener: # Write into DB if update_project: self.dbcon.Session["AVALON_PROJECT"] = get_kitsu_project_name( - data["project_id"]) + data["project_id"] + ) self.dbcon.bulk_write([update_project]) if new_project: @@ -158,7 +161,8 @@ class Listener: collections = self.dbcon.database.list_collection_names() for collection in collections: project = self.dbcon.database[collection].find_one( - {"data.zou_id": data["project_id"]}) + {"data.zou_id": data["project_id"]} + ) if project: # Delete project collection self.dbcon.database[project["name"]].drop() @@ -186,13 +190,15 @@ class Listener: ep_id = asset.get("episode_id") ep = self.get_ep_dict(ep_id) - msg = "Asset created: {proj_name} - {ep_name}" \ + msg = ( + "Asset created: {proj_name} - {ep_name}" "{asset_type_name} - {asset_name}".format( proj_name=asset["project_name"], ep_name=ep["name"] + " - " if ep is not None else "", asset_type_name=asset["asset_type_name"], - asset_name=asset["name"] + asset_name=asset["name"], ) + ) log.info(msg) def _update_asset(self, data): @@ -216,8 +222,11 @@ class Listener: # Update update_op_result = update_op_assets( - self.dbcon, gazu_project, project_doc, - [asset], zou_ids_and_asset_docs + self.dbcon, + gazu_project, + project_doc, + [asset], + zou_ids_and_asset_docs, ) if update_op_result: asset_doc_id, asset_update = update_op_result[0] @@ -238,13 +247,15 @@ class Listener: ep_id = asset["data"]["zou"].get("episode_id") ep = self.get_ep_dict(ep_id) - msg = "Asset deleted: {proj_name} - {ep_name}" \ + msg = ( + "Asset deleted: {proj_name} - {ep_name}" "{type_name} - {asset_name}".format( proj_name=asset["data"]["zou"]["project_name"], ep_name=ep["name"] + " - " if ep is not None else "", type_name=asset["data"]["zou"]["asset_type_name"], - asset_name=asset["name"] + asset_name=asset["name"], ) + ) log.info(msg) # == Episode == @@ -264,8 +275,7 @@ class Listener: # Print message msg = "Episode created: {proj_name} - {ep_name}".format( - proj_name=ep["project_name"], - ep_name=ep["name"] + proj_name=ep["project_name"], ep_name=ep["name"] ) log.info(msg) @@ -290,8 +300,11 @@ class Listener: # Update update_op_result = update_op_assets( - self.dbcon, gazu_project, project_doc, - [ep], zou_ids_and_asset_docs + self.dbcon, + gazu_project, + project_doc, + [ep], + zou_ids_and_asset_docs, ) if update_op_result: asset_doc_id, asset_update = update_op_result[0] @@ -310,11 +323,11 @@ class Listener: # Print message project = gazu.project.get_project( - ep["data"]["zou"]["project_id"]) + ep["data"]["zou"]["project_id"] + ) msg = "Episode deleted: {proj_name} - {ep_name}".format( - proj_name=project["name"], - ep_name=ep["name"] + proj_name=project["name"], ep_name=ep["name"] ) log.info(msg) @@ -337,12 +350,14 @@ class Listener: ep_id = sequence.get("episode_id") ep = self.get_ep_dict(ep_id) - msg = "Sequence created: {proj_name} - {ep_name}" \ + msg = ( + "Sequence created: {proj_name} - {ep_name}" "{sequence_name}".format( proj_name=sequence["project_name"], ep_name=ep["name"] + " - " if ep is not None else "", - sequence_name=sequence["name"] + sequence_name=sequence["name"], ) + ) log.info(msg) def _update_sequence(self, data): @@ -366,8 +381,11 @@ class Listener: # Update update_op_result = update_op_assets( - self.dbcon, gazu_project, project_doc, - [sequence], zou_ids_and_asset_docs + self.dbcon, + gazu_project, + project_doc, + [sequence], + zou_ids_and_asset_docs, ) if update_op_result: asset_doc_id, asset_update = update_op_result[0] @@ -388,14 +406,17 @@ class Listener: ep = self.get_ep_dict(ep_id) gazu_project = gazu.project.get_project( - sequence["data"]["zou"]["project_id"]) + sequence["data"]["zou"]["project_id"] + ) - msg = "Sequence deleted: {proj_name} - {ep_name}" \ + msg = ( + "Sequence deleted: {proj_name} - {ep_name}" "{sequence_name}".format( proj_name=gazu_project["name"], ep_name=ep["name"] + " - " if ep is not None else "", - sequence_name=sequence["name"] + sequence_name=sequence["name"], ) + ) log.info(msg) # == Shot == @@ -417,13 +438,15 @@ class Listener: ep_id = shot["episode_id"] ep = self.get_ep_dict(ep_id) - msg = "Shot created: {proj_name} - {ep_name}" \ + msg = ( + "Shot created: {proj_name} - {ep_name}" "{sequence_name} - {shot_name}".format( proj_name=shot["project_name"], ep_name=ep["name"] + " - " if ep is not None else "", sequence_name=shot["sequence_name"], - shot_name=shot["name"] + shot_name=shot["name"], ) + ) log.info(msg) def _update_shot(self, data): @@ -440,14 +463,18 @@ class Listener: zou_ids_and_asset_docs = { asset_doc["data"]["zou"]["id"]: asset_doc for asset_doc in get_assets(project_name) - if asset_doc["data"].get("zou", {}).get("id")} + if asset_doc["data"].get("zou", {}).get("id") + } zou_ids_and_asset_docs[shot["project_id"]] = project_doc gazu_project = gazu.project.get_project(shot["project_id"]) # Update update_op_result = update_op_assets( - self.dbcon, gazu_project, project_doc, - [shot], zou_ids_and_asset_docs + self.dbcon, + gazu_project, + project_doc, + [shot], + zou_ids_and_asset_docs, ) if update_op_result: @@ -469,13 +496,15 @@ class Listener: ep_id = shot["data"]["zou"].get("episode_id") ep = self.get_ep_dict(ep_id) - msg = "Shot deleted: {proj_name} - {ep_name}" \ + msg = ( + "Shot deleted: {proj_name} - {ep_name}" "{sequence_name} - {shot_name}".format( proj_name=shot["data"]["zou"]["project_name"], ep_name=ep["name"] + " - " if ep is not None else "", sequence_name=shot["data"]["zou"]["sequence_name"], - shot_name=shot["name"] + shot_name=shot["name"], ) + ) log.info(msg) # == Task == @@ -504,12 +533,12 @@ class Listener: parent_name = "{ep_name}{sequence_name} - {shot_name}".format( ep_name=ep["name"] + " - " if ep is not None else "", sequence_name=task["sequence"]["name"], - shot_name=task["entity"]["name"] + shot_name=task["entity"]["name"], ) asset_name = "{ep_name}{sequence_name}_{shot_name}".format( ep_name=ep["name"] + "_" if ep is not None else "", sequence_name=task["sequence"]["name"], - shot_name=task["entity"]["name"] + shot_name=task["entity"]["name"], ) # Update asset tasks with new one @@ -518,20 +547,24 @@ class Listener: asset_tasks = asset_doc["data"].get("tasks") task_type_name = task["task_type"]["name"] asset_tasks[task_type_name] = { - "type": task_type_name, "zou": task} + "type": task_type_name, + "zou": task, + } self.dbcon.update_one( {"_id": asset_doc["_id"]}, - {"$set": {"data.tasks": asset_tasks}} + {"$set": {"data.tasks": asset_tasks}}, ) # Print message - msg = "Task created: {proj} - {ent_type}{parent}" \ + msg = ( + "Task created: {proj} - {ent_type}{parent}" " - {task}".format( proj=task["project"]["name"], ent_type=ent_type + " - " if ent_type is not None else "", parent=parent_name, - task=task["task_type"]["name"] + task=task["task_type"]["name"], ) + ) log.info(msg) def _update_task(self, data): @@ -567,19 +600,19 @@ class Listener: parent_name = "{ep}{entity_type} - {entity}".format( ep=ep["name"] + " - " if ep is not None else "", entity_type=task["zou"]["entity_type"]["name"], - entity=task["zou"]["entity"]["name"] + entity=task["zou"]["entity"]["name"], ) elif entity["type"] == "Shot": parent_name = "{ep}{sequence} - {shot}".format( ep=ep["name"] + " - " if ep is not None else "", sequence=task["zou"]["sequence"]["name"], - shot=task["zou"]["entity"]["name"] + shot=task["zou"]["entity"]["name"], ) msg = "Task deleted: {proj} - {parent} - {task}".format( proj=task["zou"]["project"]["name"], parent=parent_name, - task=name + task=name, ) log.info(msg) @@ -593,6 +626,7 @@ def start_listeners(login: str, password: str): login (str): Kitsu user login password (str): Kitsu user password """ + # Refresh token every week def refresh_token_every_week(): log.info("Refreshing token...") diff --git a/openpype/modules/kitsu/utils/update_zou_with_op.py b/openpype/modules/kitsu/utils/update_zou_with_op.py index 617f037c1e..be931af233 100644 --- a/openpype/modules/kitsu/utils/update_zou_with_op.py +++ b/openpype/modules/kitsu/utils/update_zou_with_op.py @@ -83,7 +83,8 @@ def sync_zou_from_op_project( } ) gazu.project.update_project_data( - zou_project, data=project_doc["data"]) + zou_project, data=project_doc["data"] + ) gazu.project.update_project(zou_project) asset_types = gazu.asset.all_asset_types() @@ -99,8 +100,7 @@ def sync_zou_from_op_project( project_module_settings = get_project_settings(project_name)["kitsu"] dbcon.Session["AVALON_PROJECT"] = project_name asset_docs = { - asset_doc["_id"]: asset_doc - for asset_doc in get_assets(project_name) + asset_doc["_id"]: asset_doc for asset_doc in get_assets(project_name) } # Create new assets @@ -176,7 +176,8 @@ def sync_zou_from_op_project( frame_in=doc["data"]["frameStart"], frame_out=doc["data"]["frameEnd"], nb_frames=( - doc["data"]["frameEnd"] - doc["data"]["frameStart"] + 1), + doc["data"]["frameEnd"] - doc["data"]["frameStart"] + 1 + ), ) elif match.group(2): # Sequence From 67bc287321fd03287aedf222e6d9c7ebf25e3332 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Fri, 3 Mar 2023 11:12:17 +0100 Subject: [PATCH 1194/1271] Fix hound comments --- openpype/modules/kitsu/utils/update_op_with_zou.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 73b7a4249d..053e803ff3 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -175,12 +175,12 @@ def update_op_assets( elif item_type == "Shot": tasks_list = gazu.task.all_tasks_for_shot(item) item_data["tasks"] = { - item_data["tasks"] = { - t["task_type_name"]: { - "type": t["task_type_name"], - "zou": gazu.task.get_task(t["id"]), + item_data["tasks"] = { + t["task_type_name"]: { + "type": t["task_type_name"], + "zou": gazu.task.get_task(t["id"]), + } } - } for t in tasks_list } From 67e8f59935a7a1824aceb71cdc32e354c8a33a98 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Sat, 4 Mar 2023 03:27:48 +0000 Subject: [PATCH 1195/1271] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 4d6f3d43e4..2939ddbbac 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.2-nightly.3" +__version__ = "3.15.2-nightly.4" From b077815dc5adcb7c839cc77825498369402ef7af Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 5 Mar 2023 10:32:13 +0100 Subject: [PATCH 1196/1271] Get and set openpype context data on comp --- openpype/hosts/fusion/api/pipeline.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/api/pipeline.py b/openpype/hosts/fusion/api/pipeline.py index 2d0a1da8fa..b982e1c2e9 100644 --- a/openpype/hosts/fusion/api/pipeline.py +++ b/openpype/hosts/fusion/api/pipeline.py @@ -155,10 +155,12 @@ class FusionHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): return ls() def update_context_data(self, data, changes): - print(data, changes) + comp = get_current_comp() + comp.SetData("openpype", data) def get_context_data(self): - return {} + comp = get_current_comp() + return comp.GetData("openpype") or {} def on_pyblish_instance_toggled(instance, old_value, new_value): From 406bc798c45fff19edee17730ffaee587a5a8b48 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 5 Mar 2023 10:39:57 +0100 Subject: [PATCH 1197/1271] Tweak creator updates to newer style updates --- .../fusion/plugins/create/create_saver.py | 11 +++-------- .../fusion/plugins/create/create_workfile.py | 18 ++++-------------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index 439064770e..777dfb2e67 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -87,15 +87,10 @@ class CreateSaver(Creator): return qtawesome.icon("fa.eye", color="white") def update_instances(self, update_list): - for update in update_list: - instance = update.instance + for created_inst, _changes in update_list: - # Get the new values after the changes by key, ignore old value - new_data = { - key: new for key, (_old, new) in update.changes.items() - } - - tool = instance.transient_data["tool"] + new_data = created_inst.data_to_store() + tool = created_inst.transient_data["tool"] self._update_tool_with_data(tool, new_data) self._imprint(tool, new_data) diff --git a/openpype/hosts/fusion/plugins/create/create_workfile.py b/openpype/hosts/fusion/plugins/create/create_workfile.py index 917780c56e..3f11d69425 100644 --- a/openpype/hosts/fusion/plugins/create/create_workfile.py +++ b/openpype/hosts/fusion/plugins/create/create_workfile.py @@ -53,20 +53,15 @@ class FusionWorkfileCreator(AutoCreator): self._add_instance_to_context(instance) def update_instances(self, update_list): - for update in update_list: - instance = update.instance - comp = instance.transient_data["comp"] + for created_inst, _changes in update_list: + comp = created_inst.transient_data["comp"] if not hasattr(comp, "SetData"): # Comp is not alive anymore, likely closed by the user self.log.error("Workfile comp not found for existing instance." " Comp might have been closed in the meantime.") continue - # TODO: It appears sometimes this could be 'nested' - # Get the new values after the changes by key, ignore old value - new_data = { - key: new for key, (_old, new) in update.changes.items() - } + new_data = created_inst.data_to_store() self._imprint(comp, new_data) def create(self, options=None): @@ -128,9 +123,4 @@ class FusionWorkfileCreator(AutoCreator): for key in ["variant", "subset", "asset", "task"]: data.pop(key, None) - # Flatten any potential nested dicts - data = flatten_dict(data, separator=".") - - # Prefix with data key openpype.workfile - data = {f"{self.data_key}.{key}" for key, value in data.items()} - comp.SetData(data) + comp.SetData(self.data_key, data) From 37591de2913bfadd033e904fdfb88ffee550c4ad Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 5 Mar 2023 10:40:56 +0100 Subject: [PATCH 1198/1271] Change workfile Creator data key so it doesn't interfere with global comp context data in any way (Fusion allows to access nested dicts (lua tables) using the dot notation) --- openpype/hosts/fusion/plugins/create/create_workfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/create/create_workfile.py b/openpype/hosts/fusion/plugins/create/create_workfile.py index 3f11d69425..c67a9793dd 100644 --- a/openpype/hosts/fusion/plugins/create/create_workfile.py +++ b/openpype/hosts/fusion/plugins/create/create_workfile.py @@ -33,7 +33,7 @@ class FusionWorkfileCreator(AutoCreator): create_allow_context_change = False - data_key = "openpype.workfile" + data_key = "openpype_workfile" def collect_instances(self): From 5efc9e0ff0cdf0f410b7a0b92b27cc2ed03256e2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 Mar 2023 11:00:02 +0100 Subject: [PATCH 1199/1271] Editorial: Fix tasks removal (#4558) Fix tasks removal in editorial --- .../publish/extract_hierarchy_avalon.py | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/openpype/plugins/publish/extract_hierarchy_avalon.py b/openpype/plugins/publish/extract_hierarchy_avalon.py index b2a6adc210..493780645c 100644 --- a/openpype/plugins/publish/extract_hierarchy_avalon.py +++ b/openpype/plugins/publish/extract_hierarchy_avalon.py @@ -135,6 +135,38 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): ) return project_doc + def _prepare_new_tasks(self, asset_doc, entity_data): + new_tasks = entity_data.get("tasks") or {} + if not asset_doc: + return new_tasks + + old_tasks = asset_doc.get("data", {}).get("tasks") + # Just use new tasks if old are not available + if not old_tasks: + return new_tasks + + output = deepcopy(old_tasks) + # Create mapping of lowered task names from old tasks + cur_task_low_mapping = { + task_name.lower(): task_name + for task_name in old_tasks + } + # Add/update tasks from new entity data + for task_name, task_info in new_tasks.items(): + task_info = deepcopy(task_info) + task_name_low = task_name.lower() + # Add new task + if task_name_low not in cur_task_low_mapping: + output[task_name] = task_info + continue + + # Update existing task with new info + mapped_task_name = cur_task_low_mapping.pop(task_name_low) + src_task_info = output.pop(mapped_task_name) + src_task_info.update(task_info) + output[task_name] = src_task_info + return output + def sync_asset( self, asset_name, @@ -170,11 +202,12 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): data["parents"] = parents asset_doc = asset_docs_by_name.get(asset_name) + + # Tasks + data["tasks"] = self._prepare_new_tasks(asset_doc, entity_data) + # --- Create/Unarchive asset and end --- if not asset_doc: - # Just use tasks from entity data as they are - # - this is different from the case when tasks are updated - data["tasks"] = entity_data.get("tasks") or {} archived_asset_doc = None for archived_entity in archived_asset_docs_by_name[asset_name]: archived_parents = ( @@ -201,19 +234,6 @@ class ExtractHierarchyToAvalon(pyblish.api.ContextPlugin): if "data" not in asset_doc: asset_doc["data"] = {} cur_entity_data = asset_doc["data"] - cur_entity_tasks = cur_entity_data.get("tasks") or {} - - # Tasks - data["tasks"] = {} - new_tasks = entity_data.get("tasks") or {} - for task_name, task_info in new_tasks.items(): - task_info = deepcopy(task_info) - if task_name in cur_entity_tasks: - src_task_info = deepcopy(cur_entity_tasks[task_name]) - src_task_info.update(task_info) - task_info = src_task_info - - data["tasks"][task_name] = task_info changes = {} for key, value in data.items(): From 08c71380709cf672e4b930b351a0671331521610 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 6 Mar 2023 11:13:57 +0100 Subject: [PATCH 1200/1271] Nuke: moving deepcopy to abstraction --- openpype/pipeline/colorspace.py | 31 ++++++++++---------- openpype/pipeline/publish/publish_plugins.py | 5 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/openpype/pipeline/colorspace.py b/openpype/pipeline/colorspace.py index 6f68bdc5bf..2085e2d37f 100644 --- a/openpype/pipeline/colorspace.py +++ b/openpype/pipeline/colorspace.py @@ -335,9 +335,10 @@ def get_imageio_config( get_template_data_from_session) anatomy_data = get_template_data_from_session() + formatting_data = deepcopy(anatomy_data) # add project roots to anatomy data - anatomy_data["root"] = anatomy.roots - anatomy_data["platform"] = platform.system().lower() + formatting_data["root"] = anatomy.roots + formatting_data["platform"] = platform.system().lower() # get colorspace settings imageio_global, imageio_host = _get_imageio_settings( @@ -347,7 +348,7 @@ def get_imageio_config( if config_host.get("enabled"): config_data = _get_config_data( - config_host["filepath"], anatomy_data + config_host["filepath"], formatting_data ) else: config_data = None @@ -356,7 +357,7 @@ def get_imageio_config( # get config path from either global or host_name config_global = imageio_global["ocio_config"] config_data = _get_config_data( - config_global["filepath"], anatomy_data + config_global["filepath"], formatting_data ) if not config_data: @@ -372,12 +373,12 @@ def _get_config_data(path_list, anatomy_data): """Return first existing path in path list. If template is used in path inputs, - then it is formated by anatomy data + then it is formatted by anatomy data and environment variables Args: path_list (list[str]): list of abs paths - anatomy_data (dict): formating data + anatomy_data (dict): formatting data Returns: dict: config data @@ -389,30 +390,30 @@ def _get_config_data(path_list, anatomy_data): # first try host config paths for path_ in path_list: - formated_path = _format_path(path_, formatting_data) + formatted_path = _format_path(path_, formatting_data) - if not os.path.exists(formated_path): + if not os.path.exists(formatted_path): continue return { - "path": os.path.normpath(formated_path), + "path": os.path.normpath(formatted_path), "template": path_ } -def _format_path(tempate_path, formatting_data): - """Single template path formating. +def _format_path(template_path, formatting_data): + """Single template path formatting. Args: - tempate_path (str): template string + template_path (str): template string formatting_data (dict): data to be used for - template formating + template formatting Returns: - str: absolute formated path + str: absolute formatted path """ # format path for anatomy keys - formatted_path = StringTemplate(tempate_path).format( + formatted_path = StringTemplate(template_path).format( formatting_data) return os.path.abspath(formatted_path) diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index 2df98221ba..331235fadc 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -1,4 +1,3 @@ -from copy import deepcopy import inspect from abc import ABCMeta from pprint import pformat @@ -311,7 +310,7 @@ class ColormanagedPyblishPluginMixin(object): @staticmethod def get_colorspace_settings(context): - """Retuns solved settings for the host context. + """Returns solved settings for the host context. Args: context (publish.Context): publishing context @@ -324,7 +323,7 @@ class ColormanagedPyblishPluginMixin(object): project_name = context.data["projectName"] host_name = context.data["hostName"] - anatomy_data = deepcopy(context.data["anatomyData"]) + anatomy_data = context.data["anatomyData"] project_settings_ = context.data["project_settings"] config_data = get_imageio_config( From ec78ebff691eec6c124dfc95c10c9760bac1d1b5 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Mon, 6 Mar 2023 10:20:12 +0000 Subject: [PATCH 1201/1271] Add skeletalmesh family as loadable as reference --- openpype/hosts/maya/plugins/load/load_reference.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 858c9b709e..d93702a16d 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -26,6 +26,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): "rig", "camerarig", "staticMesh", + "skeletalMesh", "mvLook"] representations = ["ma", "abc", "fbx", "mb"] From 84574eaca8cc8bfa707d39d411c784621754975a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 6 Mar 2023 11:50:41 +0100 Subject: [PATCH 1202/1271] Nuke: fix clip sequence loading --- openpype/hosts/nuke/plugins/load/load_clip.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index d170276add..cb3da79ef5 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -222,18 +222,21 @@ class LoadClip(plugin.NukeLoader): """ representation = deepcopy(representation) context = representation["context"] - template = representation["data"]["template"] + + # Get the frame from the context and hash it + frame = context["frame"] + hashed_frame = "#" * len(str(frame)) + + # Replace the frame with the hash in the originalBasename if ( - "{originalBasename}" in template - and "frame" in context + "{originalBasename}" in representation["data"]["template"] ): - frame = context["frame"] - hashed_frame = "#" * len(str(frame)) origin_basename = context["originalBasename"] context["originalBasename"] = origin_basename.replace( frame, hashed_frame ) + # Replace the frame with the hash in the frame representation["context"]["frame"] = hashed_frame return representation From b513bb437d2e48b20f305fadb4b71639724a3875 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 6 Mar 2023 16:28:37 +0100 Subject: [PATCH 1203/1271] Set subset in a more correct OpenPype way --- .../hosts/fusion/plugins/create/create_saver.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index 777dfb2e67..b0c0d830a3 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -12,6 +12,7 @@ from openpype.pipeline import ( Creator, CreatedInstance ) +from openpype.client import get_asset_by_name class CreateSaver(Creator): @@ -145,14 +146,22 @@ class CreateSaver(Creator): asset = legacy_io.Session["AVALON_ASSET"] task = legacy_io.Session["AVALON_TASK"] + asset_doc = get_asset_by_name(project_name=project, + asset_name=asset) + path = tool["Clip"][comp.TIME_UNDEFINED] fname = os.path.basename(path) fname, _ext = os.path.splitext(fname) - subset = fname.rstrip(".") + variant = fname.rstrip(".") + subset = self.get_subset_name( + variant=variant, + task_name=task, + asset_doc=asset_doc, + project_name=project, + ) attrs = tool.GetAttrs() passthrough = attrs["TOOLB_PassThrough"] - variant = subset[len("render"):] return { # Required data "project": project, From 99637875efa176a7e462212880a5f4c26f2b6f78 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 6 Mar 2023 16:31:09 +0100 Subject: [PATCH 1204/1271] Do not secretly pop data that OP generates by default --- openpype/hosts/fusion/plugins/create/create_workfile.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_workfile.py b/openpype/hosts/fusion/plugins/create/create_workfile.py index c67a9793dd..e539dcf019 100644 --- a/openpype/hosts/fusion/plugins/create/create_workfile.py +++ b/openpype/hosts/fusion/plugins/create/create_workfile.py @@ -117,10 +117,4 @@ class FusionWorkfileCreator(AutoCreator): return qtawesome.icon("fa.file-o", color="white") def _imprint(self, comp, data): - - # TODO: Should this keys persist or not? I'd prefer not - # Do not persist the current context for the Workfile - for key in ["variant", "subset", "asset", "task"]: - data.pop(key, None) - comp.SetData(self.data_key, data) From b2eb14914b6644c8d5b3797751da74c37b804e83 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 6 Mar 2023 16:55:05 +0100 Subject: [PATCH 1205/1271] global, nuke: adding support for first workfile creation --- openpype/hosts/nuke/api/lib.py | 9 ++-- openpype/hosts/nuke/api/pipeline.py | 2 +- .../nuke/api/workfile_template_builder.py | 1 - openpype/hosts/nuke/api/workio.py | 2 +- .../workfile/workfile_template_builder.py | 46 +++++++++++++------ 5 files changed, 39 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index cd31e42690..793dc8fcdd 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -2682,11 +2682,12 @@ def start_workfile_template_builder(): build_workfile_template ) - # to avoid looping of the callback, remove it! - # nuke.removeOnCreate(start_workfile_template_builder, nodeClass="Root") - log.info("Starting workfile template builder...") - build_workfile_template(run_from_callback=True) + # to avoid looping of the callback, remove it! + log.info("Starting workfile template builder...") + build_workfile_template(workfile_creation_enabled=True) + + nuke.removeOnCreate(start_workfile_template_builder, nodeClass="Root") @deprecated def recreate_instance(origin_node, avalon_data=None): diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 30270a4e5f..d649ffae7f 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -155,8 +155,8 @@ def add_nuke_callbacks(): # Set context settings. nuke.addOnCreate( workfile_settings.set_context_settings, nodeClass="Root") - nuke.addOnCreate(start_workfile_template_builder, nodeClass="Root") nuke.addOnCreate(workfile_settings.set_favorites, nodeClass="Root") + nuke.addOnCreate(start_workfile_template_builder, nodeClass="Root") nuke.addOnCreate(process_workfile_builder, nodeClass="Root") # fix ffmpeg settings on script diff --git a/openpype/hosts/nuke/api/workfile_template_builder.py b/openpype/hosts/nuke/api/workfile_template_builder.py index 80db0d160c..a6805d1b14 100644 --- a/openpype/hosts/nuke/api/workfile_template_builder.py +++ b/openpype/hosts/nuke/api/workfile_template_builder.py @@ -56,7 +56,6 @@ class NukeTemplateBuilder(AbstractTemplateBuilder): return True - class NukePlaceholderPlugin(PlaceholderPlugin): node_color = 4278190335 diff --git a/openpype/hosts/nuke/api/workio.py b/openpype/hosts/nuke/api/workio.py index 65b86bf01b..5692f8e63c 100644 --- a/openpype/hosts/nuke/api/workio.py +++ b/openpype/hosts/nuke/api/workio.py @@ -13,7 +13,7 @@ def has_unsaved_changes(): def save_file(filepath): path = filepath.replace("\\", "/") - nuke.scriptSaveAs(path) + nuke.scriptSaveAs(path, overwrite=1) nuke.Root()["name"].setValue(path) nuke.Root()["project_directory"].setValue(os.path.dirname(path)) nuke.Root().setModified(False) diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 3dd769447f..d578114de2 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -443,7 +443,7 @@ class AbstractTemplateBuilder(object): level_limit=None, keep_placeholders=None, create_first_version=None, - run_from_callback=False + workfile_creation_enabled=False ): """Main callback for building workfile from template path. @@ -461,7 +461,7 @@ class AbstractTemplateBuilder(object): hosts to decide if they want to remove placeholder after it is used. create_first_version (bool): create first version of a workfile - run_from_callback (bool): If True, it might create first version + workfile_creation_enabled (bool): If True, it might create first version but ignore process if version is created """ @@ -475,13 +475,25 @@ class AbstractTemplateBuilder(object): if create_first_version is None: create_first_version = template_preset["create_first_version"] - # run creation of first version only if it is - # run from callback and no new version is created - first_creation = False - if create_first_version and run_from_callback: - first_creation = not self.create_first_workfile_version() + # check if first version is created + created_version_workfile = self.create_first_workfile_version() - if first_creation: + # if first version is created, import template and populate placeholders + if ( + create_first_version + and workfile_creation_enabled + and created_version_workfile + ): + self.import_template(template_path) + self.populate_scene_placeholders( + level_limit, keep_placeholders) + + # save workfile after template is populated + self.save_workfile(created_version_workfile) + + # ignore process if first workfile is enabled + # but a version is already created + if workfile_creation_enabled: return self.import_template(template_path) @@ -546,20 +558,26 @@ class AbstractTemplateBuilder(object): host's template file. """ last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE") + self.log.info("__ last_workfile_path: {}".format(last_workfile_path)) if os.path.exists(last_workfile_path): # ignore in case workfile existence self.log.info("Workfile already exists, skipping creation.") return False - # Save current scene, continue to open file - if isinstance(self.host, IWorkfileHost): - self.host.save_workfile(last_workfile_path) - else: - self.host.save_file(last_workfile_path) + # Create first version + self.log.info("Creating first version of workfile.") + self.save_workfile(last_workfile_path) # Confirm creation of first version - return True + return last_workfile_path + def save_workfile(self, workfile_path): + """Save workfile in current host.""" + # Save current scene, continue to open file + if isinstance(self.host, IWorkfileHost): + self.host.save_workfile(workfile_path) + else: + self.host.save_file(workfile_path) def _prepare_placeholders(self, placeholders): """Run preparation part for placeholders on plugins. From 6d9084b14424e6c41e859ce633f8b861f6619cd2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 6 Mar 2023 16:58:17 +0100 Subject: [PATCH 1206/1271] Match workfile creator logic more with the one from After Effects --- .../fusion/plugins/create/create_workfile.py | 84 ++++++++----------- 1 file changed, 37 insertions(+), 47 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_workfile.py b/openpype/hosts/fusion/plugins/create/create_workfile.py index e539dcf019..19da2c36a6 100644 --- a/openpype/hosts/fusion/plugins/create/create_workfile.py +++ b/openpype/hosts/fusion/plugins/create/create_workfile.py @@ -13,17 +13,6 @@ from openpype.pipeline import ( ) -def flatten_dict(d, parent_key=None, separator="."): - items = [] - for key, v in d.items(): - new_key = parent_key + separator + key if parent_key else key - if isinstance(v, collections.MutableMapping): - items.extend(flatten_dict(v, new_key, separator=separator).items()) - else: - items.append((new_key, v)) - return dict(items) - - class FusionWorkfileCreator(AutoCreator): identifier = "workfile" family = "workfile" @@ -61,8 +50,9 @@ class FusionWorkfileCreator(AutoCreator): " Comp might have been closed in the meantime.") continue - new_data = created_inst.data_to_store() - self._imprint(comp, new_data) + # Imprint data into the comp + data = created_inst.data_to_store() + comp.SetData(self.data_key, data) def create(self, options=None): @@ -71,50 +61,50 @@ class FusionWorkfileCreator(AutoCreator): self.log.error("Unable to find current comp") return - # TODO: Is this really necessary? - # Force kill any existing "workfile" instances + existing_instance = None for instance in self.create_context.instances: if instance.family == self.family: - self.log.debug(f"Removing instance: {instance}") - self._remove_instance_from_context(instance) + existing_instance = instance + break project_name = legacy_io.Session["AVALON_PROJECT"] asset_name = legacy_io.Session["AVALON_ASSET"] task_name = legacy_io.Session["AVALON_TASK"] host_name = legacy_io.Session["AVALON_APP"] - asset_doc = get_asset_by_name(project_name, asset_name) - subset_name = self.get_subset_name( - self.default_variant, task_name, asset_doc, - project_name, host_name - ) - data = { - "asset": asset_name, - "task": task_name, - "variant": self.default_variant - } - data.update(self.get_dynamic_data( - self.default_variant, - task_name, - asset_doc, - project_name, - host_name, - data - )) + if existing_instance is None: + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + self.default_variant, task_name, asset_doc, + project_name, host_name + ) + data = { + "asset": asset_name, + "task": task_name, + "variant": self.default_variant + } + data.update(self.get_dynamic_data( + self.default_variant, task_name, asset_doc, + project_name, host_name, None + )) - instance = CreatedInstance( - family=self.family, - subset_name=subset_name, - data=data, - creator=self - ) - instance.transient_data["comp"] = comp - self._add_instance_to_context(instance) + new_instance = CreatedInstance( + self.family, subset_name, data, self + ) + self._add_instance_to_context(new_instance) - self._imprint(comp, data) + elif ( + existing_instance["asset"] != asset_name + or existing_instance["task"] != task_name + ): + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + self.default_variant, task_name, asset_doc, + project_name, host_name + ) + existing_instance["asset"] = asset_name + existing_instance["task"] = task_name + existing_instance["subset"] = subset_name def get_icon(self): return qtawesome.icon("fa.file-o", color="white") - - def _imprint(self, comp, data): - comp.SetData(self.data_key, data) From 52fac29164d279823a72b15a1ad5ac4c1b57b8b6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 6 Mar 2023 17:07:15 +0100 Subject: [PATCH 1207/1271] Cleanup unused import --- openpype/hosts/fusion/plugins/create/create_workfile.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_workfile.py b/openpype/hosts/fusion/plugins/create/create_workfile.py index 19da2c36a6..2f78e4fe52 100644 --- a/openpype/hosts/fusion/plugins/create/create_workfile.py +++ b/openpype/hosts/fusion/plugins/create/create_workfile.py @@ -1,5 +1,3 @@ -import collections - import qtawesome from openpype.hosts.fusion.api import ( From af393688389346fc590a504514d4eb8de0c375ce Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 6 Mar 2023 17:07:56 +0100 Subject: [PATCH 1208/1271] hound --- openpype/hosts/nuke/api/lib.py | 2 +- openpype/hosts/nuke/api/workfile_template_builder.py | 4 ---- openpype/pipeline/workfile/workfile_template_builder.py | 8 +++++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 793dc8fcdd..a5a631cc70 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -2682,11 +2682,11 @@ def start_workfile_template_builder(): build_workfile_template ) - # to avoid looping of the callback, remove it! log.info("Starting workfile template builder...") build_workfile_template(workfile_creation_enabled=True) + # remove callback since it would be duplicating the workfile nuke.removeOnCreate(start_workfile_template_builder, nodeClass="Root") @deprecated diff --git a/openpype/hosts/nuke/api/workfile_template_builder.py b/openpype/hosts/nuke/api/workfile_template_builder.py index a6805d1b14..fb0afb3d55 100644 --- a/openpype/hosts/nuke/api/workfile_template_builder.py +++ b/openpype/hosts/nuke/api/workfile_template_builder.py @@ -1,8 +1,5 @@ -import os import collections - import nuke - from openpype.pipeline import registered_host from openpype.pipeline.workfile.workfile_template_builder import ( AbstractTemplateBuilder, @@ -15,7 +12,6 @@ from openpype.pipeline.workfile.workfile_template_builder import ( from openpype.tools.workfile_template_build import ( WorkfileBuildPlaceholderDialog, ) -from openpype.host import IWorkfileHost from .lib import ( find_free_space_to_paste_nodes, get_extreme_positions, diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index d578114de2..0ce59de8ad 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -461,8 +461,9 @@ class AbstractTemplateBuilder(object): hosts to decide if they want to remove placeholder after it is used. create_first_version (bool): create first version of a workfile - workfile_creation_enabled (bool): If True, it might create first version - but ignore process if version is created + workfile_creation_enabled (bool): If True, it might create + first version but ignore + process if version is created """ template_preset = self.get_template_preset() @@ -478,7 +479,8 @@ class AbstractTemplateBuilder(object): # check if first version is created created_version_workfile = self.create_first_workfile_version() - # if first version is created, import template and populate placeholders + # if first version is created, import template + # and populate placeholders if ( create_first_version and workfile_creation_enabled From de50783c0435ec75a8ac7d9b29068c96a7bab8de Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 6 Mar 2023 18:34:56 +0100 Subject: [PATCH 1209/1271] Nuke: Add option to use new creating system in workfile template builder (#4545) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added option to use new creating system in workfile template builder * fix spaces * use 'create' method on create context to trigger creation * fix attribute access * adding headless to creators and workfile builder abstraction * adding noqa for hound * hound --------- Co-authored-by: Jakub Jezek Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- .../maya/api/workfile_template_builder.py | 2 + openpype/hosts/nuke/api/plugin.py | 6 +- .../nuke/plugins/create/create_write_image.py | 2 +- .../plugins/create/create_write_prerender.py | 2 +- .../plugins/create/create_write_render.py | 2 +- .../workfile/workfile_template_builder.py | 97 ++++++++++++++----- 6 files changed, 81 insertions(+), 30 deletions(-) diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index 2f550e787a..90ab6e21e0 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -22,6 +22,8 @@ PLACEHOLDER_SET = "PLACEHOLDERS_SET" class MayaTemplateBuilder(AbstractTemplateBuilder): """Concrete implementation of AbstractTemplateBuilder for maya""" + use_legacy_creators = True + def import_template(self, path): """Import template into current scene. Block if a template is already loaded. diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 6c2d4b84be..aec87be5ab 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -239,7 +239,11 @@ class NukeCreator(NewCreator): def get_pre_create_attr_defs(self): return [ - BoolDef("use_selection", label="Use selection") + BoolDef( + "use_selection", + default=not self.create_context.headless, + label="Use selection" + ) ] def get_creator_settings(self, project_settings, settings_key=None): diff --git a/openpype/hosts/nuke/plugins/create/create_write_image.py b/openpype/hosts/nuke/plugins/create/create_write_image.py index 1e23b3ad7f..d38253ab2f 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_image.py +++ b/openpype/hosts/nuke/plugins/create/create_write_image.py @@ -35,7 +35,7 @@ class CreateWriteImage(napi.NukeWriteCreator): attr_defs = [ BoolDef( "use_selection", - default=True, + default=not self.create_context.headless, label="Use selection" ), self._get_render_target_enum(), diff --git a/openpype/hosts/nuke/plugins/create/create_write_prerender.py b/openpype/hosts/nuke/plugins/create/create_write_prerender.py index 1603bf17e3..8103cb7c4d 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_prerender.py +++ b/openpype/hosts/nuke/plugins/create/create_write_prerender.py @@ -34,7 +34,7 @@ class CreateWritePrerender(napi.NukeWriteCreator): attr_defs = [ BoolDef( "use_selection", - default=True, + default=not self.create_context.headless, label="Use selection" ), self._get_render_target_enum() diff --git a/openpype/hosts/nuke/plugins/create/create_write_render.py b/openpype/hosts/nuke/plugins/create/create_write_render.py index 72fcb4f232..23efa62e36 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_render.py +++ b/openpype/hosts/nuke/plugins/create/create_write_render.py @@ -31,7 +31,7 @@ class CreateWriteRender(napi.NukeWriteCreator): attr_defs = [ BoolDef( "use_selection", - default=True, + default=not self.create_context.headless, label="Use selection" ), self._get_render_target_enum() diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index 119e4aaeb7..27214af79f 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -43,7 +43,8 @@ from openpype.pipeline.load import ( load_with_repre_context, ) from openpype.pipeline.create import ( - discover_legacy_creator_plugins + discover_legacy_creator_plugins, + CreateContext, ) @@ -91,6 +92,7 @@ class AbstractTemplateBuilder(object): """ _log = None + use_legacy_creators = False def __init__(self, host): # Get host name @@ -110,6 +112,7 @@ class AbstractTemplateBuilder(object): self._placeholder_plugins = None self._loaders_by_name = None self._creators_by_name = None + self._create_context = None self._system_settings = None self._project_settings = None @@ -171,6 +174,16 @@ class AbstractTemplateBuilder(object): .get("type") ) + @property + def create_context(self): + if self._create_context is None: + self._create_context = CreateContext( + self.host, + discover_publish_plugins=False, + headless=True + ) + return self._create_context + def get_placeholder_plugin_classes(self): """Get placeholder plugin classes that can be used to build template. @@ -235,18 +248,29 @@ class AbstractTemplateBuilder(object): self._loaders_by_name = get_loaders_by_name() return self._loaders_by_name + def _collect_legacy_creators(self): + creators_by_name = {} + for creator in discover_legacy_creator_plugins(): + if not creator.enabled: + continue + creator_name = creator.__name__ + if creator_name in creators_by_name: + raise KeyError( + "Duplicated creator name {} !".format(creator_name) + ) + creators_by_name[creator_name] = creator + self._creators_by_name = creators_by_name + + def _collect_creators(self): + self._creators_by_name = dict(self.create_context.creators) + def get_creators_by_name(self): if self._creators_by_name is None: - self._creators_by_name = {} - for creator in discover_legacy_creator_plugins(): - if not creator.enabled: - continue - creator_name = creator.__name__ - if creator_name in self._creators_by_name: - raise KeyError( - "Duplicated creator name {} !".format(creator_name) - ) - self._creators_by_name[creator_name] = creator + if self.use_legacy_creators: + self._collect_legacy_creators() + else: + self._collect_creators() + return self._creators_by_name def get_shared_data(self, key): @@ -1579,6 +1603,8 @@ class PlaceholderCreateMixin(object): placeholder (PlaceholderItem): Placeholder item with information about requested publishable instance. """ + + legacy_create = self.builder.use_legacy_creators creator_name = placeholder.data["creator"] create_variant = placeholder.data["create_variant"] @@ -1589,17 +1615,28 @@ class PlaceholderCreateMixin(object): task_name = legacy_io.Session["AVALON_TASK"] asset_name = legacy_io.Session["AVALON_ASSET"] - # get asset id - asset_doc = get_asset_by_name(project_name, asset_name, fields=["_id"]) - assert asset_doc, "No current asset found in Session" - asset_id = asset_doc['_id'] + if legacy_create: + asset_doc = get_asset_by_name( + project_name, asset_name, fields=["_id"] + ) + assert asset_doc, "No current asset found in Session" + subset_name = creator_plugin.get_subset_name( + create_variant, + task_name, + asset_doc["_id"], + project_name + ) - subset_name = creator_plugin.get_subset_name( - create_variant, - task_name, - asset_id, - project_name - ) + else: + asset_doc = get_asset_by_name(project_name, asset_name) + assert asset_doc, "No current asset found in Session" + subset_name = creator_plugin.get_subset_name( + create_variant, + task_name, + asset_doc, + project_name, + self.builder.host_name + ) creator_data = { "creator_name": creator_name, @@ -1612,12 +1649,20 @@ class PlaceholderCreateMixin(object): # compile subset name from variant try: - creator_instance = creator_plugin( - subset_name, - asset_name - ).process() + if legacy_create: + creator_instance = creator_plugin( + subset_name, + asset_name + ).process() + else: + creator_instance = self.builder.create_context.create( + creator_plugin.identifier, + create_variant, + asset_doc, + task_name=task_name + ) - except Exception: + except: # noqa: E722 failed = True self.create_failed(placeholder, creator_data) From 16cece3e499b0336e490b1ac0bf01d69f715d0f6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 6 Mar 2023 19:45:17 +0100 Subject: [PATCH 1210/1271] Fusion: get filepath from representation instead of listing files from publish folder --- .../fusion/plugins/load/load_sequence.py | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/openpype/hosts/fusion/plugins/load/load_sequence.py b/openpype/hosts/fusion/plugins/load/load_sequence.py index 6f44c61d1b..9daf4b007d 100644 --- a/openpype/hosts/fusion/plugins/load/load_sequence.py +++ b/openpype/hosts/fusion/plugins/load/load_sequence.py @@ -1,11 +1,9 @@ -import os import contextlib -from openpype.client import get_version_by_id -from openpype.pipeline import ( - load, - legacy_io, - get_representation_path, +import openpype.pipeline.load as load +from openpype.pipeline.load import ( + get_representation_context, + get_representation_path_from_context ) from openpype.hosts.fusion.api import ( imprint_container, @@ -141,7 +139,7 @@ class FusionLoadSequence(load.LoaderPlugin): namespace = context['asset']['name'] # Use the first file for now - path = self._get_first_image(os.path.dirname(self.fname)) + path = get_representation_path_from_context(context) # Create the Loader with the filename path set comp = get_current_comp() @@ -210,13 +208,11 @@ class FusionLoadSequence(load.LoaderPlugin): assert tool.ID == "Loader", "Must be Loader" comp = tool.Comp() - root = os.path.dirname(get_representation_path(representation)) - path = self._get_first_image(root) + context = get_representation_context(representation) + path = get_representation_path_from_context(context) # Get start frame from version data - project_name = legacy_io.active_project() - version = get_version_by_id(project_name, representation["parent"]) - start = self._get_start(version, tool) + start = self._get_start(context["version"], tool) with comp_lock_and_undo_chunk(comp, "Update Loader"): @@ -249,11 +245,6 @@ class FusionLoadSequence(load.LoaderPlugin): with comp_lock_and_undo_chunk(comp, "Remove Loader"): tool.Delete() - def _get_first_image(self, root): - """Get first file in representation root""" - files = sorted(os.listdir(root)) - return os.path.join(root, files[0]) - def _get_start(self, version_doc, tool): """Return real start frame of published files (incl. handles)""" data = version_doc["data"] From bc1ef9229c2250aa0be84917bf6bc23e9ec65354 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Mar 2023 10:39:20 +0100 Subject: [PATCH 1211/1271] Photoshop: context is not changed in publisher (#4570) * OP-5025 - fix - proper changing of context When PS is already opened, new opening from different context should change it. * OP-5025 - open last workfile for new context if present * OP-5025 - remove unneeded assignemnt * OP-5025 - removed whitespace --- openpype/hosts/photoshop/api/launch_logic.py | 79 ++++++++++++++++---- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/openpype/hosts/photoshop/api/launch_logic.py b/openpype/hosts/photoshop/api/launch_logic.py index a4377a9972..89ba6ad4e6 100644 --- a/openpype/hosts/photoshop/api/launch_logic.py +++ b/openpype/hosts/photoshop/api/launch_logic.py @@ -10,10 +10,20 @@ from wsrpc_aiohttp import ( from qtpy import QtCore -from openpype.lib import Logger -from openpype.pipeline import legacy_io +from openpype.lib import Logger, StringTemplate +from openpype.pipeline import ( + registered_host, + Anatomy, +) +from openpype.pipeline.workfile import ( + get_workfile_template_key_from_context, + get_last_workfile, +) +from openpype.pipeline.template_data import get_template_data_with_names from openpype.tools.utils import host_tools from openpype.tools.adobe_webserver.app import WebServerTool +from openpype.pipeline.context_tools import change_current_context +from openpype.client import get_asset_by_name from .ws_stub import PhotoshopServerStub @@ -310,23 +320,28 @@ class PhotoshopRoute(WebSocketRoute): # client functions async def set_context(self, project, asset, task): """ - Sets 'project' and 'asset' to envs, eg. setting context + Sets 'project' and 'asset' to envs, eg. setting context. - Args: - project (str) - asset (str) + Opens last workile from that context if exists. + + Args: + project (str) + asset (str) + task (str """ log.info("Setting context change") - log.info("project {} asset {} ".format(project, asset)) - if project: - legacy_io.Session["AVALON_PROJECT"] = project - os.environ["AVALON_PROJECT"] = project - if asset: - legacy_io.Session["AVALON_ASSET"] = asset - os.environ["AVALON_ASSET"] = asset - if task: - legacy_io.Session["AVALON_TASK"] = task - os.environ["AVALON_TASK"] = task + log.info(f"project {project} asset {asset} task {task}") + + asset_doc = get_asset_by_name(project, asset) + change_current_context(asset_doc, task) + + last_workfile_path = self._get_last_workfile_path(project, + asset, + task) + if last_workfile_path and os.path.exists(last_workfile_path): + ProcessLauncher.execute_in_main_thread( + lambda: stub().open(last_workfile_path)) + async def read(self): log.debug("photoshop.read client calls server server calls " @@ -356,3 +371,35 @@ class PhotoshopRoute(WebSocketRoute): # Required return statement. return "nothing" + + def _get_last_workfile_path(self, project_name, asset_name, task_name): + """Returns last workfile path if exists""" + host = registered_host() + host_name = "photoshop" + template_key = get_workfile_template_key_from_context( + asset_name, + task_name, + host_name, + project_name=project_name + ) + anatomy = Anatomy(project_name) + + data = get_template_data_with_names( + project_name, asset_name, task_name, host_name + ) + data["root"] = anatomy.roots + + file_template = anatomy.templates[template_key]["file"] + + # Define saving file extension + extensions = host.get_workfile_extensions() + + folder_template = anatomy.templates[template_key]["folder"] + work_root = StringTemplate.format_strict_template( + folder_template, data + ) + last_workfile_path = get_last_workfile( + work_root, file_template, data, extensions, True + ) + + return last_workfile_path From 73e0ba9cb266507c0f7ea562d6895bcd2dbaaddb Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 10:53:06 +0100 Subject: [PATCH 1212/1271] Set colorspace based on file rules in imageio settings --- openpype/hosts/fusion/plugins/publish/render_local.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 0eca7f6cdd..212242630b 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -1,9 +1,11 @@ import os import pyblish.api +from openpype.pipeline import publish from openpype.hosts.fusion.api import comp_lock_and_undo_chunk -class Fusionlocal(pyblish.api.InstancePlugin): +class Fusionlocal(pyblish.api.InstancePlugin, + publish.ColormanagedPyblishPluginMixin): """Render the current Fusion composition locally. Extract the result of savers by starting a comp render @@ -50,6 +52,11 @@ class Fusionlocal(pyblish.api.InstancePlugin): "stagingDir": output_dir, } + self.set_representation_colorspace( + representation=repre, + context=context, + ) + if "representations" not in instance.data: instance.data["representations"] = [] instance.data["representations"].append(repre) From 1bb7dbc9d9707335d246e9a7a924859c1d09fd84 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 10:55:40 +0100 Subject: [PATCH 1213/1271] Make sure repre preview copy is a deepcopy --- openpype/hosts/fusion/plugins/publish/render_local.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 212242630b..9ed17f23c6 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -1,4 +1,6 @@ import os +import copy + import pyblish.api from openpype.pipeline import publish from openpype.hosts.fusion.api import comp_lock_and_undo_chunk @@ -62,7 +64,7 @@ class Fusionlocal(pyblish.api.InstancePlugin, instance.data["representations"].append(repre) # review representation - repre_preview = repre.copy() + repre_preview = copy.deepcopy(repre) repre_preview["name"] = repre_preview["ext"] = "mp4" repre_preview["tags"] = ["review", "ftrackreview", "delete"] instance.data["representations"].append(repre_preview) From 70611ee884d63400e9466deaa66be7beb89d0003 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 11:36:12 +0100 Subject: [PATCH 1214/1271] Make sure to add the `comp` transient data for new instances --- openpype/hosts/fusion/plugins/create/create_workfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/fusion/plugins/create/create_workfile.py b/openpype/hosts/fusion/plugins/create/create_workfile.py index 2f78e4fe52..0bb3a0d3d4 100644 --- a/openpype/hosts/fusion/plugins/create/create_workfile.py +++ b/openpype/hosts/fusion/plugins/create/create_workfile.py @@ -89,6 +89,7 @@ class FusionWorkfileCreator(AutoCreator): new_instance = CreatedInstance( self.family, subset_name, data, self ) + new_instance.transient_data["comp"] = comp self._add_instance_to_context(new_instance) elif ( From 113b958369ae9853a3e9872a00d5c925d359b381 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 12:05:58 +0100 Subject: [PATCH 1215/1271] Collect Fusion workfile representation --- .../plugins/publish/collect_workfile.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 openpype/hosts/fusion/plugins/publish/collect_workfile.py diff --git a/openpype/hosts/fusion/plugins/publish/collect_workfile.py b/openpype/hosts/fusion/plugins/publish/collect_workfile.py new file mode 100644 index 0000000000..4c288edb3e --- /dev/null +++ b/openpype/hosts/fusion/plugins/publish/collect_workfile.py @@ -0,0 +1,26 @@ +import os + +import pyblish.api + + +class CollectFusionWorkfile(pyblish.api.InstancePlugin): + """Collect Fusion workfile representation.""" + + order = pyblish.api.CollectorOrder + 0.1 + label = "Collect Workfile" + hosts = ["fusion"] + families = ["workfile"] + + def process(self, instance): + + current_file = instance.context.data["currentFile"] + + folder, file = os.path.split(current_file) + filename, ext = os.path.splitext(file) + + instance.data['representations'] = [{ + 'name': ext.lstrip("."), + 'ext': ext.lstrip("."), + 'files': file, + "stagingDir": folder, + }] From b3636b9f558ace05722798e2343fcbc01ba55ca4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 7 Mar 2023 13:45:49 +0100 Subject: [PATCH 1216/1271] General: Input representation ids are not ObjectIds (#4576) * input representation ids are not ObjectIds during publishing * changed set back to list * use 'setdefault' to set 'inputVersions' * added default value to 'get' * Use default value in second loop too Co-authored-by: Roy Nieterau * simplify variable assignment Co-authored-by: Roy Nieterau --------- Co-authored-by: Roy Nieterau --- .../fusion/plugins/publish/collect_inputs.py | 5 +---- .../houdini/plugins/publish/collect_inputs.py | 5 +---- .../hosts/maya/plugins/publish/collect_inputs.py | 4 +--- .../collect_input_representations_to_versions.py | 15 ++++++++------- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/collect_inputs.py b/openpype/hosts/fusion/plugins/publish/collect_inputs.py index 8f9857b02f..b6619fdcd6 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_inputs.py +++ b/openpype/hosts/fusion/plugins/publish/collect_inputs.py @@ -1,5 +1,3 @@ -from bson.objectid import ObjectId - import pyblish.api from openpype.pipeline import registered_host @@ -108,7 +106,6 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin): # Collect containers for the given set of nodes containers = collect_input_containers(nodes) - inputs = [ObjectId(c["representation"]) for c in containers] + inputs = [c["representation"] for c in containers] instance.data["inputRepresentations"] = inputs - self.log.info("Collected inputs: %s" % inputs) diff --git a/openpype/hosts/houdini/plugins/publish/collect_inputs.py b/openpype/hosts/houdini/plugins/publish/collect_inputs.py index 0b54b244bb..6411376ea3 100644 --- a/openpype/hosts/houdini/plugins/publish/collect_inputs.py +++ b/openpype/hosts/houdini/plugins/publish/collect_inputs.py @@ -1,5 +1,3 @@ -from bson.objectid import ObjectId - import pyblish.api from openpype.pipeline import registered_host @@ -117,7 +115,6 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin): # Collect containers for the given set of nodes containers = collect_input_containers(nodes) - inputs = [ObjectId(c["representation"]) for c in containers] + inputs = [c["representation"] for c in containers] instance.data["inputRepresentations"] = inputs - self.log.info("Collected inputs: %s" % inputs) diff --git a/openpype/hosts/maya/plugins/publish/collect_inputs.py b/openpype/hosts/maya/plugins/publish/collect_inputs.py index 470fceffc9..9c3f0f5efa 100644 --- a/openpype/hosts/maya/plugins/publish/collect_inputs.py +++ b/openpype/hosts/maya/plugins/publish/collect_inputs.py @@ -1,5 +1,4 @@ import copy -from bson.objectid import ObjectId from maya import cmds import maya.api.OpenMaya as om @@ -165,9 +164,8 @@ class CollectUpstreamInputs(pyblish.api.InstancePlugin): containers = collect_input_containers(scene_containers, nodes) - inputs = [ObjectId(c["representation"]) for c in containers] + inputs = [c["representation"] for c in containers] instance.data["inputRepresentations"] = inputs - self.log.info("Collected inputs: %s" % inputs) def _collect_renderlayer_inputs(self, scene_containers, instance): diff --git a/openpype/plugins/publish/collect_input_representations_to_versions.py b/openpype/plugins/publish/collect_input_representations_to_versions.py index 18a19bce80..54a3214647 100644 --- a/openpype/plugins/publish/collect_input_representations_to_versions.py +++ b/openpype/plugins/publish/collect_input_representations_to_versions.py @@ -23,7 +23,8 @@ class CollectInputRepresentationsToVersions(pyblish.api.ContextPlugin): representations = set() for instance in context: inst_repre = instance.data.get("inputRepresentations", []) - representations.update(inst_repre) + if inst_repre: + representations.update(inst_repre) representations_docs = get_representations( project_name=context.data["projectEntity"]["name"], @@ -31,7 +32,8 @@ class CollectInputRepresentationsToVersions(pyblish.api.ContextPlugin): fields=["_id", "parent"]) representation_id_to_version_id = { - repre["_id"]: repre["parent"] for repre in representations_docs + str(repre["_id"]): repre["parent"] + for repre in representations_docs } for instance in context: @@ -39,9 +41,8 @@ class CollectInputRepresentationsToVersions(pyblish.api.ContextPlugin): if not inst_repre: continue - input_versions = instance.data.get("inputVersions", []) + input_versions = instance.data.setdefault("inputVersions", []) for repre_id in inst_repre: - repre_id = ObjectId(repre_id) - version_id = representation_id_to_version_id[repre_id] - input_versions.append(version_id) - instance.data["inputVersions"] = input_versions + version_id = representation_id_to_version_id.get(repre_id) + if version_id: + input_versions.append(version_id) From f94fb76a238c5fc24ff26d84061fc7f7f5d5c90f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 13:56:53 +0100 Subject: [PATCH 1217/1271] Update OCIO config hook to use the correct imageio settings --- .../fusion/hooks/pre_fusion_ocio_hook.py | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py b/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py index d1ae5f64fd..6bf0f55081 100644 --- a/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py +++ b/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py @@ -1,7 +1,7 @@ -import os -import platform +from openpype.lib import PreLaunchHook -from openpype.lib import PreLaunchHook, ApplicationLaunchFailed +from openpype.pipeline.colorspace import get_imageio_config +from openpype.pipeline.template_data import get_template_data_with_names class FusionPreLaunchOCIO(PreLaunchHook): @@ -11,24 +11,22 @@ class FusionPreLaunchOCIO(PreLaunchHook): def execute(self): """Hook entry method.""" - # get image io - project_settings = self.data["project_settings"] + template_data = get_template_data_with_names( + project_name=self.data["project_name"], + asset_name=self.data["asset_name"], + task_name=self.data["task_name"], + host_name=self.host_name, + system_settings=self.data["system_settings"] + ) - # make sure anatomy settings are having flame key - imageio_fusion = project_settings["fusion"]["imageio"] - - ocio = imageio_fusion.get("ocio") - enabled = ocio.get("enabled", False) - if not enabled: - return - - platform_key = platform.system().lower() - ocio_path = ocio["configFilePath"][platform_key] - if not ocio_path: - raise ApplicationLaunchFailed( - "Fusion OCIO is enabled in project settings but no OCIO config" - f"path is set for your current platform: {platform_key}" - ) + config_data = get_imageio_config( + project_name=self.data["project_name"], + host_name=self.host_name, + project_settings=self.data["project_settings"], + anatomy_data=template_data, + anatomy=self.data["anatomy"] + ) + ocio_path = config_data["path"] self.log.info(f"Setting OCIO config path: {ocio_path}") - self.launch_context.env["OCIO"] = os.pathsep.join(ocio_path) + self.launch_context.env["OCIO"] = ocio_path From d47f0054deb49827890ad3b070e282d74aa2c62a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 14:38:42 +0100 Subject: [PATCH 1218/1271] Fix actions --- .../hosts/fusion/plugins/publish/validate_background_depth.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/validate_background_depth.py b/openpype/hosts/fusion/plugins/publish/validate_background_depth.py index 261533de01..db2c4f0dd9 100644 --- a/openpype/hosts/fusion/plugins/publish/validate_background_depth.py +++ b/openpype/hosts/fusion/plugins/publish/validate_background_depth.py @@ -11,12 +11,11 @@ class ValidateBackgroundDepth(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder label = "Validate Background Depth 32 bit" - actions = [RepairAction] hosts = ["fusion"] families = ["render"] optional = True - actions = [SelectInvalidAction] + actions = [SelectInvalidAction, RepairAction] @classmethod def get_invalid(cls, instance): From b4727101c969689fb8bebbfc7afde20680da7dfb Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 14:39:20 +0100 Subject: [PATCH 1219/1271] Directly collect comp frame ranges in Collect comp --- .../fusion/plugins/publish/collect_comp.py | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/collect_comp.py b/openpype/hosts/fusion/plugins/publish/collect_comp.py index dfa540fa7f..911071c9a0 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_comp.py +++ b/openpype/hosts/fusion/plugins/publish/collect_comp.py @@ -1,10 +1,26 @@ -import os - import pyblish.api from openpype.hosts.fusion.api import get_current_comp +def get_comp_render_range(comp): + """Return comp's start-end render range and global start-end range.""" + comp_attrs = comp.GetAttrs() + start = comp_attrs["COMPN_RenderStart"] + end = comp_attrs["COMPN_RenderEnd"] + global_start = comp_attrs["COMPN_GlobalStart"] + global_end = comp_attrs["COMPN_GlobalEnd"] + + # Whenever render ranges are undefined fall back + # to the comp's global start and end + if start == -1000000000: + start = global_start + if end == -1000000000: + end = global_end + + return start, end, global_start, global_end + + class CollectCurrentCompFusion(pyblish.api.ContextPlugin): """Collect current comp""" @@ -15,10 +31,17 @@ class CollectCurrentCompFusion(pyblish.api.ContextPlugin): def process(self, context): """Collect all image sequence tools""" - current_comp = get_current_comp() - assert current_comp, "Must have active Fusion composition" - context.data["currentComp"] = current_comp + comp = get_current_comp() + assert comp, "Must have active Fusion composition" + context.data["currentComp"] = comp # Store path to current file - filepath = current_comp.GetAttrs().get("COMPS_FileName", "") + filepath = comp.GetAttrs().get("COMPS_FileName", "") context.data['currentFile'] = filepath + + # Store comp render ranges + start, end, global_start, global_end = get_comp_render_range(comp) + context.data["frameStart"] = int(start) + context.data["frameEnd"] = int(end) + context.data["frameStartHandle"] = int(global_start) + context.data["frameEndHandle"] = int(global_end) From a4ae05086cbbd7968b37801c3a70f0a763ab487e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 14:41:16 +0100 Subject: [PATCH 1220/1271] Allow to enable/disable review per saver instance + Don't create a copy of representation for review but just mark representation as review + Change Collect instances into InstancePlugin to just collect instance data per instance --- .../fusion/plugins/create/create_saver.py | 10 ++ .../plugins/publish/collect_instances.py | 122 ++++++++---------- .../fusion/plugins/publish/render_local.py | 6 +- 3 files changed, 66 insertions(+), 72 deletions(-) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index b0c0d830a3..bf11dc95c5 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -7,6 +7,7 @@ from openpype.hosts.fusion.api import ( comp_lock_and_undo_chunk ) +from openpype.lib import BoolDef from openpype.pipeline import ( legacy_io, Creator, @@ -192,3 +193,12 @@ class CreateSaver(Creator): return return data + + def get_instance_attr_defs(self): + return [ + BoolDef( + "review", + default=True, + label="Review" + ) + ] diff --git a/openpype/hosts/fusion/plugins/publish/collect_instances.py b/openpype/hosts/fusion/plugins/publish/collect_instances.py index 4e5e151789..1e6d095cc2 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_instances.py +++ b/openpype/hosts/fusion/plugins/publish/collect_instances.py @@ -3,25 +3,7 @@ import os import pyblish.api -def get_comp_render_range(comp): - """Return comp's start-end render range and global start-end range.""" - comp_attrs = comp.GetAttrs() - start = comp_attrs["COMPN_RenderStart"] - end = comp_attrs["COMPN_RenderEnd"] - global_start = comp_attrs["COMPN_GlobalStart"] - global_end = comp_attrs["COMPN_GlobalEnd"] - - # Whenever render ranges are undefined fall back - # to the comp's global start and end - if start == -1000000000: - start = global_start - if end == -1000000000: - end = global_end - - return start, end, global_start, global_end - - -class CollectInstances(pyblish.api.ContextPlugin): +class CollectInstanceData(pyblish.api.InstancePlugin): """Collect Fusion saver instances This additionally stores the Comp start and end render range in the @@ -33,59 +15,63 @@ class CollectInstances(pyblish.api.ContextPlugin): label = "Collect Instances Data" hosts = ["fusion"] - def process(self, context): + def process(self, instance): """Collect all image sequence tools""" - from openpype.hosts.fusion.api.lib import get_frame_path + context = instance.context - comp = context.data["currentComp"] - start, end, global_start, global_end = get_comp_render_range(comp) - context.data["frameStart"] = int(start) - context.data["frameEnd"] = int(end) - context.data["frameStartHandle"] = int(global_start) - context.data["frameEndHandle"] = int(global_end) + # Include creator attributes directly as instance data + creator_attributes = instance.data["creator_attributes"] + instance.data.update(creator_attributes) + + # Include start and end render frame in label + subset = instance.data["subset"] + start = context.data["frameStart"] + end = context.data["frameEnd"] + label = "{subset} ({start}-{end})".format(subset=subset, + start=int(start), + end=int(end)) + instance.data.update({ + "label": label, + + # todo: Allow custom frame range per instance + "frameStart": context.data["frameStart"], + "frameEnd": context.data["frameEnd"], + "frameStartHandle": context.data["frameStartHandle"], + "frameEndHandle": context.data["frameStartHandle"], + "fps": context.data["fps"], + }) + + # Add review family if the instance is marked as 'review' + # This could be done through a 'review' Creator attribute. + if instance.data.get("review", False): + self.log.info("Adding review family..") + instance.data["families"].append("review") + + if instance.data["family"] == "render": + # TODO: This should probably move into a collector of + # its own for the "render" family + from openpype.hosts.fusion.api.lib import get_frame_path + comp = context.data["currentComp"] + + # This is only the case for savers currently but not + # for workfile instances. So we assume saver here. + tool = instance.data["transientData"]["tool"] + path = tool["Clip"][comp.TIME_UNDEFINED] + + filename = os.path.basename(path) + head, padding, tail = get_frame_path(filename) + ext = os.path.splitext(path)[1] + assert tail == ext, ("Tail does not match %s" % ext) - for instance in context: - # Include start and end render frame in label - subset = instance.data["subset"] - label = "{subset} ({start}-{end})".format(subset=subset, - start=int(start), - end=int(end)) instance.data.update({ - "label": label, - # todo: Allow custom frame range per instance - "task": context.data["task"], - "frameStart": context.data["frameStart"], - "frameEnd": context.data["frameEnd"], - "frameStartHandle": context.data["frameStartHandle"], - "frameEndHandle": context.data["frameStartHandle"], - "fps": context.data["fps"], + "path": path, + "outputDir": os.path.dirname(path), + "ext": ext, # todo: should be redundant? + + # Backwards compatibility: embed tool in instance.data + "tool": tool }) - if instance.data["family"] == "render": - # TODO: This should probably move into a collector of - # its own for the "render" family - # This is only the case for savers currently but not - # for workfile instances. So we assume saver here. - tool = instance.data["transientData"]["tool"] - path = tool["Clip"][comp.TIME_UNDEFINED] - - filename = os.path.basename(path) - head, padding, tail = get_frame_path(filename) - ext = os.path.splitext(path)[1] - assert tail == ext, ("Tail does not match %s" % ext) - - instance.data.update({ - "path": path, - "outputDir": os.path.dirname(path), - "ext": ext, # todo: should be redundant? - - "families": ["render", "review"], - "family": "render", - - # Backwards compatibility: embed tool in instance.data - "tool": tool - }) - - # Add tool itself as member - instance.append(tool) + # Add tool itself as member + instance.append(tool) diff --git a/openpype/hosts/fusion/plugins/publish/render_local.py b/openpype/hosts/fusion/plugins/publish/render_local.py index 53d8eb64e1..30943edd4b 100644 --- a/openpype/hosts/fusion/plugins/publish/render_local.py +++ b/openpype/hosts/fusion/plugins/publish/render_local.py @@ -53,10 +53,8 @@ class Fusionlocal(pyblish.api.InstancePlugin): instance.data["representations"].append(repre) # review representation - repre_preview = repre.copy() - repre_preview["name"] = repre_preview["ext"] = "mp4" - repre_preview["tags"] = ["review", "ftrackreview", "delete"] - instance.data["representations"].append(repre_preview) + if instance.data.get("review", False): + repre["tags"] = ["review", "ftrackreview"] def render_once(self, context): """Render context comp only once, even with more render instances""" From c43a8b073296cb95ba1700faf5e32cb9f7c31fa4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 16:06:44 +0100 Subject: [PATCH 1221/1271] Collect comp frame range later in publishing - Otherwise it gets overridden by global plugin `CollectContextEntities` --- .../fusion/plugins/publish/collect_comp.py | 25 ----------- .../publish/collect_comp_frame_range.py | 41 +++++++++++++++++++ 2 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py diff --git a/openpype/hosts/fusion/plugins/publish/collect_comp.py b/openpype/hosts/fusion/plugins/publish/collect_comp.py index 911071c9a0..d26bf66d1f 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_comp.py +++ b/openpype/hosts/fusion/plugins/publish/collect_comp.py @@ -3,24 +3,6 @@ import pyblish.api from openpype.hosts.fusion.api import get_current_comp -def get_comp_render_range(comp): - """Return comp's start-end render range and global start-end range.""" - comp_attrs = comp.GetAttrs() - start = comp_attrs["COMPN_RenderStart"] - end = comp_attrs["COMPN_RenderEnd"] - global_start = comp_attrs["COMPN_GlobalStart"] - global_end = comp_attrs["COMPN_GlobalEnd"] - - # Whenever render ranges are undefined fall back - # to the comp's global start and end - if start == -1000000000: - start = global_start - if end == -1000000000: - end = global_end - - return start, end, global_start, global_end - - class CollectCurrentCompFusion(pyblish.api.ContextPlugin): """Collect current comp""" @@ -38,10 +20,3 @@ class CollectCurrentCompFusion(pyblish.api.ContextPlugin): # Store path to current file filepath = comp.GetAttrs().get("COMPS_FileName", "") context.data['currentFile'] = filepath - - # Store comp render ranges - start, end, global_start, global_end = get_comp_render_range(comp) - context.data["frameStart"] = int(start) - context.data["frameEnd"] = int(end) - context.data["frameStartHandle"] = int(global_start) - context.data["frameEndHandle"] = int(global_end) diff --git a/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py b/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py new file mode 100644 index 0000000000..dc88dd79c6 --- /dev/null +++ b/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py @@ -0,0 +1,41 @@ +import pyblish.api + +from openpype.hosts.fusion.api import get_current_comp + + +def get_comp_render_range(comp): + """Return comp's start-end render range and global start-end range.""" + comp_attrs = comp.GetAttrs() + start = comp_attrs["COMPN_RenderStart"] + end = comp_attrs["COMPN_RenderEnd"] + global_start = comp_attrs["COMPN_GlobalStart"] + global_end = comp_attrs["COMPN_GlobalEnd"] + + # Whenever render ranges are undefined fall back + # to the comp's global start and end + if start == -1000000000: + start = global_start + if end == -1000000000: + end = global_end + + return start, end, global_start, global_end + + +class CollectFusionCompFrameRanges(pyblish.api.ContextPlugin): + """Collect current comp""" + + order = pyblish.api.CollectorOrder - 0.05 + label = "Collect Comp Frame Ranges" + hosts = ["fusion"] + + def process(self, context): + """Collect all image sequence tools""" + + comp = context.data["currentComp"] + + # Store comp render ranges + start, end, global_start, global_end = get_comp_render_range(comp) + context.data["frameStart"] = int(start) + context.data["frameEnd"] = int(end) + context.data["frameStartHandle"] = int(global_start) + context.data["frameEndHandle"] = int(global_end) From f6b8a8df61af591427e1192ecf7ce416db338db6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 16:07:54 +0100 Subject: [PATCH 1222/1271] Revert redundant variable name change since plugin is now basically reverted --- openpype/hosts/fusion/plugins/publish/collect_comp.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/collect_comp.py b/openpype/hosts/fusion/plugins/publish/collect_comp.py index d26bf66d1f..d1c49790fa 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_comp.py +++ b/openpype/hosts/fusion/plugins/publish/collect_comp.py @@ -13,10 +13,10 @@ class CollectCurrentCompFusion(pyblish.api.ContextPlugin): def process(self, context): """Collect all image sequence tools""" - comp = get_current_comp() - assert comp, "Must have active Fusion composition" - context.data["currentComp"] = comp + current_comp = get_current_comp() + assert current_comp, "Must have active Fusion composition" + context.data["currentComp"] = current_comp # Store path to current file - filepath = comp.GetAttrs().get("COMPS_FileName", "") + filepath = current_comp.GetAttrs().get("COMPS_FileName", "") context.data['currentFile'] = filepath From 146f5cd439652d454b656163f00b1521bf5ee227 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 16:09:36 +0100 Subject: [PATCH 1223/1271] Add descriptive comment --- .../hosts/fusion/plugins/publish/collect_comp_frame_range.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py b/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py index dc88dd79c6..98128e1ccf 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py +++ b/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py @@ -24,6 +24,8 @@ def get_comp_render_range(comp): class CollectFusionCompFrameRanges(pyblish.api.ContextPlugin): """Collect current comp""" + # We run this after CollectorOrder - 0.1 otherwise it gets + # overridden by global plug-in `CollectContextEntities` order = pyblish.api.CollectorOrder - 0.05 label = "Collect Comp Frame Ranges" hosts = ["fusion"] From 43d084cf7f5a7f277a44c50bab623f31a74a8975 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 16:09:55 +0100 Subject: [PATCH 1224/1271] Remove unused import --- .../hosts/fusion/plugins/publish/collect_comp_frame_range.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py b/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py index 98128e1ccf..c6d7a73a04 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py +++ b/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py @@ -1,7 +1,5 @@ import pyblish.api -from openpype.hosts.fusion.api import get_current_comp - def get_comp_render_range(comp): """Return comp's start-end render range and global start-end range.""" From 1b18483f7b480665922847ceb556375d49026d35 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 7 Mar 2023 16:41:35 +0100 Subject: [PATCH 1225/1271] use right type for signal emit (#4584) --- openpype/tools/attribute_defs/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/attribute_defs/widgets.py b/openpype/tools/attribute_defs/widgets.py index 18e2e13d06..0d4e1e88a9 100644 --- a/openpype/tools/attribute_defs/widgets.py +++ b/openpype/tools/attribute_defs/widgets.py @@ -186,7 +186,7 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget): class _BaseAttrDefWidget(QtWidgets.QWidget): # Type 'object' may not work with older PySide versions - value_changed = QtCore.Signal(object, uuid.UUID) + value_changed = QtCore.Signal(object, str) def __init__(self, attr_def, parent): super(_BaseAttrDefWidget, self).__init__(parent) From 70163a2f255413fbe706101b261dac3c3e65e2a8 Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Tue, 7 Mar 2023 17:26:09 +0100 Subject: [PATCH 1226/1271] Added Create button to menu and set tab data for create and publish btn --- openpype/hosts/fusion/api/menu.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/fusion/api/menu.py b/openpype/hosts/fusion/api/menu.py index 568e03464d..e37380017e 100644 --- a/openpype/hosts/fusion/api/menu.py +++ b/openpype/hosts/fusion/api/menu.py @@ -7,11 +7,11 @@ from openpype.style import load_stylesheet from openpype.lib import register_event_callback from openpype.hosts.fusion.scripts import ( set_rendermode, - duplicate_with_inputs + duplicate_with_inputs, ) from openpype.hosts.fusion.api.lib import ( set_asset_framerange, - set_asset_resolution + set_asset_resolution, ) from openpype.pipeline import legacy_io from openpype.resources import get_openpype_icon_filepath @@ -45,14 +45,17 @@ class OpenPypeMenu(QtWidgets.QWidget): self.setWindowTitle("OpenPype") asset_label = QtWidgets.QLabel("Context", self) - asset_label.setStyleSheet("""QLabel { + asset_label.setStyleSheet( + """QLabel { font-size: 14px; font-weight: 600; color: #5f9fb8; - }""") + }""" + ) asset_label.setAlignment(QtCore.Qt.AlignHCenter) workfiles_btn = QtWidgets.QPushButton("Workfiles...", self) + create_btn = QtWidgets.QPushButton("Create...", self) publish_btn = QtWidgets.QPushButton("Publish...", self) load_btn = QtWidgets.QPushButton("Load...", self) manager_btn = QtWidgets.QPushButton("Manage...", self) @@ -76,6 +79,7 @@ class OpenPypeMenu(QtWidgets.QWidget): layout.addSpacing(20) layout.addWidget(load_btn) + layout.addWidget(create_btn) layout.addWidget(publish_btn) layout.addWidget(manager_btn) @@ -99,13 +103,15 @@ class OpenPypeMenu(QtWidgets.QWidget): self.asset_label = asset_label workfiles_btn.clicked.connect(self.on_workfile_clicked) + create_btn.clicked.connect(self.on_create_clicked) publish_btn.clicked.connect(self.on_publish_clicked) load_btn.clicked.connect(self.on_load_clicked) manager_btn.clicked.connect(self.on_manager_clicked) libload_btn.clicked.connect(self.on_libload_clicked) rendermode_btn.clicked.connect(self.on_rendermode_clicked) duplicate_with_inputs_btn.clicked.connect( - self.on_duplicate_with_inputs_clicked) + self.on_duplicate_with_inputs_clicked + ) set_resolution_btn.clicked.connect(self.on_set_resolution_clicked) set_framerange_btn.clicked.connect(self.on_set_framerange_clicked) @@ -127,7 +133,6 @@ class OpenPypeMenu(QtWidgets.QWidget): self.asset_label.setText(label) def register_callback(self, name, fn): - # Create a wrapper callback that we only store # for as long as we want it to persist as callback def _callback(*args): @@ -142,8 +147,11 @@ class OpenPypeMenu(QtWidgets.QWidget): def on_workfile_clicked(self): host_tools.show_workfiles() + def on_create_clicked(self): + host_tools.show_publisher(tab="create") + def on_publish_clicked(self): - host_tools.show_publisher() + host_tools.show_publisher(tab="publish") def on_load_clicked(self): host_tools.show_loader(use_context=True) From b1fac42e94a668e9b072dc5f64edca865d06afcd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 7 Mar 2023 17:26:31 +0100 Subject: [PATCH 1227/1271] updating pr tempate --- .github/pull_request_template.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 20ae298f70..2adaffd23d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,16 +1,9 @@ -## Brief description -First sentence is brief description. - -## Description -Next paragraf is more elaborate text with more info. This will be displayed for example in collapsed form under the first sentence in a changelog. +## Changelog Description +Paragraphs contain detailed information on the changes made to the product or service, providing an in-depth description of the updates and enhancements. They can be used to explain the reasoning behind the changes, or to highlight the importance of the new features. Paragraphs can often include links to further information or support documentation. ## Additional info -The rest will be ignored in changelog and should contain any additional -technical information. - -## Documentation (add _"type: documentation"_ label) -[feature_documentation](future_url_after_it_will_be_merged) +Paragraphs of text giving context of additional technical information or code examples. ## Testing notes: 1. start with this step -2. follow this step \ No newline at end of file +2. follow this step From 9c9c134a794a5ab9bd36a415ef15d2b2b64dbdd8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 17:39:05 +0100 Subject: [PATCH 1228/1271] Use passthrough state of saver tool to store and load the active state --- openpype/hosts/fusion/plugins/create/create_saver.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py index bf11dc95c5..e581bac20f 100644 --- a/openpype/hosts/fusion/plugins/create/create_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_saver.py @@ -109,6 +109,12 @@ class CreateSaver(Creator): def _imprint(self, tool, data): # Save all data in a "openpype.{key}" = value data + + active = data.pop("active", None) + if active is not None: + # Use active value to set the passthrough state + tool.SetAttrs({"TOOLB_PassThrough": not active}) + for key, value in data.items(): tool.SetData(f"openpype.{key}", value) @@ -192,6 +198,11 @@ class CreateSaver(Creator): if key not in data or data[key] != value: return + # Get active state from the actual tool state + attrs = tool.GetAttrs() + passthrough = attrs["TOOLB_PassThrough"] + data["active"] = not passthrough + return data def get_instance_attr_defs(self): From 5c8bbe28713aaabbabf99b4739f1654a0bea4b71 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 17:41:03 +0100 Subject: [PATCH 1229/1271] Remove pyblish callback which does nothing in new publisher --- openpype/hosts/fusion/api/pipeline.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/openpype/hosts/fusion/api/pipeline.py b/openpype/hosts/fusion/api/pipeline.py index b982e1c2e9..a768a3f0f8 100644 --- a/openpype/hosts/fusion/api/pipeline.py +++ b/openpype/hosts/fusion/api/pipeline.py @@ -102,9 +102,6 @@ class FusionHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): register_creator_plugin_path(CREATE_PATH) register_inventory_action_path(INVENTORY_PATH) - pyblish.api.register_callback( - "instanceToggled", on_pyblish_instance_toggled) - # Register events register_event_callback("open", on_after_open) register_event_callback("save", on_save) @@ -163,29 +160,6 @@ class FusionHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): return comp.GetData("openpype") or {} -def on_pyblish_instance_toggled(instance, old_value, new_value): - """Toggle saver tool passthrough states on instance toggles.""" - comp = instance.context.data.get("currentComp") - if not comp: - return - - savers = [tool for tool in instance if - getattr(tool, "ID", None) == "Saver"] - if not savers: - return - - # Whether instances should be passthrough based on new value - passthrough = not new_value - with comp_lock_and_undo_chunk(comp, - undo_queue_name="Change instance " - "active state"): - for tool in savers: - attrs = tool.GetAttrs() - current = attrs["TOOLB_PassThrough"] - if current != passthrough: - tool.SetAttrs({"TOOLB_PassThrough": passthrough}) - - def on_new(event): comp = event["Rets"]["comp"] validate_comp_prefs(comp, force_repair=True) From 0f037666f83226e9ec832a58a49ee848683b9b04 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 17:49:31 +0100 Subject: [PATCH 1230/1271] Change menu order to how it was originally and match with e.g. maya menu order --- openpype/hosts/fusion/api/menu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/fusion/api/menu.py b/openpype/hosts/fusion/api/menu.py index e37380017e..343f5f803a 100644 --- a/openpype/hosts/fusion/api/menu.py +++ b/openpype/hosts/fusion/api/menu.py @@ -56,8 +56,8 @@ class OpenPypeMenu(QtWidgets.QWidget): workfiles_btn = QtWidgets.QPushButton("Workfiles...", self) create_btn = QtWidgets.QPushButton("Create...", self) - publish_btn = QtWidgets.QPushButton("Publish...", self) load_btn = QtWidgets.QPushButton("Load...", self) + publish_btn = QtWidgets.QPushButton("Publish...", self) manager_btn = QtWidgets.QPushButton("Manage...", self) libload_btn = QtWidgets.QPushButton("Library...", self) rendermode_btn = QtWidgets.QPushButton("Set render mode...", self) @@ -78,8 +78,8 @@ class OpenPypeMenu(QtWidgets.QWidget): layout.addSpacing(20) - layout.addWidget(load_btn) layout.addWidget(create_btn) + layout.addWidget(load_btn) layout.addWidget(publish_btn) layout.addWidget(manager_btn) From acbfb5985b52516ac99d45bd6b5e7f8121d89b6c Mon Sep 17 00:00:00 2001 From: Jacob Danell Date: Mon, 6 Mar 2023 14:50:00 +0100 Subject: [PATCH 1231/1271] Fixed task itteration From the last PR (https://github.com/ynput/OpenPype/pull/4425) a comment-commit last second messed up and resultet in two lines being the same, crashing the script. This fixes that. --- .../modules/kitsu/utils/update_op_with_zou.py | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 053e803ff3..4fa8cf9fdd 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -95,7 +95,8 @@ def update_op_assets( op_asset = create_op_asset(item) insert_result = dbcon.insert_one(op_asset) item_doc = get_asset_by_id( - project_name, insert_result.inserted_id) + project_name, insert_result.inserted_id + ) # Update asset item_data = deepcopy(item_doc["data"]) @@ -133,39 +134,47 @@ def update_op_assets( try: fps = float(item_data.get("fps")) except (TypeError, ValueError): - fps = float(gazu_project.get( - "fps", project_doc["data"].get("fps", 25))) + fps = float( + gazu_project.get("fps", project_doc["data"].get("fps", 25)) + ) item_data["fps"] = fps # Resolution, fall back to project default match_res = re.match( r"(\d+)x(\d+)", - item_data.get("resolution", gazu_project.get("resolution")) + item_data.get("resolution", gazu_project.get("resolution")), ) if match_res: item_data["resolutionWidth"] = int(match_res.group(1)) item_data["resolutionHeight"] = int(match_res.group(2)) else: item_data["resolutionWidth"] = project_doc["data"].get( - "resolutionWidth") + "resolutionWidth" + ) item_data["resolutionHeight"] = project_doc["data"].get( - "resolutionHeight") + "resolutionHeight" + ) # Properties that doesn't fully exist in Kitsu. # Guessing those property names below: # Pixel Aspect Ratio item_data["pixelAspect"] = item_data.get( - "pixel_aspect", project_doc["data"].get("pixelAspect")) + "pixel_aspect", project_doc["data"].get("pixelAspect") + ) # Handle Start item_data["handleStart"] = item_data.get( - "handle_start", project_doc["data"].get("handleStart")) + "handle_start", project_doc["data"].get("handleStart") + ) # Handle End item_data["handleEnd"] = item_data.get( - "handle_end", project_doc["data"].get("handleEnd")) + "handle_end", project_doc["data"].get("handleEnd") + ) # Clip In item_data["clipIn"] = item_data.get( - "clip_in", project_doc["data"].get("clipIn")) + "clip_in", project_doc["data"].get("clipIn") + ) # Clip Out item_data["clipOut"] = item_data.get( - "clip_out", project_doc["data"].get("clipOut")) + "clip_out", project_doc["data"].get("clipOut") + ) # Tasks tasks_list = [] @@ -175,11 +184,9 @@ def update_op_assets( elif item_type == "Shot": tasks_list = gazu.task.all_tasks_for_shot(item) item_data["tasks"] = { - item_data["tasks"] = { - t["task_type_name"]: { - "type": t["task_type_name"], - "zou": gazu.task.get_task(t["id"]), - } + t["task_type_name"]: { + "type": t["task_type_name"], + "zou": gazu.task.get_task(t["id"]), } for t in tasks_list } @@ -218,7 +225,9 @@ def update_op_assets( if parent_zou_id_dict is not None: visual_parent_doc_id = ( parent_zou_id_dict.get("_id") - if parent_zou_id_dict else None) + if parent_zou_id_dict + else None + ) if visual_parent_doc_id is None: # Find root folder doc ("Assets" or "Shots") @@ -345,7 +354,8 @@ def write_project_to_op(project: dict, dbcon: AvalonMongoDB) -> UpdateOne: def sync_all_projects( - login: str, password: str, ignore_projects: list = None): + login: str, password: str, ignore_projects: list = None +): """Update all OP projects in DB with Zou data. Args: @@ -390,7 +400,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): if not project: project = gazu.project.get_project_by_name(project["name"]) - log.info("Synchronizing {}...".format(project['name'])) + log.info("Synchronizing {}...".format(project["name"])) # Get all assets from zou all_assets = gazu.asset.all_assets_for_project(project) @@ -473,8 +483,11 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): [ UpdateOne({"_id": id}, update) for id, update in update_op_assets( - dbcon, project, project_dict, - all_entities, zou_ids_and_asset_docs + dbcon, + project, + project_dict, + all_entities, + zou_ids_and_asset_docs, ) ] ) From 40125fa6a5518b7ca202a892a3f4196913058600 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 10:17:44 +0100 Subject: [PATCH 1232/1271] Avoid error in PySide6+ --- openpype/widgets/popup.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/openpype/widgets/popup.py b/openpype/widgets/popup.py index 97a8461060..28bbd45072 100644 --- a/openpype/widgets/popup.py +++ b/openpype/widgets/popup.py @@ -98,15 +98,22 @@ class Popup(QtWidgets.QDialog): height = window.height() height = max(height, window.sizeHint().height()) - desktop_geometry = QtWidgets.QDesktopWidget().availableGeometry() - screen_geometry = window.geometry() + try: + screen = QtWidgets.QApplication.primaryScreen() + desktop_geometry = screen.availableGeometry() + except AttributeError: + # Backwards compatibility for older Qt versions + # PySide6 removed QDesktopWidget + desktop_geometry = QtWidgets.QDesktopWidget().availableGeometry() - screen_width = screen_geometry.width() - screen_height = screen_geometry.height() + window_geometry = window.geometry() + + screen_width = window_geometry.width() + screen_height = window_geometry.height() # Calculate width and height of system tray - systray_width = screen_geometry.width() - desktop_geometry.width() - systray_height = screen_geometry.height() - desktop_geometry.height() + systray_width = window_geometry.width() - desktop_geometry.width() + systray_height = window_geometry.height() - desktop_geometry.height() padding = 10 From 300a4435101e8ae6608ef4024f002606f9f867d5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 11:08:10 +0100 Subject: [PATCH 1233/1271] Use screen of window instead of primary screen Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/widgets/popup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/widgets/popup.py b/openpype/widgets/popup.py index 28bbd45072..225c5e18a1 100644 --- a/openpype/widgets/popup.py +++ b/openpype/widgets/popup.py @@ -99,7 +99,7 @@ class Popup(QtWidgets.QDialog): height = max(height, window.sizeHint().height()) try: - screen = QtWidgets.QApplication.primaryScreen() + screen = window.screen() desktop_geometry = screen.availableGeometry() except AttributeError: # Backwards compatibility for older Qt versions From ecfea3dee2be05318ec9cfb88802f357fdb2c0e9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Mar 2023 20:42:49 +0100 Subject: [PATCH 1234/1271] Explicitly set the `handleStart` and `handleEnd` otherwise other global plug-ins will force in other data like asset data. --- .../hosts/fusion/plugins/publish/collect_comp_frame_range.py | 2 ++ openpype/hosts/fusion/plugins/publish/collect_instances.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py b/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py index c6d7a73a04..fbd7606cd7 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py +++ b/openpype/hosts/fusion/plugins/publish/collect_comp_frame_range.py @@ -39,3 +39,5 @@ class CollectFusionCompFrameRanges(pyblish.api.ContextPlugin): context.data["frameEnd"] = int(end) context.data["frameStartHandle"] = int(global_start) context.data["frameEndHandle"] = int(global_end) + context.data["handleStart"] = int(start) - int(global_start) + context.data["handleEnd"] = int(global_end) - int(end) diff --git a/openpype/hosts/fusion/plugins/publish/collect_instances.py b/openpype/hosts/fusion/plugins/publish/collect_instances.py index 1e6d095cc2..af227f03db 100644 --- a/openpype/hosts/fusion/plugins/publish/collect_instances.py +++ b/openpype/hosts/fusion/plugins/publish/collect_instances.py @@ -39,6 +39,8 @@ class CollectInstanceData(pyblish.api.InstancePlugin): "frameEnd": context.data["frameEnd"], "frameStartHandle": context.data["frameStartHandle"], "frameEndHandle": context.data["frameStartHandle"], + "handleStart": context.data["handleStart"], + "handleEnd": context.data["handleEnd"], "fps": context.data["fps"], }) From 5088edfdade4bcb8d096ef45502ba0cf235fb6a5 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 8 Mar 2023 03:30:35 +0000 Subject: [PATCH 1235/1271] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 2939ddbbac..c7a5e9bea5 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.2-nightly.4" +__version__ = "3.15.2-nightly.5" From fb0f39b3ccff52ac2df9e2dfe0cbb743bfb3ac92 Mon Sep 17 00:00:00 2001 From: Alexey Bogomolov <11698866+movalex@users.noreply.github.com> Date: Wed, 8 Mar 2023 12:38:23 +0300 Subject: [PATCH 1236/1271] add up to 3 decimals precision to the frame rate settings (#4571) * add up to 3 decimals to fps allows input 23.976 to the FPS settings both in Project Manager and the Project Anatomy. * set fps and pixel aspect precision steps default values --- .../schemas/schema_anatomy_attributes.json | 2 +- .../project_manager/project_manager/delegates.py | 5 ++++- .../tools/project_manager/project_manager/view.py | 14 ++++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_attributes.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_attributes.json index 3667c9d5d8..a728024376 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_attributes.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_attributes.json @@ -10,7 +10,7 @@ "type": "number", "key": "fps", "label": "Frame Rate", - "decimal": 2, + "decimal": 3, "minimum": 0 }, { diff --git a/openpype/tools/project_manager/project_manager/delegates.py b/openpype/tools/project_manager/project_manager/delegates.py index 79e9554b0f..023dd668ec 100644 --- a/openpype/tools/project_manager/project_manager/delegates.py +++ b/openpype/tools/project_manager/project_manager/delegates.py @@ -83,15 +83,18 @@ class NumberDelegate(QtWidgets.QStyledItemDelegate): decimals(int): How many decimal points can be used. Float will be used as value if is higher than 0. """ - def __init__(self, minimum, maximum, decimals, *args, **kwargs): + def __init__(self, minimum, maximum, decimals, step, *args, **kwargs): super(NumberDelegate, self).__init__(*args, **kwargs) self.minimum = minimum self.maximum = maximum self.decimals = decimals + self.step = step def createEditor(self, parent, option, index): if self.decimals > 0: editor = DoubleSpinBoxScrollFixed(parent) + editor.setSingleStep(self.step) + editor.setDecimals(self.decimals) else: editor = SpinBoxScrollFixed(parent) diff --git a/openpype/tools/project_manager/project_manager/view.py b/openpype/tools/project_manager/project_manager/view.py index fa08943ea5..b35491c5b2 100644 --- a/openpype/tools/project_manager/project_manager/view.py +++ b/openpype/tools/project_manager/project_manager/view.py @@ -26,10 +26,11 @@ class NameDef: class NumberDef: - def __init__(self, minimum=None, maximum=None, decimals=None): + def __init__(self, minimum=None, maximum=None, decimals=None, step=None): self.minimum = 0 if minimum is None else minimum self.maximum = 999999999 if maximum is None else maximum self.decimals = 0 if decimals is None else decimals + self.step = 1 if decimals is None else step class TypeDef: @@ -73,14 +74,14 @@ class HierarchyView(QtWidgets.QTreeView): "type": TypeDef(), "frameStart": NumberDef(1), "frameEnd": NumberDef(1), - "fps": NumberDef(1, decimals=2), + "fps": NumberDef(1, decimals=3, step=1), "resolutionWidth": NumberDef(0), "resolutionHeight": NumberDef(0), "handleStart": NumberDef(0), "handleEnd": NumberDef(0), "clipIn": NumberDef(1), "clipOut": NumberDef(1), - "pixelAspect": NumberDef(0, decimals=2), + "pixelAspect": NumberDef(0, decimals=2, step=0.01), "tools_env": ToolsDef() } @@ -96,6 +97,10 @@ class HierarchyView(QtWidgets.QTreeView): "stretch": QtWidgets.QHeaderView.Interactive, "width": 140 }, + "fps": { + "stretch": QtWidgets.QHeaderView.Interactive, + "width": 65 + }, "tools_env": { "stretch": QtWidgets.QHeaderView.Interactive, "width": 200 @@ -148,7 +153,8 @@ class HierarchyView(QtWidgets.QTreeView): delegate = NumberDelegate( item_type.minimum, item_type.maximum, - item_type.decimals + item_type.decimals, + item_type.step ) elif isinstance(item_type, TypeDef): From 0f45af2d36f5a29f550d412c0d346154d0f855c6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 6 Mar 2023 15:10:37 +0100 Subject: [PATCH 1237/1271] use 'get_representations' instead of 'legacy_io' query --- .../hosts/unreal/plugins/load/load_layout.py | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/openpype/hosts/unreal/plugins/load/load_layout.py b/openpype/hosts/unreal/plugins/load/load_layout.py index c1d66ddf2a..18653e81cb 100644 --- a/openpype/hosts/unreal/plugins/load/load_layout.py +++ b/openpype/hosts/unreal/plugins/load/load_layout.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- """Loader for layouts.""" import json +import collections from pathlib import Path import unreal @@ -12,9 +13,7 @@ from unreal import FBXImportType from unreal import MovieSceneLevelVisibilityTrack from unreal import MovieSceneSubTrack -from bson.objectid import ObjectId - -from openpype.client import get_asset_by_name, get_assets +from openpype.client import get_asset_by_name, get_assets, get_representations from openpype.pipeline import ( discover_loader_plugins, loaders_from_representation, @@ -410,6 +409,29 @@ class LayoutLoader(plugin.Loader): return sequence, (min_frame, max_frame) + def _get_repre_docs_by_version_id(self, project_name, data): + version_ids = { + element.get("version") + for element in data + if element.get("representation") + } + version_ids.discard(None) + + output = collections.defaultdict(list) + if not version_ids: + return output + + repre_docs = get_representations( + project_name, + representation_names=["fbx", "abc"], + version_ids=version_ids, + fields=["_id", "parent", "name"] + ) + for repre_doc in repre_docs: + version_id = str(repre_doc["parent"]) + output[version_id].append(repre_doc) + return output + def _process(self, lib_path, asset_dir, sequence, repr_loaded=None): ar = unreal.AssetRegistryHelpers.get_asset_registry() @@ -429,31 +451,21 @@ class LayoutLoader(plugin.Loader): loaded_assets = [] + repre_docs_by_version_id = self._get_repre_docs_by_version_id(data) for element in data: representation = None repr_format = None if element.get('representation'): - # representation = element.get('representation') - - self.log.info(element.get("version")) - - valid_formats = ['fbx', 'abc'] - - repr_data = legacy_io.find_one({ - "type": "representation", - "parent": ObjectId(element.get("version")), - "name": {"$in": valid_formats} - }) - repr_format = repr_data.get('name') - - if not repr_data: + repre_docs = repre_docs_by_version_id[element.get("version")] + if not repre_docs: self.log.error( f"No valid representation found for version " f"{element.get('version')}") continue + repre_doc = repre_docs[0] + representation = str(repre_doc["_id"]) + repr_format = repre_doc["name"] - representation = str(repr_data.get('_id')) - print(representation) # This is to keep compatibility with old versions of the # json format. elif element.get('reference_fbx'): From 47b1daf0f5ef0cf11b89026c8b342b18782e1956 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 8 Mar 2023 11:21:38 +0100 Subject: [PATCH 1238/1271] get project name other way --- openpype/hosts/unreal/plugins/load/load_layout.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/plugins/load/load_layout.py b/openpype/hosts/unreal/plugins/load/load_layout.py index 18653e81cb..63d415a52b 100644 --- a/openpype/hosts/unreal/plugins/load/load_layout.py +++ b/openpype/hosts/unreal/plugins/load/load_layout.py @@ -409,7 +409,7 @@ class LayoutLoader(plugin.Loader): return sequence, (min_frame, max_frame) - def _get_repre_docs_by_version_id(self, project_name, data): + def _get_repre_docs_by_version_id(self, data): version_ids = { element.get("version") for element in data @@ -421,6 +421,7 @@ class LayoutLoader(plugin.Loader): if not version_ids: return output + project_name = legacy_io.active_project() repre_docs = get_representations( project_name, representation_names=["fbx", "abc"], From c58778194f31b37235b201d9f0132cf97909aa77 Mon Sep 17 00:00:00 2001 From: Joseff Date: Wed, 8 Mar 2023 16:30:30 +0100 Subject: [PATCH 1239/1271] Implementation of a new splash screen --- .../unreal/hooks/pre_workfile_preparation.py | 90 ++++- .../OpenPype/Private/AssetContainer.cpp | 6 +- openpype/hosts/unreal/lib.py | 22 +- openpype/hosts/unreal/ue_workers.py | 338 ++++++++++++++++++ openpype/widgets/README.md | 102 ++++++ openpype/widgets/splash_screen.py | 253 +++++++++++++ 6 files changed, 793 insertions(+), 18 deletions(-) create mode 100644 openpype/hosts/unreal/ue_workers.py create mode 100644 openpype/widgets/README.md create mode 100644 openpype/widgets/splash_screen.py diff --git a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py index 4c9f8258f5..8ede80f7fd 100644 --- a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py +++ b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py @@ -3,7 +3,11 @@ import os import copy from pathlib import Path +from openpype.widgets.splash_screen import SplashScreen +from qtpy import QtCore +from openpype.hosts.unreal.ue_workers import UEProjectGenerationWorker, UEPluginInstallWorker +from openpype import resources from openpype.lib import ( PreLaunchHook, ApplicationLaunchFailed, @@ -22,6 +26,7 @@ class UnrealPrelaunchHook(PreLaunchHook): shell script. """ + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -58,6 +63,70 @@ class UnrealPrelaunchHook(PreLaunchHook): # Return filename return filled_anatomy[workfile_template_key]["file"] + def exec_plugin_install(self, engine_path: Path, env: dict = None): + # set up the QThread and worker with necessary signals + env = env or os.environ + q_thread = QtCore.QThread() + ue_plugin_worker = UEPluginInstallWorker() + + q_thread.started.connect(ue_plugin_worker.run) + ue_plugin_worker.setup(engine_path, env) + ue_plugin_worker.moveToThread(q_thread) + + splash_screen = SplashScreen("Installing plugin", + resources.get_resource("app_icons", "ue4.png")) + + # set up the splash screen with necessary triggers + ue_plugin_worker.installing.connect(splash_screen.update_top_label_text) + ue_plugin_worker.progress.connect(splash_screen.update_progress) + ue_plugin_worker.log.connect(splash_screen.append_log) + ue_plugin_worker.finished.connect(splash_screen.quit_and_close) + ue_plugin_worker.failed.connect(splash_screen.fail) + + splash_screen.start_thread(q_thread) + splash_screen.show_ui() + + if not splash_screen.was_proc_successful(): + raise ApplicationLaunchFailed("Couldn't run the application! " + "Plugin failed to install!") + + def exec_ue_project_gen(self, + engine_version: str, + unreal_project_name: str, + engine_path: Path, + project_dir: Path): + self.log.info(( + f"{self.signature} Creating unreal " + f"project [ {unreal_project_name} ]" + )) + + q_thread = QtCore.QThread() + ue_project_worker = UEProjectGenerationWorker() + ue_project_worker.setup( + engine_version, + unreal_project_name, + engine_path, + project_dir + ) + ue_project_worker.moveToThread(q_thread) + q_thread.started.connect(ue_project_worker.run) + + splash_screen = SplashScreen("Initializing UE project", + resources.get_resource("app_icons", "ue4.png")) + + ue_project_worker.stage_begin.connect(splash_screen.update_top_label_text) + ue_project_worker.progress.connect(splash_screen.update_progress) + ue_project_worker.log.connect(splash_screen.append_log) + ue_project_worker.finished.connect(splash_screen.quit_and_close) + ue_project_worker.failed.connect(splash_screen.fail) + + splash_screen.start_thread(q_thread) + splash_screen.show_ui() + + if not splash_screen.was_proc_successful(): + raise ApplicationLaunchFailed("Couldn't run the application! " + "Failed to generate the project!") + def execute(self): """Hook entry method.""" workdir = self.launch_context.env["AVALON_WORKDIR"] @@ -137,23 +206,18 @@ class UnrealPrelaunchHook(PreLaunchHook): if self.launch_context.env.get(env_key): os.environ[env_key] = self.launch_context.env[env_key] - engine_path = detected[engine_version] + engine_path: Path = Path(detected[engine_version]) - unreal_lib.try_installing_plugin(Path(engine_path), os.environ) + if not unreal_lib.check_plugin_existence(engine_path): + self.exec_plugin_install(engine_path) project_file = project_path / unreal_project_filename - if not project_file.is_file(): - self.log.info(( - f"{self.signature} creating unreal " - f"project [ {unreal_project_name} ]" - )) - unreal_lib.create_unreal_project( - unreal_project_name, - engine_version, - project_path, - engine_path=Path(engine_path) - ) + if not project_file.is_file(): + self.exec_ue_project_gen(engine_version, + unreal_project_name, + engine_path, + project_path) self.launch_context.env["OPENPYPE_UNREAL_VERSION"] = engine_version # Append project file to launch arguments diff --git a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainer.cpp b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainer.cpp index 0bea9e3d78..06dcd67808 100644 --- a/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainer.cpp +++ b/openpype/hosts/unreal/integration/UE_5.0/OpenPype/Source/OpenPype/Private/AssetContainer.cpp @@ -30,7 +30,7 @@ void UAssetContainer::OnAssetAdded(const FAssetData& AssetData) // get asset path and class FString assetPath = AssetData.GetFullName(); - FString assetFName = AssetData.AssetClassPath.ToString(); + FString assetFName = AssetData.ObjectPath.ToString(); UE_LOG(LogTemp, Log, TEXT("asset name %s"), *assetFName); // split path assetPath.ParseIntoArray(split, TEXT(" "), true); @@ -60,7 +60,7 @@ void UAssetContainer::OnAssetRemoved(const FAssetData& AssetData) // get asset path and class FString assetPath = AssetData.GetFullName(); - FString assetFName = AssetData.AssetClassPath.ToString(); + FString assetFName = AssetData.ObjectPath.ToString(); // split path assetPath.ParseIntoArray(split, TEXT(" "), true); @@ -93,7 +93,7 @@ void UAssetContainer::OnAssetRenamed(const FAssetData& AssetData, const FString& // get asset path and class FString assetPath = AssetData.GetFullName(); - FString assetFName = AssetData.AssetClassPath.ToString(); + FString assetFName = AssetData.ObjectPath.ToString(); // split path assetPath.ParseIntoArray(split, TEXT(" "), true); diff --git a/openpype/hosts/unreal/lib.py b/openpype/hosts/unreal/lib.py index 28a5106042..08cc57a6cd 100644 --- a/openpype/hosts/unreal/lib.py +++ b/openpype/hosts/unreal/lib.py @@ -365,6 +365,26 @@ def _get_build_id(engine_path: Path, ue_version: str) -> str: return "{" + loaded_modules.get("BuildId") + "}" +def check_plugin_existence(engine_path: Path, env: dict = None) -> bool: + env = env or os.environ + integration_plugin_path: Path = Path(env.get("OPENPYPE_UNREAL_PLUGIN", "")) + + if not os.path.isdir(integration_plugin_path): + raise RuntimeError("Path to the integration plugin is null!") + + # Create a path to the plugin in the engine + op_plugin_path: Path = engine_path / "Engine/Plugins/Marketplace/OpenPype" + + if not op_plugin_path.is_dir(): + return False + + if not (op_plugin_path / "Binaries").is_dir() \ + or not (op_plugin_path / "Intermediate").is_dir(): + return False + + return True + + def try_installing_plugin(engine_path: Path, env: dict = None) -> None: env = env or os.environ @@ -377,7 +397,6 @@ def try_installing_plugin(engine_path: Path, env: dict = None) -> None: op_plugin_path: Path = engine_path / "Engine/Plugins/Marketplace/OpenPype" if not op_plugin_path.is_dir(): - print("--- OpenPype Plugin is not present. Installing ...") op_plugin_path.mkdir(parents=True, exist_ok=True) engine_plugin_config_path: Path = op_plugin_path / "Config" @@ -387,7 +406,6 @@ def try_installing_plugin(engine_path: Path, env: dict = None) -> None: if not (op_plugin_path / "Binaries").is_dir() \ or not (op_plugin_path / "Intermediate").is_dir(): - print("--- Binaries are not present. Building the plugin ...") _build_and_move_plugin(engine_path, op_plugin_path, env) diff --git a/openpype/hosts/unreal/ue_workers.py b/openpype/hosts/unreal/ue_workers.py new file mode 100644 index 0000000000..35735fcaa1 --- /dev/null +++ b/openpype/hosts/unreal/ue_workers.py @@ -0,0 +1,338 @@ +import json +import os +import platform +import re +import subprocess +from distutils import dir_util +from pathlib import Path +from typing import List + +import openpype.hosts.unreal.lib as ue_lib + +from qtpy import QtCore + + +def parse_comp_progress(line: str, progress_signal: QtCore.Signal(int)) -> int: + match = re.search('\[[1-9]+/[0-9]+\]', line) + if match is not None: + split: list[str] = match.group().split('/') + curr: float = float(split[0][1:]) + total: float = float(split[1][:-1]) + progress_signal.emit(int((curr / total) * 100.0)) + + +def parse_prj_progress(line: str, progress_signal: QtCore.Signal(int)) -> int: + match = re.search('@progress', line) + if match is not None: + percent_match = re.search('\d{1,3}', line) + progress_signal.emit(int(percent_match.group())) + + +class UEProjectGenerationWorker(QtCore.QObject): + finished = QtCore.Signal(str) + failed = QtCore.Signal(str) + progress = QtCore.Signal(int) + log = QtCore.Signal(str) + stage_begin = QtCore.Signal(str) + + ue_version: str = None + project_name: str = None + env = None + engine_path: Path = None + project_dir: Path = None + dev_mode = False + + def setup(self, ue_version: str, + project_name, + engine_path: Path, + project_dir: Path, + dev_mode: bool = False, + env: dict = None): + + self.ue_version = ue_version + self.project_dir = project_dir + self.env = env or os.environ + + preset = ue_lib.get_project_settings( + project_name + )["unreal"]["project_setup"] + + if dev_mode or preset["dev_mode"]: + self.dev_mode = True + + self.project_name = project_name + self.engine_path = engine_path + + def run(self): + + + ue_id = ".".join(self.ue_version.split(".")[:2]) + # get unreal engine identifier + # ------------------------------------------------------------------------- + # FIXME (antirotor): As of 4.26 this is problem with UE4 built from + # sources. In that case Engine ID is calculated per machine/user and not + # from Engine files as this code then reads. This then prevents UE4 + # to directly open project as it will complain about project being + # created in different UE4 version. When user convert such project + # to his UE4 version, Engine ID is replaced in uproject file. If some + # other user tries to open it, it will present him with similar error. + + # engine_path should be the location of UE_X.X folder + + ue_editor_exe = ue_lib.get_editor_exe_path(self.engine_path, + self.ue_version) + cmdlet_project = ue_lib.get_path_to_cmdlet_project(self.ue_version) + project_file = self.project_dir / f"{self.project_name}.uproject" + + print("--- Generating a new project ...") + # 1st stage + stage_count = 2 + if self.dev_mode: + stage_count = 4 + + self.stage_begin.emit(f'Generating a new UE project ... 1 out of ' + f'{stage_count}') + + commandlet_cmd = [f'{ue_editor_exe.as_posix()}', + f'{cmdlet_project.as_posix()}', + f'-run=OPGenerateProject', + f'{project_file.resolve().as_posix()}'] + + if self.dev_mode: + commandlet_cmd.append('-GenerateCode') + + gen_process = subprocess.Popen(commandlet_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + for line in gen_process.stdout: + decoded_line = line.decode(errors="replace") + print(decoded_line, end='') + self.log.emit(decoded_line) + gen_process.stdout.close() + return_code = gen_process.wait() + + if return_code and return_code != 0: + msg = 'Failed to generate ' + self.project_name \ + + f' project! Exited with return code {return_code}' + self.failed.emit(msg, return_code) + raise RuntimeError(msg) + + print("--- Project has been generated successfully.") + self.stage_begin.emit(f'Writing the Engine ID of the build UE ... 1 out' + f' of {stage_count}') + + with open(project_file.as_posix(), mode="r+") as pf: + pf_json = json.load(pf) + pf_json["EngineAssociation"] = ue_lib.get_build_id(self.engine_path, + self.ue_version) + pf.seek(0) + json.dump(pf_json, pf, indent=4) + pf.truncate() + print(f'--- Engine ID has been written into the project file') + + self.progress.emit(90) + if self.dev_mode: + # 2nd stage + self.stage_begin.emit(f'Generating project files ... 2 out of ' + f'{stage_count}') + + self.progress.emit(0) + ubt_path = ue_lib.get_path_to_ubt(self.engine_path, self.ue_version) + + arch = "Win64" + if platform.system().lower() == "windows": + arch = "Win64" + elif platform.system().lower() == "linux": + arch = "Linux" + elif platform.system().lower() == "darwin": + # we need to test this out + arch = "Mac" + + gen_prj_files_cmd = [ubt_path.as_posix(), + "-projectfiles", + f"-project={project_file}", + "-progress"] + gen_proc = subprocess.Popen(gen_prj_files_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + for line in gen_proc.stdout: + decoded_line: str = line.decode(errors='replace') + print(decoded_line, end='') + self.log.emit(decoded_line) + parse_prj_progress(decoded_line, self.progress) + + gen_proc.stdout.close() + return_code = gen_proc.wait() + + if return_code and return_code != 0: + msg = 'Failed to generate project files! ' \ + f'Exited with return code {return_code}' + self.failed.emit(msg, return_code) + raise RuntimeError(msg) + + self.stage_begin.emit(f'Building the project ... 3 out of ' + f'{stage_count}') + self.progress.emit(0) + # 3rd stage + build_prj_cmd = [ubt_path.as_posix(), + f"-ModuleWithSuffix={self.project_name},3555", + arch, + "Development", + "-TargetType=Editor", + f'-Project={project_file}', + f'{project_file}', + "-IgnoreJunk"] + + build_prj_proc = subprocess.Popen(build_prj_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + for line in build_prj_proc.stdout: + decoded_line: str = line.decode(errors='replace') + print(decoded_line, end='') + self.log.emit(decoded_line) + parse_comp_progress(decoded_line, self.progress) + + build_prj_proc.stdout.close() + return_code = build_prj_proc.wait() + + if return_code and return_code != 0: + msg = 'Failed to build project! ' \ + f'Exited with return code {return_code}' + self.failed.emit(msg, return_code) + raise RuntimeError(msg) + + # ensure we have PySide2 installed in engine + + self.progress.emit(0) + self.stage_begin.emit(f'Checking PySide2 installation... {stage_count} ' + f'out of {stage_count}') + python_path = None + if platform.system().lower() == "windows": + python_path = self.engine_path / ("Engine/Binaries/ThirdParty/" + "Python3/Win64/python.exe") + + if platform.system().lower() == "linux": + python_path = self.engine_path / ("Engine/Binaries/ThirdParty/" + "Python3/Linux/bin/python3") + + if platform.system().lower() == "darwin": + python_path = self.engine_path / ("Engine/Binaries/ThirdParty/" + "Python3/Mac/bin/python3") + + if not python_path: + msg = "Unsupported platform" + self.failed.emit(msg, 1) + raise NotImplementedError(msg) + if not python_path.exists(): + msg = f"Unreal Python not found at {python_path}" + self.failed.emit(msg, 1) + raise RuntimeError(msg) + subprocess.check_call( + [python_path.as_posix(), "-m", "pip", "install", "pyside2"] + ) + self.progress.emit(100) + self.finished.emit("Project successfully built!") + + +class UEPluginInstallWorker(QtCore.QObject): + finished = QtCore.Signal(str) + installing = QtCore.Signal(str) + failed = QtCore.Signal(str, int) + progress = QtCore.Signal(int) + log = QtCore.Signal(str) + + engine_path: Path = None + env = None + + def setup(self, engine_path: Path, env: dict = None, ): + self.engine_path = engine_path + self.env = env or os.environ + + def _build_and_move_plugin(self, plugin_build_path: Path): + uat_path: Path = ue_lib.get_path_to_uat(self.engine_path) + src_plugin_dir = Path(self.env.get("OPENPYPE_UNREAL_PLUGIN", "")) + + if not os.path.isdir(src_plugin_dir): + msg = "Path to the integration plugin is null!" + self.failed.emit(msg, 1) + raise RuntimeError(msg) + + if not uat_path.is_file(): + msg = "Building failed! Path to UAT is invalid!" + self.failed.emit(msg, 1) + raise RuntimeError(msg) + + temp_dir: Path = src_plugin_dir.parent / "Temp" + temp_dir.mkdir(exist_ok=True) + uplugin_path: Path = src_plugin_dir / "OpenPype.uplugin" + + # in order to successfully build the plugin, + # It must be built outside the Engine directory and then moved + build_plugin_cmd: List[str] = [f'{uat_path.as_posix()}', + 'BuildPlugin', + f'-Plugin={uplugin_path.as_posix()}', + f'-Package={temp_dir.as_posix()}'] + + build_proc = subprocess.Popen(build_plugin_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + for line in build_proc.stdout: + decoded_line: str = line.decode(errors='replace') + print(decoded_line, end='') + self.log.emit(decoded_line) + parse_comp_progress(decoded_line, self.progress) + + build_proc.stdout.close() + return_code = build_proc.wait() + + if return_code and return_code != 0: + msg = 'Failed to build plugin' \ + f' project! Exited with return code {return_code}' + self.failed.emit(msg, return_code) + raise RuntimeError(msg) + + # Copy the contents of the 'Temp' dir into the + # 'OpenPype' directory in the engine + dir_util.copy_tree(temp_dir.as_posix(), + plugin_build_path.as_posix()) + + # We need to also copy the config folder. + # The UAT doesn't include the Config folder in the build + plugin_install_config_path: Path = plugin_build_path / "Config" + src_plugin_config_path = src_plugin_dir / "Config" + + dir_util.copy_tree(src_plugin_config_path.as_posix(), + plugin_install_config_path.as_posix()) + + dir_util.remove_tree(temp_dir.as_posix()) + + def run(self): + src_plugin_dir = Path(self.env.get("OPENPYPE_UNREAL_PLUGIN", "")) + + if not os.path.isdir(src_plugin_dir): + msg = "Path to the integration plugin is null!" + self.failed.emit(msg, 1) + raise RuntimeError(msg) + + # Create a path to the plugin in the engine + op_plugin_path = self.engine_path / \ + "Engine/Plugins/Marketplace/OpenPype" + + if not op_plugin_path.is_dir(): + self.installing.emit("Installing and building the plugin ...") + op_plugin_path.mkdir(parents=True, exist_ok=True) + + engine_plugin_config_path = op_plugin_path / "Config" + engine_plugin_config_path.mkdir(exist_ok=True) + + dir_util._path_created = {} + + if not (op_plugin_path / "Binaries").is_dir() \ + or not (op_plugin_path / "Intermediate").is_dir(): + self.installing.emit("Building the plugin ...") + print("--- Building the plugin...") + + self._build_and_move_plugin(op_plugin_path) + + self.finished.emit("Plugin successfully installed") diff --git a/openpype/widgets/README.md b/openpype/widgets/README.md new file mode 100644 index 0000000000..cda83a95d3 --- /dev/null +++ b/openpype/widgets/README.md @@ -0,0 +1,102 @@ +# Widgets + +## Splash Screen + +This widget is used for executing a monitoring progress of a process which has been executed on a different thread. + +To properly use this widget certain preparation has to be done in order to correctly execute the process and show the +splash screen. + +### Prerequisites + +In order to run a function or an operation on another thread, a `QtCore.QObject` class needs to be created with the +desired code. The class has to have a method as an entry point for the thread to execute the code. + +For utilizing the functionalities of the splash screen, certain signals need to be declared to let it know what is +happening in the thread and how is it progressing. It is also recommended to have a function to set up certain variables +which are needed in the worker's code + +For example: +```python +from qtpy import QtCore + +class ExampleWorker(QtCore.QObject): + + finished = QtCore.Signal() + failed = QtCore.Signal(str) + progress = QtCore.Signal(int) + log = QtCore.Signal(str) + stage_begin = QtCore.Signal(str) + + foo = None + bar = None + + def run(self): + # The code goes here + print("Hello world!") + self.finished.emit() + + def setup(self, + foo: str, + bar: str,): + self.foo = foo + self.bar = bar +``` + +### Creating the splash screen + +```python +import os +from qtpy import QtCore +from pathlib import Path +from openpype.widgets.splash_screen import SplashScreen +from openpype import resources + + +def exec_plugin_install( engine_path: Path, env: dict = None): + env = env or os.environ + q_thread = QtCore.QThread() + example_worker = ExampleWorker() + + q_thread.started.connect(example_worker.run) + example_worker.setup(engine_path, env) + example_worker.moveToThread(q_thread) + + splash_screen = SplashScreen("Executing process ...", + resources.get_openpype_icon_filepath()) + + # set up the splash screen with necessary events + example_worker.installing.connect(splash_screen.update_top_label_text) + example_worker.progress.connect(splash_screen.update_progress) + example_worker.log.connect(splash_screen.append_log) + example_worker.finished.connect(splash_screen.quit_and_close) + example_worker.failed.connect(splash_screen.fail) + + splash_screen.start_thread(q_thread) + splash_screen.show_ui() +``` + +In this example code, before executing the process the worker needs to be instantiated and moved onto a newly created +`QtCore.QThread` object. After this, needed signals have to be connected to the desired slots to make full use of +the splash screen. Finally, the `start_thread` and `show_ui` is called. + +**Note that when the `show_ui` function is called the thread is blocked until the splash screen quits automatically, or +it is closed by the user in case the process fails! The `start_thread` method in that case must be called before +showing the UI!** + +The most important signals are +```python +q_thread.started.connect(example_worker.run) +``` + and +```python +example_worker.finished.connect(splash_screen.quit_and_close) +``` + +These ensure that when the `start_thread` method is called (which takes as a parameter the `QtCore.QThread` object and +saves it as a reference), the `QThread` object starts and signals the worker to +start executing its own code. Once the worker is done and emits a signal that it has finished with the `quit_and_close` +slot, the splash screen quits the `QtCore.QThread` and closes itself. + +It is highly recommended to also use the `fail` slot in case an exception or other error occurs during the execution of +the worker's code (You would use in this case the `failed` signal in the `ExampleWorker`). diff --git a/openpype/widgets/splash_screen.py b/openpype/widgets/splash_screen.py new file mode 100644 index 0000000000..4a7598180d --- /dev/null +++ b/openpype/widgets/splash_screen.py @@ -0,0 +1,253 @@ +from qtpy import QtWidgets, QtCore, QtGui +from openpype import style, resources +from igniter.nice_progress_bar import NiceProgressBar + + +class SplashScreen(QtWidgets.QDialog): + """Splash screen for executing a process on another thread. It is able + to inform about the progress of the process and log given information. + """ + + splash_icon = None + top_label = None + show_log_btn: QtWidgets.QLabel = None + progress_bar = None + log_text: QtWidgets.QLabel = None + scroll_area: QtWidgets.QScrollArea = None + close_btn: QtWidgets.QPushButton = None + scroll_bar: QtWidgets.QScrollBar = None + + is_log_visible = False + is_scroll_auto = True + + thread_return_code = None + q_thread: QtCore.QThread = None + + def __init__(self, + window_title: str, + splash_icon=None, + window_icon=None): + """ + Args: + window_title (str): String which sets the window title + splash_icon (str | bytes | None): A resource (pic) which is used for + the splash icon + window_icon (str | bytes | None: A resource (pic) which is used for + the window's icon + """ + super(SplashScreen, self).__init__() + + if splash_icon is None: + splash_icon = resources.get_openpype_icon_filepath() + + if window_icon is None: + window_icon = resources.get_openpype_icon_filepath() + + self.splash_icon = splash_icon + self.setWindowIcon(QtGui.QIcon(window_icon)) + self.setWindowTitle(window_title) + self.init_ui() + + def was_proc_successful(self) -> bool: + if self.thread_return_code == 0: + return True + return False + + def start_thread(self, q_thread: QtCore.QThread): + """Saves the reference to this thread and starts it. + + Args: + q_thread (QtCore.QThread): A QThread containing a given worker + (QtCore.QObject) + + Returns: + None + """ + if not q_thread: + raise RuntimeError("Failed to run a worker thread! The thread is null!") + + self.q_thread = q_thread + self.q_thread.start() + + @QtCore.Slot() + def quit_and_close(self): + """Quits the thread and closes the splash screen. Note that this means + the thread has exited with the return code 0! + + Returns: + None + """ + self.thread_return_code = 0 + self.q_thread.quit() + self.close() + + @QtCore.Slot() + def toggle_log(self): + if self.is_log_visible: + self.scroll_area.hide() + width = self.width() + self.adjustSize() + self.resize(width, self.height()) + else: + self.scroll_area.show() + self.scroll_bar.setValue(self.scroll_bar.maximum()) + self.resize(self.width(), 300) + + self.is_log_visible = not self.is_log_visible + + def show_ui(self): + """Shows the splash screen. BEWARE THAT THIS FUNCTION IS BLOCKING + (The execution of code can not proceed further beyond this function + until the splash screen is closed!) + + Returns: + None + """ + self.show() + self.exec_() + + def init_ui(self): + self.resize(450, 100) + self.setMinimumWidth(250) + self.setStyleSheet(style.load_stylesheet()) + + # Top Section + self.top_label = QtWidgets.QLabel(self); + self.top_label.setText("Starting process ...") + self.top_label.setWordWrap(True) + + icon = QtWidgets.QLabel(self) + icon.setPixmap(QtGui.QPixmap(self.splash_icon)) + icon.setFixedHeight(45) + icon.setFixedWidth(45) + icon.setScaledContents(True) + + self.close_btn = QtWidgets.QPushButton(self) + self.close_btn.setText("Quit") + self.close_btn.clicked.connect(self.close) + self.close_btn.setFixedWidth(80) + self.close_btn.hide() + + self.show_log_btn = QtWidgets.QPushButton(self) + self.show_log_btn.setText("Show log") + self.show_log_btn.setFixedWidth(80) + self.show_log_btn.clicked.connect(self.toggle_log) + + button_layout = QtWidgets.QVBoxLayout() + button_layout.addWidget(self.show_log_btn) + button_layout.addWidget(self.close_btn) + + # Progress Bar + self.progress_bar = NiceProgressBar() + self.progress_bar.setValue(0) + self.progress_bar.setAlignment(QtCore.Qt.AlignTop) + + # Log Content + self.scroll_area = QtWidgets.QScrollArea(self) + self.scroll_area.hide() + log_widget = QtWidgets.QWidget(self.scroll_area) + self.scroll_area.setWidgetResizable(True) + self.scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.scroll_area.setWidget(log_widget) + + self.scroll_bar = self.scroll_area.verticalScrollBar() + self.scroll_bar.sliderMoved.connect(self.on_scroll) + + self.log_text = QtWidgets.QLabel(self) + self.log_text.setText('') + self.log_text.setAlignment(QtCore.Qt.AlignTop) + + log_layout = QtWidgets.QVBoxLayout(log_widget) + log_layout.addWidget(self.log_text) + + top_layout = QtWidgets.QHBoxLayout() + top_layout.setAlignment(QtCore.Qt.AlignTop) + top_layout.addWidget(icon) + top_layout.addSpacing(10) + top_layout.addWidget(self.top_label) + top_layout.addSpacing(10) + top_layout.addLayout(button_layout) + + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.addLayout(top_layout) + main_layout.addSpacing(10) + main_layout.addWidget(self.progress_bar) + main_layout.addSpacing(10) + main_layout.addWidget(self.scroll_area) + + self.setWindowFlags( + QtCore.Qt.Window + | QtCore.Qt.CustomizeWindowHint + | QtCore.Qt.WindowTitleHint + | QtCore.Qt.WindowMinimizeButtonHint + ) + + desktop_rect = QtWidgets.QApplication.desktop().availableGeometry(self) + center = desktop_rect.center() + self.move( + center.x() - (self.width() * 0.5), + center.y() - (self.height() * 0.5) + ) + + @QtCore.Slot(int) + def update_progress(self, value: int): + self.progress_bar.setValue(value) + + @QtCore.Slot(str) + def update_top_label_text(self, text: str): + self.top_label.setText(text) + + @QtCore.Slot(str, str) + def append_log(self, text: str, end: str = ''): + """A slot used for receiving log info and appending it to scroll area's + content. + Args: + text (str): A log text that will append to the current one in the scroll + area. + end (str): end string which can be appended to the end of the given + line (for ex. a line break). + + Returns: + None + """ + self.log_text.setText(self.log_text.text() + text + end) + if self.is_scroll_auto: + self.scroll_bar.setValue(self.scroll_bar.maximum()) + + @QtCore.Slot(int) + def on_scroll(self, position: int): + """ + A slot for the vertical scroll bar's movement. This ensures the + auto-scrolling feature of the scroll area when the scroll bar is at its + maximum value. + + Args: + position (int): Position value of the scroll bar. + + Returns: + None + """ + if self.scroll_bar.maximum() == position: + self.is_scroll_auto = True + return + + self.is_scroll_auto = False + + @QtCore.Slot(str, int) + def fail(self, text: str, return_code: int = 1): + """ + A slot used for signals which can emit when a worker (process) has + failed. at this moment the splash screen doesn't close by itself. + it has to be closed by the user. + + Args: + text (str): A text which can be set to the top label. + + Returns: + return_code (int): Return code of the thread's code + """ + self.top_label.setText(text) + self.close_btn.show() + self.thread_return_code = return_code + self.q_thread.exit(return_code) From 57faf21309a5271cc6845e674311abb7a2eff06d Mon Sep 17 00:00:00 2001 From: Joseff Date: Wed, 8 Mar 2023 17:18:54 +0100 Subject: [PATCH 1240/1271] Cleaned up the code, fixed the hanging thread --- .../unreal/hooks/pre_workfile_preparation.py | 21 +++++++++++++------ openpype/hosts/unreal/lib.py | 4 ++-- openpype/hosts/unreal/ue_workers.py | 20 +++++++----------- openpype/widgets/splash_screen.py | 15 ++++++++----- 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py index 8ede80f7fd..c3f9ea7e72 100644 --- a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py +++ b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py @@ -5,7 +5,10 @@ import copy from pathlib import Path from openpype.widgets.splash_screen import SplashScreen from qtpy import QtCore -from openpype.hosts.unreal.ue_workers import UEProjectGenerationWorker, UEPluginInstallWorker +from openpype.hosts.unreal.ue_workers import ( + UEProjectGenerationWorker, + UEPluginInstallWorker +) from openpype import resources from openpype.lib import ( @@ -73,8 +76,10 @@ class UnrealPrelaunchHook(PreLaunchHook): ue_plugin_worker.setup(engine_path, env) ue_plugin_worker.moveToThread(q_thread) - splash_screen = SplashScreen("Installing plugin", - resources.get_resource("app_icons", "ue4.png")) + splash_screen = SplashScreen( + "Installing plugin", + resources.get_resource("app_icons", "ue4.png") + ) # set up the splash screen with necessary triggers ue_plugin_worker.installing.connect(splash_screen.update_top_label_text) @@ -111,10 +116,14 @@ class UnrealPrelaunchHook(PreLaunchHook): ue_project_worker.moveToThread(q_thread) q_thread.started.connect(ue_project_worker.run) - splash_screen = SplashScreen("Initializing UE project", - resources.get_resource("app_icons", "ue4.png")) + splash_screen = SplashScreen( + "Initializing UE project", + resources.get_resource("app_icons", "ue4.png") + ) - ue_project_worker.stage_begin.connect(splash_screen.update_top_label_text) + ue_project_worker.stage_begin.connect( + splash_screen.update_top_label_text + ) ue_project_worker.progress.connect(splash_screen.update_progress) ue_project_worker.log.connect(splash_screen.append_log) ue_project_worker.finished.connect(splash_screen.quit_and_close) diff --git a/openpype/hosts/unreal/lib.py b/openpype/hosts/unreal/lib.py index 08cc57a6cd..86ce0bb033 100644 --- a/openpype/hosts/unreal/lib.py +++ b/openpype/hosts/unreal/lib.py @@ -252,7 +252,7 @@ def create_unreal_project(project_name: str, with open(project_file.as_posix(), mode="r+") as pf: pf_json = json.load(pf) - pf_json["EngineAssociation"] = _get_build_id(engine_path, ue_version) + pf_json["EngineAssociation"] = get_build_id(engine_path, ue_version) pf.seek(0) json.dump(pf_json, pf, indent=4) pf.truncate() @@ -338,7 +338,7 @@ def get_path_to_ubt(engine_path: Path, ue_version: str) -> Path: return Path(u_build_tool_path) -def _get_build_id(engine_path: Path, ue_version: str) -> str: +def get_build_id(engine_path: Path, ue_version: str) -> str: ue_modules = Path() if platform.system().lower() == "windows": ue_modules_path = engine_path / "Engine/Binaries/Win64" diff --git a/openpype/hosts/unreal/ue_workers.py b/openpype/hosts/unreal/ue_workers.py index 35735fcaa1..7dd08144e6 100644 --- a/openpype/hosts/unreal/ue_workers.py +++ b/openpype/hosts/unreal/ue_workers.py @@ -64,19 +64,6 @@ class UEProjectGenerationWorker(QtCore.QObject): self.engine_path = engine_path def run(self): - - - ue_id = ".".join(self.ue_version.split(".")[:2]) - # get unreal engine identifier - # ------------------------------------------------------------------------- - # FIXME (antirotor): As of 4.26 this is problem with UE4 built from - # sources. In that case Engine ID is calculated per machine/user and not - # from Engine files as this code then reads. This then prevents UE4 - # to directly open project as it will complain about project being - # created in different UE4 version. When user convert such project - # to his UE4 version, Engine ID is replaced in uproject file. If some - # other user tries to open it, it will present him with similar error. - # engine_path should be the location of UE_X.X folder ue_editor_exe = ue_lib.get_editor_exe_path(self.engine_path, @@ -122,10 +109,17 @@ class UEProjectGenerationWorker(QtCore.QObject): self.stage_begin.emit(f'Writing the Engine ID of the build UE ... 1 out' f' of {stage_count}') + if not project_file.is_file(): + msg = "Failed to write the Engine ID into .uproject file! Can " \ + "not read!" + self.failed.emit(msg) + raise RuntimeError(msg) + with open(project_file.as_posix(), mode="r+") as pf: pf_json = json.load(pf) pf_json["EngineAssociation"] = ue_lib.get_build_id(self.engine_path, self.ue_version) + print(pf_json["EngineAssociation"]) pf.seek(0) json.dump(pf_json, pf, indent=4) pf.truncate() diff --git a/openpype/widgets/splash_screen.py b/openpype/widgets/splash_screen.py index 4a7598180d..6af19e991c 100644 --- a/openpype/widgets/splash_screen.py +++ b/openpype/widgets/splash_screen.py @@ -64,7 +64,8 @@ class SplashScreen(QtWidgets.QDialog): None """ if not q_thread: - raise RuntimeError("Failed to run a worker thread! The thread is null!") + raise RuntimeError("Failed to run a worker thread! " + "The thread is null!") self.q_thread = q_thread self.q_thread.start() @@ -147,8 +148,12 @@ class SplashScreen(QtWidgets.QDialog): self.scroll_area.hide() log_widget = QtWidgets.QWidget(self.scroll_area) self.scroll_area.setWidgetResizable(True) - self.scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) - self.scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.scroll_area.setHorizontalScrollBarPolicy( + QtCore.Qt.ScrollBarAlwaysOn + ) + self.scroll_area.setVerticalScrollBarPolicy( + QtCore.Qt.ScrollBarAlwaysOn + ) self.scroll_area.setWidget(log_widget) self.scroll_bar = self.scroll_area.verticalScrollBar() @@ -203,8 +208,8 @@ class SplashScreen(QtWidgets.QDialog): """A slot used for receiving log info and appending it to scroll area's content. Args: - text (str): A log text that will append to the current one in the scroll - area. + text (str): A log text that will append to the current one in the + scroll area. end (str): end string which can be appended to the end of the given line (for ex. a line break). From 78d737e4b30e1a890ff0ca6d085050d88bdedc6b Mon Sep 17 00:00:00 2001 From: Joseff Date: Wed, 8 Mar 2023 17:20:50 +0100 Subject: [PATCH 1241/1271] Reformatted the file --- openpype/widgets/splash_screen.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/widgets/splash_screen.py b/openpype/widgets/splash_screen.py index 6af19e991c..fffe143ea5 100644 --- a/openpype/widgets/splash_screen.py +++ b/openpype/widgets/splash_screen.py @@ -30,8 +30,8 @@ class SplashScreen(QtWidgets.QDialog): """ Args: window_title (str): String which sets the window title - splash_icon (str | bytes | None): A resource (pic) which is used for - the splash icon + splash_icon (str | bytes | None): A resource (pic) which is used + for the splash icon window_icon (str | bytes | None: A resource (pic) which is used for the window's icon """ @@ -113,7 +113,7 @@ class SplashScreen(QtWidgets.QDialog): self.setStyleSheet(style.load_stylesheet()) # Top Section - self.top_label = QtWidgets.QLabel(self); + self.top_label = QtWidgets.QLabel(self) self.top_label.setText("Starting process ...") self.top_label.setWordWrap(True) From fc67c5a2c0b4d5a3bd4abae3ab860cc9f81a3762 Mon Sep 17 00:00:00 2001 From: Joseff Date: Wed, 8 Mar 2023 17:23:37 +0100 Subject: [PATCH 1242/1271] Fixed the line indentation. --- openpype/hosts/unreal/ue_workers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/unreal/ue_workers.py b/openpype/hosts/unreal/ue_workers.py index 7dd08144e6..2162357912 100644 --- a/openpype/hosts/unreal/ue_workers.py +++ b/openpype/hosts/unreal/ue_workers.py @@ -310,8 +310,8 @@ class UEPluginInstallWorker(QtCore.QObject): raise RuntimeError(msg) # Create a path to the plugin in the engine - op_plugin_path = self.engine_path / \ - "Engine/Plugins/Marketplace/OpenPype" + op_plugin_path = self.engine_path / "Engine/Plugins/Marketplace" \ + "/OpenPype" if not op_plugin_path.is_dir(): self.installing.emit("Installing and building the plugin ...") From e5b7349dff710bc2577e1a9adba39cde9748e231 Mon Sep 17 00:00:00 2001 From: Joseff Date: Wed, 8 Mar 2023 17:26:27 +0100 Subject: [PATCH 1243/1271] Code cleanup --- openpype/hosts/unreal/ue_workers.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/unreal/ue_workers.py b/openpype/hosts/unreal/ue_workers.py index 2162357912..00f83a7d7a 100644 --- a/openpype/hosts/unreal/ue_workers.py +++ b/openpype/hosts/unreal/ue_workers.py @@ -106,8 +106,8 @@ class UEProjectGenerationWorker(QtCore.QObject): raise RuntimeError(msg) print("--- Project has been generated successfully.") - self.stage_begin.emit(f'Writing the Engine ID of the build UE ... 1 out' - f' of {stage_count}') + self.stage_begin.emit(f'Writing the Engine ID of the build UE ... 1' + f' out of {stage_count}') if not project_file.is_file(): msg = "Failed to write the Engine ID into .uproject file! Can " \ @@ -117,8 +117,10 @@ class UEProjectGenerationWorker(QtCore.QObject): with open(project_file.as_posix(), mode="r+") as pf: pf_json = json.load(pf) - pf_json["EngineAssociation"] = ue_lib.get_build_id(self.engine_path, - self.ue_version) + pf_json["EngineAssociation"] = ue_lib.get_build_id( + self.engine_path, + self.ue_version + ) print(pf_json["EngineAssociation"]) pf.seek(0) json.dump(pf_json, pf, indent=4) @@ -132,7 +134,8 @@ class UEProjectGenerationWorker(QtCore.QObject): f'{stage_count}') self.progress.emit(0) - ubt_path = ue_lib.get_path_to_ubt(self.engine_path, self.ue_version) + ubt_path = ue_lib.get_path_to_ubt(self.engine_path, + self.ue_version) arch = "Win64" if platform.system().lower() == "windows": @@ -199,8 +202,8 @@ class UEProjectGenerationWorker(QtCore.QObject): # ensure we have PySide2 installed in engine self.progress.emit(0) - self.stage_begin.emit(f'Checking PySide2 installation... {stage_count} ' - f'out of {stage_count}') + self.stage_begin.emit(f'Checking PySide2 installation... {stage_count}' + f' out of {stage_count}') python_path = None if platform.system().lower() == "windows": python_path = self.engine_path / ("Engine/Binaries/ThirdParty/" From 7941c73f82ce7d708e7387f0db4f39df54c58f67 Mon Sep 17 00:00:00 2001 From: Joseff Date: Wed, 8 Mar 2023 17:27:25 +0100 Subject: [PATCH 1244/1271] Code cleanup --- openpype/hosts/unreal/hooks/pre_workfile_preparation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py index c3f9ea7e72..da12bc75de 100644 --- a/openpype/hosts/unreal/hooks/pre_workfile_preparation.py +++ b/openpype/hosts/unreal/hooks/pre_workfile_preparation.py @@ -82,7 +82,9 @@ class UnrealPrelaunchHook(PreLaunchHook): ) # set up the splash screen with necessary triggers - ue_plugin_worker.installing.connect(splash_screen.update_top_label_text) + ue_plugin_worker.installing.connect( + splash_screen.update_top_label_text + ) ue_plugin_worker.progress.connect(splash_screen.update_progress) ue_plugin_worker.log.connect(splash_screen.append_log) ue_plugin_worker.finished.connect(splash_screen.quit_and_close) From 3b64f515b25ebeccd4235a02722aa1e1eef26bc0 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Sat, 11 Mar 2023 03:26:36 +0000 Subject: [PATCH 1245/1271] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index c7a5e9bea5..e8124f1466 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.2-nightly.5" +__version__ = "3.15.2-nightly.6" From e2902e86232d738433a9de45f84eb3d3b0b296e1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 11 Mar 2023 20:49:26 +0100 Subject: [PATCH 1246/1271] Fix #4109: Unify menu labels for "Set Frame Range" and "Set Resolution" --- openpype/hosts/blender/api/ops.py | 4 ++-- openpype/hosts/houdini/startup/MainMenuCommon.xml | 2 +- openpype/hosts/maya/api/menu.py | 4 ++-- openpype/hosts/resolve/api/menu.py | 8 ++++---- website/docs/artist_hosts_maya.md | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/blender/api/ops.py b/openpype/hosts/blender/api/ops.py index 481c199db2..b1fa13acb9 100644 --- a/openpype/hosts/blender/api/ops.py +++ b/openpype/hosts/blender/api/ops.py @@ -382,8 +382,8 @@ class TOPBAR_MT_avalon(bpy.types.Menu): layout.operator(LaunchLibrary.bl_idname, text="Library...") layout.separator() layout.operator(LaunchWorkFiles.bl_idname, text="Work Files...") - # TODO (jasper): maybe add 'Reload Pipeline', 'Reset Frame Range' and - # 'Reset Resolution'? + # TODO (jasper): maybe add 'Reload Pipeline', 'Set Frame Range' and + # 'Set Resolution'? def draw_avalon_menu(self, context): diff --git a/openpype/hosts/houdini/startup/MainMenuCommon.xml b/openpype/hosts/houdini/startup/MainMenuCommon.xml index c08114b71b..e12000b855 100644 --- a/openpype/hosts/houdini/startup/MainMenuCommon.xml +++ b/openpype/hosts/houdini/startup/MainMenuCommon.xml @@ -67,7 +67,7 @@ host_tools.show_workfiles(parent) - + / ``` -Doing **OpenPype → Reset Resolution** will set correct resolution on camera. +Doing **OpenPype → Set Resolution** will set correct resolution on camera. Scene is now ready for submission and should publish without errors. From 7efb019ebc799905e4fc126883aaf9ffb86936ec Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 11 Mar 2023 20:50:30 +0100 Subject: [PATCH 1247/1271] Match id with label --- openpype/hosts/houdini/startup/MainMenuCommon.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/houdini/startup/MainMenuCommon.xml b/openpype/hosts/houdini/startup/MainMenuCommon.xml index e12000b855..47595ebd8c 100644 --- a/openpype/hosts/houdini/startup/MainMenuCommon.xml +++ b/openpype/hosts/houdini/startup/MainMenuCommon.xml @@ -66,7 +66,7 @@ host_tools.show_workfiles(parent) ]]> - + Date: Mon, 13 Mar 2023 10:32:19 +0100 Subject: [PATCH 1248/1271] SiteSync: host dirmap is not working properly (#4563) - only editable keys are returned from Site Sync module to Local Settings Cleaner approach even if LS UI is going away in Ayon. - use remote_site only if is local_drive provider - remove unwanted import - cache mapping, update logging Mapping was called multiple times for Nuke. Logging was too verbose. --- openpype/host/dirmap.py | 63 +++++++++---------- openpype/hosts/nuke/api/lib.py | 54 +++++++++------- .../modules/sync_server/sync_server_module.py | 20 +++++- .../local_settings/projects_widget.py | 2 +- 4 files changed, 81 insertions(+), 58 deletions(-) diff --git a/openpype/host/dirmap.py b/openpype/host/dirmap.py index 347c5fbf85..1d084cccad 100644 --- a/openpype/host/dirmap.py +++ b/openpype/host/dirmap.py @@ -39,7 +39,6 @@ class HostDirmap(object): self._project_settings = project_settings self._sync_module = sync_module # to limit reinit of Modules self._log = None - self._mapping = None # cache mapping @property def sync_module(self): @@ -70,29 +69,28 @@ class HostDirmap(object): """Run host dependent remapping from source_path to destination_path""" pass - def process_dirmap(self): + def process_dirmap(self, mapping=None): # type: (dict) -> None """Go through all paths in Settings and set them using `dirmap`. If artists has Site Sync enabled, take dirmap mapping directly from Local Settings when artist is syncing workfile locally. - Args: - project_settings (dict): Settings for current project. """ - if not self._mapping: - self._mapping = self.get_mappings(self.project_settings) - if not self._mapping: + if not mapping: + mapping = self.get_mappings() + if not mapping: return - self.log.info("Processing directory mapping ...") self.on_enable_dirmap() - self.log.info("mapping:: {}".format(self._mapping)) - for k, sp in enumerate(self._mapping["source-path"]): - dst = self._mapping["destination-path"][k] + for k, sp in enumerate(mapping["source-path"]): + dst = mapping["destination-path"][k] try: + # add trailing slash if missing + sp = os.path.join(sp, '') + dst = os.path.join(dst, '') print("{} -> {}".format(sp, dst)) self.dirmap_routine(sp, dst) except IndexError: @@ -110,28 +108,24 @@ class HostDirmap(object): ) continue - def get_mappings(self, project_settings): + def get_mappings(self): """Get translation from source-path to destination-path. It checks if Site Sync is enabled and user chose to use local site, in that case configuration in Local Settings takes precedence """ - local_mapping = self._get_local_sync_dirmap(project_settings) dirmap_label = "{}-dirmap".format(self.host_name) - if ( - not self.project_settings[self.host_name].get(dirmap_label) - and not local_mapping - ): - return {} - mapping_settings = self.project_settings[self.host_name][dirmap_label] - mapping_enabled = mapping_settings["enabled"] or bool(local_mapping) + mapping_sett = self.project_settings[self.host_name].get(dirmap_label, + {}) + local_mapping = self._get_local_sync_dirmap() + mapping_enabled = mapping_sett.get("enabled") or bool(local_mapping) if not mapping_enabled: return {} mapping = ( local_mapping - or mapping_settings["paths"] + or mapping_sett["paths"] or {} ) @@ -141,28 +135,27 @@ class HostDirmap(object): or not mapping.get("source-path") ): return {} + self.log.info("Processing directory mapping ...") + self.log.info("mapping:: {}".format(mapping)) return mapping - def _get_local_sync_dirmap(self, project_settings): + def _get_local_sync_dirmap(self): """ Returns dirmap if synch to local project is enabled. Only valid mapping is from roots of remote site to local site set in Local Settings. - Args: - project_settings (dict) Returns: dict : { "source-path": [XXX], "destination-path": [YYYY]} """ + project_name = os.getenv("AVALON_PROJECT") mapping = {} - - if not project_settings["global"]["sync_server"]["enabled"]: + if (not self.sync_module.enabled or + project_name not in self.sync_module.get_enabled_projects()): return mapping - project_name = os.getenv("AVALON_PROJECT") - active_site = self.sync_module.get_local_normalized_site( self.sync_module.get_active_site(project_name)) remote_site = self.sync_module.get_local_normalized_site( @@ -171,11 +164,7 @@ class HostDirmap(object): "active {} - remote {}".format(active_site, remote_site) ) - if ( - active_site == "local" - and project_name in self.sync_module.get_enabled_projects() - and active_site != remote_site - ): + if active_site == "local" and active_site != remote_site: sync_settings = self.sync_module.get_sync_project_setting( project_name, exclude_locals=False, @@ -188,7 +177,15 @@ class HostDirmap(object): self.log.debug("local overrides {}".format(active_overrides)) self.log.debug("remote overrides {}".format(remote_overrides)) + current_platform = platform.system().lower() + remote_provider = self.sync_module.get_provider_for_site( + project_name, remote_site + ) + # dirmap has sense only with regular disk provider, in the workfile + # wont be root on cloud or sftp provider + if remote_provider != "local_drive": + remote_site = "studio" for root_name, active_site_dir in active_overrides.items(): remote_site_dir = ( remote_overrides.get(root_name) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index a5a631cc70..2a14096f0e 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -2861,10 +2861,10 @@ class NukeDirmap(HostDirmap): pass def dirmap_routine(self, source_path, destination_path): - log.debug("{}: {}->{}".format(self.file_name, - source_path, destination_path)) source_path = source_path.lower().replace(os.sep, '/') destination_path = destination_path.lower().replace(os.sep, '/') + log.debug("Map: {} with: {}->{}".format(self.file_name, + source_path, destination_path)) if platform.system().lower() == "windows": self.file_name = self.file_name.lower().replace( source_path, destination_path) @@ -2878,6 +2878,7 @@ class DirmapCache: _project_name = None _project_settings = None _sync_module = None + _mapping = None @classmethod def project_name(cls): @@ -2897,6 +2898,36 @@ class DirmapCache: cls._sync_module = ModulesManager().modules_by_name["sync_server"] return cls._sync_module + @classmethod + def mapping(cls): + return cls._mapping + + @classmethod + def set_mapping(cls, mapping): + cls._mapping = mapping + + +def dirmap_file_name_filter(file_name): + """Nuke callback function with single full path argument. + + Checks project settings for potential mapping from source to dest. + """ + + dirmap_processor = NukeDirmap( + file_name, + "nuke", + DirmapCache.project_name(), + DirmapCache.project_settings(), + DirmapCache.sync_module(), + ) + if not DirmapCache.mapping(): + DirmapCache.set_mapping(dirmap_processor.get_mappings()) + + dirmap_processor.process_dirmap(DirmapCache.mapping()) + if os.path.exists(dirmap_processor.file_name): + return dirmap_processor.file_name + return file_name + @contextlib.contextmanager def node_tempfile(): @@ -2942,25 +2973,6 @@ def duplicate_node(node): return dupli_node -def dirmap_file_name_filter(file_name): - """Nuke callback function with single full path argument. - - Checks project settings for potential mapping from source to dest. - """ - - dirmap_processor = NukeDirmap( - file_name, - "nuke", - DirmapCache.project_name(), - DirmapCache.project_settings(), - DirmapCache.sync_module(), - ) - dirmap_processor.process_dirmap() - if os.path.exists(dirmap_processor.file_name): - return dirmap_processor.file_name - return file_name - - def get_group_io_nodes(nodes): """Get the input and the output of a group of nodes.""" diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 28863c091a..5a4fa07e98 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -1472,13 +1472,15 @@ class SyncServerModule(OpenPypeModule, ITrayModule): return sync_settings - def get_all_site_configs(self, project_name=None): + def get_all_site_configs(self, project_name=None, + local_editable_only=False): """ Returns (dict) with all sites configured system wide. Args: project_name (str)(optional): if present, check if not disabled - + local_editable_only (bool)(opt): if True return only Local + Setting configurable (for LS UI) Returns: (dict): {'studio': {'provider':'local_drive'...}, 'MY_LOCAL': {'provider':....}} @@ -1499,9 +1501,21 @@ class SyncServerModule(OpenPypeModule, ITrayModule): if site_settings: detail.update(site_settings) system_sites[site] = detail - system_sites.update(self._get_default_site_configs(sync_enabled, project_name)) + if local_editable_only: + local_schema = SyncServerModule.get_local_settings_schema() + editable_keys = {} + for provider_code, editables in local_schema.items(): + editable_keys[provider_code] = ["enabled", "provider"] + for editable_item in editables: + editable_keys[provider_code].append(editable_item["key"]) + + for _, site in system_sites.items(): + provider = site["provider"] + for site_config_key in list(site.keys()): + if site_config_key not in editable_keys[provider]: + site.pop(site_config_key, None) return system_sites diff --git a/openpype/tools/settings/local_settings/projects_widget.py b/openpype/tools/settings/local_settings/projects_widget.py index bdf291524c..4a4148d7cd 100644 --- a/openpype/tools/settings/local_settings/projects_widget.py +++ b/openpype/tools/settings/local_settings/projects_widget.py @@ -272,7 +272,7 @@ class SitesWidget(QtWidgets.QWidget): ) site_configs = sync_server_module.get_all_site_configs( - self._project_name) + self._project_name, local_editable_only=True) roots_entity = ( self.project_settings[PROJECT_ANATOMY_KEY][LOCAL_ROOTS_KEY] From 86184a8ee0525c3f361850cae0b576aca051bdf4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 13 Mar 2023 11:20:59 +0100 Subject: [PATCH 1249/1271] Tools: Fix recursive filtering (#4597) * check for 'filterRegExp' first * use recursive filtering option if is available --- openpype/tools/loader/widgets.py | 6 +++--- openpype/tools/sceneinventory/model.py | 6 +++--- openpype/tools/sceneinventory/window.py | 6 +++--- .../tools/settings/settings/search_dialog.py | 12 +++++------ .../model_filter_proxy_recursive_sort.py | 6 +++--- openpype/tools/utils/models.py | 20 ++++++++++++++----- 6 files changed, 33 insertions(+), 23 deletions(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 98ac9c871f..b3aa381d14 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -295,10 +295,10 @@ class SubsetWidget(QtWidgets.QWidget): self.model.set_grouping(state) def _subset_changed(self, text): - if hasattr(self.proxy, "setFilterRegularExpression"): - self.proxy.setFilterRegularExpression(text) - else: + if hasattr(self.proxy, "setFilterRegExp"): self.proxy.setFilterRegExp(text) + else: + self.proxy.setFilterRegularExpression(text) self.view.expandAll() def set_loading_state(self, loading, empty): diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index 3398743aec..680dfd5a51 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -482,10 +482,10 @@ class FilterProxyModel(QtCore.QSortFilterProxyModel): return True # Filter by regex - if hasattr(self, "filterRegularExpression"): - regex = self.filterRegularExpression() - else: + if hasattr(self, "filterRegExp"): regex = self.filterRegExp() + else: + regex = self.filterRegularExpression() pattern = regex.pattern() if pattern: pattern = re.escape(pattern) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index 8a6e43f796..89424fd746 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -160,10 +160,10 @@ class SceneInventoryWindow(QtWidgets.QDialog): self._model.set_hierarchy_view(enabled) def _on_text_filter_change(self, text_filter): - if hasattr(self._proxy, "setFilterRegularExpression"): - self._proxy.setFilterRegularExpression(text_filter) - else: + if hasattr(self._proxy, "setFilterRegExp"): self._proxy.setFilterRegExp(text_filter) + else: + self._proxy.setFilterRegularExpression(text_filter) def _on_outdated_state_change(self): self._proxy.set_filter_outdated( diff --git a/openpype/tools/settings/settings/search_dialog.py b/openpype/tools/settings/settings/search_dialog.py index 33a4d16e98..59750c02e1 100644 --- a/openpype/tools/settings/settings/search_dialog.py +++ b/openpype/tools/settings/settings/search_dialog.py @@ -27,10 +27,10 @@ class RecursiveSortFilterProxyModel(QtCore.QSortFilterProxyModel): if not parent.isValid(): return False - if hasattr(self, "filterRegularExpression"): - regex = self.filterRegularExpression() - else: + if hasattr(self, "filterRegExp"): regex = self.filterRegExp() + else: + regex = self.filterRegularExpression() pattern = regex.pattern() if pattern and regex.isValid(): @@ -111,10 +111,10 @@ class SearchEntitiesDialog(QtWidgets.QDialog): def _on_filter_timer(self): text = self._filter_edit.text() - if hasattr(self._proxy, "setFilterRegularExpression"): - self._proxy.setFilterRegularExpression(text) - else: + if hasattr(self._proxy, "setFilterRegExp"): self._proxy.setFilterRegExp(text) + else: + self._proxy.setFilterRegularExpression(text) # WARNING This expanding and resizing is relatively slow. self._view.expandAll() diff --git a/openpype/tools/standalonepublish/widgets/model_filter_proxy_recursive_sort.py b/openpype/tools/standalonepublish/widgets/model_filter_proxy_recursive_sort.py index 5c72e2049b..602faaa489 100644 --- a/openpype/tools/standalonepublish/widgets/model_filter_proxy_recursive_sort.py +++ b/openpype/tools/standalonepublish/widgets/model_filter_proxy_recursive_sort.py @@ -5,10 +5,10 @@ from qtpy import QtCore class RecursiveSortFilterProxyModel(QtCore.QSortFilterProxyModel): """Filters to the regex if any of the children matches allow parent""" def filterAcceptsRow(self, row, parent): - if hasattr(self, "filterRegularExpression"): - regex = self.filterRegularExpression() - else: + if hasattr(self, "filterRegExp"): regex = self.filterRegExp() + else: + regex = self.filterRegularExpression() pattern = regex.pattern() if pattern: model = self.sourceModel() diff --git a/openpype/tools/utils/models.py b/openpype/tools/utils/models.py index 270e00b2ef..94645af110 100644 --- a/openpype/tools/utils/models.py +++ b/openpype/tools/utils/models.py @@ -202,11 +202,20 @@ class RecursiveSortFilterProxyModel(QtCore.QSortFilterProxyModel): Use case: Filtering by string - parent won't be filtered if does not match the filter string but first checks if any children does. """ + + def __init__(self, *args, **kwargs): + super(RecursiveSortFilterProxyModel, self).__init__(*args, **kwargs) + recursive_enabled = False + if hasattr(self, "setRecursiveFilteringEnabled"): + self.setRecursiveFilteringEnabled(True) + recursive_enabled = True + self._recursive_enabled = recursive_enabled + def filterAcceptsRow(self, row, parent_index): - if hasattr(self, "filterRegularExpression"): - regex = self.filterRegularExpression() - else: + if hasattr(self, "filterRegExp"): regex = self.filterRegExp() + else: + regex = self.filterRegularExpression() pattern = regex.pattern() if pattern: @@ -219,8 +228,9 @@ class RecursiveSortFilterProxyModel(QtCore.QSortFilterProxyModel): # Check current index itself value = model.data(source_index, self.filterRole()) - if re.search(pattern, value, re.IGNORECASE): - return True + matched = bool(re.search(pattern, value, re.IGNORECASE)) + if matched or self._recursive_enabled: + return matched rows = model.rowCount(source_index) for idx in range(rows): From d6555321ae38ba2bb2a41d46236415d1c8ec4cdf Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 13 Mar 2023 14:30:56 +0000 Subject: [PATCH 1250/1271] [Automated] Release --- CHANGELOG.md | 848 ++++++++++++++++++++++++++++++++++++++++++++ openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 850 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7ecbc83bf..145c2e2c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,854 @@ # Changelog +[Full Changelog](https://github.com/ynput/OpenPype/compare/3.15.1...3.15.2) + +### **🆕 New features** + + +
+maya gltf texture convertor and validator #4261 + +Continuity of the gltf extractor implementation + +Continuity of the gltf extractor https://github.com/pypeclub/OpenPype/pull/4192UPDATE:**Validator for GLSL Shader**: Validate whether the mesh uses GLSL Shader. If not it will error out. The user can choose to perform the repair action and it will help to assign glsl shader. If the mesh with Stringray PBS, the repair action will also check to see if there is any linked texture such as Color, Occulsion, and Normal Map. If yes, it will help to relink the related textures to the glsl shader.*****If the mesh uses the PBS Shader, + + +___ + +
+ + +
+Unreal: New Publisher #4370 + +Implementation of the new publisher for Unreal. + +The implementation of the new publisher for Unreal. This PR includes the changes for all the existing creators to be compatible with the new publisher.The basic creator has been split in two distinct creators: +- `UnrealAssetCreator`, works with assets in the Content Browser. +- `UnrealActorCreator` that works with actors in the scene. + + +___ + +
+ + +
+Implementation of a new splash screen #4592 + +Implemented a new splash screen widget to reflect a process running in the background. This widget can be used for other tasks than UE. **Also fixed the compilation error of the AssetContainer.cpp when trying to build the plugin in UE 5.0** + + +___ + +
+ + +
+Deadline for 3dsMax #4439 + +Setting up deadline for 3dsmax + +Setting up deadline for 3dsmax by setting render outputs and viewport camera + + +___ + +
+ + +
+Nuke: adding nukeassist #4494 + +Adding support for NukeAssist + +For support of NukeAssist we had to limit some Nuke features since NukeAssist itself Nuke with limitations. We do not support Creator and Publisher. User can only Load versions with version control. User can also set Framerange and Colorspace. + + +___ + +
+ +### **🚀 Enhancements** + + +
+Maya: OP-2630 acescg maya #4340 + +Resolves #2712 + + +___ + +
+ + +
+Default Ftrack Family on RenderLayer #4458 + +With default settings, renderlayers in Maya were not being tagged with the Ftrack family leading to confusion when doing reviews. + + +___ + +
+ + +
+Maya: Maya Playblast Options - OP-3783 #4487 + +Replacement PR for #3912. Adds more options for playblasts to preferences/settings. + +Adds the following as options in generating playblasts, matching viewport settings. +- Use default material +- Wireframe on shaded +- X-ray +- X-ray Joints +- X-ray active component + + +___ + +
+ + +
+Maya: Passing custom attributes to alembic - OP-4111 #4516 + +Passing custom attributes to alembic + +This PR makes it possible to pass all user defined attributes along to the alembic representation. + + +___ + +
+ + +
+Maya: Options for VrayProxy output - OP-2010 #4525 + +Options for output of VrayProxy. + +Client requested more granular control of output from VrayProxy instance. Exposed options on the instance and settings for vrmesh and alembic. + + +___ + +
+ + +
+Maya: Validate missing instance attributes #4559 + +Validate missing instance attributes. + +New attributes can be introduced as new features come in. Old instances will need to be updated with these attributes for the documentation to make sense, and users do not have to recreate the instances. + + +___ + +
+ + +
+Refactored Generation of UE Projects, installation of plugins moved to the engine #4369 + +Improved the way how OpenPype works with generation of UE projects. Also the installation of the plugin has been altered to install into the engine + +OpenPype now uses the appropriate tools to generate UE projects. Unreal Build Tool (UBT) and a "Commandlet Project" is needed to properly generate a BP project, or C++ code in case that `dev_mode = True`, folders, the .uproject file and many other resources.On the plugin's side, it is built seperately with the UnrealAutomationTool (UAT) and then it's contents are moved under the `Engine/Plugins/Marketplace/OpenPype` directory. + + +___ + +
+ + +
+Unreal: Use client functions in Layout loader #4578 + +Use 'get_representations' instead of 'legacy_io' query in layout loader. + +This is removing usage of `find_one` called on `legacy_io` and use rather client functions as preparation for AYON connection. Also all representations are queried at once instead of one by one. + + +___ + +
+ + +
+General: Support for extensions filtering in loaders #4492 + +Added extensions filtering support to loader plugins. + +To avoid possible backwards compatibility break is filtering exactly the same and filtering by extensions is enabled only if class attribute 'extensions' is set. + + +___ + +
+ + +
+Nuke: multiple reformat in baking review profiles #4514 + +Added support for multiple reformat nodes in baking profiles. + +Old settings for single reformat node is supported and prioritised just in case studios are using it and backward compatibility is needed. Warnings in Nuke terminal are notifying users to switch settings to new workflow. Settings are also explaining the migration way. + + +___ + +
+ + +
+Nuke: Add option to use new creating system in workfile template builder #4545 + +Nuke workfile template builder can use new creators instead of legacy creators. + +Modified workfile template builder to have option to say if legacy creators should be used or new creators. Legacy creators are disabled by default, so Maya has changed the value. + + +___ + +
+ + +
+Global, Nuke: Workfile first version with template processing #4579 + +Supporting new template workfile builder with toggle for creation of first version of workfile in case there is none yet. + + +___ + +
+ + +
+Fusion: New Publisher #4523 + +This is an updated PR for @BigRoy 's old PR (https://github.com/ynput/OpenPype/pull/3892).I have merged it with code from OP 3.15.1-nightly.6 and made sure it works as expected.This converts the old publishing system to the new one. It implements Fusion as a new host addon. + + +- Create button removed in OpenPype menu in favor of the new Publisher +- Draft refactor validations to raise PublishValidationError +- Implement Creator for New Publisher +- Implement Fusion as Host addon + + +___ + +
+ + +
+TVPaint: Use Publisher tool #4471 + +Use Publisher tool and new creation system in TVPaint integration. + +Using new creation system makes TVPaint integration a little bit easier to maintain for artists. Removed unneeded tools Creator and Subset Manager tools. Goal is to keep the integration work as close as possible to previous integration. Some changes were made but primarilly because they were not right using previous system.All creators create instance with final family instead of changing the family during extraction. Render passes are not related to group id but to render layer instance. Render layer is still related to group. Workfile, review and scene render instances are created using autocreators instead of auto-collection during publishing. Subset names are fully filled during publishing but instance labels are filled on refresh with the last known right value. Implemented basic of legacy convertor which should convert render layers and render passes. + + +___ + +
+ + +
+TVPaint: Auto-detect render creation #4496 + +Create plugin which will create Render Layer and Render Pass instances based on information in the scene. + +Added new creator that must be triggered by artist. The create plugin will first create Render Layer instances if were not created yet. For variant is used color group name. The creator has option to rename color groups by template defined in settings -> Template may use index of group by it's usage in scene (from bottom to top). After Render Layers will create Render Passes. Render Pass is created for each individual TVPaint layer in any group that had created Render Layer. It's name is used as variant (pass). + + +___ + +
+ + +
+TVPaint: Small enhancements #4501 + +Small enhancements in TVPaint integration which did not get to https://github.com/ynput/OpenPype/pull/4471. + +It was found out that `opacity` returned from `tv_layerinfo` is always empty and is dangerous to add it to layer information. Added information about "current" layer to layers information. Disable review of Render Layer and Render Pass instances by default. In most of productions is used only "scene review". Skip usage of `"enabled"` key from settings in automated layer/pass creation. + + +___ + +
+ + +
+Global: color v3 global oiio transcoder plugin #4291 + +Implements possibility to use `oiiotool` to transcode image sequences from one color space to another(s). + +Uses collected `colorspaceData` information about source color spaces, these information needs to be collected previously in each DCC interested in color management.Uses profiles configured in Settings to create single or multiple new representations (and file extensions) with different color spaces.New representations might replace existing one, each new representation might contain different tags and custom tags to control its integration step. + + +___ + +
+ + +
+Deadline: Added support for multiple install dirs in Deadline #4451 + +SearchDirectoryList returns FIRST existing so if you would have multiple OP install dirs, it won't search for appropriate version in later ones. + + +___ + +
+ + +
+Ftrack: Upload reviewables with original name #4483 + +Ftrack can integrate reviewables with original filenames. + +As ftrack have restrictions about names of components the only way how to achieve the result was to upload the same file twice, one with required name and one with origin name. + + +___ + +
+ + +
+TVPaint: Ignore transparency in Render Pass #4499 + +It is possible to ignore layers transparency during Render Pass extraction. + +Render pass extraction does not respect opacity of TVPaint layers set in scene during extraction. It can be enabled/disabled in settings. + + +___ + +
+ + +
+Anatomy: Preparation for different root overrides #4521 + +Prepare Anatomy to handle only 'studio' site override on it's own. + +Change how Anatomy fill root overrides based on requested site name. The logic which decide what is active site was moved to sync server addon and the same for receiving root overrides of local site. The Anatomy resolve only studio site overrides anything else is handled by sync server. BaseAnatomy only expect root overrides value and does not need site name. Validation of site name happens in sync server same as resolving if site name is local or not. + + +___ + +
+ + +
+Nuke | Global: colormanaged plugin in collection #4556 + +Colormanaged extractor had changed to Mixin class so it can be added to any stage of publishing rather then just to Exctracting.Nuke is no collecting colorspaceData to representation collected on already rendered images. + +Mixin class can no be used as secondary parent in publishing plugins. + + +___ + +
+ +### **🐛 Bug fixes** + + +
+look publishing and srgb colorspace in maya #4276 + +Check the OCIO color management is enabled before doing linearize colorspace for converting the texture maps into tx files. + +Check whether the OCIO color management is enabled before the condition of converting the texture to tx extension. + + +___ + +
+ + +
+Maya: extract Thumbnail "No active model panel found" - OP-3849 #4421 + +Error when extracting playblast with no model panel. + +If `project_settings/maya/publish/ExtractPlayblast/capture_preset/Viewport Options/override_viewport_options` were off and publishing without showing any model panel, the extraction would fail. + + +___ + +
+ + +
+Maya: Fix setting scene fps with float input #4488 + +Returned value of float fps on integer values would return float. + +This PR fixes the case when switching between integer fps values for example 24 > 25. Issue was when setting the scene fps, the original float value was used which makes it unpredictable whether the value is float or integer when mapping the fps values. + + +___ + +
+ + +
+Maya: Multipart fix #4497 + +Fix multipart logic in render products. + +Each renderer has a different way of defining whether output images is multipart, so we need to define it for each renderer. Also before the `multipart` class variable was defined multiple times in several places, which made it tricky to debug where `multipart` was defined. Now its created on initialization and referenced as `self.multipart` + + +___ + +
+ + +
+Maya: Set pool on tile assembly - OP-2012 #4520 + +Set pool on tile assembly + +Pool for publishing and tiling jobs, need to use the settings (`project_settings/deadline/publish/ProcessSubmittedJobOnFarm/deadline_pool`) else fallback on primary pool (`project_settings/deadline/publish/CollectDeadlinePools/primary_pool`) + + +___ + +
+ + +
+Maya: Extract review with handles #4527 + +Review was not extracting properly with/without handles. + +Review instance was not created properly resulting in the frame range on the instance including handles. + + +___ + +
+ + +
+Maya: Fix broken lib. #4529 + +Fix broken lib. + +This commit from this PR broke the Maya lib module. + + +___ + +
+ + +
+Maya: Validate model name - OP-4983 #4539 + +Validate model name issues. + +Couple of issues with validate model name; +- missing platform extraction from settings +- map function should be list comprehension +- code cosmetics + + +___ + +
+ + +
+Maya: SkeletalMesh family loadable as reference #4573 + +In Maya, fix the SkeletalMesh family not loadable as reference. + + +___ + +
+ + +
+Unreal: fix loaders because of missing AssetContainer #4536 + +Fixing Unreal loaders, where changes in OpenPype Unreal integration plugin deleted AssetContainer. + +`AssetContainer` and `AssetContainerFactory` are still used to mark loaded instances. Because of optimizations in Integration plugin we've accidentally removed them but that broke loader. + + +___ + +
+ + +
+3dsmax unable to delete loaded asset in the scene inventory #4507 + +Fix the bug of being unable to delete loaded asset in the Scene Inventory + +Fix the bug of being unable to delete loaded asset in the Scene Inventory + + +___ + +
+ + +
+Hiero/Nuke: originalBasename editorial publishing and loading #4453 + +Publishing and loading `originalBasename` is working as expected + +Frame-ranges on version document is now correctly defined to fit original media frame range which is published. It means loading is now correctly identifying frame start and end on clip loader in Nuke. + + +___ + +
+ + +
+Nuke: Fix workfile template placeholder creation #4512 + +Template placeholder creation was erroring out in Nuke due to the Workfile template builder not being able to find any of the plugins for the Nuke host. + +Move `get_workfile_build_placeholder_plugins` function to NukeHost class as workfile template builder expects. + + +___ + +
+ + +
+Nuke: creator farm attributes from deadline submit plugin settings #4519 + +Defaults in farm attributes are sourced from settings. + +Settings for deadline nuke submitter are now used during nuke render and prerender creator plugins. + + +___ + +
+ + +
+Nuke: fix clip sequence loading #4574 + +Nuke is loading correctly clip from image sequence created without "{originalBasename}" token in anatomy template. + + +___ + +
+ + +
+Fusion: Fix files collection and small bug-fixes #4423 + +Fixed Fusion review-representation and small bug-fixes + +This fixes the problem with review-file generation that stopped the publishing on second publish before the fix.The problem was that Fusion simply looked at all the files in the render-folder instead of only gathering the needed frames for the review.Also includes a fix to get the handle start/end that before throw an error if the data didn't exist (like from a kitsu sync). + + +___ + +
+ + +
+Fusion: Updated render_local.py to not only process the first instance #4522 + +Moved the `__hasRun` to `render_once()` so the check only happens with the rendering. Currently only the first render node gets the representations added.Critical PR + + +___ + +
+ + +
+Fusion: Load sequence fix filepath resolving from representation #4580 + +Resolves issue mentioned on discord by @movalex:The loader was incorrectly trying to find the file in the publish folder which resulted in just picking 'any first file'. + +This gets the filepath from representation instead of taking the first file from listing files from publish folder. + + +___ + +
+ + +
+Fusion: Fix review burnin start and end frame #4590 + +Fix the burnin start and end frame for reviews. Without this the asset document's start and end handle would've been added to the _burnin_ frame range even though that would've been incorrect since the handles are based on the comp saver's render range instead. + + +___ + +
+ + +
+Harmony: missing set of frame range when opening scene #4485 + +Frame range gets set from DB everytime scene is opened. + +Added also check for not up-to-date loaded containers. + + +___ + +
+ + +
+Photoshop: context is not changed in publisher #4570 + +When PS is already open and artists launch new task, it should keep only opened PS open, but change context. + +Problem were occurring in Workfile app where under new task files from old task were shown. This fixes this and adds opening of last workfile for new context if workfile exists. + + +___ + +
+ + +
+hiero: fix effect item node class #4543 + +Collected effect name after renaming is saving correct class name. + + +___ + +
+ + +
+Bugfix/OP-4616 vray multipart #4297 + +This fixes a bug where multipart vray renders would not make a review in Ftrack. + + +___ + +
+ + +
+Maya: Fix changed location of reset_frame_range #4491 + +Location in commands caused cyclic import + + +___ + +
+ + +
+global: source template fixed frame duplication #4503 + +Duplication is not happening. + +Template is using `originalBasename` which already assume all necessary elements are part of the file name so there was no need for additional optional name elements. + + +___ + +
+ + +
+Deadline: Hint to use Python 3 #4518 + +Added shebank to give deadline hint which python should be used. + +Deadline has issues with Python 2 (especially with `os.scandir`). When a shebank is added to file header deadline will use python 3 mode instead of python 2 which fix the issue. + + +___ + +
+ + +
+Publisher: Prevent access to create tab after publish start #4528 + +Prevent access to create tab after publish start. + +Disable create button in instance view on publish start and enable it again on reset. Even with that make sure that it is not possible to go to create tab if the tab is disabled. + + +___ + +
+ + +
+Color Transcoding: store target_colorspace as new colorspace #4544 + +When transcoding into new colorspace, representation must carry this information instead original color space. + + +___ + +
+ + +
+Deadline: fix submit_publish_job #4552 + +Fix submit_publish_job + +Resolves #4541 + + +___ + +
+ + +
+Kitsu: Fix task itteration in update-op-with-zou #4577 + +From the last PR (https://github.com/ynput/OpenPype/pull/4425) a comment-commit last second messed up the code and resulted in two lines being the same, crashing the script. This PR fixes that. +___ + +
+ + +
+AttrDefs: Fix type for PySide6 #4584 + +Use right type in signal emit for value change of attribute definitions. + +Changed `UUID` type to `str`. This is not an issue with PySide2 but it is with PySide6. + + +___ + +
+ +### **🔀 Refactored code** + + +
+Scene Inventory: Avoid using ObjectId #4524 + +Avoid using conversion to ObjectId type in scene inventory tool. + +Preparation for AYON compatibility where ObjectId won't be used for ids. Representation ids from loaded containers are not converted to ObjectId but kept as strings which also required some changes when working with representation documents. + + +___ + +
+ +### **Merged pull requests** + + +
+SiteSync: host dirmap is not working properly #4563 + +If artists uses SiteSync with real remote (gdrive, dropbox, sftp) drive, Local Settings were throwing error `string indices must be integers`. + +Logic was reworked to provide only `local_drive` values to be overrriden by Local Settings. If remote site is `gdrive` etc. mapping to `studio` is provided as it is expected that workfiles will have imported from `studio` location and not from `gdrive` folder.Also Nuke dirmap was reworked to be less verbose and much faster. + + +___ + +
+ + +
+General: Input representation ids are not ObjectIds #4576 + +Don't use `ObjectId` as representation ids during publishing. + +Representation ids are kept as strings during publishing instead of converting them to `ObjectId`. This change is pre-requirement for AYON connection.Inputs are used for integration of links and for farm publishing (or at least it looks like). + + +___ + +
+ + +
+Shotgrid: Fixes on Deadline submissions #4498 + +A few other bug fixes for getting Nuke submission to Deadline work smoothly using Shotgrid integration. + +Continuing on the work done on this other PR this fixes a few other bugs I came across with further tests. + + +___ + +
+ + +
+Fusion: New Publisher #3892 + +This converts the old publishing system to the new one. It implements Fusion as a new host addon. + + +- Create button removed in OpenPype menu in favor of the new Publisher +- Draft refactor validations to raise `PublishValidationError` +- Implement Creator for New Publisher +- Implement Fusion as Host addon + + +___ + +
+ + +
+Make Kitsu work with Tray Publisher, added kitsureview tag, fixed sync-problems. #4425 + +Make Kitsu work with Tray Publisher, added kitsureview tag, fixed sync-problems. + +This PR updates the way the module gather info for the current publish so it now works with Tray Publisher.It fixes the data that gets synced from Kitsu to OP so all needed data gets registered even if it doesn't exist on Kitsus side.It also adds the tag "Add review to Kitsu" and adds it to Burn In so previews gets generated by default to Kitsu. + + +___ + +
+ + +
+Maya: V-Ray Set Image Format from settings #4566 + +Resolves #4565 + +Set V-Ray Image Format using settings. + + +___ + +
+ + + + ## [3.15.1](https://github.com/ynput/OpenPype/tree/3.15.1) [Full Changelog](https://github.com/ynput/OpenPype/compare/3.15.0...3.15.1) diff --git a/openpype/version.py b/openpype/version.py index e8124f1466..6ab03c2121 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.2-nightly.6" +__version__ = "3.15.2" diff --git a/pyproject.toml b/pyproject.toml index 2fc4f6fe39..02370a4f10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.1" # OpenPype +version = "3.15.2" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From ff19ae038519d1ccf85e4145c66e416609a94edf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 13 Mar 2023 15:37:23 +0100 Subject: [PATCH 1251/1271] Added setup_only to tests (#4591) Allows to download test zip, unzip and restore DB in preparation for new test. --- openpype/cli.py | 8 ++++++-- openpype/pype_commands.py | 5 ++++- tests/conftest.py | 10 ++++++++++ tests/lib/testing_classes.py | 22 +++++++++++++++++++--- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/openpype/cli.py b/openpype/cli.py index 5c47088a44..a650a9fdcc 100644 --- a/openpype/cli.py +++ b/openpype/cli.py @@ -367,11 +367,15 @@ def run(script): "--timeout", help="Provide specific timeout value for test case", default=None) +@click.option("-so", + "--setup_only", + help="Only create dbs, do not run tests", + default=None) def runtests(folder, mark, pyargs, test_data_folder, persist, app_variant, - timeout): + timeout, setup_only): """Run all automatic tests after proper initialization via start.py""" PypeCommands().run_tests(folder, mark, pyargs, test_data_folder, - persist, app_variant, timeout) + persist, app_variant, timeout, setup_only) @main.command() diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index 932fdc9be4..dc5b3d63c3 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -270,7 +270,7 @@ class PypeCommands: pass def run_tests(self, folder, mark, pyargs, - test_data_folder, persist, app_variant, timeout): + test_data_folder, persist, app_variant, timeout, setup_only): """ Runs tests from 'folder' @@ -311,6 +311,9 @@ class PypeCommands: if timeout: args.extend(["--timeout", timeout]) + if setup_only: + args.extend(["--setup_only", setup_only]) + print("run_tests args: {}".format(args)) import pytest pytest.main(args) diff --git a/tests/conftest.py b/tests/conftest.py index 7b58b0314d..4f7c17244b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,11 @@ def pytest_addoption(parser): help="Overwrite default timeout" ) + parser.addoption( + "--setup_only", action="store", default=None, + help="True - only setup test, do not run any tests" + ) + @pytest.fixture(scope="module") def test_data_folder(request): @@ -45,6 +50,11 @@ def timeout(request): return request.config.getoption("--timeout") +@pytest.fixture(scope="module") +def setup_only(request): + return request.config.getoption("--setup_only") + + @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): # execute all other hooks to obtain the report object diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 2bafa16971..300024dc98 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -243,6 +243,8 @@ class PublishTest(ModuleUnitTest): PERSIST = True # True - keep test_db, test_openpype, outputted test files TEST_DATA_FOLDER = None # use specific folder of unzipped test file + SETUP_ONLY = False + @pytest.fixture(scope="module") def app_name(self, app_variant): """Returns calculated value for ApplicationManager. Eg.(nuke/12-2)""" @@ -286,8 +288,13 @@ class PublishTest(ModuleUnitTest): @pytest.fixture(scope="module") def launched_app(self, dbcon, download_test_data, last_workfile_path, - startup_scripts, app_args, app_name, output_folder_url): + startup_scripts, app_args, app_name, output_folder_url, + setup_only): """Launch host app""" + if setup_only or self.SETUP_ONLY: + print("Creating only setup for test, not launching app") + yield + return # set schema - for integrate_new from openpype import PACKAGE_DIR # Path to OpenPype's schema @@ -316,8 +323,12 @@ class PublishTest(ModuleUnitTest): @pytest.fixture(scope="module") def publish_finished(self, dbcon, launched_app, download_test_data, - timeout): + timeout, setup_only): """Dummy fixture waiting for publish to finish""" + if setup_only or self.SETUP_ONLY: + print("Creating only setup for test, not launching app") + yield False + return import time time_start = time.time() timeout = timeout or self.TIMEOUT @@ -334,11 +345,16 @@ class PublishTest(ModuleUnitTest): def test_folder_structure_same(self, dbcon, publish_finished, download_test_data, output_folder_url, - skip_compare_folders): + skip_compare_folders, + setup_only): """Check if expected and published subfolders contain same files. Compares only presence, not size nor content! """ + if setup_only or self.SETUP_ONLY: + print("Creating only setup for test, not launching app") + return + published_dir_base = output_folder_url expected_dir_base = os.path.join(download_test_data, "expected") From 5d17ae2857b00bec100442957cdf1ae1164a39ca Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 11 Mar 2023 07:32:44 +0100 Subject: [PATCH 1252/1271] Houdini: Create button open new publisher's "create" tab Also made publish button enforce going to the "publish" tab. --- openpype/hosts/houdini/startup/MainMenuCommon.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/startup/MainMenuCommon.xml b/openpype/hosts/houdini/startup/MainMenuCommon.xml index c08114b71b..8e24ce3b99 100644 --- a/openpype/hosts/houdini/startup/MainMenuCommon.xml +++ b/openpype/hosts/houdini/startup/MainMenuCommon.xml @@ -10,7 +10,7 @@ import hou from openpype.tools.utils import host_tools parent = hou.qt.mainWindow() -host_tools.show_creator(parent) +host_tools.show_publisher(parent, tab="create") ]]>
@@ -30,7 +30,7 @@ host_tools.show_loader(parent=parent, use_context=True) import hou from openpype.tools.utils import host_tools parent = hou.qt.mainWindow() -host_tools.show_publisher(parent) +host_tools.show_publisher(parent, tab="publish") ]]>
From ed7a4e424368a7503eb024ceea7b35be9f8bc6d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 14:32:06 +0000 Subject: [PATCH 1253/1271] Bump @sideway/formula from 3.0.0 to 3.0.1 in /website Bumps [@sideway/formula](https://github.com/sideway/formula) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/sideway/formula/releases) - [Commits](https://github.com/sideway/formula/compare/v3.0.0...v3.0.1) --- updated-dependencies: - dependency-name: "@sideway/formula" 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 559c58f931..d250e48b9d 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -1669,9 +1669,9 @@ "@hapi/hoek" "^9.0.0" "@sideway/formula@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" - integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== "@sideway/pinpoint@^2.0.0": version "2.0.0" From f55ac53c82e4b75b445bcfd99753a9ee56180ccf Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 13 Mar 2023 23:12:34 +0100 Subject: [PATCH 1254/1271] Fix typos --- openpype/settings/entities/schemas/README.md | 2 +- website/docs/admin_environment.md | 2 +- website/docs/admin_hosts_maya.md | 2 +- website/docs/admin_settings.md | 2 +- website/docs/admin_use.md | 2 +- website/docs/artist_hosts_3dsmax.md | 4 ++-- website/docs/artist_hosts_hiero.md | 6 +++--- website/docs/artist_hosts_photoshop.md | 2 +- website/docs/artist_tools_sync_queu.md | 2 +- website/docs/dev_colorspace.md | 4 ++-- website/docs/dev_host_implementation.md | 4 ++-- website/docs/dev_publishing.md | 2 +- website/docs/dev_settings.md | 6 +++--- website/docs/dev_testing.md | 2 +- website/docs/module_kitsu.md | 2 +- website/docs/module_site_sync.md | 2 +- website/docs/project_settings/settings_project_global.md | 6 +++--- website/docs/project_settings/settings_project_nuke.md | 2 +- .../docs/project_settings/settings_project_standalone.md | 2 +- 19 files changed, 28 insertions(+), 28 deletions(-) diff --git a/openpype/settings/entities/schemas/README.md b/openpype/settings/entities/schemas/README.md index b4c878fe0f..cff614a4bb 100644 --- a/openpype/settings/entities/schemas/README.md +++ b/openpype/settings/entities/schemas/README.md @@ -350,7 +350,7 @@ How output of the schema could look like on save: - number input, can be used for both integer and float - key `"decimal"` defines how many decimal places will be used, 0 is for integer input (Default: `0`) - key `"minimum"` as minimum allowed number to enter (Default: `-99999`) - - key `"maxium"` as maximum allowed number to enter (Default: `99999`) + - key `"maximum"` as maximum allowed number to enter (Default: `99999`) - key `"steps"` will change single step value of UI inputs (using arrows and wheel scroll) - for UI it is possible to show slider to enable this option set `show_slider` to `true` ``` diff --git a/website/docs/admin_environment.md b/website/docs/admin_environment.md index 1eb755b90b..29b70a6c47 100644 --- a/website/docs/admin_environment.md +++ b/website/docs/admin_environment.md @@ -27,4 +27,4 @@ import TabItem from '@theme/TabItem'; - for more details on how to use it go [here](admin_use#check-for-mongodb-database-connection) ## OPENPYPE_USERNAME -- if set it overides system created username +- if set it overrides system created username diff --git a/website/docs/admin_hosts_maya.md b/website/docs/admin_hosts_maya.md index 0e77f29fc2..ae0cf76f53 100644 --- a/website/docs/admin_hosts_maya.md +++ b/website/docs/admin_hosts_maya.md @@ -142,7 +142,7 @@ Fill in the necessary fields (the optional fields are regex filters) ![new place holder](assets/maya-placeholder_new.png) - - Builder type: Wether the the placeholder should load current asset representations or linked assets representations + - Builder type: Whether the the placeholder should load current asset representations or linked assets representations - Representation: Representation that will be loaded (ex: ma, abc, png, etc...) diff --git a/website/docs/admin_settings.md b/website/docs/admin_settings.md index 8626ef16ba..dcbe740d0c 100644 --- a/website/docs/admin_settings.md +++ b/website/docs/admin_settings.md @@ -76,7 +76,7 @@ You can also reset any settings to OpenPype default by doing `right click` and ` Many settings are useful to be adjusted on a per-project basis. To identify project overrides, they are marked with **orange edge** and **orange labels** in the settings GUI. -The process of settting project overrides is similar to setting the Studio defaults. The key difference is to select a particular project you want to be configure. Those projects can be found on the left hand side of the Project Settings tab. +The process of setting project overrides is similar to setting the Studio defaults. The key difference is to select a particular project you want to be configure. Those projects can be found on the left hand side of the Project Settings tab. In the image below you can see all three overrides at the same time. 1. Deadline has **no changes to the OpenPype defaults** at all — **grey** colour of left bar. diff --git a/website/docs/admin_use.md b/website/docs/admin_use.md index c92ac3a77a..c1d1de0e8c 100644 --- a/website/docs/admin_use.md +++ b/website/docs/admin_use.md @@ -68,7 +68,7 @@ Add `--headless` to run OpenPype without graphical UI (useful on server or on au `--verbose` `` - change log verbose level of OpenPype loggers. -Level value can be integer in range `0-50` or one of enum strings `"notset" (0)`, `"debug" (10)`, `"info" (20)`, `"warning" (30)`, `"error" (40)`, `"ciritcal" (50)`. Value is stored to `OPENPYPE_LOG_LEVEL` environment variable for next processes. +Level value can be integer in range `0-50` or one of enum strings `"notset" (0)`, `"debug" (10)`, `"info" (20)`, `"warning" (30)`, `"error" (40)`, `"critical" (50)`. Value is stored to `OPENPYPE_LOG_LEVEL` environment variable for next processes. ```shell openpype_console --verbose debug diff --git a/website/docs/artist_hosts_3dsmax.md b/website/docs/artist_hosts_3dsmax.md index 71ba8785dc..12c1f40181 100644 --- a/website/docs/artist_hosts_3dsmax.md +++ b/website/docs/artist_hosts_3dsmax.md @@ -47,7 +47,7 @@ This is the core functional area for you as a user. Most of your actions will ta ![Menu OpenPype](assets/3dsmax_menu_first_OP.png) :::note OpenPype Menu -User should use this menu exclusively for **Opening/Saving** when dealing with work files not standard ```File Menu``` even though user still being able perform file operations via this menu but prefferably just performing quick saves during work session not saving actual workfile versions. +User should use this menu exclusively for **Opening/Saving** when dealing with work files not standard ```File Menu``` even though user still being able perform file operations via this menu but preferably just performing quick saves during work session not saving actual workfile versions. ::: ## Working With Scene Files @@ -73,7 +73,7 @@ OpenPype correctly names it and add version to the workfile. This basically happ etc. -Basically meaning user is free of guessing what is the correct naming and other neccessities to keep everthing in order and managed. +Basically meaning user is free of guessing what is the correct naming and other necessities to keep everything in order and managed. > Note: user still has also other options for naming like ```Subversion```, ```Artist's Note``` but we won't dive into those now. diff --git a/website/docs/artist_hosts_hiero.md b/website/docs/artist_hosts_hiero.md index 136ed6ea9c..977c7a7baf 100644 --- a/website/docs/artist_hosts_hiero.md +++ b/website/docs/artist_hosts_hiero.md @@ -231,14 +231,14 @@ All published instances that will replace the place holder must contain unique i ![Create menu](assets/nuke_publishedinstance.png) -The informations about these objects are given by the user by filling the extra attributes of the Place Holder +The information about these objects are given by the user by filling the extra attributes of the Place Holder ![Create menu](assets/nuke_fillingExtraAttributes.png) ### Update Place Holder -This tool alows the user to change the information provided in the extra attributes of the selected Place Holder. +This tool allows the user to change the information provided in the extra attributes of the selected Place Holder. ![Create menu](assets/nuke_updatePlaceHolder.png) @@ -250,7 +250,7 @@ This tool imports the template used and replaces the existed PlaceHolders with t ![Create menu](assets/nuke_buildWorfileFromTemplate.png) #### Result -- Replace `PLACEHOLDER` node in the template with the published instance corresponding to the informations provided in extra attributes of the Place Holder +- Replace `PLACEHOLDER` node in the template with the published instance corresponding to the information provided in extra attributes of the Place Holder ![Create menu](assets/nuke_buildworkfile.png) diff --git a/website/docs/artist_hosts_photoshop.md b/website/docs/artist_hosts_photoshop.md index 88bfb1484d..12203a5c6e 100644 --- a/website/docs/artist_hosts_photoshop.md +++ b/website/docs/artist_hosts_photoshop.md @@ -75,7 +75,7 @@ enabled instances, you could see more information after clicking on `Details` ta ![Image instances creates](assets/photoshop_publish_validations.png) -In this dialog you could see publishable instances in left colummn, triggered plugins in the middle and logs in the right column. +In this dialog you could see publishable instances in left column, triggered plugins in the middle and logs in the right column. In left column you could see that `review` instance was created automatically. This instance flattens all publishable instances or all visible layers if no publishable instances were created into single image which could serve as a single reviewable element (for example in Ftrack). diff --git a/website/docs/artist_tools_sync_queu.md b/website/docs/artist_tools_sync_queu.md index 770c2f77ad..7dac8638f9 100644 --- a/website/docs/artist_tools_sync_queu.md +++ b/website/docs/artist_tools_sync_queu.md @@ -2,7 +2,7 @@ id: artist_tools_sync_queue title: Sync Queue sidebar_label: Sync Queue -description: Track sites syncronization progress. +description: Track sites synchronization progress. --- # Sync Queue diff --git a/website/docs/dev_colorspace.md b/website/docs/dev_colorspace.md index fe9a0ec1e3..b46d80e1a2 100644 --- a/website/docs/dev_colorspace.md +++ b/website/docs/dev_colorspace.md @@ -24,8 +24,8 @@ It's up to the Loaders to read these values and apply the correct expected color ### Keys - **colorspace** - string value used in other publish plugins and loaders - **config** - storing two versions of path. - - **path** - is formated and with baked platform root. It is used for posible need to find out where we were sourcing color config during publishing. - - **template** - unformated tempate resolved from settings. It is used for other plugins targeted to remote publish which could be processed at different platform. + - **path** - is formatted and with baked platform root. It is used for possible need to find out where we were sourcing color config during publishing. + - **template** - unformatted template resolved from settings. It is used for other plugins targeted to remote publish which could be processed at different platform. ### Example { diff --git a/website/docs/dev_host_implementation.md b/website/docs/dev_host_implementation.md index 3702483ad1..e62043723f 100644 --- a/website/docs/dev_host_implementation.md +++ b/website/docs/dev_host_implementation.md @@ -45,10 +45,10 @@ openpype/hosts/{host name} ``` ### Launch Hooks -Launch hooks are not directly connected to host implementation, but they can be used to modify launch of process which may be crutial for the implementation. Launch hook are plugins called when DCC is launched. They are processed in sequence before and after launch. Pre launch hooks can change how process of DCC is launched, e.g. change subprocess flags, modify environments or modify launch arguments. If prelaunch hook crashes the application is not launched at all. Postlaunch hooks are triggered after launch of subprocess. They can be used to change statuses in your project tracker, start timer, etc. Crashed postlaunch hooks have no effect on rest of postlaunch hooks or launched process. They can be filtered by platform, host and application and order is defined by integer value. Hooks inside host are automatically loaded (one reason why folder name should match host name) or can be defined from modules. Hooks execution share same launch context where can be stored data used across multiple hooks (please be very specific in stored keys e.g. 'project' vs. 'project_name'). For more detailed information look into `openpype/lib/applications.py`. +Launch hooks are not directly connected to host implementation, but they can be used to modify launch of process which may be crucial for the implementation. Launch hook are plugins called when DCC is launched. They are processed in sequence before and after launch. Pre launch hooks can change how process of DCC is launched, e.g. change subprocess flags, modify environments or modify launch arguments. If prelaunch hook crashes the application is not launched at all. Postlaunch hooks are triggered after launch of subprocess. They can be used to change statuses in your project tracker, start timer, etc. Crashed postlaunch hooks have no effect on rest of postlaunch hooks or launched process. They can be filtered by platform, host and application and order is defined by integer value. Hooks inside host are automatically loaded (one reason why folder name should match host name) or can be defined from modules. Hooks execution share same launch context where can be stored data used across multiple hooks (please be very specific in stored keys e.g. 'project' vs. 'project_name'). For more detailed information look into `openpype/lib/applications.py`. ### Public interface -Public face is at this moment related to launching of the DCC. At this moment there there is only option to modify environment variables before launch by implementing function `add_implementation_envs` (must be available in `openpype/hosts/{host name}/__init__.py`). The function is called after pre launch hooks, as last step before subprocess launch, to be able set environment variables crutial for proper integration. It is also good place for functions that are used in prelaunch hooks and in-DCC integration. Future plans are to be able get workfiles extensions from here. Right now workfiles extensions are hardcoded in `openpype/pipeline/constants.py` under `HOST_WORKFILE_EXTENSIONS`, we would like to handle hosts as addons similar to OpenPype modules, and more improvements which are now hardcoded. +Public face is at this moment related to launching of the DCC. At this moment there there is only option to modify environment variables before launch by implementing function `add_implementation_envs` (must be available in `openpype/hosts/{host name}/__init__.py`). The function is called after pre launch hooks, as last step before subprocess launch, to be able set environment variables crucial for proper integration. It is also good place for functions that are used in prelaunch hooks and in-DCC integration. Future plans are to be able get workfiles extensions from here. Right now workfiles extensions are hardcoded in `openpype/pipeline/constants.py` under `HOST_WORKFILE_EXTENSIONS`, we would like to handle hosts as addons similar to OpenPype modules, and more improvements which are now hardcoded. ### Integration We've prepared base class `HostBase` in `openpype/host/host.py` to define minimum requirements and provide some default method implementations. The minimum requirement for a host is `name` attribute, this host would not be able to do much but is valid. To extend functionality we've prepared interfaces that helps to identify what is host capable of and if is possible to use certain tools with it. For those cases we defined interfaces for each workflow. `IWorkfileHost` interface add requirement to implement workfiles related methods which makes host usable in combination with Workfiles tool. `ILoadHost` interface add requirements to be able load, update, switch or remove referenced representations which should add support to use Loader and Scene Inventory tools. `INewPublisher` interface is required to be able use host with new OpenPype publish workflow. This is what must or can be implemented to allow certain functionality. `HostBase` will have more responsibility which will be taken from global variables in future. This process won't happen at once, but will be slow to keep backwards compatibility for some time. diff --git a/website/docs/dev_publishing.md b/website/docs/dev_publishing.md index 135f6cd985..2c57537223 100644 --- a/website/docs/dev_publishing.md +++ b/website/docs/dev_publishing.md @@ -415,7 +415,7 @@ class CreateRender(Creator): # - 'asset' - asset name # - 'task' - task name # - 'variant' - variant - # - 'family' - instnace family + # - 'family' - instance family # Check if should use selection or not if pre_create_data.get("use_selection"): diff --git a/website/docs/dev_settings.md b/website/docs/dev_settings.md index 94590345e8..1010169a5f 100644 --- a/website/docs/dev_settings.md +++ b/website/docs/dev_settings.md @@ -355,7 +355,7 @@ These inputs wraps another inputs into {key: value} relation { "type": "text", "key": "command", - "label": "Comand" + "label": "Command" } ] }, @@ -420,7 +420,7 @@ How output of the schema could look like on save: - number input, can be used for both integer and float - key `"decimal"` defines how many decimal places will be used, 0 is for integer input (Default: `0`) - key `"minimum"` as minimum allowed number to enter (Default: `-99999`) - - key `"maxium"` as maximum allowed number to enter (Default: `99999`) + - key `"maximum"` as maximum allowed number to enter (Default: `99999`) - key `"steps"` will change single step value of UI inputs (using arrows and wheel scroll) - for UI it is possible to show slider to enable this option set `show_slider` to `true` ```javascript @@ -602,7 +602,7 @@ How output of the schema could look like on save: - there are 2 possible ways how to set the type: 1.) dictionary with item modifiers (`number` input has `minimum`, `maximum` and `decimals`) in that case item type must be set as value of `"type"` (example below) 2.) item type name as string without modifiers (e.g. [text](#text)) - 3.) enhancement of 1.) there is also support of `template` type but be carefull about endless loop of templates + 3.) enhancement of 1.) there is also support of `template` type but be careful about endless loop of templates - goal of using `template` is to easily change same item definitions in multiple lists 1.) with item modifiers diff --git a/website/docs/dev_testing.md b/website/docs/dev_testing.md index 7136ceb479..434c1ca9ff 100644 --- a/website/docs/dev_testing.md +++ b/website/docs/dev_testing.md @@ -57,7 +57,7 @@ Content: Contains end to end testing in a DCC. Currently it is setup to start DCC application with prepared worfkile, run publish process and compare results in DB and file system automatically. This approach is implemented as it should work in any DCC application and should cover most common use cases. Not all hosts allow "real headless" publishing, but all hosts should allow to trigger -publish process programatically when UI of host is actually running. +publish process programmatically when UI of host is actually running. There will be eventually also possibility to build workfile and publish it programmatically, this would work only in DCCs that support it (Maya, Nuke). diff --git a/website/docs/module_kitsu.md b/website/docs/module_kitsu.md index 7738ee1ce2..0424939786 100644 --- a/website/docs/module_kitsu.md +++ b/website/docs/module_kitsu.md @@ -41,4 +41,4 @@ openpype_console module kitsu push-to-zou -l me@domain.ext -p my_password ## Q&A ### Is it safe to rename an entity from Kitsu? Absolutely! Entities are linked by their unique IDs between the two databases. -But renaming from the OP's Project Manager won't apply the change to Kitsu, it'll be overriden during the next synchronization. +But renaming from the OP's Project Manager won't apply the change to Kitsu, it'll be overridden during the next synchronization. diff --git a/website/docs/module_site_sync.md b/website/docs/module_site_sync.md index 2e9cf01102..3e5794579c 100644 --- a/website/docs/module_site_sync.md +++ b/website/docs/module_site_sync.md @@ -89,7 +89,7 @@ all share the same provider). Handles files stored on disk storage. -Local drive provider is the most basic one that is used for accessing all standard hard disk storage scenarios. It will work with any storage that can be mounted on your system in a standard way. This could correspond to a physical external hard drive, network mounted storage, internal drive or even VPN connected network drive. It doesn't care about how te drive is mounted, but you must be able to point to it with a simple directory path. +Local drive provider is the most basic one that is used for accessing all standard hard disk storage scenarios. It will work with any storage that can be mounted on your system in a standard way. This could correspond to a physical external hard drive, network mounted storage, internal drive or even VPN connected network drive. It doesn't care about how the drive is mounted, but you must be able to point to it with a simple directory path. Default sites `local` and `studio` both use local drive provider. diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index b320b5502f..6c1a269b1f 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem'; Project settings can have project specific values. Each new project is using studio values defined in **default** project but these values can be modified or overridden per project. :::warning Default studio values -Projects always use default project values unless they have [project override](../admin_settings#project-overrides) (orage colour). Any changes in default project may affect all existing projects. +Projects always use default project values unless they have [project override](../admin_settings#project-overrides) (orange colour). Any changes in default project may affect all existing projects. ::: ## Color Management (ImageIO) @@ -39,14 +39,14 @@ Procedure of resolving path (from above example) will look first into path 1st a ### Using File rules File rules are inspired by [OCIO v2 configuration]((https://opencolorio.readthedocs.io/en/latest/guides/authoring/rules.html)). Each rule has a unique name which can be overridden by host-specific _File rules_ (example: `project_settings/nuke/imageio/file_rules/rules`). -The _input pattern_ matching uses REGEX expression syntax (try [regexr.com](https://regexr.com/)). Matching rules procedure's intention is to be used during publishing or loading of representation. Since the publishing procedure is run before integrator formate publish template path, make sure the pattern is working or any work render path. +The _input pattern_ matching uses REGEX expression syntax (try [regexr.com](https://regexr.com/)). Matching rules procedure's intention is to be used during publishing or loading of representation. Since the publishing procedure is run before integrator format publish template path, make sure the pattern is working or any work render path. :::warning Colorspace name input The **colorspace name** value is a raw string input and no validation is run after saving project settings. We recommend to open the specified `config.ocio` file and copy pasting the exact colorspace names. ::: ### Extract OIIO Transcode -OIIOTools transcoder plugin with configurable output presets. Any incoming representation with `colorspaceData` is convertable to single or multiple representations with different target colorspaces or display and viewer names found in linked **config.ocio** file. +OIIOTools transcoder plugin with configurable output presets. Any incoming representation with `colorspaceData` is convertible to single or multiple representations with different target colorspaces or display and viewer names found in linked **config.ocio** file. `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. diff --git a/website/docs/project_settings/settings_project_nuke.md b/website/docs/project_settings/settings_project_nuke.md index b3ee5f77a6..c9c3d12df9 100644 --- a/website/docs/project_settings/settings_project_nuke.md +++ b/website/docs/project_settings/settings_project_nuke.md @@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem'; Project settings can have project specific values. Each new project is using studio values defined in **default** project but these values can be modified or overridden per project. :::warning Default studio values -Projects always use default project values unless they have [project override](../admin_settings#project-overrides) (orage colour). Any changes in default project may affect all existing projects. +Projects always use default project values unless they have [project override](../admin_settings#project-overrides) (orange colour). Any changes in default project may affect all existing projects. ::: ## Workfile Builder diff --git a/website/docs/project_settings/settings_project_standalone.md b/website/docs/project_settings/settings_project_standalone.md index 778aba2942..1383bd488e 100644 --- a/website/docs/project_settings/settings_project_standalone.md +++ b/website/docs/project_settings/settings_project_standalone.md @@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem'; Project settings can have project specific values. Each new project is using studio values defined in **default** project but these values can be modified or overridden per project. :::warning Default studio values -Projects always use default project values unless they have [project override](../admin_settings#project-overrides) (orage colour). Any changes in default project may affect all existing projects. +Projects always use default project values unless they have [project override](../admin_settings#project-overrides) (orange colour). Any changes in default project may affect all existing projects. ::: ## Creator Plugins From 8f2112dd2642b02976a93cb0cabaffc1162b80ee Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 13 Mar 2023 23:13:50 +0100 Subject: [PATCH 1255/1271] Fix case of extension --- website/docs/artist_hosts_aftereffects.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/artist_hosts_aftereffects.md b/website/docs/artist_hosts_aftereffects.md index a9c9ca49fa..939ef4034c 100644 --- a/website/docs/artist_hosts_aftereffects.md +++ b/website/docs/artist_hosts_aftereffects.md @@ -34,7 +34,7 @@ a correct name. You should use it instead of standard file saving dialog. In AfterEffects you'll find the tools in the `OpenPype` extension: -![Extension](assets/photoshop_extension.PNG) +![Extension](assets/photoshop_extension.png) You can show the extension panel by going to `Window` > `Extensions` > `OpenPype`. @@ -104,7 +104,7 @@ There are currently 2 options of `render` item: When you want to load existing published work, you can use the `Loader` tool. You can reach it in the extension's panel. -![Loader](assets/photoshop_loader.PNG) +![Loader](assets/photoshop_loader.png) The supported families for loading into AfterEffects are: @@ -128,7 +128,7 @@ Now that we have some content loaded, you can manage which version is loaded. Th Loaded images have to stay as smart layers in order to be updated. If you rasterize the layer, you can no longer update it to a different version using OpenPype tools. ::: -![Loader](assets/photoshop_manage.PNG) +![Loader](assets/photoshop_manage.png) You can switch to a previous version of the image or update to the latest. From c262ac3255b39c1847720b6f1d51bf993a42d865 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 13 Mar 2023 23:14:11 +0100 Subject: [PATCH 1256/1271] Refactor case of extension to match surrounding files --- website/docs/artist_hosts_harmony.md | 2 +- .../{harmony_creator.PNG => harmony_creator.png} | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename website/docs/assets/{harmony_creator.PNG => harmony_creator.png} (100%) diff --git a/website/docs/artist_hosts_harmony.md b/website/docs/artist_hosts_harmony.md index aa9355e221..eaa6edc42a 100644 --- a/website/docs/artist_hosts_harmony.md +++ b/website/docs/artist_hosts_harmony.md @@ -44,7 +44,7 @@ Because the saving to the network location happens in the background, be careful `OpenPype > Create` -![Creator](assets/harmony_creator.PNG) +![Creator](assets/harmony_creator.png) These are the families supported in Harmony: diff --git a/website/docs/assets/harmony_creator.PNG b/website/docs/assets/harmony_creator.png similarity index 100% rename from website/docs/assets/harmony_creator.PNG rename to website/docs/assets/harmony_creator.png From fbe93aae39d7950639c5fbf99aca124b5ccd9ae3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 13 Mar 2023 23:21:05 +0100 Subject: [PATCH 1257/1271] Grammar tweaks --- website/docs/admin_settings.md | 2 +- website/docs/manager_ftrack.md | 2 +- website/docs/module_ftrack.md | 2 +- website/docs/module_kitsu.md | 2 +- website/docs/pype2/admin_ftrack.md | 2 +- website/docs/system_introduction.md | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/website/docs/admin_settings.md b/website/docs/admin_settings.md index dcbe740d0c..51bfdf7c33 100644 --- a/website/docs/admin_settings.md +++ b/website/docs/admin_settings.md @@ -7,7 +7,7 @@ sidebar_label: Working with settings import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -OpenPype stores all of it's settings and configuration in the mongo database. To make the configuration as easy as possible we provide a robust GUI where you can access and change everything that is configurable +OpenPype stores all of its settings and configuration in the mongo database. To make the configuration as easy as possible we provide a robust GUI where you can access and change everything that is configurable **Settings** GUI can be started from the tray menu *Admin -> Studio Settings*. diff --git a/website/docs/manager_ftrack.md b/website/docs/manager_ftrack.md index b5ca167838..836d84405e 100644 --- a/website/docs/manager_ftrack.md +++ b/website/docs/manager_ftrack.md @@ -4,7 +4,7 @@ title: Ftrack sidebar_label: Project Manager --- -Ftrack is currently the main project management option for OpenPype. This documentation assumes that you are familiar with Ftrack and it's basic principles. If you're new to Ftrack, we recommend having a thorough look at [Ftrack Official Documentation](https://help.ftrack.com/en/). +Ftrack is currently the main project management option for OpenPype. This documentation assumes that you are familiar with Ftrack and its basic principles. If you're new to Ftrack, we recommend having a thorough look at [Ftrack Official Documentation](https://help.ftrack.com/en/). ## Project management Setting project attributes is the key to properly working pipeline. diff --git a/website/docs/module_ftrack.md b/website/docs/module_ftrack.md index 6d5529b512..9111e4658c 100644 --- a/website/docs/module_ftrack.md +++ b/website/docs/module_ftrack.md @@ -8,7 +8,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -Ftrack is currently the main project management option for OpenPype. This documentation assumes that you are familiar with Ftrack and it's basic principles. If you're new to Ftrack, we recommend having a thorough look at [Ftrack Official Documentation](http://ftrack.rtd.ftrack.com/en/stable/). +Ftrack is currently the main project management option for OpenPype. This documentation assumes that you are familiar with Ftrack and its basic principles. If you're new to Ftrack, we recommend having a thorough look at [Ftrack Official Documentation](http://ftrack.rtd.ftrack.com/en/stable/). ## Prepare Ftrack for OpenPype diff --git a/website/docs/module_kitsu.md b/website/docs/module_kitsu.md index 0424939786..7be2a42c45 100644 --- a/website/docs/module_kitsu.md +++ b/website/docs/module_kitsu.md @@ -7,7 +7,7 @@ sidebar_label: Kitsu import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -Kitsu is a great open source production tracker and can be used for project management instead of Ftrack. This documentation assumes that you are familiar with Kitsu and it's basic principles. If you're new to Kitsu, we recommend having a thorough look at [Kitsu Official Documentation](https://kitsu.cg-wire.com/). +Kitsu is a great open source production tracker and can be used for project management instead of Ftrack. This documentation assumes that you are familiar with Kitsu and its basic principles. If you're new to Kitsu, we recommend having a thorough look at [Kitsu Official Documentation](https://kitsu.cg-wire.com/). ## Prepare Kitsu for OpenPype diff --git a/website/docs/pype2/admin_ftrack.md b/website/docs/pype2/admin_ftrack.md index a81147bece..4ebe1a7add 100644 --- a/website/docs/pype2/admin_ftrack.md +++ b/website/docs/pype2/admin_ftrack.md @@ -8,7 +8,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -Ftrack is currently the main project management option for Pype. This documentation assumes that you are familiar with Ftrack and it's basic principles. If you're new to Ftrack, we recommend having a thorough look at [Ftrack Official Documentation](http://ftrack.rtd.ftrack.com/en/stable/). +Ftrack is currently the main project management option for Pype. This documentation assumes that you are familiar with Ftrack and its basic principles. If you're new to Ftrack, we recommend having a thorough look at [Ftrack Official Documentation](http://ftrack.rtd.ftrack.com/en/stable/). ## Prepare Ftrack for Pype diff --git a/website/docs/system_introduction.md b/website/docs/system_introduction.md index 05627b5359..d64b023704 100644 --- a/website/docs/system_introduction.md +++ b/website/docs/system_introduction.md @@ -15,9 +15,9 @@ various usage scenarios. ## Studio Preparation -You can find detailed breakdown of technical requirements [here](dev_requirements), but in general OpenPype should be able +You can find a detailed breakdown of technical requirements [here](dev_requirements), but in general OpenPype should be able to operate in most studios fairly quickly. The main obstacles are usually related to workflows and habits, that -might not be fully compatible with what OpenPype is expecting or enforcing. It is recommended to go through artists [key concepts](artist_concepts) to get idea about basics. +might not be fully compatible with what OpenPype is expecting or enforcing. It is recommended to go through artists [key concepts](artist_concepts) to get comfortable with the basics. Keep in mind that if you run into any workflows that are not supported, it's usually just because we haven't hit that particular case and it can most likely be added upon request. From 5aae66794b4c98d3ef2c6db7c049799f7f0d9a6b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 13 Mar 2023 23:21:37 +0100 Subject: [PATCH 1258/1271] Highlight studio settings versus local settings more --- website/docs/admin_settings.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/website/docs/admin_settings.md b/website/docs/admin_settings.md index 51bfdf7c33..1128cc00c6 100644 --- a/website/docs/admin_settings.md +++ b/website/docs/admin_settings.md @@ -11,8 +11,11 @@ OpenPype stores all of its settings and configuration in the mongo database. To **Settings** GUI can be started from the tray menu *Admin -> Studio Settings*. -Please keep in mind that these settings are set-up for the full studio and not per-individual. If you're looking for individual artist settings, you can head to -[Local Settings](admin_settings_local.md) section in the artist documentation. +:::important Studio Settings versus Local Settings +Please keep in mind that these settings are set up for the full studio and not per-individual. If you're looking for individual artist settings, you can head to +[Local Settings](admin_settings_local.md) section in the documentation. +::: + ## Categories From 3f9f9bc6d1dc13a500cbd1b7cc76f0567b0fd854 Mon Sep 17 00:00:00 2001 From: moonyuet Date: Tue, 14 Mar 2023 10:10:16 +0100 Subject: [PATCH 1259/1271] fix the bug of removing an instance --- openpype/hosts/max/api/plugin.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/max/api/plugin.py b/openpype/hosts/max/api/plugin.py index c16d9e61ec..c7b9b64d29 100644 --- a/openpype/hosts/max/api/plugin.py +++ b/openpype/hosts/max/api/plugin.py @@ -101,7 +101,12 @@ class MaxCreator(Creator, MaxCreatorBase): instance_node = rt.getNodeByName( instance.data.get("instance_node")) if instance_node: - rt.delete(rt.getNodeByName(instance_node)) + rt.select(instance_node) + unparent_cmd = f""" + for o in selection do for c in o.children do c.parent = undefined + """ + rt.execute(unparent_cmd) + rt.delete(instance_node) self._remove_instance_from_context(instance) From 8199faedc0880b21e39fd9a03c77965d5f34c436 Mon Sep 17 00:00:00 2001 From: moonyuet Date: Tue, 14 Mar 2023 10:15:46 +0100 Subject: [PATCH 1260/1271] cosmetic issue fix --- openpype/hosts/max/api/plugin.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openpype/hosts/max/api/plugin.py b/openpype/hosts/max/api/plugin.py index c7b9b64d29..b54568b360 100644 --- a/openpype/hosts/max/api/plugin.py +++ b/openpype/hosts/max/api/plugin.py @@ -102,10 +102,7 @@ class MaxCreator(Creator, MaxCreatorBase): instance.data.get("instance_node")) if instance_node: rt.select(instance_node) - unparent_cmd = f""" - for o in selection do for c in o.children do c.parent = undefined - """ - rt.execute(unparent_cmd) + rt.execute(f'for o in selection do for c in o.children do c.parent = undefined') # noqa rt.delete(instance_node) self._remove_instance_from_context(instance) From 5b43ce940c990592d69e360cf880aa35ce6009d5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 14 Mar 2023 10:02:23 +0100 Subject: [PATCH 1261/1271] Maya: Avoid error on right click in Loader if Arnold `mtoa` is not loaded --- openpype/hosts/maya/plugins/load/load_arnold_standin.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_arnold_standin.py b/openpype/hosts/maya/plugins/load/load_arnold_standin.py index ab69d62ef5..11a2bd1966 100644 --- a/openpype/hosts/maya/plugins/load/load_arnold_standin.py +++ b/openpype/hosts/maya/plugins/load/load_arnold_standin.py @@ -2,7 +2,6 @@ import os import clique import maya.cmds as cmds -import mtoa.ui.arnoldmenu from openpype.settings import get_project_settings from openpype.pipeline import ( @@ -36,6 +35,11 @@ class ArnoldStandinLoader(load.LoaderPlugin): color = "orange" def load(self, context, name, namespace, options): + + # Make sure to load arnold before importing `mtoa.ui.arnoldmenu` + cmds.loadPlugin("mtoa", quiet=True) + import mtoa.ui.arnoldmenu + version = context['version'] version_data = version.get("data", {}) From 182bc4b5e866a8a756f020bf79617e38e749743d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 11 Mar 2023 20:20:18 +0100 Subject: [PATCH 1262/1271] Fix #4357: Move Look Assigner tool to maya since it's Maya only --- openpype/hosts/maya/api/customize.py | 3 +- openpype/hosts/maya/api/menu.py | 3 +- openpype/hosts/maya/tools/__init__.py | 26 +++++++++++++++ .../maya}/tools/mayalookassigner/LICENSE | 0 .../maya}/tools/mayalookassigner/__init__.py | 0 .../maya}/tools/mayalookassigner/app.py | 0 .../maya}/tools/mayalookassigner/commands.py | 0 .../maya}/tools/mayalookassigner/models.py | 0 .../maya}/tools/mayalookassigner/views.py | 0 .../tools/mayalookassigner/vray_proxies.py | 0 .../maya}/tools/mayalookassigner/widgets.py | 0 openpype/tools/utils/host_tools.py | 32 ------------------- 12 files changed, 30 insertions(+), 34 deletions(-) create mode 100644 openpype/hosts/maya/tools/__init__.py rename openpype/{ => hosts/maya}/tools/mayalookassigner/LICENSE (100%) rename openpype/{ => hosts/maya}/tools/mayalookassigner/__init__.py (100%) rename openpype/{ => hosts/maya}/tools/mayalookassigner/app.py (100%) rename openpype/{ => hosts/maya}/tools/mayalookassigner/commands.py (100%) rename openpype/{ => hosts/maya}/tools/mayalookassigner/models.py (100%) rename openpype/{ => hosts/maya}/tools/mayalookassigner/views.py (100%) rename openpype/{ => hosts/maya}/tools/mayalookassigner/vray_proxies.py (100%) rename openpype/{ => hosts/maya}/tools/mayalookassigner/widgets.py (100%) diff --git a/openpype/hosts/maya/api/customize.py b/openpype/hosts/maya/api/customize.py index f66858dfb6..f4c4d6ed88 100644 --- a/openpype/hosts/maya/api/customize.py +++ b/openpype/hosts/maya/api/customize.py @@ -11,6 +11,7 @@ import maya.mel as mel from openpype import resources from openpype.tools.utils import host_tools from .lib import get_main_window +from ..tools import show_look_assigner log = logging.getLogger(__name__) @@ -112,7 +113,7 @@ def override_toolbox_ui(): annotation="Look Manager", label="Look Manager", image=os.path.join(icons, "lookmanager.png"), - command=host_tools.show_look_assigner, + command=show_look_assigner, width=icon_size, height=icon_size, parent=parent diff --git a/openpype/hosts/maya/api/menu.py b/openpype/hosts/maya/api/menu.py index 0f48a133a6..efd9d414b6 100644 --- a/openpype/hosts/maya/api/menu.py +++ b/openpype/hosts/maya/api/menu.py @@ -12,6 +12,7 @@ from openpype.pipeline.workfile import BuildWorkfile from openpype.tools.utils import host_tools from openpype.hosts.maya.api import lib, lib_rendersettings from .lib import get_main_window, IS_HEADLESS +from ..tools import show_look_assigner from .workfile_template_builder import ( create_placeholder, @@ -139,7 +140,7 @@ def install(): cmds.menuItem( "Look assigner...", - command=lambda *args: host_tools.show_look_assigner( + command=lambda *args: show_look_assigner( parent_widget ) ) diff --git a/openpype/hosts/maya/tools/__init__.py b/openpype/hosts/maya/tools/__init__.py new file mode 100644 index 0000000000..a83bb36310 --- /dev/null +++ b/openpype/hosts/maya/tools/__init__.py @@ -0,0 +1,26 @@ +from ....tools.utils.host_tools import HostToolsHelper, qt_app_context + +class MayaToolsSingleton: + _look_assigner = None + + +def get_look_assigner_tool(parent): + """Create, cache and return look assigner tool window.""" + if self._look_assigner is None: + from .mayalookassigner import MayaLookAssignerWindow + mayalookassigner_window = MayaLookAssignerWindow(parent) + MayaToolsSingleton._look_assigner = mayalookassigner_window + return MayaToolsSingleton._look_assigner + + +def show_look_assigner(parent=None): + """Look manager is Maya specific tool for look management.""" + + with qt_app_context(): + look_assigner_tool = get_look_assigner_tool(parent) + look_assigner_tool.show() + + # Pull window to the front. + look_assigner_tool.raise_() + look_assigner_tool.activateWindow() + look_assigner_tool.showNormal() \ No newline at end of file diff --git a/openpype/tools/mayalookassigner/LICENSE b/openpype/hosts/maya/tools/mayalookassigner/LICENSE similarity index 100% rename from openpype/tools/mayalookassigner/LICENSE rename to openpype/hosts/maya/tools/mayalookassigner/LICENSE diff --git a/openpype/tools/mayalookassigner/__init__.py b/openpype/hosts/maya/tools/mayalookassigner/__init__.py similarity index 100% rename from openpype/tools/mayalookassigner/__init__.py rename to openpype/hosts/maya/tools/mayalookassigner/__init__.py diff --git a/openpype/tools/mayalookassigner/app.py b/openpype/hosts/maya/tools/mayalookassigner/app.py similarity index 100% rename from openpype/tools/mayalookassigner/app.py rename to openpype/hosts/maya/tools/mayalookassigner/app.py diff --git a/openpype/tools/mayalookassigner/commands.py b/openpype/hosts/maya/tools/mayalookassigner/commands.py similarity index 100% rename from openpype/tools/mayalookassigner/commands.py rename to openpype/hosts/maya/tools/mayalookassigner/commands.py diff --git a/openpype/tools/mayalookassigner/models.py b/openpype/hosts/maya/tools/mayalookassigner/models.py similarity index 100% rename from openpype/tools/mayalookassigner/models.py rename to openpype/hosts/maya/tools/mayalookassigner/models.py diff --git a/openpype/tools/mayalookassigner/views.py b/openpype/hosts/maya/tools/mayalookassigner/views.py similarity index 100% rename from openpype/tools/mayalookassigner/views.py rename to openpype/hosts/maya/tools/mayalookassigner/views.py diff --git a/openpype/tools/mayalookassigner/vray_proxies.py b/openpype/hosts/maya/tools/mayalookassigner/vray_proxies.py similarity index 100% rename from openpype/tools/mayalookassigner/vray_proxies.py rename to openpype/hosts/maya/tools/mayalookassigner/vray_proxies.py diff --git a/openpype/tools/mayalookassigner/widgets.py b/openpype/hosts/maya/tools/mayalookassigner/widgets.py similarity index 100% rename from openpype/tools/mayalookassigner/widgets.py rename to openpype/hosts/maya/tools/mayalookassigner/widgets.py diff --git a/openpype/tools/utils/host_tools.py b/openpype/tools/utils/host_tools.py index e8593a8ae2..ac242d24d2 100644 --- a/openpype/tools/utils/host_tools.py +++ b/openpype/tools/utils/host_tools.py @@ -38,7 +38,6 @@ class HostToolsHelper: self._subset_manager_tool = None self._scene_inventory_tool = None self._library_loader_tool = None - self._look_assigner_tool = None self._experimental_tools_dialog = None @property @@ -219,27 +218,6 @@ class HostToolsHelper: raise ImportError("No Pyblish GUI found") - def get_look_assigner_tool(self, parent): - """Create, cache and return look assigner tool window.""" - if self._look_assigner_tool is None: - from openpype.tools.mayalookassigner import MayaLookAssignerWindow - - mayalookassigner_window = MayaLookAssignerWindow(parent) - self._look_assigner_tool = mayalookassigner_window - return self._look_assigner_tool - - def show_look_assigner(self, parent=None): - """Look manager is Maya specific tool for look management.""" - - with qt_app_context(): - look_assigner_tool = self.get_look_assigner_tool(parent) - look_assigner_tool.show() - - # Pull window to the front. - look_assigner_tool.raise_() - look_assigner_tool.activateWindow() - look_assigner_tool.showNormal() - def get_experimental_tools_dialog(self, parent=None): """Dialog of experimental tools. @@ -315,9 +293,6 @@ class HostToolsHelper: elif tool_name == "sceneinventory": return self.get_scene_inventory_tool(parent, *args, **kwargs) - elif tool_name == "lookassigner": - return self.get_look_assigner_tool(parent, *args, **kwargs) - elif tool_name == "publish": self.log.info("Can't return publish tool window.") @@ -356,9 +331,6 @@ class HostToolsHelper: elif tool_name == "sceneinventory": self.show_scene_inventory(parent, *args, **kwargs) - elif tool_name == "lookassigner": - self.show_look_assigner(parent, *args, **kwargs) - elif tool_name == "publish": self.show_publish(parent, *args, **kwargs) @@ -436,10 +408,6 @@ def show_scene_inventory(parent=None): _SingletonPoint.show_tool_by_name("sceneinventory", parent) -def show_look_assigner(parent=None): - _SingletonPoint.show_tool_by_name("lookassigner", parent) - - def show_publish(parent=None): _SingletonPoint.show_tool_by_name("publish", parent) From 1d62d15c206515669f6fb6acce6da3a98d4a9657 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 11 Mar 2023 20:24:52 +0100 Subject: [PATCH 1263/1271] Shush hound --- openpype/hosts/maya/tools/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/tools/__init__.py b/openpype/hosts/maya/tools/__init__.py index a83bb36310..ccb014a3b9 100644 --- a/openpype/hosts/maya/tools/__init__.py +++ b/openpype/hosts/maya/tools/__init__.py @@ -1,4 +1,4 @@ -from ....tools.utils.host_tools import HostToolsHelper, qt_app_context +from ....tools.utils.host_tools import qt_app_context class MayaToolsSingleton: _look_assigner = None @@ -6,7 +6,7 @@ class MayaToolsSingleton: def get_look_assigner_tool(parent): """Create, cache and return look assigner tool window.""" - if self._look_assigner is None: + if MayaToolsSingleton._look_assigner is None: from .mayalookassigner import MayaLookAssignerWindow mayalookassigner_window = MayaLookAssignerWindow(parent) MayaToolsSingleton._look_assigner = mayalookassigner_window @@ -23,4 +23,4 @@ def show_look_assigner(parent=None): # Pull window to the front. look_assigner_tool.raise_() look_assigner_tool.activateWindow() - look_assigner_tool.showNormal() \ No newline at end of file + look_assigner_tool.showNormal() From 888dbdb569d8c9b658be578c846a7bd0727dd8c7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 13 Mar 2023 13:38:59 +0100 Subject: [PATCH 1264/1271] Relative import to absolute import Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/maya/tools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/tools/__init__.py b/openpype/hosts/maya/tools/__init__.py index ccb014a3b9..643c90bacd 100644 --- a/openpype/hosts/maya/tools/__init__.py +++ b/openpype/hosts/maya/tools/__init__.py @@ -1,4 +1,4 @@ -from ....tools.utils.host_tools import qt_app_context +from openpype.tools.utils.host_tools import qt_app_context class MayaToolsSingleton: _look_assigner = None From 11b1b98d25f9937c49b977e5e12c4b0491669ae8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 14 Mar 2023 09:56:42 +0100 Subject: [PATCH 1265/1271] Shush hound --- openpype/hosts/maya/tools/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/tools/__init__.py b/openpype/hosts/maya/tools/__init__.py index 643c90bacd..bd1e302cd2 100644 --- a/openpype/hosts/maya/tools/__init__.py +++ b/openpype/hosts/maya/tools/__init__.py @@ -1,5 +1,6 @@ from openpype.tools.utils.host_tools import qt_app_context + class MayaToolsSingleton: _look_assigner = None From ecfb57913d52325323d1c2fcb8b6519e03ff201b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 14 Mar 2023 16:05:40 +0100 Subject: [PATCH 1266/1271] update yarn.lock --- website/yarn.lock | 10884 ++++++++++++++++++++++---------------------- 1 file changed, 5476 insertions(+), 5408 deletions(-) diff --git a/website/yarn.lock b/website/yarn.lock index d250e48b9d..2edf57abf4 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -3,56 +3,56 @@ "@algolia/autocomplete-core@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.5.2.tgz#ec0178e07b44fd74a057728ac157291b26cecf37" - integrity sha512-DY0bhyczFSS1b/CqJlTE/nQRtnTAHl6IemIkBy0nEWnhDzRDdtdx4p5Uuk3vwAFxwEEgi1WqKwgSSMx6DpNL4A== + "integrity" "sha512-DY0bhyczFSS1b/CqJlTE/nQRtnTAHl6IemIkBy0nEWnhDzRDdtdx4p5Uuk3vwAFxwEEgi1WqKwgSSMx6DpNL4A==" + "resolved" "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.5.2.tgz" + "version" "1.5.2" dependencies: "@algolia/autocomplete-shared" "1.5.2" "@algolia/autocomplete-preset-algolia@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.5.2.tgz#36c5638cc6dba6ea46a86e5a0314637ca40a77ca" - integrity sha512-3MRYnYQFJyovANzSX2CToS6/5cfVjbLLqFsZTKcvF3abhQzxbqwwaMBlJtt620uBUOeMzhdfasKhCc40+RHiZw== + "integrity" "sha512-3MRYnYQFJyovANzSX2CToS6/5cfVjbLLqFsZTKcvF3abhQzxbqwwaMBlJtt620uBUOeMzhdfasKhCc40+RHiZw==" + "resolved" "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.5.2.tgz" + "version" "1.5.2" dependencies: "@algolia/autocomplete-shared" "1.5.2" "@algolia/autocomplete-shared@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.5.2.tgz#e157f9ad624ab8fd940ff28bd2094cdf199cdd79" - integrity sha512-ylQAYv5H0YKMfHgVWX0j0NmL8XBcAeeeVQUmppnnMtzDbDnca6CzhKj3Q8eF9cHCgcdTDdb5K+3aKyGWA0obug== + "integrity" "sha512-ylQAYv5H0YKMfHgVWX0j0NmL8XBcAeeeVQUmppnnMtzDbDnca6CzhKj3Q8eF9cHCgcdTDdb5K+3aKyGWA0obug==" + "resolved" "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.5.2.tgz" + "version" "1.5.2" "@algolia/cache-browser-local-storage@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.12.1.tgz#23f4f219963b96918d0524acd09d4d646541d888" - integrity sha512-ERFFOnC9740xAkuO0iZTQqm2AzU7Dpz/s+g7o48GlZgx5p9GgNcsuK5eS0GoW/tAK+fnKlizCtlFHNuIWuvfsg== + "integrity" "sha512-ERFFOnC9740xAkuO0iZTQqm2AzU7Dpz/s+g7o48GlZgx5p9GgNcsuK5eS0GoW/tAK+fnKlizCtlFHNuIWuvfsg==" + "resolved" "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/cache-common" "4.12.1" "@algolia/cache-common@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.12.1.tgz#d3f1676ca9c404adce0f78d68f6381bedb44cd9c" - integrity sha512-UugTER3V40jT+e19Dmph5PKMeliYKxycNPwrPNADin0RcWNfT2QksK9Ff2N2W7UKraqMOzoeDb4LAJtxcK1a8Q== + "integrity" "sha512-UugTER3V40jT+e19Dmph5PKMeliYKxycNPwrPNADin0RcWNfT2QksK9Ff2N2W7UKraqMOzoeDb4LAJtxcK1a8Q==" + "resolved" "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.12.1.tgz" + "version" "4.12.1" "@algolia/cache-in-memory@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.12.1.tgz#0ef6aac2f8feab5b46fc130beb682bbd21b55244" - integrity sha512-U6iaunaxK1lHsAf02UWF58foKFEcrVLsHwN56UkCtwn32nlP9rz52WOcHsgk6TJrL8NDcO5swMjtOQ5XHESFLw== + "integrity" "sha512-U6iaunaxK1lHsAf02UWF58foKFEcrVLsHwN56UkCtwn32nlP9rz52WOcHsgk6TJrL8NDcO5swMjtOQ5XHESFLw==" + "resolved" "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/cache-common" "4.12.1" "@algolia/client-account@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.12.1.tgz#e838c9283db2fab32a425dd13c77da321d48fd8b" - integrity sha512-jGo4ConJNoMdTCR2zouO0jO/JcJmzOK6crFxMMLvdnB1JhmMbuIKluOTJVlBWeivnmcsqb7r0v7qTCPW5PAyxQ== + "integrity" "sha512-jGo4ConJNoMdTCR2zouO0jO/JcJmzOK6crFxMMLvdnB1JhmMbuIKluOTJVlBWeivnmcsqb7r0v7qTCPW5PAyxQ==" + "resolved" "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/client-common" "4.12.1" "@algolia/client-search" "4.12.1" "@algolia/transporter" "4.12.1" "@algolia/client-analytics@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.12.1.tgz#2976d658655a1590cf84cfb596aa75a204f6dec4" - integrity sha512-h1It7KXzIthlhuhfBk7LteYq72tym9maQDUsyRW0Gft8b6ZQahnRak9gcCvKwhcJ1vJoP7T7JrNYGiYSicTD9g== + "integrity" "sha512-h1It7KXzIthlhuhfBk7LteYq72tym9maQDUsyRW0Gft8b6ZQahnRak9gcCvKwhcJ1vJoP7T7JrNYGiYSicTD9g==" + "resolved" "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/client-common" "4.12.1" "@algolia/client-search" "4.12.1" @@ -60,99 +60,121 @@ "@algolia/transporter" "4.12.1" "@algolia/client-common@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.12.1.tgz#104ccefe96bda3ff926bc70c31ff6d17c41b6107" - integrity sha512-obnJ8eSbv+h94Grk83DTGQ3bqhViSWureV6oK1s21/KMGWbb3DkduHm+lcwFrMFkjSUSzosLBHV9EQUIBvueTw== + "integrity" "sha512-obnJ8eSbv+h94Grk83DTGQ3bqhViSWureV6oK1s21/KMGWbb3DkduHm+lcwFrMFkjSUSzosLBHV9EQUIBvueTw==" + "resolved" "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/requester-common" "4.12.1" "@algolia/transporter" "4.12.1" "@algolia/client-personalization@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.12.1.tgz#f63d1890f95de850e1c8e41c1d57adda521d9e7f" - integrity sha512-sMSnjjPjRgByGHYygV+5L/E8a6RgU7l2GbpJukSzJ9GRY37tHmBHuvahv8JjdCGJ2p7QDYLnQy5bN5Z02qjc7Q== + "integrity" "sha512-sMSnjjPjRgByGHYygV+5L/E8a6RgU7l2GbpJukSzJ9GRY37tHmBHuvahv8JjdCGJ2p7QDYLnQy5bN5Z02qjc7Q==" + "resolved" "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/client-common" "4.12.1" "@algolia/requester-common" "4.12.1" "@algolia/transporter" "4.12.1" -"@algolia/client-search@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.12.1.tgz#fcd7a974be5d39d5c336d7f2e89577ffa66aefdd" - integrity sha512-MwwKKprfY6X2nJ5Ki/ccXM2GDEePvVjZnnoOB2io3dLKW4fTqeSRlC5DRXeFD7UM0vOPPHr4ItV2aj19APKNVQ== +"@algolia/client-search@^4.9.1", "@algolia/client-search@4.12.1": + "integrity" "sha512-MwwKKprfY6X2nJ5Ki/ccXM2GDEePvVjZnnoOB2io3dLKW4fTqeSRlC5DRXeFD7UM0vOPPHr4ItV2aj19APKNVQ==" + "resolved" "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/client-common" "4.12.1" "@algolia/requester-common" "4.12.1" "@algolia/transporter" "4.12.1" "@algolia/events@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@algolia/events/-/events-4.0.1.tgz#fd39e7477e7bc703d7f893b556f676c032af3950" - integrity sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ== + "integrity" "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + "resolved" "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz" + "version" "4.0.1" "@algolia/logger-common@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.12.1.tgz#d6501b4d9d242956257ba8e10f6b4bbf6863baa4" - integrity sha512-fCgrzlXGATNqdFTxwx0GsyPXK+Uqrx1SZ3iuY2VGPPqdt1a20clAG2n2OcLHJpvaa6vMFPlJyWvbqAgzxdxBlQ== + "integrity" "sha512-fCgrzlXGATNqdFTxwx0GsyPXK+Uqrx1SZ3iuY2VGPPqdt1a20clAG2n2OcLHJpvaa6vMFPlJyWvbqAgzxdxBlQ==" + "resolved" "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.12.1.tgz" + "version" "4.12.1" "@algolia/logger-console@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.12.1.tgz#841edd39dd5c5530a69fc66084bfee3254dd0807" - integrity sha512-0owaEnq/davngQMYqxLA4KrhWHiXujQ1CU3FFnyUcMyBR7rGHI48zSOUpqnsAXrMBdSH6rH5BDkSUUFwsh8RkQ== + "integrity" "sha512-0owaEnq/davngQMYqxLA4KrhWHiXujQ1CU3FFnyUcMyBR7rGHI48zSOUpqnsAXrMBdSH6rH5BDkSUUFwsh8RkQ==" + "resolved" "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/logger-common" "4.12.1" "@algolia/requester-browser-xhr@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.12.1.tgz#2d0c18ee188d7cae0e4a930e5e89989e3c4a816b" - integrity sha512-OaMxDyG0TZG0oqz1lQh9e3woantAG1bLnuwq3fmypsrQxra4IQZiyn1x+kEb69D2TcXApI5gOgrD4oWhtEVMtw== + "integrity" "sha512-OaMxDyG0TZG0oqz1lQh9e3woantAG1bLnuwq3fmypsrQxra4IQZiyn1x+kEb69D2TcXApI5gOgrD4oWhtEVMtw==" + "resolved" "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/requester-common" "4.12.1" "@algolia/requester-common@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.12.1.tgz#95bb6539da7199da3e205341cea8f27267f7af29" - integrity sha512-XWIrWQNJ1vIrSuL/bUk3ZwNMNxl+aWz6dNboRW6+lGTcMIwc3NBFE90ogbZKhNrFRff8zI4qCF15tjW+Fyhpow== + "integrity" "sha512-XWIrWQNJ1vIrSuL/bUk3ZwNMNxl+aWz6dNboRW6+lGTcMIwc3NBFE90ogbZKhNrFRff8zI4qCF15tjW+Fyhpow==" + "resolved" "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.12.1.tgz" + "version" "4.12.1" "@algolia/requester-node-http@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.12.1.tgz#c9df97ff1daa7e58c5c2b1f28cf7163005edccb0" - integrity sha512-awBtwaD+s0hxkA1aehYn8F0t9wqGoBVWgY4JPHBmp1ChO3pK7RKnnvnv7QQa9vTlllX29oPt/BBVgMo1Z3n1Qg== + "integrity" "sha512-awBtwaD+s0hxkA1aehYn8F0t9wqGoBVWgY4JPHBmp1ChO3pK7RKnnvnv7QQa9vTlllX29oPt/BBVgMo1Z3n1Qg==" + "resolved" "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/requester-common" "4.12.1" "@algolia/transporter@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.12.1.tgz#61b9829916c474f42e2d4a6eada0d6c138379945" - integrity sha512-BGeNgdEHc6dXIk2g8kdlOoQ6fQ6OIaKQcplEj7HPoi+XZUeAvRi3Pff3QWd7YmybWkjzd9AnTzieTASDWhL+sQ== + "integrity" "sha512-BGeNgdEHc6dXIk2g8kdlOoQ6fQ6OIaKQcplEj7HPoi+XZUeAvRi3Pff3QWd7YmybWkjzd9AnTzieTASDWhL+sQ==" + "resolved" "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/cache-common" "4.12.1" "@algolia/logger-common" "4.12.1" "@algolia/requester-common" "4.12.1" -"@ampproject/remapping@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" - integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== +"@ampproject/remapping@^2.2.0": + "integrity" "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==" + "resolved" "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz" + "version" "2.2.0" dependencies: - "@jridgewell/trace-mapping" "^0.3.0" + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.8.3": + "integrity" "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==" + "resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/highlight" "^7.16.7" + "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4", "@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.0.tgz#86850b8597ea6962089770952075dcaabb8dba34" - integrity sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.20.5": + "integrity" "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==" + "resolved" "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz" + "version" "7.21.0" -"@babel/core@7.12.9": - version "7.12.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" - integrity sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ== +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.15.5", "@babel/core@^7.16.0", "@babel/core@^7.4.0-0": + "integrity" "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==" + "resolved" "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz" + "version" "7.21.3" + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.21.3" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.3" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.3" + "@babel/types" "^7.21.3" + "convert-source-map" "^1.7.0" + "debug" "^4.1.0" + "gensync" "^1.0.0-beta.2" + "json5" "^2.2.2" + "semver" "^6.3.0" + +"@babel/core@^7.11.6", "@babel/core@7.12.9": + "integrity" "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==" + "resolved" "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz" + "version" "7.12.9" dependencies: "@babel/code-frame" "^7.10.4" "@babel/generator" "^7.12.5" @@ -162,74 +184,55 @@ "@babel/template" "^7.12.7" "@babel/traverse" "^7.12.9" "@babel/types" "^7.12.7" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" + "convert-source-map" "^1.7.0" + "debug" "^4.1.0" + "gensync" "^1.0.0-beta.1" + "json5" "^2.1.2" + "lodash" "^4.17.19" + "resolve" "^1.3.2" + "semver" "^5.4.1" + "source-map" "^0.5.0" -"@babel/core@^7.15.5", "@babel/core@^7.16.0": - version "7.17.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.5.tgz#6cd2e836058c28f06a4ca8ee7ed955bbf37c8225" - integrity sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA== +"@babel/generator@^7.12.5", "@babel/generator@^7.16.0", "@babel/generator@^7.21.3": + "integrity" "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==" + "resolved" "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz" + "version" "7.21.3" dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.3" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-module-transforms" "^7.16.7" - "@babel/helpers" "^7.17.2" - "@babel/parser" "^7.17.3" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" - "@babel/types" "^7.17.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - -"@babel/generator@^7.12.5", "@babel/generator@^7.16.0", "@babel/generator@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.3.tgz#a2c30b0c4f89858cb87050c3ffdfd36bdf443200" - integrity sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg== - dependencies: - "@babel/types" "^7.17.0" - jsesc "^2.5.1" - source-map "^0.5.0" + "@babel/types" "^7.21.3" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + "jsesc" "^2.5.1" "@babel/helper-annotate-as-pure@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" - integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== + "integrity" "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==" + "resolved" "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/types" "^7.16.7" "@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" - integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== + "integrity" "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==" + "resolved" "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-explode-assignable-expression" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" - integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.20.7": + "integrity" "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz" + "version" "7.20.7" dependencies: - "@babel/compat-data" "^7.16.4" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.17.5" - semver "^6.3.0" + "@babel/compat-data" "^7.20.5" + "@babel/helper-validator-option" "^7.18.6" + "browserslist" "^4.21.3" + "lru-cache" "^5.1.1" + "semver" "^6.3.0" "@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7": - version "7.17.1" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz#9699f14a88833a7e055ce57dcd3ffdcd25186b21" - integrity sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ== + "integrity" "sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz" + "version" "7.17.1" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-environment-visitor" "^7.16.7" @@ -240,122 +243,112 @@ "@babel/helper-split-export-declaration" "^7.16.7" "@babel/helper-create-regexp-features-plugin@^7.16.7": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" - integrity sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA== + "integrity" "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==" + "resolved" "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz" + "version" "7.17.0" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" - regexpu-core "^5.0.1" + "regexpu-core" "^5.0.1" "@babel/helper-define-polyfill-provider@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" - integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== + "integrity" "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==" + "resolved" "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz" + "version" "0.3.1" dependencies: "@babel/helper-compilation-targets" "^7.13.0" "@babel/helper-module-imports" "^7.12.13" "@babel/helper-plugin-utils" "^7.13.0" "@babel/traverse" "^7.13.0" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" + "debug" "^4.1.1" + "lodash.debounce" "^4.0.8" + "resolve" "^1.14.2" + "semver" "^6.1.2" -"@babel/helper-environment-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" - integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== - dependencies: - "@babel/types" "^7.16.7" +"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.9": + "integrity" "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" + "resolved" "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" + "version" "7.18.9" "@babel/helper-explode-assignable-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" - integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== + "integrity" "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/types" "^7.16.7" -"@babel/helper-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" - integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== +"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.21.0": + "integrity" "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==" + "resolved" "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz" + "version" "7.21.0" dependencies: - "@babel/helper-get-function-arity" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" -"@babel/helper-get-function-arity@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" - integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== +"@babel/helper-hoist-variables@^7.16.7", "@babel/helper-hoist-variables@^7.18.6": + "integrity" "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==" + "resolved" "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-hoist-variables@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" - integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== - dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" "@babel/helper-member-expression-to-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" - integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== + "integrity" "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==" + "resolved" "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" - integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6": + "integrity" "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==" + "resolved" "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" - integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.7", "@babel/helper-module-transforms@^7.21.2": + "integrity" "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz" + "version" "7.21.2" dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" "@babel/helper-optimise-call-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" - integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== + "integrity" "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==" + "resolved" "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/types" "^7.16.7" -"@babel/helper-plugin-utils@7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" - integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== - "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" - integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== + "integrity" "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==" + "resolved" "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz" + "version" "7.16.7" + +"@babel/helper-plugin-utils@7.10.4": + "integrity" "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + "resolved" "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz" + "version" "7.10.4" "@babel/helper-remap-async-to-generator@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" - integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw== + "integrity" "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==" + "resolved" "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-wrap-function" "^7.16.8" "@babel/types" "^7.16.8" "@babel/helper-replace-supers@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" - integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== + "integrity" "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==" + "resolved" "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-environment-visitor" "^7.16.7" "@babel/helper-member-expression-to-functions" "^7.16.7" @@ -363,173 +356,169 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-simple-access@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" - integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== +"@babel/helper-simple-access@^7.16.7", "@babel/helper-simple-access@^7.20.2": + "integrity" "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==" + "resolved" "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz" + "version" "7.20.2" dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.20.2" "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" - integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== + "integrity" "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==" + "resolved" "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz" + "version" "7.16.0" dependencies: "@babel/types" "^7.16.0" -"@babel/helper-split-export-declaration@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" - integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== +"@babel/helper-split-export-declaration@^7.16.7", "@babel/helper-split-export-declaration@^7.18.6": + "integrity" "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==" + "resolved" "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-string-parser@^7.19.4": + "integrity" "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" + "resolved" "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz" + "version" "7.19.4" -"@babel/helper-validator-option@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" - integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== +"@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + "integrity" "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + "resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" + "version" "7.19.1" + +"@babel/helper-validator-option@^7.16.7", "@babel/helper-validator-option@^7.18.6": + "integrity" "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz" + "version" "7.21.0" "@babel/helper-wrap-function@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" - integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw== + "integrity" "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==" + "resolved" "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-function-name" "^7.16.7" "@babel/template" "^7.16.7" "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helpers@^7.12.5", "@babel/helpers@^7.17.2": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417" - integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ== +"@babel/helpers@^7.12.5", "@babel/helpers@^7.21.0": + "integrity" "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==" + "resolved" "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz" + "version" "7.21.0" dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.0" - "@babel/types" "^7.17.0" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" -"@babel/highlight@^7.16.7": - version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" - integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== +"@babel/highlight@^7.18.6": + "integrity" "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==" + "resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - chalk "^2.0.0" - js-tokens "^4.0.0" + "@babel/helper-validator-identifier" "^7.18.6" + "chalk" "^2.0.0" + "js-tokens" "^4.0.0" -"@babel/parser@^7.12.7", "@babel/parser@^7.16.4", "@babel/parser@^7.16.7", "@babel/parser@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.3.tgz#b07702b982990bf6fdc1da5049a23fece4c5c3d0" - integrity sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA== +"@babel/parser@^7.12.7", "@babel/parser@^7.16.4", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3": + "integrity" "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==" + "resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz" + "version" "7.21.3" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050" - integrity sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg== + "integrity" "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz#cc001234dfc139ac45f6bcf801866198c8c72ff9" - integrity sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw== + "integrity" "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-proposal-optional-chaining" "^7.16.7" "@babel/plugin-proposal-async-generator-functions@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8" - integrity sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ== + "integrity" "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-proposal-class-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" - integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== + "integrity" "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-create-class-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-proposal-class-static-block@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz#712357570b612106ef5426d13dc433ce0f200c2a" - integrity sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw== + "integrity" "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-create-class-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-dynamic-import@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" - integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg== + "integrity" "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-proposal-export-namespace-from@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz#09de09df18445a5786a305681423ae63507a6163" - integrity sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA== + "integrity" "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-proposal-json-strings@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz#9732cb1d17d9a2626a08c5be25186c195b6fa6e8" - integrity sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ== + "integrity" "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-proposal-logical-assignment-operators@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea" - integrity sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg== + "integrity" "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" - integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== + "integrity" "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-numeric-separator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" - integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw== + "integrity" "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" - integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-transform-parameters" "^7.12.1" - "@babel/plugin-proposal-object-rest-spread@^7.16.7": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" - integrity sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw== + "integrity" "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz" + "version" "7.17.3" dependencies: "@babel/compat-data" "^7.17.0" "@babel/helper-compilation-targets" "^7.16.7" @@ -537,35 +526,44 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.16.7" +"@babel/plugin-proposal-object-rest-spread@7.12.1": + "integrity" "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz" + "version" "7.12.1" + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-proposal-optional-catch-binding@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" - integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== + "integrity" "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" - integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== + "integrity" "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-proposal-private-methods@^7.16.11": - version "7.16.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz#e8df108288555ff259f4527dbe84813aac3a1c50" - integrity sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw== + "integrity" "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz" + "version" "7.16.11" dependencies: "@babel/helper-create-class-features-plugin" "^7.16.10" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-proposal-private-property-in-object@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz#b0b8cef543c2c3d57e59e2c611994861d46a3fce" - integrity sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ== + "integrity" "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-create-class-features-plugin" "^7.16.7" @@ -573,166 +571,166 @@ "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2" - integrity sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg== + "integrity" "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + "integrity" "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" + "version" "7.8.4" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + "integrity" "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" + "version" "7.12.13" dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + "integrity" "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz" + "version" "7.14.5" dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + "integrity" "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + "integrity" "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + "integrity" "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" - integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-jsx@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" - integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== + "integrity" "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-syntax-jsx@7.12.1": + "integrity" "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz" + "version" "7.12.1" + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + "integrity" "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" + "version" "7.10.4" dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + "integrity" "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + "integrity" "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" + "version" "7.10.4" dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-object-rest-spread@7.8.3", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== +"@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3", "@babel/plugin-syntax-object-rest-spread@7.8.3": + "integrity" "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + "integrity" "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + "integrity" "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + "integrity" "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz" + "version" "7.14.5" dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + "integrity" "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" + "version" "7.14.5" dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" - integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== + "integrity" "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-arrow-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" - integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== + "integrity" "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-async-to-generator@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808" - integrity sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg== + "integrity" "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-module-imports" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-transform-block-scoped-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" - integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== + "integrity" "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoping@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" - integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== + "integrity" "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-classes@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" - integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== + "integrity" "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-environment-visitor" "^7.16.7" @@ -741,174 +739,174 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" - globals "^11.1.0" + "globals" "^11.1.0" "@babel/plugin-transform-computed-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" - integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== + "integrity" "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-destructuring@^7.16.7": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz#c445f75819641788a27a0a3a759d9df911df6abc" - integrity sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg== + "integrity" "sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz" + "version" "7.17.3" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" - integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== + "integrity" "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-duplicate-keys@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz#2207e9ca8f82a0d36a5a67b6536e7ef8b08823c9" - integrity sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw== + "integrity" "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-exponentiation-operator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" - integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== + "integrity" "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-for-of@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" - integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== + "integrity" "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" - integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== + "integrity" "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-compilation-targets" "^7.16.7" "@babel/helper-function-name" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" - integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== + "integrity" "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-member-expression-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" - integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== + "integrity" "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-modules-amd@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz#b28d323016a7daaae8609781d1f8c9da42b13186" - integrity sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g== + "integrity" "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" + "babel-plugin-dynamic-import-node" "^2.3.3" "@babel/plugin-transform-modules-commonjs@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" - integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== + "integrity" "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-simple-access" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" + "babel-plugin-dynamic-import-node" "^2.3.3" "@babel/plugin-transform-modules-systemjs@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz#887cefaef88e684d29558c2b13ee0563e287c2d7" - integrity sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw== + "integrity" "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-hoist-variables" "^7.16.7" "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-validator-identifier" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" + "babel-plugin-dynamic-import-node" "^2.3.3" "@babel/plugin-transform-modules-umd@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz#23dad479fa585283dbd22215bff12719171e7618" - integrity sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ== + "integrity" "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-named-capturing-groups-regex@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz#7f860e0e40d844a02c9dcf9d84965e7dfd666252" - integrity sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw== + "integrity" "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/plugin-transform-new-target@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz#9967d89a5c243818e0800fdad89db22c5f514244" - integrity sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg== + "integrity" "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-object-super@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" - integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== + "integrity" "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" "@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" - integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== + "integrity" "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-property-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" - integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== + "integrity" "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-constant-elements@^7.14.5": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.16.7.tgz#19e9e4c2df2f6c3e6b3aea11778297d81db8df62" - integrity sha512-lF+cfsyTgwWkcw715J88JhMYJ5GpysYNLhLP1PkvkhTRN7B3e74R/1KsDxFxhRpSn0UUD3IWM4GvdBR2PEbbQQ== + "integrity" "sha512-lF+cfsyTgwWkcw715J88JhMYJ5GpysYNLhLP1PkvkhTRN7B3e74R/1KsDxFxhRpSn0UUD3IWM4GvdBR2PEbbQQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-display-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" - integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== + "integrity" "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx-development@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8" - integrity sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A== + "integrity" "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/plugin-transform-react-jsx" "^7.16.7" "@babel/plugin-transform-react-jsx@^7.16.7": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1" - integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ== + "integrity" "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz" + "version" "7.17.3" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-module-imports" "^7.16.7" @@ -917,103 +915,103 @@ "@babel/types" "^7.17.0" "@babel/plugin-transform-react-pure-annotations@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz#232bfd2f12eb551d6d7d01d13fe3f86b45eb9c67" - integrity sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA== + "integrity" "sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-regenerator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" - integrity sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q== + "integrity" "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz" + "version" "7.16.7" dependencies: - regenerator-transform "^0.14.2" + "regenerator-transform" "^0.14.2" "@babel/plugin-transform-reserved-words@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz#1d798e078f7c5958eec952059c460b220a63f586" - integrity sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg== + "integrity" "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-runtime@^7.16.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz#0a2e08b5e2b2d95c4b1d3b3371a2180617455b70" - integrity sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A== + "integrity" "sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz" + "version" "7.17.0" dependencies: "@babel/helper-module-imports" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" - semver "^6.3.0" + "babel-plugin-polyfill-corejs2" "^0.3.0" + "babel-plugin-polyfill-corejs3" "^0.5.0" + "babel-plugin-polyfill-regenerator" "^0.3.0" + "semver" "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" - integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== + "integrity" "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-spread@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" - integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== + "integrity" "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-transform-sticky-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" - integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== + "integrity" "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-template-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" - integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== + "integrity" "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-typeof-symbol@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz#9cdbe622582c21368bd482b660ba87d5545d4f7e" - integrity sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ== + "integrity" "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-typescript@^7.16.7": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" - integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== + "integrity" "sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-create-class-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-typescript" "^7.16.7" "@babel/plugin-transform-unicode-escapes@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" - integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q== + "integrity" "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-unicode-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" - integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== + "integrity" "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/preset-env@^7.15.6", "@babel/preset-env@^7.16.4": - version "7.16.11" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.16.11.tgz#5dd88fd885fae36f88fd7c8342475c9f0abe2982" - integrity sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g== + "integrity" "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==" + "resolved" "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz" + "version" "7.16.11" dependencies: "@babel/compat-data" "^7.16.8" "@babel/helper-compilation-targets" "^7.16.7" @@ -1084,27 +1082,27 @@ "@babel/plugin-transform-unicode-regex" "^7.16.7" "@babel/preset-modules" "^0.1.5" "@babel/types" "^7.16.8" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" - core-js-compat "^3.20.2" - semver "^6.3.0" + "babel-plugin-polyfill-corejs2" "^0.3.0" + "babel-plugin-polyfill-corejs3" "^0.5.0" + "babel-plugin-polyfill-regenerator" "^0.3.0" + "core-js-compat" "^3.20.2" + "semver" "^6.3.0" "@babel/preset-modules@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" - integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + "integrity" "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==" + "resolved" "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz" + "version" "0.1.5" dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" "@babel/plugin-transform-dotall-regex" "^7.4.4" "@babel/types" "^7.4.4" - esutils "^2.0.2" + "esutils" "^2.0.2" "@babel/preset-react@^7.14.5", "@babel/preset-react@^7.16.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.16.7.tgz#4c18150491edc69c183ff818f9f2aecbe5d93852" - integrity sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA== + "integrity" "sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA==" + "resolved" "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-validator-option" "^7.16.7" @@ -1114,81 +1112,82 @@ "@babel/plugin-transform-react-pure-annotations" "^7.16.7" "@babel/preset-typescript@^7.15.0", "@babel/preset-typescript@^7.16.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" - integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== + "integrity" "sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ==" + "resolved" "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.16.7" "@babel/runtime-corejs3@^7.16.3": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz#fdca2cd05fba63388babe85d349b6801b008fd13" - integrity sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg== + "integrity" "sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==" + "resolved" "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz" + "version" "7.17.2" dependencies: - core-js-pure "^3.20.2" - regenerator-runtime "^0.13.4" + "core-js-pure" "^3.20.2" + "regenerator-runtime" "^0.13.4" "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.16.3", "@babel/runtime@^7.8.4": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941" - integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw== + "integrity" "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==" + "resolved" "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz" + "version" "7.17.2" dependencies: - regenerator-runtime "^0.13.4" + "regenerator-runtime" "^0.13.4" -"@babel/template@^7.12.7", "@babel/template@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== +"@babel/template@^7.12.7", "@babel/template@^7.16.7", "@babel/template@^7.20.7": + "integrity" "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==" + "resolved" "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" + "version" "7.20.7" dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" -"@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.3", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.0", "@babel/traverse@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" - integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== +"@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.3", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3": + "integrity" "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==" + "resolved" "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz" + "version" "7.21.3" dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.3" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.3" - "@babel/types" "^7.17.0" - debug "^4.1.0" - globals "^11.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.21.3" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.3" + "@babel/types" "^7.21.3" + "debug" "^4.1.0" + "globals" "^11.1.0" -"@babel/types@^7.12.7", "@babel/types@^7.15.6", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.4.4": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" - integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== +"@babel/types@^7.12.7", "@babel/types@^7.15.6", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.6", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.4.4": + "integrity" "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==" + "resolved" "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz" + "version" "7.21.3" dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - to-fast-properties "^2.0.0" + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + "to-fast-properties" "^2.0.0" "@docsearch/css@3.0.0-alpha.50": - version "3.0.0-alpha.50" - resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.0.0-alpha.50.tgz#794c6a8d301840a49b55f5b331c7be84b9723643" - integrity sha512-QeWFCQOtS9D+Fi20liKsPXF2j/xWKh52e+P2Z1UATIdPMqmH6zoB2lcUz+cgv6PPVgWUtECeR6VSSUm71LT94w== + "integrity" "sha512-QeWFCQOtS9D+Fi20liKsPXF2j/xWKh52e+P2Z1UATIdPMqmH6zoB2lcUz+cgv6PPVgWUtECeR6VSSUm71LT94w==" + "resolved" "https://registry.npmjs.org/@docsearch/css/-/css-3.0.0-alpha.50.tgz" + "version" "3.0.0-alpha.50" "@docsearch/react@^3.0.0-alpha.39": - version "3.0.0-alpha.50" - resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.0.0-alpha.50.tgz#a7dc547836c2b221fd3aa8eb87bfb47a579ef141" - integrity sha512-oDGV1zZCRYv7MWsh6CyQVthYTRc3b4q+6kKwNYb1/g/Wf/4nJHutpxolFLHdEUDhrJ4Xi8wxwQG+lEwAVBTHPg== + "integrity" "sha512-oDGV1zZCRYv7MWsh6CyQVthYTRc3b4q+6kKwNYb1/g/Wf/4nJHutpxolFLHdEUDhrJ4Xi8wxwQG+lEwAVBTHPg==" + "resolved" "https://registry.npmjs.org/@docsearch/react/-/react-3.0.0-alpha.50.tgz" + "version" "3.0.0-alpha.50" dependencies: "@algolia/autocomplete-core" "1.5.2" "@algolia/autocomplete-preset-algolia" "1.5.2" "@docsearch/css" "3.0.0-alpha.50" - algoliasearch "^4.0.0" + "algoliasearch" "^4.0.0" "@docusaurus/core@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.0.0-beta.15.tgz#1a3f8361803767072e56c77d60332c87e59f1ad0" - integrity sha512-zXhhD0fApMSvq/9Pkm9DQxa//hGOXVCq9yMHiXOkI5D1tLec7PxtnaC5cLfGHljkN9cKIfRDYUVcG1gHymVfpA== + "integrity" "sha512-zXhhD0fApMSvq/9Pkm9DQxa//hGOXVCq9yMHiXOkI5D1tLec7PxtnaC5cLfGHljkN9cKIfRDYUVcG1gHymVfpA==" + "resolved" "https://registry.npmjs.org/@docusaurus/core/-/core-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@babel/core" "^7.16.0" "@babel/generator" "^7.16.0" @@ -1209,103 +1208,103 @@ "@docusaurus/utils-validation" "2.0.0-beta.15" "@slorber/static-site-generator-webpack-plugin" "^4.0.0" "@svgr/webpack" "^6.0.0" - autoprefixer "^10.3.5" - babel-loader "^8.2.2" - babel-plugin-dynamic-import-node "2.3.0" - boxen "^5.0.1" - chokidar "^3.5.2" - clean-css "^5.1.5" - commander "^5.1.0" - copy-webpack-plugin "^10.2.0" - core-js "^3.18.0" - css-loader "^6.5.1" - css-minimizer-webpack-plugin "^3.3.1" - cssnano "^5.0.8" - del "^6.0.0" - detect-port "^1.3.0" - escape-html "^1.0.3" - eta "^1.12.3" - file-loader "^6.2.0" - fs-extra "^10.0.0" - html-minifier-terser "^6.0.2" - html-tags "^3.1.0" - html-webpack-plugin "^5.4.0" - import-fresh "^3.3.0" - is-root "^2.1.0" - leven "^3.1.0" - lodash "^4.17.20" - mini-css-extract-plugin "^1.6.0" - nprogress "^0.2.0" - postcss "^8.3.7" - postcss-loader "^6.1.1" - prompts "^2.4.1" - react-dev-utils "^12.0.0" - react-helmet "^6.1.0" - react-loadable "npm:@docusaurus/react-loadable@5.5.2" - react-loadable-ssr-addon-v5-slorber "^1.0.1" - react-router "^5.2.0" - react-router-config "^5.1.1" - react-router-dom "^5.2.0" - remark-admonitions "^1.2.1" - rtl-detect "^1.0.4" - semver "^7.3.4" - serve-handler "^6.1.3" - shelljs "^0.8.4" - strip-ansi "^6.0.0" - terser-webpack-plugin "^5.2.4" - tslib "^2.3.1" - update-notifier "^5.1.0" - url-loader "^4.1.1" - wait-on "^6.0.0" - webpack "^5.61.0" - webpack-bundle-analyzer "^4.4.2" - webpack-dev-server "^4.7.1" - webpack-merge "^5.8.0" - webpackbar "^5.0.2" + "autoprefixer" "^10.3.5" + "babel-loader" "^8.2.2" + "babel-plugin-dynamic-import-node" "2.3.0" + "boxen" "^5.0.1" + "chokidar" "^3.5.2" + "clean-css" "^5.1.5" + "commander" "^5.1.0" + "copy-webpack-plugin" "^10.2.0" + "core-js" "^3.18.0" + "css-loader" "^6.5.1" + "css-minimizer-webpack-plugin" "^3.3.1" + "cssnano" "^5.0.8" + "del" "^6.0.0" + "detect-port" "^1.3.0" + "escape-html" "^1.0.3" + "eta" "^1.12.3" + "file-loader" "^6.2.0" + "fs-extra" "^10.0.0" + "html-minifier-terser" "^6.0.2" + "html-tags" "^3.1.0" + "html-webpack-plugin" "^5.4.0" + "import-fresh" "^3.3.0" + "is-root" "^2.1.0" + "leven" "^3.1.0" + "lodash" "^4.17.20" + "mini-css-extract-plugin" "^1.6.0" + "nprogress" "^0.2.0" + "postcss" "^8.3.7" + "postcss-loader" "^6.1.1" + "prompts" "^2.4.1" + "react-dev-utils" "^12.0.0" + "react-helmet" "^6.1.0" + "react-loadable" "npm:@docusaurus/react-loadable@5.5.2" + "react-loadable-ssr-addon-v5-slorber" "^1.0.1" + "react-router" "^5.2.0" + "react-router-config" "^5.1.1" + "react-router-dom" "^5.2.0" + "remark-admonitions" "^1.2.1" + "rtl-detect" "^1.0.4" + "semver" "^7.3.4" + "serve-handler" "^6.1.3" + "shelljs" "^0.8.4" + "strip-ansi" "^6.0.0" + "terser-webpack-plugin" "^5.2.4" + "tslib" "^2.3.1" + "update-notifier" "^5.1.0" + "url-loader" "^4.1.1" + "wait-on" "^6.0.0" + "webpack" "^5.61.0" + "webpack-bundle-analyzer" "^4.4.2" + "webpack-dev-server" "^4.7.1" + "webpack-merge" "^5.8.0" + "webpackbar" "^5.0.2" "@docusaurus/cssnano-preset@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-beta.15.tgz#033c52815c428f0f66c87eaff93ea12554ea89df" - integrity sha512-55aYURbB5dqrx64lStNcZxDx5R6bKkAawlCB7mDKx3r+Qnp3ofGW7UExLQSCbTu3axT1vJCF5D7H6ljTRYJLtA== + "integrity" "sha512-55aYURbB5dqrx64lStNcZxDx5R6bKkAawlCB7mDKx3r+Qnp3ofGW7UExLQSCbTu3axT1vJCF5D7H6ljTRYJLtA==" + "resolved" "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: - cssnano-preset-advanced "^5.1.4" - postcss "^8.3.7" - postcss-sort-media-queries "^4.1.0" + "cssnano-preset-advanced" "^5.1.4" + "postcss" "^8.3.7" + "postcss-sort-media-queries" "^4.1.0" "@docusaurus/logger@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.0.0-beta.15.tgz#6d17a05fb292d15fdc43b5fa90fd2a49ad5d40ce" - integrity sha512-5bDSHCyLfMtz6QnFfICdL5mgxbGfC7DW1V+/Q17nRdpZSPZgsNKK/Esp0zdDi1oxAyEpXMXx64nLaHL7joJxIg== + "integrity" "sha512-5bDSHCyLfMtz6QnFfICdL5mgxbGfC7DW1V+/Q17nRdpZSPZgsNKK/Esp0zdDi1oxAyEpXMXx64nLaHL7joJxIg==" + "resolved" "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: - chalk "^4.1.2" - tslib "^2.3.1" + "chalk" "^4.1.2" + "tslib" "^2.3.1" "@docusaurus/mdx-loader@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-beta.15.tgz#da23745bc73c93338dd330dad6bbc9d9fe325553" - integrity sha512-MVpytjDDao7hmPF1QSs9B5zoTgevZjiqjnX3FM1yjqdCv+chyUo0gnmYHjeG/4Gqu7jucp+dDdp6yQpzs4g09A== + "integrity" "sha512-MVpytjDDao7hmPF1QSs9B5zoTgevZjiqjnX3FM1yjqdCv+chyUo0gnmYHjeG/4Gqu7jucp+dDdp6yQpzs4g09A==" + "resolved" "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@babel/parser" "^7.16.4" "@babel/traverse" "^7.16.3" "@docusaurus/logger" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" "@mdx-js/mdx" "^1.6.21" - escape-html "^1.0.3" - file-loader "^6.2.0" - fs-extra "^10.0.0" - image-size "^1.0.1" - mdast-util-to-string "^2.0.0" - remark-emoji "^2.1.0" - stringify-object "^3.3.0" - tslib "^2.3.1" - unist-util-visit "^2.0.2" - url-loader "^4.1.1" - webpack "^5.61.0" + "escape-html" "^1.0.3" + "file-loader" "^6.2.0" + "fs-extra" "^10.0.0" + "image-size" "^1.0.1" + "mdast-util-to-string" "^2.0.0" + "remark-emoji" "^2.1.0" + "stringify-object" "^3.3.0" + "tslib" "^2.3.1" + "unist-util-visit" "^2.0.2" + "url-loader" "^4.1.1" + "webpack" "^5.61.0" "@docusaurus/plugin-content-blog@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-beta.15.tgz#6d4bf532ad3dedb4f9fd6398b0fbe481af5b77a9" - integrity sha512-VtEwkgkoNIS8JFPe+huBeBuJ8HG8Lq1JNYM/ItwQg/cwGAgP8EgwbEuKDn428oZKEI2PpgAuf5Gv4AzJWIes9A== + "integrity" "sha512-VtEwkgkoNIS8JFPe+huBeBuJ8HG8Lq1JNYM/ItwQg/cwGAgP8EgwbEuKDn428oZKEI2PpgAuf5Gv4AzJWIes9A==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/logger" "2.0.0-beta.15" @@ -1313,98 +1312,98 @@ "@docusaurus/utils" "2.0.0-beta.15" "@docusaurus/utils-common" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - cheerio "^1.0.0-rc.10" - feed "^4.2.2" - fs-extra "^10.0.0" - lodash "^4.17.20" - reading-time "^1.5.0" - remark-admonitions "^1.2.1" - tslib "^2.3.1" - utility-types "^3.10.0" - webpack "^5.61.0" + "cheerio" "^1.0.0-rc.10" + "feed" "^4.2.2" + "fs-extra" "^10.0.0" + "lodash" "^4.17.20" + "reading-time" "^1.5.0" + "remark-admonitions" "^1.2.1" + "tslib" "^2.3.1" + "utility-types" "^3.10.0" + "webpack" "^5.61.0" "@docusaurus/plugin-content-docs@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-beta.15.tgz#9486bba8abd2a6284e749718bf56743d8e4446f1" - integrity sha512-HSwNZdUKz4rpJiGbFjl/OFhSleeZUSZ6E6lk98i4iL1A5u6fIm4CHsT53yp4UUOse+lFrePTFZsyqwMA4nZZYA== + "integrity" "sha512-HSwNZdUKz4rpJiGbFjl/OFhSleeZUSZ6E6lk98i4iL1A5u6fIm4CHsT53yp4UUOse+lFrePTFZsyqwMA4nZZYA==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/logger" "2.0.0-beta.15" "@docusaurus/mdx-loader" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - combine-promises "^1.1.0" - fs-extra "^10.0.0" - import-fresh "^3.2.2" - js-yaml "^4.0.0" - lodash "^4.17.20" - remark-admonitions "^1.2.1" - shelljs "^0.8.4" - tslib "^2.3.1" - utility-types "^3.10.0" - webpack "^5.61.0" + "combine-promises" "^1.1.0" + "fs-extra" "^10.0.0" + "import-fresh" "^3.2.2" + "js-yaml" "^4.0.0" + "lodash" "^4.17.20" + "remark-admonitions" "^1.2.1" + "shelljs" "^0.8.4" + "tslib" "^2.3.1" + "utility-types" "^3.10.0" + "webpack" "^5.61.0" "@docusaurus/plugin-content-pages@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-beta.15.tgz#e488f7dcdd45cd1d46e8c2c5ff5275327a6a3c65" - integrity sha512-N7YhW5RiOY6J228z4lOoP//qX0Q48cRtxDONZ/Ohd9C5OI2vS6TD8iQuDqOIYHxH+BshjNSsKvbJ+SMIQDwysg== + "integrity" "sha512-N7YhW5RiOY6J228z4lOoP//qX0Q48cRtxDONZ/Ohd9C5OI2vS6TD8iQuDqOIYHxH+BshjNSsKvbJ+SMIQDwysg==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/mdx-loader" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - fs-extra "^10.0.0" - globby "^11.0.2" - remark-admonitions "^1.2.1" - tslib "^2.3.1" - webpack "^5.61.0" + "fs-extra" "^10.0.0" + "globby" "^11.0.2" + "remark-admonitions" "^1.2.1" + "tslib" "^2.3.1" + "webpack" "^5.61.0" "@docusaurus/plugin-debug@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-beta.15.tgz#b75d706d4f9fc4146f84015097bd837d1afb7c6b" - integrity sha512-Jth11jB/rVqPwCGdkVKSUWeXZPAr/NyPn+yeknTBk2LgQKBJ3YU5dNG0uyt0Ay+UYT01TkousPJkXhLuy4Qrsw== + "integrity" "sha512-Jth11jB/rVqPwCGdkVKSUWeXZPAr/NyPn+yeknTBk2LgQKBJ3YU5dNG0uyt0Ay+UYT01TkousPJkXhLuy4Qrsw==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" - fs-extra "^10.0.0" - react-json-view "^1.21.3" - tslib "^2.3.1" + "fs-extra" "^10.0.0" + "react-json-view" "^1.21.3" + "tslib" "^2.3.1" "@docusaurus/plugin-google-analytics@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.15.tgz#6ffebe76d9caac5383cfb78d2baa5883c9c2df6c" - integrity sha512-ELAnxNYiC2i7gfu/ViurNIdm1/DdnbEfVDmpffS9niQhOREM1U3jpxkz/ff1GIC6heOLyHTtini/CZBDoroVGw== + "integrity" "sha512-ELAnxNYiC2i7gfu/ViurNIdm1/DdnbEfVDmpffS9niQhOREM1U3jpxkz/ff1GIC6heOLyHTtini/CZBDoroVGw==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - tslib "^2.3.1" + "tslib" "^2.3.1" "@docusaurus/plugin-google-gtag@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-beta.15.tgz#4db3330d302653e8541dc3cb86a4dbfef0cc96f8" - integrity sha512-E5Rm3+dN7i3A9V5uq5sl9xTNA3aXsLwTZEA2SpOkY571dCpd+sfVvz1lR+KRY9Fy6ZHk8PqrNImgCWfIerRuZQ== + "integrity" "sha512-E5Rm3+dN7i3A9V5uq5sl9xTNA3aXsLwTZEA2SpOkY571dCpd+sfVvz1lR+KRY9Fy6ZHk8PqrNImgCWfIerRuZQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - tslib "^2.3.1" + "tslib" "^2.3.1" "@docusaurus/plugin-sitemap@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-beta.15.tgz#0cc083d9e76041897e81b4b82bcd0ccbfa65d6e5" - integrity sha512-PBjeQb2Qpe4uPdRefWL/eXCeYjrgNB/UArExYeUuP4wiY1dpw2unGNCvFUxv4hzJGmARoTLsnRkeYkUim809LQ== + "integrity" "sha512-PBjeQb2Qpe4uPdRefWL/eXCeYjrgNB/UArExYeUuP4wiY1dpw2unGNCvFUxv4hzJGmARoTLsnRkeYkUim809LQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" "@docusaurus/utils-common" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - fs-extra "^10.0.0" - sitemap "^7.0.0" - tslib "^2.3.1" + "fs-extra" "^10.0.0" + "sitemap" "^7.0.0" + "tslib" "^2.3.1" "@docusaurus/preset-classic@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.0.0-beta.15.tgz#13d2f3c4fa7c055af35541ae5e93453450efb208" - integrity sha512-3NZIXWTAzk+kOgiB8uAbD+FZv3VFR1qkU6+TW24DRenjRnXof3CkRuldhI1QI0hILm1fuJ319QRkakV8FFtXyA== + "integrity" "sha512-3NZIXWTAzk+kOgiB8uAbD+FZv3VFR1qkU6+TW24DRenjRnXof3CkRuldhI1QI0hILm1fuJ319QRkakV8FFtXyA==" + "resolved" "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/plugin-content-blog" "2.0.0-beta.15" @@ -1418,18 +1417,18 @@ "@docusaurus/theme-common" "2.0.0-beta.15" "@docusaurus/theme-search-algolia" "2.0.0-beta.15" -"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": - version "5.5.2" - resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" - integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== +"@docusaurus/react-loadable@5.5.2": + "integrity" "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz" + "version" "5.5.2" dependencies: "@types/react" "*" - prop-types "^15.6.2" + "prop-types" "^15.6.2" "@docusaurus/theme-classic@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.0.0-beta.15.tgz#35d04232f2d5fcb2007675339b0e6d0e8681be95" - integrity sha512-WwNRcQvMtQ7KDhOEHFKFHxXCdoZwLg66hT3vhqNIFMfGQuPzOP91MX5LUSo1QWHhlrD3H3Og+r7Ik/fy2bf5lQ== + "integrity" "sha512-WwNRcQvMtQ7KDhOEHFKFHxXCdoZwLg66hT3vhqNIFMfGQuPzOP91MX5LUSo1QWHhlrD3H3Og+r7Ik/fy2bf5lQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/plugin-content-blog" "2.0.0-beta.15" @@ -1441,33 +1440,33 @@ "@docusaurus/utils-common" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" "@mdx-js/react" "^1.6.21" - clsx "^1.1.1" - copy-text-to-clipboard "^3.0.1" - infima "0.2.0-alpha.37" - lodash "^4.17.20" - postcss "^8.3.7" - prism-react-renderer "^1.2.1" - prismjs "^1.23.0" - react-router-dom "^5.2.0" - rtlcss "^3.3.0" + "clsx" "^1.1.1" + "copy-text-to-clipboard" "^3.0.1" + "infima" "0.2.0-alpha.37" + "lodash" "^4.17.20" + "postcss" "^8.3.7" + "prism-react-renderer" "^1.2.1" + "prismjs" "^1.23.0" + "react-router-dom" "^5.2.0" + "rtlcss" "^3.3.0" "@docusaurus/theme-common@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.0.0-beta.15.tgz#5bd338d483e2c19d6d74d133572988241518398a" - integrity sha512-+pvarmzcyECE4nWxw+dCMKRIoes0NegrRuM9+nRsUrS/E5ywsF539kpupKIEqaMjq6AuM0CJtDoHxHHPNe0KaQ== + "integrity" "sha512-+pvarmzcyECE4nWxw+dCMKRIoes0NegrRuM9+nRsUrS/E5ywsF539kpupKIEqaMjq6AuM0CJtDoHxHHPNe0KaQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/plugin-content-blog" "2.0.0-beta.15" "@docusaurus/plugin-content-docs" "2.0.0-beta.15" "@docusaurus/plugin-content-pages" "2.0.0-beta.15" - clsx "^1.1.1" - parse-numeric-range "^1.3.0" - tslib "^2.3.1" - utility-types "^3.10.0" + "clsx" "^1.1.1" + "parse-numeric-range" "^1.3.0" + "tslib" "^2.3.1" + "utility-types" "^3.10.0" "@docusaurus/theme-search-algolia@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-beta.15.tgz#c3ad7fd8e27fcb3e072990031c08768c602cb9a4" - integrity sha512-XrrQKyjOPzmEuOcdsaAn1tzNJkNMA3PC86PwPZUaah0cYPpBGptcJYDlIW4VHIrCBfkQvhvmg/B3qKF6bMMi8g== + "integrity" "sha512-XrrQKyjOPzmEuOcdsaAn1tzNJkNMA3PC86PwPZUaah0cYPpBGptcJYDlIW4VHIrCBfkQvhvmg/B3qKF6bMMi8g==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docsearch/react" "^3.0.0-alpha.39" "@docusaurus/core" "2.0.0-beta.15" @@ -1476,268 +1475,268 @@ "@docusaurus/theme-translations" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - algoliasearch "^4.10.5" - algoliasearch-helper "^3.5.5" - clsx "^1.1.1" - eta "^1.12.3" - lodash "^4.17.20" - tslib "^2.3.1" - utility-types "^3.10.0" + "algoliasearch" "^4.10.5" + "algoliasearch-helper" "^3.5.5" + "clsx" "^1.1.1" + "eta" "^1.12.3" + "lodash" "^4.17.20" + "tslib" "^2.3.1" + "utility-types" "^3.10.0" "@docusaurus/theme-translations@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.0.0-beta.15.tgz#658397ab4c0d7784043e3cec52cef7ae09d2fb59" - integrity sha512-Lu2JDsnZaB2BcJe8Hpq5nrbS7+7bd09jT08b9vztQyvzR8PgzsthnzlLN4ilOeamRIuYJKo1pUGm0EsQBOP6Nw== + "integrity" "sha512-Lu2JDsnZaB2BcJe8Hpq5nrbS7+7bd09jT08b9vztQyvzR8PgzsthnzlLN4ilOeamRIuYJKo1pUGm0EsQBOP6Nw==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: - fs-extra "^10.0.0" - tslib "^2.3.1" + "fs-extra" "^10.0.0" + "tslib" "^2.3.1" "@docusaurus/utils-common@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.0.0-beta.15.tgz#5549b329fc750bd5e9f24952c9e3ff7cf1f63e08" - integrity sha512-kIGlSIvbE/oniUpUjI8GOkSpH8o4NXbYqAh9dqPn+TJ0KbEFY3fc80gzZQU+9SunCwJMJbIxIGevX9Ry+nackw== + "integrity" "sha512-kIGlSIvbE/oniUpUjI8GOkSpH8o4NXbYqAh9dqPn+TJ0KbEFY3fc80gzZQU+9SunCwJMJbIxIGevX9Ry+nackw==" + "resolved" "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: - tslib "^2.3.1" + "tslib" "^2.3.1" "@docusaurus/utils-validation@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.0.0-beta.15.tgz#c664bc021194db9254eb45e6b48cb7c2af269041" - integrity sha512-1oOVBCkRrsTXSYrBTsMdnj3a/R56zrx11rjF4xo0+dmm8C01Xw4msFtc3uA7VLX0HQvgHsk8xPzU5GERNdsNpg== + "integrity" "sha512-1oOVBCkRrsTXSYrBTsMdnj3a/R56zrx11rjF4xo0+dmm8C01Xw4msFtc3uA7VLX0HQvgHsk8xPzU5GERNdsNpg==" + "resolved" "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/logger" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" - joi "^17.4.2" - tslib "^2.3.1" + "joi" "^17.4.2" + "tslib" "^2.3.1" "@docusaurus/utils@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.0.0-beta.15.tgz#60868046700d5585cfa6ffc57c5f3fbed00b61fc" - integrity sha512-xkoPmFxCBkDqbZR4U3SE752OcXtWTGgZnc/pZWxItzb1IYRGNZHrzdIr7CnI7rppriuZzsyivDGiC4Ud9MWhkA== + "integrity" "sha512-xkoPmFxCBkDqbZR4U3SE752OcXtWTGgZnc/pZWxItzb1IYRGNZHrzdIr7CnI7rppriuZzsyivDGiC4Ud9MWhkA==" + "resolved" "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/logger" "2.0.0-beta.15" "@mdx-js/runtime" "^1.6.22" "@svgr/webpack" "^6.0.0" - file-loader "^6.2.0" - fs-extra "^10.0.0" - github-slugger "^1.4.0" - globby "^11.0.4" - gray-matter "^4.0.3" - js-yaml "^4.0.0" - lodash "^4.17.20" - micromatch "^4.0.4" - remark-mdx-remove-exports "^1.6.22" - remark-mdx-remove-imports "^1.6.22" - resolve-pathname "^3.0.0" - tslib "^2.3.1" - url-loader "^4.1.1" + "file-loader" "^6.2.0" + "fs-extra" "^10.0.0" + "github-slugger" "^1.4.0" + "globby" "^11.0.4" + "gray-matter" "^4.0.3" + "js-yaml" "^4.0.0" + "lodash" "^4.17.20" + "micromatch" "^4.0.4" + "remark-mdx-remove-exports" "^1.6.22" + "remark-mdx-remove-imports" "^1.6.22" + "resolve-pathname" "^3.0.0" + "tslib" "^2.3.1" + "url-loader" "^4.1.1" "@hapi/hoek@^9.0.0": - version "9.2.1" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" - integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== + "integrity" "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw==" + "resolved" "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz" + "version" "9.2.1" "@hapi/topo@^5.0.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" - integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + "integrity" "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==" + "resolved" "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz" + "version" "5.1.0" dependencies: "@hapi/hoek" "^9.0.0" -"@jridgewell/gen-mapping@^0.3.0": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== +"@jridgewell/gen-mapping@^0.1.0": + "integrity" "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==" + "resolved" "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz" + "version" "0.1.1" + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + "integrity" "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==" + "resolved" "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" + "version" "0.3.2" dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@3.1.0": + "integrity" "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + "resolved" "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" + "version" "3.1.0" -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + "integrity" "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + "resolved" "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + "version" "1.1.2" "@jridgewell/source-map@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" - integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + "integrity" "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==" + "resolved" "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz" + "version" "0.3.2" dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@1.4.14": + "integrity" "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" + "version" "1.4.14" -"@jridgewell/trace-mapping@^0.3.0": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" - integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + "integrity" "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==" + "resolved" "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz" + "version" "0.3.17" dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" - integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@mdx-js/mdx@1.6.22", "@mdx-js/mdx@^1.6.21": - version "1.6.22" - resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" - integrity sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA== +"@mdx-js/mdx@^1.6.21", "@mdx-js/mdx@1.6.22": + "integrity" "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==" + "resolved" "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz" + "version" "1.6.22" dependencies: "@babel/core" "7.12.9" "@babel/plugin-syntax-jsx" "7.12.1" "@babel/plugin-syntax-object-rest-spread" "7.8.3" "@mdx-js/util" "1.6.22" - babel-plugin-apply-mdx-type-prop "1.6.22" - babel-plugin-extract-import-names "1.6.22" - camelcase-css "2.0.1" - detab "2.0.4" - hast-util-raw "6.0.1" - lodash.uniq "4.5.0" - mdast-util-to-hast "10.0.1" - remark-footnotes "2.0.0" - remark-mdx "1.6.22" - remark-parse "8.0.3" - remark-squeeze-paragraphs "4.0.0" - style-to-object "0.3.0" - unified "9.2.0" - unist-builder "2.0.3" - unist-util-visit "2.0.3" + "babel-plugin-apply-mdx-type-prop" "1.6.22" + "babel-plugin-extract-import-names" "1.6.22" + "camelcase-css" "2.0.1" + "detab" "2.0.4" + "hast-util-raw" "6.0.1" + "lodash.uniq" "4.5.0" + "mdast-util-to-hast" "10.0.1" + "remark-footnotes" "2.0.0" + "remark-mdx" "1.6.22" + "remark-parse" "8.0.3" + "remark-squeeze-paragraphs" "4.0.0" + "style-to-object" "0.3.0" + "unified" "9.2.0" + "unist-builder" "2.0.3" + "unist-util-visit" "2.0.3" -"@mdx-js/react@1.6.22", "@mdx-js/react@^1.6.21": - version "1.6.22" - resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.22.tgz#ae09b4744fddc74714ee9f9d6f17a66e77c43573" - integrity sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg== +"@mdx-js/react@^1.6.21", "@mdx-js/react@1.6.22": + "integrity" "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==" + "resolved" "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz" + "version" "1.6.22" "@mdx-js/runtime@^1.6.22": - version "1.6.22" - resolved "https://registry.yarnpkg.com/@mdx-js/runtime/-/runtime-1.6.22.tgz#3edd388bf68a519ffa1aaf9c446b548165102345" - integrity sha512-p17spaO2+55VLCuxXA3LVHC4phRx60NR2XMdZ+qgVU1lKvEX4y88dmFNOzGDCPLJ03IZyKrJ/rPWWRiBrd9JrQ== + "integrity" "sha512-p17spaO2+55VLCuxXA3LVHC4phRx60NR2XMdZ+qgVU1lKvEX4y88dmFNOzGDCPLJ03IZyKrJ/rPWWRiBrd9JrQ==" + "resolved" "https://registry.npmjs.org/@mdx-js/runtime/-/runtime-1.6.22.tgz" + "version" "1.6.22" dependencies: "@mdx-js/mdx" "1.6.22" "@mdx-js/react" "1.6.22" - buble-jsx-only "^0.19.8" + "buble-jsx-only" "^0.19.8" "@mdx-js/util@1.6.22": - version "1.6.22" - resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" - integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== + "integrity" "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==" + "resolved" "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz" + "version" "1.6.22" "@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + "integrity" "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + "version" "2.1.5" dependencies: "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" + "run-parallel" "^1.1.9" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + "integrity" "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + "version" "2.0.5" "@nodelib/fs.walk@^1.2.3": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + "integrity" "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + "version" "1.2.8" dependencies: "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" + "fastq" "^1.6.0" "@polka/url@^1.0.0-next.20": - version "1.0.0-next.21" - resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" - integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== + "integrity" "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==" + "resolved" "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz" + "version" "1.0.0-next.21" "@sideway/address@^4.1.3": - version "4.1.3" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27" - integrity sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ== + "integrity" "sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ==" + "resolved" "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz" + "version" "4.1.3" dependencies: "@hapi/hoek" "^9.0.0" "@sideway/formula@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" - integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + "integrity" "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + "resolved" "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz" + "version" "3.0.1" "@sideway/pinpoint@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" - integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "integrity" "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + "resolved" "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz" + "version" "2.0.0" "@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + "integrity" "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + "resolved" "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" + "version" "0.14.0" "@slorber/static-site-generator-webpack-plugin@^4.0.0": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.1.tgz#0c8852146441aaa683693deaa5aee2f991d94841" - integrity sha512-PSv4RIVO1Y3kvHxjvqeVisk3E9XFoO04uwYBDWe217MFqKspplYswTuKLiJu0aLORQWzuQjfVsSlLPojwfYsLw== + "integrity" "sha512-PSv4RIVO1Y3kvHxjvqeVisk3E9XFoO04uwYBDWe217MFqKspplYswTuKLiJu0aLORQWzuQjfVsSlLPojwfYsLw==" + "resolved" "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.1.tgz" + "version" "4.0.1" dependencies: - bluebird "^3.7.1" - cheerio "^0.22.0" - eval "^0.1.4" - url "^0.11.0" - webpack-sources "^1.4.3" + "bluebird" "^3.7.1" + "cheerio" "^0.22.0" + "eval" "^0.1.4" + "url" "^0.11.0" + "webpack-sources" "^1.4.3" "@svgr/babel-plugin-add-jsx-attribute@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz#bd6d1ff32a31b82b601e73672a789cc41e84fe18" - integrity sha512-MdPdhdWLtQsjd29Wa4pABdhWbaRMACdM1h31BY+c6FghTZqNGT7pEYdBoaGeKtdTOBC/XNFQaKVj+r/Ei2ryWA== + "integrity" "sha512-MdPdhdWLtQsjd29Wa4pABdhWbaRMACdM1h31BY+c6FghTZqNGT7pEYdBoaGeKtdTOBC/XNFQaKVj+r/Ei2ryWA==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-remove-jsx-attribute@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.0.0.tgz#58654908beebfa069681a83332544b17e5237e89" - integrity sha512-aVdtfx9jlaaxc3unA6l+M9YRnKIZjOhQPthLKqmTXC8UVkBLDRGwPKo+r8n3VZN8B34+yVajzPTZ+ptTSuZZCw== + "integrity" "sha512-aVdtfx9jlaaxc3unA6l+M9YRnKIZjOhQPthLKqmTXC8UVkBLDRGwPKo+r8n3VZN8B34+yVajzPTZ+ptTSuZZCw==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-remove-jsx-empty-expression@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.0.0.tgz#d06dd6e8a8f603f92f9979bb9990a1f85a4f57ba" - integrity sha512-Ccj42ApsePD451AZJJf1QzTD1B/BOU392URJTeXFxSK709i0KUsGtbwyiqsKu7vsYxpTM0IA5clAKDyf9RCZyA== + "integrity" "sha512-Ccj42ApsePD451AZJJf1QzTD1B/BOU392URJTeXFxSK709i0KUsGtbwyiqsKu7vsYxpTM0IA5clAKDyf9RCZyA==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-replace-jsx-attribute-value@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.0.0.tgz#0b85837577b02c31c09c758a12932820f5245cee" - integrity sha512-88V26WGyt1Sfd1emBYmBJRWMmgarrExpKNVmI9vVozha4kqs6FzQJ/Kp5+EYli1apgX44518/0+t9+NU36lThQ== + "integrity" "sha512-88V26WGyt1Sfd1emBYmBJRWMmgarrExpKNVmI9vVozha4kqs6FzQJ/Kp5+EYli1apgX44518/0+t9+NU36lThQ==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-svg-dynamic-title@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.0.0.tgz#28236ec26f7ab9d486a487d36ae52d58ba15676f" - integrity sha512-F7YXNLfGze+xv0KMQxrl2vkNbI9kzT9oDK55/kUuymh1ACyXkMV+VZWX1zEhSTfEKh7VkHVZGmVtHg8eTZ6PRg== + "integrity" "sha512-F7YXNLfGze+xv0KMQxrl2vkNbI9kzT9oDK55/kUuymh1ACyXkMV+VZWX1zEhSTfEKh7VkHVZGmVtHg8eTZ6PRg==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-svg-em-dimensions@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.0.0.tgz#40267c5dea1b43c4f83a0eb6169e08b43d8bafce" - integrity sha512-+rghFXxdIqJNLQK08kwPBD3Z22/0b2tEZ9lKiL/yTfuyj1wW8HUXu4bo/XkogATIYuXSghVQOOCwURXzHGKyZA== + "integrity" "sha512-+rghFXxdIqJNLQK08kwPBD3Z22/0b2tEZ9lKiL/yTfuyj1wW8HUXu4bo/XkogATIYuXSghVQOOCwURXzHGKyZA==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-transform-react-native-svg@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.0.0.tgz#eb688d0a5f539e34d268d8a516e81f5d7fede7c9" - integrity sha512-VaphyHZ+xIKv5v0K0HCzyfAaLhPGJXSk2HkpYfXIOKb7DjLBv0soHDxNv6X0vr2titsxE7klb++u7iOf7TSrFQ== + "integrity" "sha512-VaphyHZ+xIKv5v0K0HCzyfAaLhPGJXSk2HkpYfXIOKb7DjLBv0soHDxNv6X0vr2titsxE7klb++u7iOf7TSrFQ==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-transform-svg-component@^6.2.0": - version "6.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.2.0.tgz#7ba61d9fc1fb42b0ba1a04e4630019fa7e993c4f" - integrity sha512-bhYIpsORb++wpsp91fymbFkf09Z/YEKR0DnFjxvN+8JHeCUD2unnh18jIMKnDJTWtvpTaGYPXELVe4OOzFI0xg== + "integrity" "sha512-bhYIpsORb++wpsp91fymbFkf09Z/YEKR0DnFjxvN+8JHeCUD2unnh18jIMKnDJTWtvpTaGYPXELVe4OOzFI0xg==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.2.0.tgz" + "version" "6.2.0" "@svgr/babel-preset@^6.2.0": - version "6.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.2.0.tgz#1d3ad8c7664253a4be8e4a0f0e6872f30d8af627" - integrity sha512-4WQNY0J71JIaL03DRn0vLiz87JXx0b9dYm2aA8XHlQJQoixMl4r/soYHm8dsaJZ3jWtkCiOYy48dp9izvXhDkQ== + "integrity" "sha512-4WQNY0J71JIaL03DRn0vLiz87JXx0b9dYm2aA8XHlQJQoixMl4r/soYHm8dsaJZ3jWtkCiOYy48dp9izvXhDkQ==" + "resolved" "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.2.0.tgz" + "version" "6.2.0" dependencies: "@svgr/babel-plugin-add-jsx-attribute" "^6.0.0" "@svgr/babel-plugin-remove-jsx-attribute" "^6.0.0" @@ -1748,46 +1747,46 @@ "@svgr/babel-plugin-transform-react-native-svg" "^6.0.0" "@svgr/babel-plugin-transform-svg-component" "^6.2.0" -"@svgr/core@^6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.2.1.tgz#195de807a9f27f9e0e0d678e01084b05c54fdf61" - integrity sha512-NWufjGI2WUyrg46mKuySfviEJ6IxHUOm/8a3Ph38VCWSp+83HBraCQrpEM3F3dB6LBs5x8OElS8h3C0oOJaJAA== +"@svgr/core@^6.0.0", "@svgr/core@^6.2.1": + "integrity" "sha512-NWufjGI2WUyrg46mKuySfviEJ6IxHUOm/8a3Ph38VCWSp+83HBraCQrpEM3F3dB6LBs5x8OElS8h3C0oOJaJAA==" + "resolved" "https://registry.npmjs.org/@svgr/core/-/core-6.2.1.tgz" + "version" "6.2.1" dependencies: "@svgr/plugin-jsx" "^6.2.1" - camelcase "^6.2.0" - cosmiconfig "^7.0.1" + "camelcase" "^6.2.0" + "cosmiconfig" "^7.0.1" "@svgr/hast-util-to-babel-ast@^6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.2.1.tgz#ae065567b74cbe745afae617053adf9a764bea25" - integrity sha512-pt7MMkQFDlWJVy9ULJ1h+hZBDGFfSCwlBNW1HkLnVi7jUhyEXUaGYWi1x6bM2IXuAR9l265khBT4Av4lPmaNLQ== + "integrity" "sha512-pt7MMkQFDlWJVy9ULJ1h+hZBDGFfSCwlBNW1HkLnVi7jUhyEXUaGYWi1x6bM2IXuAR9l265khBT4Av4lPmaNLQ==" + "resolved" "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.2.1.tgz" + "version" "6.2.1" dependencies: "@babel/types" "^7.15.6" - entities "^3.0.1" + "entities" "^3.0.1" "@svgr/plugin-jsx@^6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.2.1.tgz#5668f1d2aa18c2f1bb7a1fc9f682d3f9aed263bd" - integrity sha512-u+MpjTsLaKo6r3pHeeSVsh9hmGRag2L7VzApWIaS8imNguqoUwDq/u6U/NDmYs/KAsrmtBjOEaAAPbwNGXXp1g== + "integrity" "sha512-u+MpjTsLaKo6r3pHeeSVsh9hmGRag2L7VzApWIaS8imNguqoUwDq/u6U/NDmYs/KAsrmtBjOEaAAPbwNGXXp1g==" + "resolved" "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.2.1.tgz" + "version" "6.2.1" dependencies: "@babel/core" "^7.15.5" "@svgr/babel-preset" "^6.2.0" "@svgr/hast-util-to-babel-ast" "^6.2.1" - svg-parser "^2.0.2" + "svg-parser" "^2.0.2" "@svgr/plugin-svgo@^6.2.0": - version "6.2.0" - resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-6.2.0.tgz#4cbe6a33ccccdcae4e3b63ded64cc1cbe1faf48c" - integrity sha512-oDdMQONKOJEbuKwuy4Np6VdV6qoaLLvoY86hjvQEgU82Vx1MSWRyYms6Sl0f+NtqxLI/rDVufATbP/ev996k3Q== + "integrity" "sha512-oDdMQONKOJEbuKwuy4Np6VdV6qoaLLvoY86hjvQEgU82Vx1MSWRyYms6Sl0f+NtqxLI/rDVufATbP/ev996k3Q==" + "resolved" "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.2.0.tgz" + "version" "6.2.0" dependencies: - cosmiconfig "^7.0.1" - deepmerge "^4.2.2" - svgo "^2.5.0" + "cosmiconfig" "^7.0.1" + "deepmerge" "^4.2.2" + "svgo" "^2.5.0" "@svgr/webpack@^6.0.0": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.2.1.tgz#ef5d51c1b6be4e7537fb9f76b3f2b2e22b63c58d" - integrity sha512-h09ngMNd13hnePwgXa+Y5CgOjzlCvfWLHg+MBnydEedAnuLRzUHUJmGS3o2OsrhxTOOqEsPOFt5v/f6C5Qulcw== + "integrity" "sha512-h09ngMNd13hnePwgXa+Y5CgOjzlCvfWLHg+MBnydEedAnuLRzUHUJmGS3o2OsrhxTOOqEsPOFt5v/f6C5Qulcw==" + "resolved" "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.2.1.tgz" + "version" "6.2.1" dependencies: "@babel/core" "^7.15.5" "@babel/plugin-transform-react-constant-elements" "^7.14.5" @@ -1799,81 +1798,81 @@ "@svgr/plugin-svgo" "^6.2.0" "@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + "integrity" "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==" + "resolved" "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" + "version" "1.1.2" dependencies: - defer-to-connect "^1.0.1" + "defer-to-connect" "^1.0.1" "@trysound/sax@0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" - integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + "integrity" "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + "resolved" "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz" + "version" "0.2.0" "@types/body-parser@*": - version "1.19.2" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" - integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + "integrity" "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==" + "resolved" "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz" + "version" "1.19.2" dependencies: "@types/connect" "*" "@types/node" "*" "@types/bonjour@^3.5.9": - version "3.5.10" - resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" - integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== + "integrity" "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==" + "resolved" "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz" + "version" "3.5.10" dependencies: "@types/node" "*" "@types/connect-history-api-fallback@^1.3.5": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" - integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== + "integrity" "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==" + "resolved" "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz" + "version" "1.3.5" dependencies: "@types/express-serve-static-core" "*" "@types/node" "*" "@types/connect@*": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + "integrity" "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==" + "resolved" "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" + "version" "3.4.35" dependencies: "@types/node" "*" "@types/eslint-scope@^3.7.3": - version "3.7.3" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" - integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g== + "integrity" "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==" + "resolved" "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz" + "version" "3.7.3" dependencies: "@types/eslint" "*" "@types/estree" "*" "@types/eslint@*": - version "8.4.1" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.1.tgz#c48251553e8759db9e656de3efc846954ac32304" - integrity sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA== + "integrity" "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==" + "resolved" "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz" + "version" "8.4.1" dependencies: "@types/estree" "*" "@types/json-schema" "*" "@types/estree@*", "@types/estree@^0.0.51": - version "0.0.51" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" - integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + "integrity" "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + "resolved" "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz" + "version" "0.0.51" "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": - version "4.17.28" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" - integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== + "integrity" "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==" + "resolved" "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz" + "version" "4.17.28" dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" "@types/express@*", "@types/express@^4.17.13": - version "4.17.13" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" - integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + "integrity" "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==" + "resolved" "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz" + "version" "4.17.13" dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "^4.17.18" @@ -1881,172 +1880,172 @@ "@types/serve-static" "*" "@types/hast@^2.0.0": - version "2.3.4" - resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" - integrity sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g== + "integrity" "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==" + "resolved" "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz" + "version" "2.3.4" dependencies: "@types/unist" "*" "@types/html-minifier-terser@^6.0.0": - version "6.1.0" - resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" - integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + "integrity" "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + "resolved" "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + "version" "6.1.0" "@types/http-proxy@^1.17.8": - version "1.17.8" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.8.tgz#968c66903e7e42b483608030ee85800f22d03f55" - integrity sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA== + "integrity" "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==" + "resolved" "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz" + "version" "1.17.8" dependencies: "@types/node" "*" "@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": - version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" - integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + "integrity" "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" + "resolved" "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz" + "version" "7.0.9" "@types/mdast@^3.0.0": - version "3.0.10" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" - integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA== + "integrity" "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==" + "resolved" "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz" + "version" "3.0.10" dependencies: "@types/unist" "*" "@types/mime@^1": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" - integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + "integrity" "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + "resolved" "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz" + "version" "1.3.2" "@types/node@*", "@types/node@^17.0.5": - version "17.0.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074" - integrity sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA== + "integrity" "sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==" + "resolved" "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz" + "version" "17.0.18" "@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + "integrity" "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + "resolved" "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" + "version" "4.0.0" "@types/parse5@^5.0.0": - version "5.0.3" - resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" - integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + "integrity" "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" + "resolved" "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz" + "version" "5.0.3" "@types/prop-types@*": - version "15.7.4" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" - integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== + "integrity" "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==" + "resolved" "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz" + "version" "15.7.4" "@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + "integrity" "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "resolved" "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" + "version" "6.9.7" "@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + "integrity" "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "resolved" "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" + "version" "1.2.4" -"@types/react@*": - version "17.0.39" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce" - integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug== +"@types/react@*", "@types/react@>= 16.8.0 < 18.0.0": + "integrity" "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==" + "resolved" "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz" + "version" "17.0.39" dependencies: "@types/prop-types" "*" "@types/scheduler" "*" - csstype "^3.0.2" + "csstype" "^3.0.2" "@types/retry@^0.12.0": - version "0.12.1" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065" - integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g== + "integrity" "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" + "resolved" "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz" + "version" "0.12.1" "@types/sax@^1.2.1": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.4.tgz#8221affa7f4f3cb21abd22f244cfabfa63e6a69e" - integrity sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw== + "integrity" "sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==" + "resolved" "https://registry.npmjs.org/@types/sax/-/sax-1.2.4.tgz" + "version" "1.2.4" dependencies: "@types/node" "*" "@types/scheduler@*": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" - integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + "integrity" "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + "resolved" "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz" + "version" "0.16.2" "@types/serve-index@^1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" - integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== + "integrity" "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==" + "resolved" "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz" + "version" "1.9.1" dependencies: "@types/express" "*" "@types/serve-static@*": - version "1.13.10" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" - integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + "integrity" "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==" + "resolved" "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz" + "version" "1.13.10" dependencies: "@types/mime" "^1" "@types/node" "*" "@types/sockjs@^0.3.33": - version "0.3.33" - resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" - integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== + "integrity" "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==" + "resolved" "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz" + "version" "0.3.33" dependencies: "@types/node" "*" "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" - integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== + "integrity" "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + "resolved" "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz" + "version" "2.0.6" "@types/ws@^8.2.2": - version "8.2.2" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21" - integrity sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg== + "integrity" "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==" + "resolved" "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz" + "version" "8.2.2" dependencies: "@types/node" "*" "@webassemblyjs/ast@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" - integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + "integrity" "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/helper-numbers" "1.11.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.1" "@webassemblyjs/floating-point-hex-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" - integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + "integrity" "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz" + "version" "1.11.1" "@webassemblyjs/helper-api-error@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" - integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + "integrity" "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz" + "version" "1.11.1" "@webassemblyjs/helper-buffer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" - integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + "integrity" "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz" + "version" "1.11.1" "@webassemblyjs/helper-numbers@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" - integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + "integrity" "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/floating-point-hex-parser" "1.11.1" "@webassemblyjs/helper-api-error" "1.11.1" "@xtuc/long" "4.2.2" "@webassemblyjs/helper-wasm-bytecode@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" - integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + "integrity" "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz" + "version" "1.11.1" "@webassemblyjs/helper-wasm-section@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" - integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + "integrity" "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-buffer" "1.11.1" @@ -2054,28 +2053,28 @@ "@webassemblyjs/wasm-gen" "1.11.1" "@webassemblyjs/ieee754@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" - integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + "integrity" "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz" + "version" "1.11.1" dependencies: "@xtuc/ieee754" "^1.2.0" "@webassemblyjs/leb128@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" - integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + "integrity" "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz" + "version" "1.11.1" dependencies: "@xtuc/long" "4.2.2" "@webassemblyjs/utf8@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" - integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + "integrity" "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz" + "version" "1.11.1" "@webassemblyjs/wasm-edit@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" - integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + "integrity" "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-buffer" "1.11.1" @@ -2087,9 +2086,9 @@ "@webassemblyjs/wast-printer" "1.11.1" "@webassemblyjs/wasm-gen@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" - integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + "integrity" "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.1" @@ -2098,9 +2097,9 @@ "@webassemblyjs/utf8" "1.11.1" "@webassemblyjs/wasm-opt@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" - integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + "integrity" "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-buffer" "1.11.1" @@ -2108,9 +2107,9 @@ "@webassemblyjs/wasm-parser" "1.11.1" "@webassemblyjs/wasm-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" - integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + "integrity" "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-api-error" "1.11.1" @@ -2120,124 +2119,124 @@ "@webassemblyjs/utf8" "1.11.1" "@webassemblyjs/wast-printer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" - integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + "integrity" "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + "integrity" "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + "resolved" "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" + "version" "1.2.0" "@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + "integrity" "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + "resolved" "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" + "version" "4.2.2" -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== +"accepts@~1.3.4", "accepts@~1.3.5", "accepts@~1.3.8": + "integrity" "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==" + "resolved" "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" + "version" "1.3.8" dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" + "mime-types" "~2.1.34" + "negotiator" "0.6.3" -acorn-dynamic-import@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948" - integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw== +"acorn-dynamic-import@^4.0.0": + "integrity" "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==" + "resolved" "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz" + "version" "4.0.0" -acorn-import-assertions@^1.7.6: - version "1.8.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" - integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== +"acorn-import-assertions@^1.7.6": + "integrity" "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" + "resolved" "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz" + "version" "1.8.0" -acorn-jsx@^5.0.1: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== +"acorn-jsx@^5.0.1": + "integrity" "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" + "resolved" "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + "version" "5.3.2" -acorn-walk@^8.0.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== +"acorn-walk@^8.0.0": + "integrity" "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + "resolved" "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" + "version" "8.2.0" -acorn@^6.1.1: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== +"acorn@^6.0.0", "acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^6.1.1": + "integrity" "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz" + "version" "6.4.2" -acorn@^8.0.4, acorn@^8.4.1, acorn@^8.5.0: - version "8.7.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +"acorn@^8", "acorn@^8.0.4", "acorn@^8.4.1", "acorn@^8.5.0": + "integrity" "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz" + "version" "8.7.1" -address@^1.0.1, address@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" - integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== +"address@^1.0.1", "address@^1.1.2": + "integrity" "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==" + "resolved" "https://registry.npmjs.org/address/-/address-1.1.2.tgz" + "version" "1.1.2" -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== +"aggregate-error@^3.0.0": + "integrity" "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==" + "resolved" "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" + "version" "3.1.0" dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" + "clean-stack" "^2.0.0" + "indent-string" "^4.0.0" -ajv-formats@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== +"ajv-formats@^2.1.1": + "integrity" "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==" + "resolved" "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" + "version" "2.1.1" dependencies: - ajv "^8.0.0" + "ajv" "^8.0.0" -ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== +"ajv-keywords@^3.4.1", "ajv-keywords@^3.5.2": + "integrity" "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" + "resolved" "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" + "version" "3.5.2" -ajv-keywords@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" - integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== +"ajv-keywords@^5.0.0": + "integrity" "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==" + "resolved" "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz" + "version" "5.1.0" dependencies: - fast-deep-equal "^3.1.3" + "fast-deep-equal" "^3.1.3" -ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== +"ajv@^6.12.2", "ajv@^6.12.4", "ajv@^6.12.5", "ajv@^6.9.1": + "integrity" "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + "version" "6.12.6" dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" + "fast-deep-equal" "^3.1.1" + "fast-json-stable-stringify" "^2.0.0" + "json-schema-traverse" "^0.4.1" + "uri-js" "^4.2.2" -ajv@^8.0.0, ajv@^8.8.0: - version "8.10.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d" - integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw== +"ajv@^8.0.0", "ajv@^8.8.0", "ajv@^8.8.2": + "integrity" "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz" + "version" "8.10.0" dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" + "fast-deep-equal" "^3.1.1" + "json-schema-traverse" "^1.0.0" + "require-from-string" "^2.0.2" + "uri-js" "^4.2.2" -algoliasearch-helper@^3.5.5: - version "3.7.0" - resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.7.0.tgz#c0a0493df84d850360f664ad7a9d4fc78a94fd78" - integrity sha512-XJ3QfERBLfeVCyTVx80gon7r3/rgm/CE8Ha1H7cbablRe/X7SfYQ14g/eO+MhjVKIQp+gy9oC6G5ilmLwS1k6w== +"algoliasearch-helper@^3.5.5": + "integrity" "sha512-XJ3QfERBLfeVCyTVx80gon7r3/rgm/CE8Ha1H7cbablRe/X7SfYQ14g/eO+MhjVKIQp+gy9oC6G5ilmLwS1k6w==" + "resolved" "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.7.0.tgz" + "version" "3.7.0" dependencies: "@algolia/events" "^4.0.1" -algoliasearch@^4.0.0, algoliasearch@^4.10.5: - version "4.12.1" - resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.12.1.tgz#574a2c5424c4b6681c026928fb810be2d2ec3924" - integrity sha512-c0dM1g3zZBJrkzE5GA/Nu1y3fFxx3LCzxKzcmp2dgGS8P4CjszB/l3lsSh2MSrrK1Hn/KV4BlbBMXtYgG1Bfrw== +"algoliasearch@^4.0.0", "algoliasearch@^4.10.5", "algoliasearch@^4.9.1", "algoliasearch@>= 3.1 < 5": + "integrity" "sha512-c0dM1g3zZBJrkzE5GA/Nu1y3fFxx3LCzxKzcmp2dgGS8P4CjszB/l3lsSh2MSrrK1Hn/KV4BlbBMXtYgG1Bfrw==" + "resolved" "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/cache-browser-local-storage" "4.12.1" "@algolia/cache-common" "4.12.1" @@ -2254,2429 +2253,2448 @@ algoliasearch@^4.0.0, algoliasearch@^4.10.5: "@algolia/requester-node-http" "4.12.1" "@algolia/transporter" "4.12.1" -ansi-align@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" - integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== +"ansi-align@^3.0.0": + "integrity" "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==" + "resolved" "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz" + "version" "3.0.1" dependencies: - string-width "^4.1.0" + "string-width" "^4.1.0" -ansi-html-community@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" - integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== +"ansi-html-community@^0.0.8": + "integrity" "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" + "resolved" "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz" + "version" "0.0.8" -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +"ansi-regex@^5.0.1": + "integrity" "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + "version" "5.0.1" -ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== +"ansi-regex@^6.0.1": + "integrity" "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + "version" "6.0.1" -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== +"ansi-styles@^3.2.1": + "integrity" "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + "version" "3.2.1" dependencies: - color-convert "^1.9.0" + "color-convert" "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== +"ansi-styles@^4.0.0", "ansi-styles@^4.1.0": + "integrity" "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + "version" "4.3.0" dependencies: - color-convert "^2.0.1" + "color-convert" "^2.0.1" -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== +"anymatch@~3.1.2": + "integrity" "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==" + "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" + "version" "3.1.2" dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" + "normalize-path" "^3.0.0" + "picomatch" "^2.0.4" -arg@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.1.tgz#eb0c9a8f77786cad2af8ff2b862899842d7b6adb" - integrity sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA== +"arg@^5.0.0": + "integrity" "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==" + "resolved" "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz" + "version" "5.0.1" -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== +"argparse@^1.0.7": + "integrity" "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==" + "resolved" "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + "version" "1.0.10" dependencies: - sprintf-js "~1.0.2" + "sprintf-js" "~1.0.2" -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +"argparse@^2.0.1": + "integrity" "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "resolved" "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + "version" "2.0.1" -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= +"array-flatten@^2.1.0": + "integrity" "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + "resolved" "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz" + "version" "2.1.2" -array-flatten@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== +"array-flatten@1.1.1": + "integrity" "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "resolved" "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + "version" "1.1.1" -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +"array-union@^2.1.0": + "integrity" "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + "resolved" "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + "version" "2.1.0" -array-union@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-3.0.1.tgz#da52630d327f8b88cfbfb57728e2af5cd9b6b975" - integrity sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw== +"array-union@^3.0.1": + "integrity" "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==" + "resolved" "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz" + "version" "3.0.1" -asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= +"asap@~2.0.3": + "integrity" "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + "resolved" "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" + "version" "2.0.6" -async@^2.6.2: - version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== +"async@^2.6.2": + "integrity" "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==" + "resolved" "https://registry.npmjs.org/async/-/async-2.6.4.tgz" + "version" "2.6.4" dependencies: - lodash "^4.17.14" + "lodash" "^4.17.14" -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +"at-least-node@^1.0.0": + "integrity" "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + "resolved" "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" + "version" "1.0.0" -autoprefixer@^10.3.5, autoprefixer@^10.3.7: - version "10.4.2" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.2.tgz#25e1df09a31a9fba5c40b578936b90d35c9d4d3b" - integrity sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ== +"autoprefixer@^10.3.5", "autoprefixer@^10.3.7": + "integrity" "sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==" + "resolved" "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz" + "version" "10.4.2" dependencies: - browserslist "^4.19.1" - caniuse-lite "^1.0.30001297" - fraction.js "^4.1.2" - normalize-range "^0.1.2" - picocolors "^1.0.0" - postcss-value-parser "^4.2.0" + "browserslist" "^4.19.1" + "caniuse-lite" "^1.0.30001297" + "fraction.js" "^4.1.2" + "normalize-range" "^0.1.2" + "picocolors" "^1.0.0" + "postcss-value-parser" "^4.2.0" -axios@^0.25.0: - version "0.25.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" - integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== +"axios@^0.25.0": + "integrity" "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==" + "resolved" "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz" + "version" "0.25.0" dependencies: - follow-redirects "^1.14.7" + "follow-redirects" "^1.14.7" -babel-loader@^8.2.2: - version "8.2.3" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.3.tgz#8986b40f1a64cacfcb4b8429320085ef68b1342d" - integrity sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw== +"babel-loader@^8.2.2": + "integrity" "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==" + "resolved" "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz" + "version" "8.2.3" dependencies: - find-cache-dir "^3.3.1" - loader-utils "^1.4.0" - make-dir "^3.1.0" - schema-utils "^2.6.5" + "find-cache-dir" "^3.3.1" + "loader-utils" "^1.4.0" + "make-dir" "^3.1.0" + "schema-utils" "^2.6.5" -babel-plugin-apply-mdx-type-prop@1.6.22: - version "1.6.22" - resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz#d216e8fd0de91de3f1478ef3231e05446bc8705b" - integrity sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ== +"babel-plugin-apply-mdx-type-prop@1.6.22": + "integrity" "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz" + "version" "1.6.22" dependencies: "@babel/helper-plugin-utils" "7.10.4" "@mdx-js/util" "1.6.22" -babel-plugin-dynamic-import-node@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" - integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== +"babel-plugin-dynamic-import-node@^2.3.3": + "integrity" "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz" + "version" "2.3.3" dependencies: - object.assign "^4.1.0" + "object.assign" "^4.1.0" -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== +"babel-plugin-dynamic-import-node@2.3.0": + "integrity" "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz" + "version" "2.3.0" dependencies: - object.assign "^4.1.0" + "object.assign" "^4.1.0" -babel-plugin-extract-import-names@1.6.22: - version "1.6.22" - resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz#de5f9a28eb12f3eb2578bf74472204e66d1a13dc" - integrity sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ== +"babel-plugin-extract-import-names@1.6.22": + "integrity" "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz" + "version" "1.6.22" dependencies: "@babel/helper-plugin-utils" "7.10.4" -babel-plugin-polyfill-corejs2@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" - integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== +"babel-plugin-polyfill-corejs2@^0.3.0": + "integrity" "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz" + "version" "0.3.1" dependencies: "@babel/compat-data" "^7.13.11" "@babel/helper-define-polyfill-provider" "^0.3.1" - semver "^6.1.1" + "semver" "^6.1.1" -babel-plugin-polyfill-corejs3@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" - integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== +"babel-plugin-polyfill-corejs3@^0.5.0": + "integrity" "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz" + "version" "0.5.2" dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" - core-js-compat "^3.21.0" + "core-js-compat" "^3.21.0" -babel-plugin-polyfill-regenerator@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" - integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== +"babel-plugin-polyfill-regenerator@^0.3.0": + "integrity" "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz" + "version" "0.3.1" dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" -bail@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" - integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== +"bail@^1.0.0": + "integrity" "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==" + "resolved" "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz" + "version" "1.0.5" -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +"balanced-match@^1.0.0": + "integrity" "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + "version" "1.0.2" -base16@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70" - integrity sha1-4pf2DX7BAUp6lxo568ipjAtoHnA= +"base16@^1.0.0": + "integrity" "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" + "resolved" "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz" + "version" "1.0.0" -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= +"batch@0.6.1": + "integrity" "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + "resolved" "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" + "version" "0.6.1" -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +"big.js@^5.2.2": + "integrity" "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + "resolved" "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" + "version" "5.2.2" -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +"binary-extensions@^2.0.0": + "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + "version" "2.2.0" -bluebird@^3.7.1: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +"bluebird@^3.7.1": + "integrity" "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + "resolved" "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" + "version" "3.7.2" -body-parser@1.19.2: - version "1.19.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" - integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== +"body-parser@1.19.2": + "integrity" "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==" + "resolved" "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz" + "version" "1.19.2" dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.8.1" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.9.7" - raw-body "2.4.3" - type-is "~1.6.18" + "bytes" "3.1.2" + "content-type" "~1.0.4" + "debug" "2.6.9" + "depd" "~1.1.2" + "http-errors" "1.8.1" + "iconv-lite" "0.4.24" + "on-finished" "~2.3.0" + "qs" "6.9.7" + "raw-body" "2.4.3" + "type-is" "~1.6.18" -bonjour@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" - integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= +"bonjour@^3.5.0": + "integrity" "sha1-jokKGD2O6aI5OzhExpGkK897yfU=" + "resolved" "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz" + "version" "3.5.0" dependencies: - array-flatten "^2.1.0" - deep-equal "^1.0.1" - dns-equal "^1.0.0" - dns-txt "^2.0.2" - multicast-dns "^6.0.1" - multicast-dns-service-types "^1.1.0" + "array-flatten" "^2.1.0" + "deep-equal" "^1.0.1" + "dns-equal" "^1.0.0" + "dns-txt" "^2.0.2" + "multicast-dns" "^6.0.1" + "multicast-dns-service-types" "^1.1.0" -boolbase@^1.0.0, boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +"boolbase@^1.0.0", "boolbase@~1.0.0": + "integrity" "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + "resolved" "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" + "version" "1.0.0" -boxen@^5.0.0, boxen@^5.0.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" - integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== +"boxen@^5.0.0", "boxen@^5.0.1": + "integrity" "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==" + "resolved" "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz" + "version" "5.1.2" dependencies: - ansi-align "^3.0.0" - camelcase "^6.2.0" - chalk "^4.1.0" - cli-boxes "^2.2.1" - string-width "^4.2.2" - type-fest "^0.20.2" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" + "ansi-align" "^3.0.0" + "camelcase" "^6.2.0" + "chalk" "^4.1.0" + "cli-boxes" "^2.2.1" + "string-width" "^4.2.2" + "type-fest" "^0.20.2" + "widest-line" "^3.1.0" + "wrap-ansi" "^7.0.0" -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== +"brace-expansion@^1.1.7": + "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" + "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + "version" "1.1.11" dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" + "balanced-match" "^1.0.0" + "concat-map" "0.0.1" -braces@^3.0.1, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +"braces@^3.0.1", "braces@~3.0.2": + "integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==" + "resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + "version" "3.0.2" dependencies: - fill-range "^7.0.1" + "fill-range" "^7.0.1" -browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.18.1, browserslist@^4.19.1: - version "4.19.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" - integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== +"browserslist@^4.0.0", "browserslist@^4.14.5", "browserslist@^4.16.6", "browserslist@^4.18.1", "browserslist@^4.19.1", "browserslist@^4.21.3", "browserslist@>= 4.21.0": + "integrity" "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==" + "resolved" "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz" + "version" "4.21.5" dependencies: - caniuse-lite "^1.0.30001286" - electron-to-chromium "^1.4.17" - escalade "^3.1.1" - node-releases "^2.0.1" - picocolors "^1.0.0" + "caniuse-lite" "^1.0.30001449" + "electron-to-chromium" "^1.4.284" + "node-releases" "^2.0.8" + "update-browserslist-db" "^1.0.10" -buble-jsx-only@^0.19.8: - version "0.19.8" - resolved "https://registry.yarnpkg.com/buble-jsx-only/-/buble-jsx-only-0.19.8.tgz#6e3524aa0f1c523de32496ac9aceb9cc2b493867" - integrity sha512-7AW19pf7PrKFnGTEDzs6u9+JZqQwM1VnLS19OlqYDhXomtFFknnoQJAPHeg84RMFWAvOhYrG7harizJNwUKJsA== +"buble-jsx-only@^0.19.8": + "integrity" "sha512-7AW19pf7PrKFnGTEDzs6u9+JZqQwM1VnLS19OlqYDhXomtFFknnoQJAPHeg84RMFWAvOhYrG7harizJNwUKJsA==" + "resolved" "https://registry.npmjs.org/buble-jsx-only/-/buble-jsx-only-0.19.8.tgz" + "version" "0.19.8" dependencies: - acorn "^6.1.1" - acorn-dynamic-import "^4.0.0" - acorn-jsx "^5.0.1" - chalk "^2.4.2" - magic-string "^0.25.3" - minimist "^1.2.0" - regexpu-core "^4.5.4" + "acorn" "^6.1.1" + "acorn-dynamic-import" "^4.0.0" + "acorn-jsx" "^5.0.1" + "chalk" "^2.4.2" + "magic-string" "^0.25.3" + "minimist" "^1.2.0" + "regexpu-core" "^4.5.4" -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +"buffer-from@^1.0.0": + "integrity" "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "resolved" "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + "version" "1.1.2" -buffer-indexof@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" - integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== +"buffer-indexof@^1.0.0": + "integrity" "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + "resolved" "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz" + "version" "1.1.1" -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= +"bytes@3.0.0": + "integrity" "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + "resolved" "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" + "version" "3.0.0" -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +"bytes@3.1.2": + "integrity" "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + "resolved" "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + "version" "3.1.2" -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== +"cacheable-request@^6.0.0": + "integrity" "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==" + "resolved" "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz" + "version" "6.1.0" dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" + "clone-response" "^1.0.2" + "get-stream" "^5.1.0" + "http-cache-semantics" "^4.0.0" + "keyv" "^3.0.0" + "lowercase-keys" "^2.0.0" + "normalize-url" "^4.1.0" + "responselike" "^1.0.2" -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== +"call-bind@^1.0.0", "call-bind@^1.0.2": + "integrity" "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==" + "resolved" "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + "version" "1.0.2" dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + "function-bind" "^1.1.1" + "get-intrinsic" "^1.0.2" -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +"callsites@^3.0.0": + "integrity" "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + "resolved" "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + "version" "3.1.0" -camel-case@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" - integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== +"camel-case@^4.1.2": + "integrity" "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==" + "resolved" "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" + "version" "4.1.2" dependencies: - pascal-case "^3.1.2" - tslib "^2.0.3" + "pascal-case" "^3.1.2" + "tslib" "^2.0.3" -camelcase-css@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" - integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== +"camelcase-css@2.0.1": + "integrity" "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + "resolved" "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" + "version" "2.0.1" -camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +"camelcase@^6.2.0": + "integrity" "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + "version" "6.3.0" -caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== +"caniuse-api@^3.0.0": + "integrity" "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==" + "resolved" "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz" + "version" "3.0.0" dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" + "browserslist" "^4.0.0" + "caniuse-lite" "^1.0.0" + "lodash.memoize" "^4.1.2" + "lodash.uniq" "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001297: - version "1.0.30001312" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz#e11eba4b87e24d22697dae05455d5aea28550d5f" - integrity sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ== +"caniuse-lite@^1.0.0", "caniuse-lite@^1.0.30001297", "caniuse-lite@^1.0.30001449": + "integrity" "sha512-ewtFBSfWjEmxUgNBSZItFSmVtvk9zkwkl1OfRZlKA8slltRN+/C/tuGVrF9styXkN36Yu3+SeJ1qkXxDEyNZ5w==" + "resolved" "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001466.tgz" + "version" "1.0.30001466" -ccount@^1.0.0, ccount@^1.0.3: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" - integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== +"ccount@^1.0.0", "ccount@^1.0.3": + "integrity" "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==" + "resolved" "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz" + "version" "1.1.0" -chalk@^2.0.0, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== +"chalk@^2.0.0": + "integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + "version" "2.4.2" dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" + "ansi-styles" "^3.2.1" + "escape-string-regexp" "^1.0.5" + "supports-color" "^5.3.0" -chalk@^4.1.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== +"chalk@^2.4.2": + "integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + "version" "2.4.2" dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" + "ansi-styles" "^3.2.1" + "escape-string-regexp" "^1.0.5" + "supports-color" "^5.3.0" -character-entities-legacy@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" - integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== - -character-entities@^1.0.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" - integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== - -character-reference-invalid@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" - integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== - -cheerio-select@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823" - integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg== +"chalk@^4.1.0", "chalk@^4.1.2": + "integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + "version" "4.1.2" dependencies: - css-select "^4.1.3" - css-what "^5.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" - domutils "^2.7.0" + "ansi-styles" "^4.1.0" + "supports-color" "^7.1.0" -cheerio@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" - integrity sha1-qbqoYKP5tZWmuBsahocxIe06Jp4= - dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.0" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash.assignin "^4.0.9" - lodash.bind "^4.1.4" - lodash.defaults "^4.0.1" - lodash.filter "^4.4.0" - lodash.flatten "^4.2.0" - lodash.foreach "^4.3.0" - lodash.map "^4.4.0" - lodash.merge "^4.4.0" - lodash.pick "^4.2.1" - lodash.reduce "^4.4.0" - lodash.reject "^4.4.0" - lodash.some "^4.4.0" +"character-entities-legacy@^1.0.0": + "integrity" "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" + "resolved" "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz" + "version" "1.1.4" -cheerio@^1.0.0-rc.10: - version "1.0.0-rc.10" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e" - integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw== - dependencies: - cheerio-select "^1.5.0" - dom-serializer "^1.3.2" - domhandler "^4.2.0" - htmlparser2 "^6.1.0" - parse5 "^6.0.1" - parse5-htmlparser2-tree-adapter "^6.0.1" - tslib "^2.2.0" +"character-entities@^1.0.0": + "integrity" "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" + "resolved" "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz" + "version" "1.2.4" -chokidar@^3.4.2, chokidar@^3.5.2, chokidar@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== +"character-reference-invalid@^1.0.0": + "integrity" "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" + "resolved" "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz" + "version" "1.1.4" + +"cheerio-select@^1.5.0": + "integrity" "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==" + "resolved" "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz" + "version" "1.5.0" dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" + "css-select" "^4.1.3" + "css-what" "^5.0.1" + "domelementtype" "^2.2.0" + "domhandler" "^4.2.0" + "domutils" "^2.7.0" + +"cheerio@^0.22.0": + "integrity" "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=" + "resolved" "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz" + "version" "0.22.0" + dependencies: + "css-select" "~1.2.0" + "dom-serializer" "~0.1.0" + "entities" "~1.1.1" + "htmlparser2" "^3.9.1" + "lodash.assignin" "^4.0.9" + "lodash.bind" "^4.1.4" + "lodash.defaults" "^4.0.1" + "lodash.filter" "^4.4.0" + "lodash.flatten" "^4.2.0" + "lodash.foreach" "^4.3.0" + "lodash.map" "^4.4.0" + "lodash.merge" "^4.4.0" + "lodash.pick" "^4.2.1" + "lodash.reduce" "^4.4.0" + "lodash.reject" "^4.4.0" + "lodash.some" "^4.4.0" + +"cheerio@^1.0.0-rc.10": + "integrity" "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==" + "resolved" "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz" + "version" "1.0.0-rc.10" + dependencies: + "cheerio-select" "^1.5.0" + "dom-serializer" "^1.3.2" + "domhandler" "^4.2.0" + "htmlparser2" "^6.1.0" + "parse5" "^6.0.1" + "parse5-htmlparser2-tree-adapter" "^6.0.1" + "tslib" "^2.2.0" + +"chokidar@^3.4.2", "chokidar@^3.5.2", "chokidar@^3.5.3": + "integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==" + "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + "version" "3.5.3" + dependencies: + "anymatch" "~3.1.2" + "braces" "~3.0.2" + "glob-parent" "~5.1.2" + "is-binary-path" "~2.1.0" + "is-glob" "~4.0.1" + "normalize-path" "~3.0.0" + "readdirp" "~3.6.0" optionalDependencies: - fsevents "~2.3.2" + "fsevents" "~2.3.2" -chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== +"chrome-trace-event@^1.0.2": + "integrity" "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + "resolved" "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz" + "version" "1.0.3" -ci-info@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +"ci-info@^2.0.0": + "integrity" "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + "resolved" "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" + "version" "2.0.0" -classnames@^2.2.6: - version "2.3.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" - integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== +"classnames@^2.2.6": + "integrity" "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + "resolved" "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz" + "version" "2.3.1" -clean-css@^5.1.5, clean-css@^5.2.2: - version "5.2.4" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.2.4.tgz#982b058f8581adb2ae062520808fb2429bd487a4" - integrity sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg== +"clean-css@^5.1.5", "clean-css@^5.2.2": + "integrity" "sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg==" + "resolved" "https://registry.npmjs.org/clean-css/-/clean-css-5.2.4.tgz" + "version" "5.2.4" dependencies: - source-map "~0.6.0" + "source-map" "~0.6.0" -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +"clean-stack@^2.0.0": + "integrity" "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + "resolved" "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" + "version" "2.2.0" -cli-boxes@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" - integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== +"cli-boxes@^2.2.1": + "integrity" "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" + "resolved" "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz" + "version" "2.2.1" -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== +"clone-deep@^4.0.1": + "integrity" "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==" + "resolved" "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" + "version" "4.0.1" dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" + "is-plain-object" "^2.0.4" + "kind-of" "^6.0.2" + "shallow-clone" "^3.0.0" -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= +"clone-response@^1.0.2": + "integrity" "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=" + "resolved" "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" + "version" "1.0.2" dependencies: - mimic-response "^1.0.0" + "mimic-response" "^1.0.0" -clsx@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" - integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== +"clsx@^1.1.1": + "integrity" "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" + "resolved" "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz" + "version" "1.1.1" -collapse-white-space@^1.0.2: - version "1.0.6" - resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" - integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== +"collapse-white-space@^1.0.2": + "integrity" "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==" + "resolved" "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz" + "version" "1.0.6" -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== +"color-convert@^1.9.0": + "integrity" "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==" + "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + "version" "1.9.3" dependencies: - color-name "1.1.3" + "color-name" "1.1.3" -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== +"color-convert@^2.0.1": + "integrity" "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==" + "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + "version" "2.0.1" dependencies: - color-name "~1.1.4" + "color-name" "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +"color-name@~1.1.4": + "integrity" "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + "version" "1.1.4" -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +"color-name@1.1.3": + "integrity" "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + "version" "1.1.3" -colord@^2.9.1: - version "2.9.2" - resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1" - integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ== +"colord@^2.9.1": + "integrity" "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==" + "resolved" "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz" + "version" "2.9.2" -colorette@^2.0.10: - version "2.0.16" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" - integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== +"colorette@^2.0.10": + "integrity" "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" + "resolved" "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz" + "version" "2.0.16" -combine-promises@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/combine-promises/-/combine-promises-1.1.0.tgz#72db90743c0ca7aab7d0d8d2052fd7b0f674de71" - integrity sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg== +"combine-promises@^1.1.0": + "integrity" "sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg==" + "resolved" "https://registry.npmjs.org/combine-promises/-/combine-promises-1.1.0.tgz" + "version" "1.1.0" -comma-separated-tokens@^1.0.0: - version "1.0.8" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" - integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== +"comma-separated-tokens@^1.0.0": + "integrity" "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==" + "resolved" "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz" + "version" "1.0.8" -commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +"commander@^2.20.0": + "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + "version" "2.20.3" -commander@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== +"commander@^5.1.0": + "integrity" "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + "resolved" "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz" + "version" "5.1.0" -commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +"commander@^7.2.0": + "integrity" "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + "resolved" "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" + "version" "7.2.0" -commander@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +"commander@^8.3.0": + "integrity" "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + "resolved" "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" + "version" "8.3.0" -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +"commondir@^1.0.1": + "integrity" "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + "resolved" "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" + "version" "1.0.1" -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== +"compressible@~2.0.16": + "integrity" "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==" + "resolved" "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" + "version" "2.0.18" dependencies: - mime-db ">= 1.43.0 < 2" + "mime-db" ">= 1.43.0 < 2" -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== +"compression@^1.7.4": + "integrity" "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==" + "resolved" "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz" + "version" "1.7.4" dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" + "accepts" "~1.3.5" + "bytes" "3.0.0" + "compressible" "~2.0.16" + "debug" "2.6.9" + "on-headers" "~1.0.2" + "safe-buffer" "5.1.2" + "vary" "~1.1.2" -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +"concat-map@0.0.1": + "integrity" "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "version" "0.0.1" -configstore@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== +"configstore@^5.0.1": + "integrity" "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==" + "resolved" "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz" + "version" "5.0.1" dependencies: - dot-prop "^5.2.0" - graceful-fs "^4.1.2" - make-dir "^3.0.0" - unique-string "^2.0.0" - write-file-atomic "^3.0.0" - xdg-basedir "^4.0.0" + "dot-prop" "^5.2.0" + "graceful-fs" "^4.1.2" + "make-dir" "^3.0.0" + "unique-string" "^2.0.0" + "write-file-atomic" "^3.0.0" + "xdg-basedir" "^4.0.0" -connect-history-api-fallback@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" - integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== +"connect-history-api-fallback@^1.6.0": + "integrity" "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + "resolved" "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz" + "version" "1.6.0" -consola@^2.15.3: - version "2.15.3" - resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" - integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== +"consola@^2.15.3": + "integrity" "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + "resolved" "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz" + "version" "2.15.3" -content-disposition@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= +"content-disposition@0.5.2": + "integrity" "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + "resolved" "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz" + "version" "0.5.2" -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== +"content-disposition@0.5.4": + "integrity" "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==" + "resolved" "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" + "version" "0.5.4" dependencies: - safe-buffer "5.2.1" + "safe-buffer" "5.2.1" -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== +"content-type@~1.0.4": + "integrity" "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "resolved" "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" + "version" "1.0.4" -convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== +"convert-source-map@^1.7.0": + "integrity" "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==" + "resolved" "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz" + "version" "1.8.0" dependencies: - safe-buffer "~5.1.1" + "safe-buffer" "~5.1.1" -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= +"cookie-signature@1.0.6": + "integrity" "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "resolved" "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + "version" "1.0.6" -cookie@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +"cookie@0.4.2": + "integrity" "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + "resolved" "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" + "version" "0.4.2" -copy-text-to-clipboard@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz#8cbf8f90e0a47f12e4a24743736265d157bce69c" - integrity sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q== +"copy-text-to-clipboard@^3.0.1": + "integrity" "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==" + "resolved" "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz" + "version" "3.0.1" -copy-webpack-plugin@^10.2.0: - version "10.2.4" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz#6c854be3fdaae22025da34b9112ccf81c63308fe" - integrity sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg== +"copy-webpack-plugin@^10.2.0": + "integrity" "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==" + "resolved" "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz" + "version" "10.2.4" dependencies: - fast-glob "^3.2.7" - glob-parent "^6.0.1" - globby "^12.0.2" - normalize-path "^3.0.0" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" + "fast-glob" "^3.2.7" + "glob-parent" "^6.0.1" + "globby" "^12.0.2" + "normalize-path" "^3.0.0" + "schema-utils" "^4.0.0" + "serialize-javascript" "^6.0.0" -core-js-compat@^3.20.2, core-js-compat@^3.21.0: - version "3.21.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.21.1.tgz#cac369f67c8d134ff8f9bd1623e3bc2c42068c82" - integrity sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g== +"core-js-compat@^3.20.2", "core-js-compat@^3.21.0": + "integrity" "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==" + "resolved" "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz" + "version" "3.21.1" dependencies: - browserslist "^4.19.1" - semver "7.0.0" + "browserslist" "^4.19.1" + "semver" "7.0.0" -core-js-pure@^3.20.2: - version "3.21.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.21.1.tgz#8c4d1e78839f5f46208de7230cebfb72bc3bdb51" - integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ== +"core-js-pure@^3.20.2": + "integrity" "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==" + "resolved" "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz" + "version" "3.21.1" -core-js@^3.18.0: - version "3.21.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94" - integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig== +"core-js@^3.18.0": + "integrity" "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==" + "resolved" "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz" + "version" "3.21.1" -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +"core-util-is@~1.0.0": + "integrity" "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "resolved" "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + "version" "1.0.3" -cosmiconfig@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" - integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== +"cosmiconfig@^6.0.0": + "integrity" "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==" + "resolved" "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" + "version" "6.0.0" dependencies: "@types/parse-json" "^4.0.0" - import-fresh "^3.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.7.2" + "import-fresh" "^3.1.0" + "parse-json" "^5.0.0" + "path-type" "^4.0.0" + "yaml" "^1.7.2" -cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== +"cosmiconfig@^7.0.0", "cosmiconfig@^7.0.1": + "integrity" "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==" + "resolved" "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz" + "version" "7.0.1" dependencies: "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" + "import-fresh" "^3.2.1" + "parse-json" "^5.0.0" + "path-type" "^4.0.0" + "yaml" "^1.10.0" -cross-fetch@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== +"cross-fetch@^3.1.5": + "integrity" "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==" + "resolved" "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz" + "version" "3.1.5" dependencies: - node-fetch "2.6.7" + "node-fetch" "2.6.7" -cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== +"cross-spawn@^7.0.3": + "integrity" "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==" + "resolved" "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + "version" "7.0.3" dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" + "path-key" "^3.1.0" + "shebang-command" "^2.0.0" + "which" "^2.0.1" -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== +"crypto-random-string@^2.0.0": + "integrity" "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + "resolved" "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz" + "version" "2.0.0" -css-declaration-sorter@^6.0.3: - version "6.1.4" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz#b9bfb4ed9a41f8dcca9bf7184d849ea94a8294b4" - integrity sha512-lpfkqS0fctcmZotJGhnxkIyJWvBXgpyi2wsFd4J8VB7wzyrT6Ch/3Q+FMNJpjK4gu1+GN5khOnpU2ZVKrLbhCw== +"css-declaration-sorter@^6.0.3": + "integrity" "sha512-lpfkqS0fctcmZotJGhnxkIyJWvBXgpyi2wsFd4J8VB7wzyrT6Ch/3Q+FMNJpjK4gu1+GN5khOnpU2ZVKrLbhCw==" + "resolved" "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz" + "version" "6.1.4" dependencies: - timsort "^0.3.0" + "timsort" "^0.3.0" -css-loader@^6.5.1: - version "6.6.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.6.0.tgz#c792ad5510bd1712618b49381bd0310574fafbd3" - integrity sha512-FK7H2lisOixPT406s5gZM1S3l8GrfhEBT3ZiL2UX1Ng1XWs0y2GPllz/OTyvbaHe12VgQrIXIzuEGVlbUhodqg== +"css-loader@^6.5.1": + "integrity" "sha512-FK7H2lisOixPT406s5gZM1S3l8GrfhEBT3ZiL2UX1Ng1XWs0y2GPllz/OTyvbaHe12VgQrIXIzuEGVlbUhodqg==" + "resolved" "https://registry.npmjs.org/css-loader/-/css-loader-6.6.0.tgz" + "version" "6.6.0" dependencies: - icss-utils "^5.1.0" - postcss "^8.4.5" - postcss-modules-extract-imports "^3.0.0" - postcss-modules-local-by-default "^4.0.0" - postcss-modules-scope "^3.0.0" - postcss-modules-values "^4.0.0" - postcss-value-parser "^4.2.0" - semver "^7.3.5" + "icss-utils" "^5.1.0" + "postcss" "^8.4.5" + "postcss-modules-extract-imports" "^3.0.0" + "postcss-modules-local-by-default" "^4.0.0" + "postcss-modules-scope" "^3.0.0" + "postcss-modules-values" "^4.0.0" + "postcss-value-parser" "^4.2.0" + "semver" "^7.3.5" -css-minimizer-webpack-plugin@^3.3.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz#ab78f781ced9181992fe7b6e4f3422e76429878f" - integrity sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q== +"css-minimizer-webpack-plugin@^3.3.1": + "integrity" "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==" + "resolved" "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz" + "version" "3.4.1" dependencies: - cssnano "^5.0.6" - jest-worker "^27.0.2" - postcss "^8.3.5" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" - source-map "^0.6.1" + "cssnano" "^5.0.6" + "jest-worker" "^27.0.2" + "postcss" "^8.3.5" + "schema-utils" "^4.0.0" + "serialize-javascript" "^6.0.0" + "source-map" "^0.6.1" -css-select@^4.1.3: - version "4.2.1" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.2.1.tgz#9e665d6ae4c7f9d65dbe69d0316e3221fb274cdd" - integrity sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ== +"css-select@^4.1.3": + "integrity" "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==" + "resolved" "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz" + "version" "4.2.1" dependencies: - boolbase "^1.0.0" - css-what "^5.1.0" - domhandler "^4.3.0" - domutils "^2.8.0" - nth-check "^2.0.1" + "boolbase" "^1.0.0" + "css-what" "^5.1.0" + "domhandler" "^4.3.0" + "domutils" "^2.8.0" + "nth-check" "^2.0.1" -css-select@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" - integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= +"css-select@~1.2.0": + "integrity" "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=" + "resolved" "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz" + "version" "1.2.0" dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" + "boolbase" "~1.0.0" + "css-what" "2.1" + "domutils" "1.5.1" + "nth-check" "~1.0.1" -css-tree@^1.1.2, css-tree@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== +"css-tree@^1.1.2", "css-tree@^1.1.3": + "integrity" "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==" + "resolved" "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + "version" "1.1.3" dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" + "mdn-data" "2.0.14" + "source-map" "^0.6.1" -css-what@2.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== +"css-what@^5.0.1", "css-what@^5.1.0": + "integrity" "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==" + "resolved" "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz" + "version" "5.1.0" -css-what@^5.0.1, css-what@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" - integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== +"css-what@2.1": + "integrity" "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" + "resolved" "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz" + "version" "2.1.3" -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +"cssesc@^3.0.0": + "integrity" "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + "resolved" "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + "version" "3.0.0" -cssnano-preset-advanced@^5.1.4: - version "5.1.12" - resolved "https://registry.yarnpkg.com/cssnano-preset-advanced/-/cssnano-preset-advanced-5.1.12.tgz#11f5b0c4e3c32bcfd475465a283fa14dec8df972" - integrity sha512-5WWV9mbqVNwH4nRjs5UbhNl7eKo+16eYNzGogmz0Sa6iqWUeLdN8oo83WuTTqz5vjEKhTbRM5oX6WV1i6ees6g== +"cssnano-preset-advanced@^5.1.4": + "integrity" "sha512-5WWV9mbqVNwH4nRjs5UbhNl7eKo+16eYNzGogmz0Sa6iqWUeLdN8oo83WuTTqz5vjEKhTbRM5oX6WV1i6ees6g==" + "resolved" "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.1.12.tgz" + "version" "5.1.12" dependencies: - autoprefixer "^10.3.7" - cssnano-preset-default "^5.1.12" - postcss-discard-unused "^5.0.3" - postcss-merge-idents "^5.0.3" - postcss-reduce-idents "^5.0.3" - postcss-zindex "^5.0.2" + "autoprefixer" "^10.3.7" + "cssnano-preset-default" "^5.1.12" + "postcss-discard-unused" "^5.0.3" + "postcss-merge-idents" "^5.0.3" + "postcss-reduce-idents" "^5.0.3" + "postcss-zindex" "^5.0.2" -cssnano-preset-default@^5.1.12: - version "5.1.12" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.12.tgz#64e2ad8e27a279e1413d2d2383ef89a41c909be9" - integrity sha512-rO/JZYyjW1QNkWBxMGV28DW7d98UDLaF759frhli58QFehZ+D/LSmwQ2z/ylBAe2hUlsIWTq6NYGfQPq65EF9w== +"cssnano-preset-default@^5.1.12": + "integrity" "sha512-rO/JZYyjW1QNkWBxMGV28DW7d98UDLaF759frhli58QFehZ+D/LSmwQ2z/ylBAe2hUlsIWTq6NYGfQPq65EF9w==" + "resolved" "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.12.tgz" + "version" "5.1.12" dependencies: - css-declaration-sorter "^6.0.3" - cssnano-utils "^3.0.2" - postcss-calc "^8.2.0" - postcss-colormin "^5.2.5" - postcss-convert-values "^5.0.4" - postcss-discard-comments "^5.0.3" - postcss-discard-duplicates "^5.0.3" - postcss-discard-empty "^5.0.3" - postcss-discard-overridden "^5.0.4" - postcss-merge-longhand "^5.0.6" - postcss-merge-rules "^5.0.6" - postcss-minify-font-values "^5.0.4" - postcss-minify-gradients "^5.0.6" - postcss-minify-params "^5.0.5" - postcss-minify-selectors "^5.1.3" - postcss-normalize-charset "^5.0.3" - postcss-normalize-display-values "^5.0.3" - postcss-normalize-positions "^5.0.4" - postcss-normalize-repeat-style "^5.0.4" - postcss-normalize-string "^5.0.4" - postcss-normalize-timing-functions "^5.0.3" - postcss-normalize-unicode "^5.0.4" - postcss-normalize-url "^5.0.5" - postcss-normalize-whitespace "^5.0.4" - postcss-ordered-values "^5.0.5" - postcss-reduce-initial "^5.0.3" - postcss-reduce-transforms "^5.0.4" - postcss-svgo "^5.0.4" - postcss-unique-selectors "^5.0.4" + "css-declaration-sorter" "^6.0.3" + "cssnano-utils" "^3.0.2" + "postcss-calc" "^8.2.0" + "postcss-colormin" "^5.2.5" + "postcss-convert-values" "^5.0.4" + "postcss-discard-comments" "^5.0.3" + "postcss-discard-duplicates" "^5.0.3" + "postcss-discard-empty" "^5.0.3" + "postcss-discard-overridden" "^5.0.4" + "postcss-merge-longhand" "^5.0.6" + "postcss-merge-rules" "^5.0.6" + "postcss-minify-font-values" "^5.0.4" + "postcss-minify-gradients" "^5.0.6" + "postcss-minify-params" "^5.0.5" + "postcss-minify-selectors" "^5.1.3" + "postcss-normalize-charset" "^5.0.3" + "postcss-normalize-display-values" "^5.0.3" + "postcss-normalize-positions" "^5.0.4" + "postcss-normalize-repeat-style" "^5.0.4" + "postcss-normalize-string" "^5.0.4" + "postcss-normalize-timing-functions" "^5.0.3" + "postcss-normalize-unicode" "^5.0.4" + "postcss-normalize-url" "^5.0.5" + "postcss-normalize-whitespace" "^5.0.4" + "postcss-ordered-values" "^5.0.5" + "postcss-reduce-initial" "^5.0.3" + "postcss-reduce-transforms" "^5.0.4" + "postcss-svgo" "^5.0.4" + "postcss-unique-selectors" "^5.0.4" -cssnano-utils@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.0.2.tgz#d82b4991a27ba6fec644b39bab35fe027137f516" - integrity sha512-KhprijuQv2sP4kT92sSQwhlK3SJTbDIsxcfIEySB0O+3m9esFOai7dP9bMx5enHAh2MwarVIcnwiWoOm01RIbQ== +"cssnano-utils@^3.0.2": + "integrity" "sha512-KhprijuQv2sP4kT92sSQwhlK3SJTbDIsxcfIEySB0O+3m9esFOai7dP9bMx5enHAh2MwarVIcnwiWoOm01RIbQ==" + "resolved" "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.0.2.tgz" + "version" "3.0.2" -cssnano@^5.0.6, cssnano@^5.0.8: - version "5.0.17" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.17.tgz#ff45713c05cfc780a1aeb3e663b6f224d091cabf" - integrity sha512-fmjLP7k8kL18xSspeXTzRhaFtRI7DL9b8IcXR80JgtnWBpvAzHT7sCR/6qdn0tnxIaINUN6OEQu83wF57Gs3Xw== +"cssnano@^5.0.6", "cssnano@^5.0.8": + "integrity" "sha512-fmjLP7k8kL18xSspeXTzRhaFtRI7DL9b8IcXR80JgtnWBpvAzHT7sCR/6qdn0tnxIaINUN6OEQu83wF57Gs3Xw==" + "resolved" "https://registry.npmjs.org/cssnano/-/cssnano-5.0.17.tgz" + "version" "5.0.17" dependencies: - cssnano-preset-default "^5.1.12" - lilconfig "^2.0.3" - yaml "^1.10.2" + "cssnano-preset-default" "^5.1.12" + "lilconfig" "^2.0.3" + "yaml" "^1.10.2" -csso@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== +"csso@^4.2.0": + "integrity" "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==" + "resolved" "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz" + "version" "4.2.0" dependencies: - css-tree "^1.1.2" + "css-tree" "^1.1.2" -csstype@^3.0.2: - version "3.0.10" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" - integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== +"csstype@^3.0.2": + "integrity" "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==" + "resolved" "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz" + "version" "3.0.10" -debug@2.6.9, debug@^2.6.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== +"debug@^2.6.0", "debug@2.6.9": + "integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==" + "resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + "version" "2.6.9" dependencies: - ms "2.0.0" + "ms" "2.0.0" -debug@^3.1.1: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== +"debug@^3.1.1": + "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==" + "resolved" "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + "version" "3.2.7" dependencies: - ms "^2.1.1" + "ms" "^2.1.1" -debug@^4.1.0, debug@^4.1.1: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== +"debug@^4.1.0": + "integrity" "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" + "version" "4.3.3" dependencies: - ms "2.1.2" + "ms" "2.1.2" -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= +"debug@^4.1.1": + "integrity" "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" + "version" "4.3.3" dependencies: - mimic-response "^1.0.0" + "ms" "2.1.2" -deep-equal@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" - integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== +"decompress-response@^3.3.0": + "integrity" "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=" + "resolved" "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" + "version" "3.3.0" dependencies: - is-arguments "^1.0.4" - is-date-object "^1.0.1" - is-regex "^1.0.4" - object-is "^1.0.1" - object-keys "^1.1.1" - regexp.prototype.flags "^1.2.0" + "mimic-response" "^1.0.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -deepmerge@^1.3.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" - integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== - -deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - -default-gateway@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" - integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== +"deep-equal@^1.0.1": + "integrity" "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==" + "resolved" "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz" + "version" "1.1.1" dependencies: - execa "^5.0.0" + "is-arguments" "^1.0.4" + "is-date-object" "^1.0.1" + "is-regex" "^1.0.4" + "object-is" "^1.0.1" + "object-keys" "^1.1.1" + "regexp.prototype.flags" "^1.2.0" -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +"deep-extend@^0.6.0": + "integrity" "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + "resolved" "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" + "version" "0.6.0" -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +"deepmerge@^1.3.2": + "integrity" "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==" + "resolved" "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz" + "version" "1.5.2" -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== +"deepmerge@^4.2.2": + "integrity" "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + "resolved" "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz" + "version" "4.2.2" + +"default-gateway@^6.0.3": + "integrity" "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==" + "resolved" "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" + "version" "6.0.3" dependencies: - object-keys "^1.0.12" + "execa" "^5.0.0" -del@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" - integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ== +"defer-to-connect@^1.0.1": + "integrity" "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + "resolved" "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz" + "version" "1.1.3" + +"define-lazy-prop@^2.0.0": + "integrity" "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + "resolved" "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" + "version" "2.0.0" + +"define-properties@^1.1.3": + "integrity" "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==" + "resolved" "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" + "version" "1.1.3" dependencies: - globby "^11.0.1" - graceful-fs "^4.2.4" - is-glob "^4.0.1" - is-path-cwd "^2.2.0" - is-path-inside "^3.0.2" - p-map "^4.0.0" - rimraf "^3.0.2" - slash "^3.0.0" + "object-keys" "^1.0.12" -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -detab@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.4.tgz#b927892069aff405fbb9a186fe97a44a92a94b43" - integrity sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g== +"del@^6.0.0": + "integrity" "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==" + "resolved" "https://registry.npmjs.org/del/-/del-6.0.0.tgz" + "version" "6.0.0" dependencies: - repeat-string "^1.5.4" + "globby" "^11.0.1" + "graceful-fs" "^4.2.4" + "is-glob" "^4.0.1" + "is-path-cwd" "^2.2.0" + "is-path-inside" "^3.0.2" + "p-map" "^4.0.0" + "rimraf" "^3.0.2" + "slash" "^3.0.0" -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== +"depd@~1.1.2": + "integrity" "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "resolved" "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" + "version" "1.1.2" -detect-port-alt@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" - integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== +"destroy@~1.0.4": + "integrity" "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "resolved" "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" + "version" "1.0.4" + +"detab@2.0.4": + "integrity" "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==" + "resolved" "https://registry.npmjs.org/detab/-/detab-2.0.4.tgz" + "version" "2.0.4" dependencies: - address "^1.0.1" - debug "^2.6.0" + "repeat-string" "^1.5.4" -detect-port@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" - integrity sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ== +"detect-node@^2.0.4": + "integrity" "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + "resolved" "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" + "version" "2.1.0" + +"detect-port-alt@^1.1.6": + "integrity" "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==" + "resolved" "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz" + "version" "1.1.6" dependencies: - address "^1.0.1" - debug "^2.6.0" + "address" "^1.0.1" + "debug" "^2.6.0" -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== +"detect-port@^1.3.0": + "integrity" "sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==" + "resolved" "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz" + "version" "1.3.0" dependencies: - path-type "^4.0.0" + "address" "^1.0.1" + "debug" "^2.6.0" -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= - -dns-packet@^1.3.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" - integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== +"dir-glob@^3.0.1": + "integrity" "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==" + "resolved" "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + "version" "3.0.1" dependencies: - ip "^1.1.0" - safe-buffer "^5.0.1" + "path-type" "^4.0.0" -dns-txt@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" - integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= +"dns-equal@^1.0.0": + "integrity" "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + "resolved" "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz" + "version" "1.0.0" + +"dns-packet@^1.3.1": + "integrity" "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==" + "resolved" "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz" + "version" "1.3.4" dependencies: - buffer-indexof "^1.0.0" + "ip" "^1.1.0" + "safe-buffer" "^5.0.1" -dom-converter@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" - integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== +"dns-txt@^2.0.2": + "integrity" "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=" + "resolved" "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz" + "version" "2.0.2" dependencies: - utila "~0.4" + "buffer-indexof" "^1.0.0" -dom-serializer@0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== +"dom-converter@^0.2.0": + "integrity" "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==" + "resolved" "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz" + "version" "0.2.0" dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" + "utila" "~0.4" -dom-serializer@^1.0.1, dom-serializer@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" - integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== +"dom-serializer@^1.0.1": + "integrity" "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz" + "version" "1.3.2" dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.0" - entities "^2.0.0" + "domelementtype" "^2.0.1" + "domhandler" "^4.2.0" + "entities" "^2.0.0" -dom-serializer@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" - integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== +"dom-serializer@^1.3.2": + "integrity" "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz" + "version" "1.3.2" dependencies: - domelementtype "^1.3.0" - entities "^1.1.1" + "domelementtype" "^2.0.1" + "domhandler" "^4.2.0" + "entities" "^2.0.0" -domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1, domelementtype@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" - integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== - -domhandler@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" - integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== +"dom-serializer@~0.1.0": + "integrity" "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz" + "version" "0.1.1" dependencies: - domelementtype "1" + "domelementtype" "^1.3.0" + "entities" "^1.1.1" -domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" - integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== +"dom-serializer@0": + "integrity" "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" + "version" "0.2.2" dependencies: - domelementtype "^2.2.0" + "domelementtype" "^2.0.1" + "entities" "^2.0.0" -domutils@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= +"domelementtype@^1.3.0", "domelementtype@^1.3.1", "domelementtype@1": + "integrity" "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" + "version" "1.3.1" + +"domelementtype@^2.0.1", "domelementtype@^2.2.0": + "integrity" "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" + "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz" + "version" "2.2.0" + +"domhandler@^2.3.0": + "integrity" "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==" + "resolved" "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz" + "version" "2.4.2" dependencies: - dom-serializer "0" - domelementtype "1" + "domelementtype" "1" -domutils@^1.5.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== +"domhandler@^4.0.0", "domhandler@^4.2.0", "domhandler@^4.3.0": + "integrity" "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==" + "resolved" "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz" + "version" "4.3.0" dependencies: - dom-serializer "0" - domelementtype "1" + "domelementtype" "^2.2.0" -domutils@^2.5.2, domutils@^2.7.0, domutils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" - integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== +"domutils@^1.5.1": + "integrity" "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==" + "resolved" "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" + "version" "1.7.0" dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" + "dom-serializer" "0" + "domelementtype" "1" -dot-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" - integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== +"domutils@^2.5.2", "domutils@^2.7.0", "domutils@^2.8.0": + "integrity" "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==" + "resolved" "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz" + "version" "2.8.0" dependencies: - no-case "^3.0.4" - tslib "^2.0.3" + "dom-serializer" "^1.0.1" + "domelementtype" "^2.2.0" + "domhandler" "^4.2.0" -dot-prop@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" - integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== +"domutils@1.5.1": + "integrity" "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=" + "resolved" "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz" + "version" "1.5.1" dependencies: - is-obj "^2.0.0" + "dom-serializer" "0" + "domelementtype" "1" -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -duplexer@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -electron-to-chromium@^1.4.17: - version "1.4.71" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.71.tgz#17056914465da0890ce00351a3b946fd4cd51ff6" - integrity sha512-Hk61vXXKRb2cd3znPE9F+2pLWdIOmP7GjiTj45y6L3W/lO+hSnUSUhq+6lEaERWBdZOHbk2s3YV5c9xVl3boVw== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - -emoticon@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-3.2.0.tgz#c008ca7d7620fac742fe1bf4af8ff8fed154ae7f" - integrity sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== +"dot-case@^3.0.4": + "integrity" "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==" + "resolved" "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" + "version" "3.0.4" dependencies: - once "^1.4.0" + "no-case" "^3.0.4" + "tslib" "^2.0.3" -enhanced-resolve@^5.8.3: - version "5.9.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz#49ac24953ac8452ed8fed2ef1340fc8e043667ee" - integrity sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA== +"dot-prop@^5.2.0": + "integrity" "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==" + "resolved" "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" + "version" "5.3.0" dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" + "is-obj" "^2.0.0" -entities@^1.1.1, entities@~1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" - integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== +"duplexer@^0.1.2": + "integrity" "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + "resolved" "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" + "version" "0.1.2" -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +"duplexer3@^0.1.4": + "integrity" "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + "resolved" "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" + "version" "0.1.4" -entities@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" - integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== +"ee-first@1.1.1": + "integrity" "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "resolved" "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + "version" "1.1.1" -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== +"electron-to-chromium@^1.4.284": + "integrity" "sha512-DE9tTy2PNmy1v55AZAO542ui+MLC2cvINMK4P2LXGsJdput/ThVG9t+QGecPuAZZSgC8XoI+Jh9M1OG9IoNSCw==" + "resolved" "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.328.tgz" + "version" "1.4.328" + +"emoji-regex@^8.0.0": + "integrity" "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + "version" "8.0.0" + +"emojis-list@^3.0.0": + "integrity" "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + "resolved" "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" + "version" "3.0.0" + +"emoticon@^3.2.0": + "integrity" "sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg==" + "resolved" "https://registry.npmjs.org/emoticon/-/emoticon-3.2.0.tgz" + "version" "3.2.0" + +"encodeurl@~1.0.2": + "integrity" "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "resolved" "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" + "version" "1.0.2" + +"end-of-stream@^1.1.0": + "integrity" "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==" + "resolved" "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" + "version" "1.4.4" dependencies: - is-arrayish "^0.2.1" + "once" "^1.4.0" -es-module-lexer@^0.9.0: - version "0.9.3" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" - integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== - -escape-html@^1.0.3, escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-scope@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== +"enhanced-resolve@^5.8.3": + "integrity" "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==" + "resolved" "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz" + "version" "5.9.0" dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" + "graceful-fs" "^4.2.4" + "tapable" "^2.2.0" -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +"entities@^1.1.1", "entities@~1.1.1": + "integrity" "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + "resolved" "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz" + "version" "1.1.2" -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== +"entities@^2.0.0": + "integrity" "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + "resolved" "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" + "version" "2.2.0" + +"entities@^3.0.1": + "integrity" "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==" + "resolved" "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz" + "version" "3.0.1" + +"error-ex@^1.3.1": + "integrity" "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==" + "resolved" "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + "version" "1.3.2" dependencies: - estraverse "^5.2.0" + "is-arrayish" "^0.2.1" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +"es-module-lexer@^0.9.0": + "integrity" "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + "resolved" "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz" + "version" "0.9.3" -estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +"escalade@^3.1.1": + "integrity" "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "resolved" "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + "version" "3.1.1" -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +"escape-goat@^2.0.0": + "integrity" "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" + "resolved" "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz" + "version" "2.1.1" -eta@^1.12.3: - version "1.12.3" - resolved "https://registry.yarnpkg.com/eta/-/eta-1.12.3.tgz#2982d08adfbef39f9fa50e2fbd42d7337e7338b1" - integrity sha512-qHixwbDLtekO/d51Yr4glcaUJCIjGVJyTzuqV4GPlgZo1YpgOKG+avQynErZIYrfM6JIJdtiG2Kox8tbb+DoGg== +"escape-html@^1.0.3", "escape-html@~1.0.3": + "integrity" "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "resolved" "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + "version" "1.0.3" -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +"escape-string-regexp@^1.0.5": + "integrity" "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + "version" "1.0.5" -eval@^0.1.4: - version "0.1.6" - resolved "https://registry.yarnpkg.com/eval/-/eval-0.1.6.tgz#9620d7d8c85515e97e6b47c5814f46ae381cb3cc" - integrity sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ== +"escape-string-regexp@^4.0.0": + "integrity" "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + "version" "4.0.0" + +"eslint-scope@5.1.1": + "integrity" "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==" + "resolved" "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + "version" "5.1.1" dependencies: - require-like ">= 0.1.1" + "esrecurse" "^4.3.0" + "estraverse" "^4.1.1" -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +"esprima@^4.0.0": + "integrity" "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "resolved" "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + "version" "4.0.1" -events@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== +"esrecurse@^4.3.0": + "integrity" "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==" + "resolved" "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + "version" "4.3.0" dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" + "estraverse" "^5.2.0" -express@^4.17.1: - version "4.17.3" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" - integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== +"estraverse@^4.1.1": + "integrity" "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + "version" "4.3.0" + +"estraverse@^5.2.0": + "integrity" "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + "version" "5.3.0" + +"esutils@^2.0.2": + "integrity" "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "resolved" "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + "version" "2.0.3" + +"eta@^1.12.3": + "integrity" "sha512-qHixwbDLtekO/d51Yr4glcaUJCIjGVJyTzuqV4GPlgZo1YpgOKG+avQynErZIYrfM6JIJdtiG2Kox8tbb+DoGg==" + "resolved" "https://registry.npmjs.org/eta/-/eta-1.12.3.tgz" + "version" "1.12.3" + +"etag@~1.8.1": + "integrity" "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "resolved" "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" + "version" "1.8.1" + +"eval@^0.1.4": + "integrity" "sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ==" + "resolved" "https://registry.npmjs.org/eval/-/eval-0.1.6.tgz" + "version" "0.1.6" dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.19.2" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.4.2" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.9.7" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.17.2" - serve-static "1.14.2" - setprototypeof "1.2.0" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" + "require-like" ">= 0.1.1" -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= +"eventemitter3@^4.0.0": + "integrity" "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "resolved" "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + "version" "4.0.7" + +"events@^3.2.0": + "integrity" "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + "resolved" "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + "version" "3.3.0" + +"execa@^5.0.0": + "integrity" "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==" + "resolved" "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + "version" "5.1.1" dependencies: - is-extendable "^0.1.0" + "cross-spawn" "^7.0.3" + "get-stream" "^6.0.0" + "human-signals" "^2.1.0" + "is-stream" "^2.0.0" + "merge-stream" "^2.0.0" + "npm-run-path" "^4.0.1" + "onetime" "^5.1.2" + "signal-exit" "^3.0.3" + "strip-final-newline" "^2.0.0" -extend@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +"express@^4.17.1": + "integrity" "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==" + "resolved" "https://registry.npmjs.org/express/-/express-4.17.3.tgz" + "version" "4.17.3" + dependencies: + "accepts" "~1.3.8" + "array-flatten" "1.1.1" + "body-parser" "1.19.2" + "content-disposition" "0.5.4" + "content-type" "~1.0.4" + "cookie" "0.4.2" + "cookie-signature" "1.0.6" + "debug" "2.6.9" + "depd" "~1.1.2" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "etag" "~1.8.1" + "finalhandler" "~1.1.2" + "fresh" "0.5.2" + "merge-descriptors" "1.0.1" + "methods" "~1.1.2" + "on-finished" "~2.3.0" + "parseurl" "~1.3.3" + "path-to-regexp" "0.1.7" + "proxy-addr" "~2.0.7" + "qs" "6.9.7" + "range-parser" "~1.2.1" + "safe-buffer" "5.2.1" + "send" "0.17.2" + "serve-static" "1.14.2" + "setprototypeof" "1.2.0" + "statuses" "~1.5.0" + "type-is" "~1.6.18" + "utils-merge" "1.0.1" + "vary" "~1.1.2" -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +"extend-shallow@^2.0.1": + "integrity" "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" + "resolved" "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "is-extendable" "^0.1.0" -fast-glob@^3.2.7, fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== +"extend@^3.0.0": + "integrity" "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "resolved" "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" + "version" "3.0.2" + +"fast-deep-equal@^3.1.1", "fast-deep-equal@^3.1.3": + "integrity" "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "resolved" "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + "version" "3.1.3" + +"fast-glob@^3.2.7", "fast-glob@^3.2.9": + "integrity" "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==" + "resolved" "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz" + "version" "3.2.11" dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" + "glob-parent" "^5.1.2" + "merge2" "^1.3.0" + "micromatch" "^4.0.4" -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +"fast-json-stable-stringify@^2.0.0": + "integrity" "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "resolved" "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + "version" "2.1.0" -fast-url-parser@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" - integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= +"fast-url-parser@1.1.3": + "integrity" "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=" + "resolved" "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz" + "version" "1.1.3" dependencies: - punycode "^1.3.2" + "punycode" "^1.3.2" -fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== +"fastq@^1.6.0": + "integrity" "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==" + "resolved" "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" + "version" "1.13.0" dependencies: - reusify "^1.0.4" + "reusify" "^1.0.4" -faye-websocket@^0.11.3: - version "0.11.4" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== +"faye-websocket@^0.11.3": + "integrity" "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==" + "resolved" "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" + "version" "0.11.4" dependencies: - websocket-driver ">=0.5.1" + "websocket-driver" ">=0.5.1" -fbemitter@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-3.0.0.tgz#00b2a1af5411254aab416cd75f9e6289bee4bff3" - integrity sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw== +"fbemitter@^3.0.0": + "integrity" "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==" + "resolved" "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz" + "version" "3.0.0" dependencies: - fbjs "^3.0.0" + "fbjs" "^3.0.0" -fbjs-css-vars@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" - integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== +"fbjs-css-vars@^1.0.0": + "integrity" "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + "resolved" "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz" + "version" "1.0.2" -fbjs@^3.0.0, fbjs@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.4.tgz#e1871c6bd3083bac71ff2da868ad5067d37716c6" - integrity sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ== +"fbjs@^3.0.0", "fbjs@^3.0.1": + "integrity" "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==" + "resolved" "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz" + "version" "3.0.4" dependencies: - cross-fetch "^3.1.5" - fbjs-css-vars "^1.0.0" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.30" + "cross-fetch" "^3.1.5" + "fbjs-css-vars" "^1.0.0" + "loose-envify" "^1.0.0" + "object-assign" "^4.1.0" + "promise" "^7.1.1" + "setimmediate" "^1.0.5" + "ua-parser-js" "^0.7.30" -feed@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/feed/-/feed-4.2.2.tgz#865783ef6ed12579e2c44bbef3c9113bc4956a7e" - integrity sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ== +"feed@^4.2.2": + "integrity" "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==" + "resolved" "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz" + "version" "4.2.2" dependencies: - xml-js "^1.6.11" + "xml-js" "^1.6.11" -file-loader@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" - integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== +"file-loader@*", "file-loader@^6.2.0": + "integrity" "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==" + "resolved" "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz" + "version" "6.2.0" dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" + "loader-utils" "^2.0.0" + "schema-utils" "^3.0.0" -filesize@^8.0.6: - version "8.0.7" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8" - integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ== +"filesize@^8.0.6": + "integrity" "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" + "resolved" "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz" + "version" "8.0.7" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +"fill-range@^7.0.1": + "integrity" "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==" + "resolved" "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + "version" "7.0.1" dependencies: - to-regex-range "^5.0.1" + "to-regex-range" "^5.0.1" -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== +"finalhandler@~1.1.2": + "integrity" "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==" + "resolved" "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" + "version" "1.1.2" dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" + "debug" "2.6.9" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "on-finished" "~2.3.0" + "parseurl" "~1.3.3" + "statuses" "~1.5.0" + "unpipe" "~1.0.0" -find-cache-dir@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" - integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== +"find-cache-dir@^3.3.1": + "integrity" "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==" + "resolved" "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" + "version" "3.3.2" dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" + "commondir" "^1.0.1" + "make-dir" "^3.0.2" + "pkg-dir" "^4.1.0" -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== +"find-up@^3.0.0": + "integrity" "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" + "version" "3.0.0" dependencies: - locate-path "^3.0.0" + "locate-path" "^3.0.0" -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== +"find-up@^4.0.0": + "integrity" "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + "version" "4.1.0" dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" + "locate-path" "^5.0.0" + "path-exists" "^4.0.0" -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== +"find-up@^5.0.0": + "integrity" "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + "version" "5.0.0" dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" + "locate-path" "^6.0.0" + "path-exists" "^4.0.0" -flux@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/flux/-/flux-4.0.3.tgz#573b504a24982c4768fdfb59d8d2ea5637d72ee7" - integrity sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw== +"flux@^4.0.1": + "integrity" "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==" + "resolved" "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz" + "version" "4.0.3" dependencies: - fbemitter "^3.0.0" - fbjs "^3.0.1" + "fbemitter" "^3.0.0" + "fbjs" "^3.0.1" -follow-redirects@^1.0.0, follow-redirects@^1.14.7: - version "1.14.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" - integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== +"follow-redirects@^1.0.0", "follow-redirects@^1.14.7": + "integrity" "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz" + "version" "1.14.9" -fork-ts-checker-webpack-plugin@^6.5.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.0.tgz#0282b335fa495a97e167f69018f566ea7d2a2b5e" - integrity sha512-cS178Y+xxtIjEUorcHddKS7yCMlrDPV31mt47blKKRfMd70Kxu5xruAFE2o9sDY6wVC5deuob/u/alD04YYHnw== +"fork-ts-checker-webpack-plugin@^6.5.0": + "integrity" "sha512-cS178Y+xxtIjEUorcHddKS7yCMlrDPV31mt47blKKRfMd70Kxu5xruAFE2o9sDY6wVC5deuob/u/alD04YYHnw==" + "resolved" "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.0.tgz" + "version" "6.5.0" dependencies: "@babel/code-frame" "^7.8.3" "@types/json-schema" "^7.0.5" - chalk "^4.1.0" - chokidar "^3.4.2" - cosmiconfig "^6.0.0" - deepmerge "^4.2.2" - fs-extra "^9.0.0" - glob "^7.1.6" - memfs "^3.1.2" - minimatch "^3.0.4" - schema-utils "2.7.0" - semver "^7.3.2" - tapable "^1.0.0" + "chalk" "^4.1.0" + "chokidar" "^3.4.2" + "cosmiconfig" "^6.0.0" + "deepmerge" "^4.2.2" + "fs-extra" "^9.0.0" + "glob" "^7.1.6" + "memfs" "^3.1.2" + "minimatch" "^3.0.4" + "schema-utils" "2.7.0" + "semver" "^7.3.2" + "tapable" "^1.0.0" -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +"forwarded@0.2.0": + "integrity" "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "resolved" "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" + "version" "0.2.0" -fraction.js@^4.1.2: - version "4.1.3" - resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.1.3.tgz#be65b0f20762ef27e1e793860bc2dfb716e99e65" - integrity sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg== +"fraction.js@^4.1.2": + "integrity" "sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg==" + "resolved" "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.3.tgz" + "version" "4.1.3" -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +"fresh@0.5.2": + "integrity" "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "resolved" "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" + "version" "0.5.2" -fs-extra@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" - integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== +"fs-extra@^10.0.0": + "integrity" "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==" + "resolved" "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz" + "version" "10.0.0" dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" + "graceful-fs" "^4.2.0" + "jsonfile" "^6.0.1" + "universalify" "^2.0.0" -fs-extra@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== +"fs-extra@^9.0.0": + "integrity" "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==" + "resolved" "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" + "version" "9.1.0" dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" + "at-least-node" "^1.0.0" + "graceful-fs" "^4.2.0" + "jsonfile" "^6.0.1" + "universalify" "^2.0.0" -fs-monkey@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" - integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== +"fs-monkey@1.0.3": + "integrity" "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + "resolved" "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz" + "version" "1.0.3" -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +"fs.realpath@^1.0.0": + "integrity" "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + "version" "1.0.0" -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +"function-bind@^1.1.1": + "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + "version" "1.1.1" -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +"gensync@^1.0.0-beta.1", "gensync@^1.0.0-beta.2": + "integrity" "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + "resolved" "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + "version" "1.0.0-beta.2" -gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-intrinsic@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== +"get-intrinsic@^1.0.2": + "integrity" "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==" + "resolved" "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" + "version" "1.1.1" dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" + "function-bind" "^1.1.1" + "has" "^1.0.3" + "has-symbols" "^1.0.1" -get-own-enumerable-property-symbols@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" - integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== +"get-own-enumerable-property-symbols@^3.0.0": + "integrity" "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + "resolved" "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz" + "version" "3.0.2" -get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== +"get-stream@^4.1.0": + "integrity" "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==" + "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" + "version" "4.1.0" dependencies: - pump "^3.0.0" + "pump" "^3.0.0" -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== +"get-stream@^5.1.0": + "integrity" "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==" + "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" + "version" "5.2.0" dependencies: - pump "^3.0.0" + "pump" "^3.0.0" -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +"get-stream@^6.0.0": + "integrity" "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + "version" "6.0.1" -github-slugger@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e" - integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ== +"github-slugger@^1.4.0": + "integrity" "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==" + "resolved" "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz" + "version" "1.4.0" -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== +"glob-parent@^5.1.2", "glob-parent@~5.1.2": + "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + "version" "5.1.2" dependencies: - is-glob "^4.0.1" + "is-glob" "^4.0.1" -glob-parent@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== +"glob-parent@^6.0.1": + "integrity" "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + "version" "6.0.2" dependencies: - is-glob "^4.0.3" + "is-glob" "^4.0.3" -glob-to-regexp@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" - integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +"glob-to-regexp@^0.4.1": + "integrity" "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + "resolved" "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" + "version" "0.4.1" -glob@^7.0.0, glob@^7.1.3, glob@^7.1.6: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +"glob@^7.0.0", "glob@^7.1.3", "glob@^7.1.6": + "integrity" "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==" + "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" + "version" "7.2.0" dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" + "fs.realpath" "^1.0.0" + "inflight" "^1.0.4" + "inherits" "2" + "minimatch" "^3.0.4" + "once" "^1.3.0" + "path-is-absolute" "^1.0.0" -global-dirs@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" - integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== +"global-dirs@^3.0.0": + "integrity" "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==" + "resolved" "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz" + "version" "3.0.0" dependencies: - ini "2.0.0" + "ini" "2.0.0" -global-modules@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== +"global-modules@^2.0.0": + "integrity" "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==" + "resolved" "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz" + "version" "2.0.0" dependencies: - global-prefix "^3.0.0" + "global-prefix" "^3.0.0" -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== +"global-prefix@^3.0.0": + "integrity" "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==" + "resolved" "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz" + "version" "3.0.0" dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" + "ini" "^1.3.5" + "kind-of" "^6.0.2" + "which" "^1.3.1" -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +"globals@^11.1.0": + "integrity" "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + "resolved" "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + "version" "11.12.0" -globby@^11.0.1, globby@^11.0.2, globby@^11.0.4: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== +"globby@^11.0.1", "globby@^11.0.2", "globby@^11.0.4": + "integrity" "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==" + "resolved" "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + "version" "11.1.0" dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" + "array-union" "^2.1.0" + "dir-glob" "^3.0.1" + "fast-glob" "^3.2.9" + "ignore" "^5.2.0" + "merge2" "^1.4.1" + "slash" "^3.0.0" -globby@^12.0.2: - version "12.2.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-12.2.0.tgz#2ab8046b4fba4ff6eede835b29f678f90e3d3c22" - integrity sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA== +"globby@^12.0.2": + "integrity" "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==" + "resolved" "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz" + "version" "12.2.0" dependencies: - array-union "^3.0.1" - dir-glob "^3.0.1" - fast-glob "^3.2.7" - ignore "^5.1.9" - merge2 "^1.4.1" - slash "^4.0.0" + "array-union" "^3.0.1" + "dir-glob" "^3.0.1" + "fast-glob" "^3.2.7" + "ignore" "^5.1.9" + "merge2" "^1.4.1" + "slash" "^4.0.0" -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== +"got@^9.6.0": + "integrity" "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==" + "resolved" "https://registry.npmjs.org/got/-/got-9.6.0.tgz" + "version" "9.6.0" dependencies: "@sindresorhus/is" "^0.14.0" "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" + "cacheable-request" "^6.0.0" + "decompress-response" "^3.3.0" + "duplexer3" "^0.1.4" + "get-stream" "^4.1.0" + "lowercase-keys" "^1.0.1" + "mimic-response" "^1.0.1" + "p-cancelable" "^1.0.0" + "to-readable-stream" "^1.0.0" + "url-parse-lax" "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: - version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" - integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== +"graceful-fs@^4.1.2", "graceful-fs@^4.1.6", "graceful-fs@^4.2.0", "graceful-fs@^4.2.4", "graceful-fs@^4.2.6", "graceful-fs@^4.2.9": + "integrity" "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + "resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" + "version" "4.2.9" -gray-matter@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" - integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== +"gray-matter@^4.0.3": + "integrity" "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==" + "resolved" "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz" + "version" "4.0.3" dependencies: - js-yaml "^3.13.1" - kind-of "^6.0.2" - section-matter "^1.0.0" - strip-bom-string "^1.0.0" + "js-yaml" "^3.13.1" + "kind-of" "^6.0.2" + "section-matter" "^1.0.0" + "strip-bom-string" "^1.0.0" -gzip-size@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" - integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== +"gzip-size@^6.0.0": + "integrity" "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==" + "resolved" "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz" + "version" "6.0.0" dependencies: - duplexer "^0.1.2" + "duplexer" "^0.1.2" -handle-thing@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" - integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== +"handle-thing@^2.0.0": + "integrity" "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + "resolved" "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz" + "version" "2.0.1" -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +"has-flag@^3.0.0": + "integrity" "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + "version" "3.0.0" -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +"has-flag@^4.0.0": + "integrity" "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + "version" "4.0.0" -has-symbols@^1.0.1, has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +"has-symbols@^1.0.1", "has-symbols@^1.0.2": + "integrity" "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + "resolved" "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz" + "version" "1.0.2" -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== +"has-tostringtag@^1.0.0": + "integrity" "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==" + "resolved" "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" + "version" "1.0.0" dependencies: - has-symbols "^1.0.2" + "has-symbols" "^1.0.2" -has-yarn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" - integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== +"has-yarn@^2.1.0": + "integrity" "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + "resolved" "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz" + "version" "2.1.0" -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +"has@^1.0.3": + "integrity" "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==" + "resolved" "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + "version" "1.0.3" dependencies: - function-bind "^1.1.1" + "function-bind" "^1.1.1" -hast-to-hyperscript@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d" - integrity sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA== +"hast-to-hyperscript@^9.0.0": + "integrity" "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==" + "resolved" "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz" + "version" "9.0.1" dependencies: "@types/unist" "^2.0.3" - comma-separated-tokens "^1.0.0" - property-information "^5.3.0" - space-separated-tokens "^1.0.0" - style-to-object "^0.3.0" - unist-util-is "^4.0.0" - web-namespaces "^1.0.0" + "comma-separated-tokens" "^1.0.0" + "property-information" "^5.3.0" + "space-separated-tokens" "^1.0.0" + "style-to-object" "^0.3.0" + "unist-util-is" "^4.0.0" + "web-namespaces" "^1.0.0" -hast-util-from-parse5@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz#3089dc0ee2ccf6ec8bc416919b51a54a589e097c" - integrity sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA== +"hast-util-from-parse5@^5.0.0": + "integrity" "sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA==" + "resolved" "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz" + "version" "5.0.3" dependencies: - ccount "^1.0.3" - hastscript "^5.0.0" - property-information "^5.0.0" - web-namespaces "^1.1.2" - xtend "^4.0.1" + "ccount" "^1.0.3" + "hastscript" "^5.0.0" + "property-information" "^5.0.0" + "web-namespaces" "^1.1.2" + "xtend" "^4.0.1" -hast-util-from-parse5@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" - integrity sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA== +"hast-util-from-parse5@^6.0.0": + "integrity" "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==" + "resolved" "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz" + "version" "6.0.1" dependencies: "@types/parse5" "^5.0.0" - hastscript "^6.0.0" - property-information "^5.0.0" - vfile "^4.0.0" - vfile-location "^3.2.0" - web-namespaces "^1.0.0" + "hastscript" "^6.0.0" + "property-information" "^5.0.0" + "vfile" "^4.0.0" + "vfile-location" "^3.2.0" + "web-namespaces" "^1.0.0" -hast-util-parse-selector@^2.0.0: - version "2.2.5" - resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" - integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== +"hast-util-parse-selector@^2.0.0": + "integrity" "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" + "resolved" "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz" + "version" "2.2.5" -hast-util-raw@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-6.0.1.tgz#973b15930b7529a7b66984c98148b46526885977" - integrity sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig== +"hast-util-raw@6.0.1": + "integrity" "sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==" + "resolved" "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-6.0.1.tgz" + "version" "6.0.1" dependencies: "@types/hast" "^2.0.0" - hast-util-from-parse5 "^6.0.0" - hast-util-to-parse5 "^6.0.0" - html-void-elements "^1.0.0" - parse5 "^6.0.0" - unist-util-position "^3.0.0" - vfile "^4.0.0" - web-namespaces "^1.0.0" - xtend "^4.0.0" - zwitch "^1.0.0" + "hast-util-from-parse5" "^6.0.0" + "hast-util-to-parse5" "^6.0.0" + "html-void-elements" "^1.0.0" + "parse5" "^6.0.0" + "unist-util-position" "^3.0.0" + "vfile" "^4.0.0" + "web-namespaces" "^1.0.0" + "xtend" "^4.0.0" + "zwitch" "^1.0.0" -hast-util-to-parse5@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" - integrity sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ== +"hast-util-to-parse5@^6.0.0": + "integrity" "sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==" + "resolved" "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz" + "version" "6.0.0" dependencies: - hast-to-hyperscript "^9.0.0" - property-information "^5.0.0" - web-namespaces "^1.0.0" - xtend "^4.0.0" - zwitch "^1.0.0" + "hast-to-hyperscript" "^9.0.0" + "property-information" "^5.0.0" + "web-namespaces" "^1.0.0" + "xtend" "^4.0.0" + "zwitch" "^1.0.0" -hastscript@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-5.1.2.tgz#bde2c2e56d04c62dd24e8c5df288d050a355fb8a" - integrity sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ== +"hastscript@^5.0.0": + "integrity" "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==" + "resolved" "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz" + "version" "5.1.2" dependencies: - comma-separated-tokens "^1.0.0" - hast-util-parse-selector "^2.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" + "comma-separated-tokens" "^1.0.0" + "hast-util-parse-selector" "^2.0.0" + "property-information" "^5.0.0" + "space-separated-tokens" "^1.0.0" -hastscript@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" - integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== +"hastscript@^6.0.0": + "integrity" "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==" + "resolved" "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz" + "version" "6.0.0" dependencies: "@types/hast" "^2.0.0" - comma-separated-tokens "^1.0.0" - hast-util-parse-selector "^2.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" + "comma-separated-tokens" "^1.0.0" + "hast-util-parse-selector" "^2.0.0" + "property-information" "^5.0.0" + "space-separated-tokens" "^1.0.0" -he@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +"he@^1.2.0": + "integrity" "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + "resolved" "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + "version" "1.2.0" -history@^4.9.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" - integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== +"history@^4.9.0": + "integrity" "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==" + "resolved" "https://registry.npmjs.org/history/-/history-4.10.1.tgz" + "version" "4.10.1" dependencies: "@babel/runtime" "^7.1.2" - loose-envify "^1.2.0" - resolve-pathname "^3.0.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - value-equal "^1.0.1" + "loose-envify" "^1.2.0" + "resolve-pathname" "^3.0.0" + "tiny-invariant" "^1.0.2" + "tiny-warning" "^1.0.0" + "value-equal" "^1.0.1" -hoist-non-react-statics@^3.1.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== +"hoist-non-react-statics@^3.1.0": + "integrity" "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==" + "resolved" "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" + "version" "3.3.2" dependencies: - react-is "^16.7.0" + "react-is" "^16.7.0" -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= +"hpack.js@^2.1.6": + "integrity" "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=" + "resolved" "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz" + "version" "2.1.6" dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" + "inherits" "^2.0.1" + "obuf" "^1.0.0" + "readable-stream" "^2.0.1" + "wbuf" "^1.1.0" -html-entities@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.2.tgz#760b404685cb1d794e4f4b744332e3b00dcfe488" - integrity sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ== +"html-entities@^2.3.2": + "integrity" "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==" + "resolved" "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz" + "version" "2.3.2" -html-minifier-terser@^6.0.2: - version "6.1.0" - resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" - integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== +"html-minifier-terser@^6.0.2": + "integrity" "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==" + "resolved" "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + "version" "6.1.0" dependencies: - camel-case "^4.1.2" - clean-css "^5.2.2" - commander "^8.3.0" - he "^1.2.0" - param-case "^3.0.4" - relateurl "^0.2.7" - terser "^5.10.0" + "camel-case" "^4.1.2" + "clean-css" "^5.2.2" + "commander" "^8.3.0" + "he" "^1.2.0" + "param-case" "^3.0.4" + "relateurl" "^0.2.7" + "terser" "^5.10.0" -html-tags@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" - integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== +"html-tags@^3.1.0": + "integrity" "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==" + "resolved" "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz" + "version" "3.1.0" -html-void-elements@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" - integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== +"html-void-elements@^1.0.0": + "integrity" "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==" + "resolved" "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz" + "version" "1.0.5" -html-webpack-plugin@^5.4.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" - integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw== +"html-webpack-plugin@^5.4.0": + "integrity" "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==" + "resolved" "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz" + "version" "5.5.0" dependencies: "@types/html-minifier-terser" "^6.0.0" - html-minifier-terser "^6.0.2" - lodash "^4.17.21" - pretty-error "^4.0.0" - tapable "^2.0.0" + "html-minifier-terser" "^6.0.2" + "lodash" "^4.17.21" + "pretty-error" "^4.0.0" + "tapable" "^2.0.0" -htmlparser2@^3.9.1: - version "3.10.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" - integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== +"htmlparser2@^3.9.1": + "integrity" "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==" + "resolved" "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz" + "version" "3.10.1" dependencies: - domelementtype "^1.3.1" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^3.1.1" + "domelementtype" "^1.3.1" + "domhandler" "^2.3.0" + "domutils" "^1.5.1" + "entities" "^1.1.1" + "inherits" "^2.0.1" + "readable-stream" "^3.1.1" -htmlparser2@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" - integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== +"htmlparser2@^6.1.0": + "integrity" "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==" + "resolved" "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz" + "version" "6.1.0" dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.5.2" - entities "^2.0.0" + "domelementtype" "^2.0.1" + "domhandler" "^4.0.0" + "domutils" "^2.5.2" + "entities" "^2.0.0" -http-cache-semantics@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" - integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== +"http-cache-semantics@^4.0.0": + "integrity" "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + "resolved" "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz" + "version" "4.1.1" -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= +"http-deceiver@^1.2.7": + "integrity" "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + "resolved" "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" + "version" "1.2.7" -http-errors@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" - integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== +"http-errors@~1.6.2": + "integrity" "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=" + "resolved" "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + "version" "1.6.3" dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.1" + "depd" "~1.1.2" + "inherits" "2.0.3" + "setprototypeof" "1.1.0" + "statuses" ">= 1.4.0 < 2" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= +"http-errors@1.8.1": + "integrity" "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==" + "resolved" "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz" + "version" "1.8.1" dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" + "depd" "~1.1.2" + "inherits" "2.0.4" + "setprototypeof" "1.2.0" + "statuses" ">= 1.5.0 < 2" + "toidentifier" "1.0.1" -http-parser-js@>=0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.5.tgz#d7c30d5d3c90d865b4a2e870181f9d6f22ac7ac5" - integrity sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA== +"http-parser-js@>=0.5.1": + "integrity" "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==" + "resolved" "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz" + "version" "0.5.5" -http-proxy-middleware@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.3.tgz#5df04f69a89f530c2284cd71eeaa51ba52243289" - integrity sha512-1bloEwnrHMnCoO/Gcwbz7eSVvW50KPES01PecpagI+YLNLci4AcuKJrujW4Mc3sBLpFxMSlsLNHS5Nl/lvrTPA== +"http-proxy-middleware@^2.0.0": + "integrity" "sha512-1bloEwnrHMnCoO/Gcwbz7eSVvW50KPES01PecpagI+YLNLci4AcuKJrujW4Mc3sBLpFxMSlsLNHS5Nl/lvrTPA==" + "resolved" "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.3.tgz" + "version" "2.0.3" dependencies: "@types/http-proxy" "^1.17.8" - http-proxy "^1.18.1" - is-glob "^4.0.1" - is-plain-obj "^3.0.0" - micromatch "^4.0.2" + "http-proxy" "^1.18.1" + "is-glob" "^4.0.1" + "is-plain-obj" "^3.0.0" + "micromatch" "^4.0.2" -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== +"http-proxy@^1.18.1": + "integrity" "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==" + "resolved" "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz" + "version" "1.18.1" dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" + "eventemitter3" "^4.0.0" + "follow-redirects" "^1.0.0" + "requires-port" "^1.0.0" -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +"human-signals@^2.1.0": + "integrity" "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + "resolved" "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + "version" "2.1.0" -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== +"iconv-lite@0.4.24": + "integrity" "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==" + "resolved" "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + "version" "0.4.24" dependencies: - safer-buffer ">= 2.1.2 < 3" + "safer-buffer" ">= 2.1.2 < 3" -icss-utils@^5.0.0, icss-utils@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" - integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== +"icss-utils@^5.0.0", "icss-utils@^5.1.0": + "integrity" "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" + "resolved" "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" + "version" "5.1.0" -ignore@^5.1.9, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +"ignore@^5.1.9", "ignore@^5.2.0": + "integrity" "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + "resolved" "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" + "version" "5.2.0" -image-size@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.1.tgz#86d6cfc2b1d19eab5d2b368d4b9194d9e48541c5" - integrity sha512-VAwkvNSNGClRw9mDHhc5Efax8PLlsOGcUTh0T/LIriC8vPA3U5PdqXWqkz406MoYHMKW8Uf9gWr05T/rYB44kQ== +"image-size@^1.0.1": + "integrity" "sha512-VAwkvNSNGClRw9mDHhc5Efax8PLlsOGcUTh0T/LIriC8vPA3U5PdqXWqkz406MoYHMKW8Uf9gWr05T/rYB44kQ==" + "resolved" "https://registry.npmjs.org/image-size/-/image-size-1.0.1.tgz" + "version" "1.0.1" dependencies: - queue "6.0.2" + "queue" "6.0.2" -immer@^9.0.7: - version "9.0.12" - resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.12.tgz#2d33ddf3ee1d247deab9d707ca472c8c942a0f20" - integrity sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA== +"immer@^9.0.7": + "integrity" "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==" + "resolved" "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz" + "version" "9.0.12" -import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.2.2, import-fresh@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== +"import-fresh@^3.1.0", "import-fresh@^3.2.1", "import-fresh@^3.2.2", "import-fresh@^3.3.0": + "integrity" "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==" + "resolved" "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + "version" "3.3.0" dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" + "parent-module" "^1.0.0" + "resolve-from" "^4.0.0" -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= +"import-lazy@^2.1.0": + "integrity" "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + "resolved" "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz" + "version" "2.1.0" -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +"imurmurhash@^0.1.4": + "integrity" "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + "resolved" "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + "version" "0.1.4" -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== +"indent-string@^4.0.0": + "integrity" "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + "resolved" "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" + "version" "4.0.0" -infima@0.2.0-alpha.37: - version "0.2.0-alpha.37" - resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.37.tgz#b87ff42d528d6d050098a560f0294fbdd12adb78" - integrity sha512-4GX7Baw+/lwS4PPW/UJNY89tWSvYG1DL6baKVdpK6mC593iRgMssxNtORMTFArLPJ/A/lzsGhRmx+z6MaMxj0Q== +"infima@0.2.0-alpha.37": + "integrity" "sha512-4GX7Baw+/lwS4PPW/UJNY89tWSvYG1DL6baKVdpK6mC593iRgMssxNtORMTFArLPJ/A/lzsGhRmx+z6MaMxj0Q==" + "resolved" "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.37.tgz" + "version" "0.2.0-alpha.37" -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= +"inflight@^1.0.4": + "integrity" "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" + "resolved" "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + "version" "1.0.6" dependencies: - once "^1.3.0" - wrappy "1" + "once" "^1.3.0" + "wrappy" "1" -inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +"inherits@^2.0.0", "inherits@^2.0.1", "inherits@^2.0.3", "inherits@~2.0.3", "inherits@2", "inherits@2.0.4": + "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + "version" "2.0.4" -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +"inherits@2.0.3": + "integrity" "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "version" "2.0.3" -ini@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== +"ini@^1.3.5", "ini@~1.3.0": + "integrity" "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "resolved" "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + "version" "1.3.8" -ini@^1.3.5, ini@~1.3.0: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +"ini@2.0.0": + "integrity" "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" + "resolved" "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" + "version" "2.0.0" -inline-style-parser@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" - integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== +"inline-style-parser@0.1.1": + "integrity" "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + "resolved" "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz" + "version" "0.1.1" -interpret@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +"interpret@^1.0.0": + "integrity" "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + "resolved" "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" + "version" "1.4.0" -ip@^1.1.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= +"ip@^1.1.0": + "integrity" "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + "resolved" "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz" + "version" "1.1.5" -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +"ipaddr.js@^2.0.1": + "integrity" "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" + "resolved" "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz" + "version" "2.0.1" -ipaddr.js@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" - integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== +"ipaddr.js@1.9.1": + "integrity" "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "resolved" "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + "version" "1.9.1" -is-alphabetical@1.0.4, is-alphabetical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" - integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== +"is-alphabetical@^1.0.0", "is-alphabetical@1.0.4": + "integrity" "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" + "resolved" "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz" + "version" "1.0.4" -is-alphanumerical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" - integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== +"is-alphanumerical@^1.0.0": + "integrity" "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==" + "resolved" "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz" + "version" "1.0.4" dependencies: - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" + "is-alphabetical" "^1.0.0" + "is-decimal" "^1.0.0" -is-arguments@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== +"is-arguments@^1.0.4": + "integrity" "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==" + "resolved" "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" + "version" "1.1.1" dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" + "call-bind" "^1.0.2" + "has-tostringtag" "^1.0.0" -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +"is-arrayish@^0.2.1": + "integrity" "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "resolved" "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + "version" "0.2.1" -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== +"is-binary-path@~2.1.0": + "integrity" "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==" + "resolved" "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + "version" "2.1.0" dependencies: - binary-extensions "^2.0.0" + "binary-extensions" "^2.0.0" -is-buffer@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== +"is-buffer@^2.0.0": + "integrity" "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + "resolved" "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" + "version" "2.0.5" -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== +"is-ci@^2.0.0": + "integrity" "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==" + "resolved" "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz" + "version" "2.0.0" dependencies: - ci-info "^2.0.0" + "ci-info" "^2.0.0" -is-core-module@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== +"is-core-module@^2.8.1": + "integrity" "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==" + "resolved" "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz" + "version" "2.8.1" dependencies: - has "^1.0.3" + "has" "^1.0.3" -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== +"is-date-object@^1.0.1": + "integrity" "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==" + "resolved" "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + "version" "1.0.5" dependencies: - has-tostringtag "^1.0.0" + "has-tostringtag" "^1.0.0" -is-decimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" - integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== +"is-decimal@^1.0.0": + "integrity" "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" + "resolved" "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz" + "version" "1.0.4" -is-docker@^2.0.0, is-docker@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +"is-docker@^2.0.0", "is-docker@^2.1.1": + "integrity" "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + "resolved" "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" + "version" "2.2.1" -is-extendable@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= +"is-extendable@^0.1.0": + "integrity" "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + "resolved" "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + "version" "0.1.1" -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +"is-extglob@^2.1.1": + "integrity" "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + "version" "2.1.1" -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +"is-fullwidth-code-point@^3.0.0": + "integrity" "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "resolved" "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + "version" "3.0.0" -is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== +"is-glob@^4.0.1", "is-glob@^4.0.3", "is-glob@~4.0.1": + "integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==" + "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + "version" "4.0.3" dependencies: - is-extglob "^2.1.1" + "is-extglob" "^2.1.1" -is-hexadecimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" - integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== +"is-hexadecimal@^1.0.0": + "integrity" "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" + "resolved" "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz" + "version" "1.0.4" -is-installed-globally@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" - integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== +"is-installed-globally@^0.4.0": + "integrity" "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==" + "resolved" "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz" + "version" "0.4.0" dependencies: - global-dirs "^3.0.0" - is-path-inside "^3.0.2" + "global-dirs" "^3.0.0" + "is-path-inside" "^3.0.2" -is-npm@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" - integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== +"is-npm@^5.0.0": + "integrity" "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==" + "resolved" "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz" + "version" "5.0.0" -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +"is-number@^7.0.0": + "integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + "version" "7.0.0" -is-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= +"is-obj@^1.0.1": + "integrity" "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + "resolved" "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz" + "version" "1.0.1" -is-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" - integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +"is-obj@^2.0.0": + "integrity" "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + "resolved" "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" + "version" "2.0.0" -is-path-cwd@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== +"is-path-cwd@^2.2.0": + "integrity" "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + "resolved" "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz" + "version" "2.2.0" -is-path-inside@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +"is-path-inside@^3.0.2": + "integrity" "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + "resolved" "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" + "version" "3.0.3" -is-plain-obj@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +"is-plain-obj@^2.0.0": + "integrity" "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" + "resolved" "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" + "version" "2.1.0" -is-plain-obj@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" - integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== +"is-plain-obj@^3.0.0": + "integrity" "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + "resolved" "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz" + "version" "3.0.0" -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== +"is-plain-object@^2.0.4": + "integrity" "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==" + "resolved" "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + "version" "2.0.4" dependencies: - isobject "^3.0.1" + "isobject" "^3.0.1" -is-regex@^1.0.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== +"is-regex@^1.0.4": + "integrity" "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==" + "resolved" "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + "version" "1.1.4" dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" + "call-bind" "^1.0.2" + "has-tostringtag" "^1.0.0" -is-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" - integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= +"is-regexp@^1.0.0": + "integrity" "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" + "resolved" "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz" + "version" "1.0.0" -is-root@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" - integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== +"is-root@^2.1.0": + "integrity" "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + "resolved" "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz" + "version" "2.1.0" -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +"is-stream@^2.0.0": + "integrity" "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + "resolved" "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + "version" "2.0.1" -is-typedarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +"is-typedarray@^1.0.0": + "integrity" "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "resolved" "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + "version" "1.0.0" -is-whitespace-character@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" - integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== +"is-whitespace-character@^1.0.0": + "integrity" "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==" + "resolved" "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz" + "version" "1.0.4" -is-word-character@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" - integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== +"is-word-character@^1.0.0": + "integrity" "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==" + "resolved" "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz" + "version" "1.0.4" -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== +"is-wsl@^2.2.0": + "integrity" "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==" + "resolved" "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + "version" "2.2.0" dependencies: - is-docker "^2.0.0" + "is-docker" "^2.0.0" -is-yarn-global@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" - integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== +"is-yarn-global@^0.3.0": + "integrity" "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + "resolved" "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz" + "version" "0.3.0" -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= +"isarray@~1.0.0": + "integrity" "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "resolved" "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "version" "1.0.0" -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +"isarray@0.0.1": + "integrity" "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "resolved" "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + "version" "0.0.1" -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +"isexe@^2.0.0": + "integrity" "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "resolved" "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + "version" "2.0.0" -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +"isobject@^3.0.1": + "integrity" "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "resolved" "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + "version" "3.0.1" -jest-worker@^27.0.2, jest-worker@^27.4.5: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== +"jest-worker@^27.0.2", "jest-worker@^27.4.5": + "integrity" "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==" + "resolved" "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" + "version" "27.5.1" dependencies: "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" + "merge-stream" "^2.0.0" + "supports-color" "^8.0.0" -joi@^17.4.2, joi@^17.6.0: - version "17.6.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" - integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw== +"joi@^17.4.2", "joi@^17.6.0": + "integrity" "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==" + "resolved" "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz" + "version" "17.6.0" dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -4684,2834 +4702,2879 @@ joi@^17.4.2, joi@^17.6.0: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +"js-tokens@^3.0.0 || ^4.0.0", "js-tokens@^4.0.0": + "integrity" "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "resolved" "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + "version" "4.0.0" -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== +"js-yaml@^3.13.1": + "integrity" "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==" + "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + "version" "3.14.1" dependencies: - argparse "^1.0.7" - esprima "^4.0.0" + "argparse" "^1.0.7" + "esprima" "^4.0.0" -js-yaml@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== +"js-yaml@^4.0.0": + "integrity" "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==" + "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + "version" "4.1.0" dependencies: - argparse "^2.0.1" + "argparse" "^2.0.1" -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +"jsesc@^2.5.1": + "integrity" "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + "resolved" "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" + "version" "2.5.2" -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= +"jsesc@~0.5.0": + "integrity" "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + "resolved" "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + "version" "0.5.0" -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +"json-buffer@3.0.0": + "integrity" "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + "resolved" "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" + "version" "3.0.0" -json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +"json-parse-better-errors@^1.0.2": + "integrity" "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + "resolved" "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" + "version" "1.0.2" -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +"json-parse-even-better-errors@^2.3.0": + "integrity" "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "resolved" "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + "version" "2.3.1" -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +"json-schema-traverse@^0.4.1": + "integrity" "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + "version" "0.4.1" -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +"json-schema-traverse@^1.0.0": + "integrity" "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + "version" "1.0.0" -json5@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== +"json5@^1.0.1": + "integrity" "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==" + "resolved" "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" + "version" "1.0.2" dependencies: - minimist "^1.2.0" + "minimist" "^1.2.0" -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" +"json5@^2.1.2", "json5@^2.2.2": + "integrity" "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + "resolved" "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + "version" "2.2.3" -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== +"jsonfile@^6.0.1": + "integrity" "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==" + "resolved" "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" + "version" "6.1.0" dependencies: - universalify "^2.0.0" + "universalify" "^2.0.0" optionalDependencies: - graceful-fs "^4.1.6" + "graceful-fs" "^4.1.6" -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== +"keyv@^3.0.0": + "integrity" "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==" + "resolved" "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz" + "version" "3.1.0" dependencies: - json-buffer "3.0.0" + "json-buffer" "3.0.0" -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +"kind-of@^6.0.0", "kind-of@^6.0.2": + "integrity" "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + "resolved" "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + "version" "6.0.3" -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +"kleur@^3.0.3": + "integrity" "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + "resolved" "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" + "version" "3.0.3" -klona@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" - integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== +"klona@^2.0.5": + "integrity" "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" + "resolved" "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz" + "version" "2.0.5" -latest-version@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" - integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== +"latest-version@^5.1.0": + "integrity" "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==" + "resolved" "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz" + "version" "5.1.0" dependencies: - package-json "^6.3.0" + "package-json" "^6.3.0" -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +"leven@^3.1.0": + "integrity" "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + "resolved" "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" + "version" "3.1.0" -lilconfig@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" - integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== +"lilconfig@^2.0.3": + "integrity" "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==" + "resolved" "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz" + "version" "2.0.4" -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +"lines-and-columns@^1.1.6": + "integrity" "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "resolved" "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + "version" "1.2.4" -loader-runner@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" - integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== +"loader-runner@^4.2.0": + "integrity" "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==" + "resolved" "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz" + "version" "4.2.0" -loader-utils@^1.4.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" - integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== +"loader-utils@^1.4.0": + "integrity" "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==" + "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz" + "version" "1.4.2" dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" + "big.js" "^5.2.2" + "emojis-list" "^3.0.0" + "json5" "^1.0.1" -loader-utils@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" - integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== +"loader-utils@^2.0.0": + "integrity" "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==" + "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz" + "version" "2.0.2" dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" + "big.js" "^5.2.2" + "emojis-list" "^3.0.0" + "json5" "^2.1.2" -loader-utils@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.0.tgz#bcecc51a7898bee7473d4bc6b845b23af8304d4f" - integrity sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ== +"loader-utils@^3.2.0": + "integrity" "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==" + "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz" + "version" "3.2.0" -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== +"locate-path@^3.0.0": + "integrity" "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" + "version" "3.0.0" dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" + "p-locate" "^3.0.0" + "path-exists" "^3.0.0" -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== +"locate-path@^5.0.0": + "integrity" "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + "version" "5.0.0" dependencies: - p-locate "^4.1.0" + "p-locate" "^4.1.0" -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== +"locate-path@^6.0.0": + "integrity" "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + "version" "6.0.0" dependencies: - p-locate "^5.0.0" + "p-locate" "^5.0.0" -lodash.assignin@^4.0.9: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" - integrity sha1-uo31+4QesKPoBEIysOJjqNxqKKI= +"lodash.assignin@^4.0.9": + "integrity" "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" + "resolved" "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz" + "version" "4.2.0" -lodash.bind@^4.1.4: - version "4.2.1" - resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" - integrity sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU= +"lodash.bind@^4.1.4": + "integrity" "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" + "resolved" "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz" + "version" "4.2.1" -lodash.curry@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" - integrity sha1-JI42By7ekGUB11lmIAqG2riyMXA= +"lodash.curry@^4.0.1": + "integrity" "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" + "resolved" "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz" + "version" "4.1.1" -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= +"lodash.debounce@^4.0.8": + "integrity" "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "resolved" "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" + "version" "4.0.8" -lodash.defaults@^4.0.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= +"lodash.defaults@^4.0.1": + "integrity" "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + "resolved" "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" + "version" "4.2.0" -lodash.filter@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" - integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4= +"lodash.filter@^4.4.0": + "integrity" "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" + "resolved" "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz" + "version" "4.6.0" -lodash.flatten@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" - integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= +"lodash.flatten@^4.2.0": + "integrity" "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + "resolved" "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" + "version" "4.4.0" -lodash.flow@^3.3.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a" - integrity sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o= +"lodash.flow@^3.3.0": + "integrity" "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" + "resolved" "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz" + "version" "3.5.0" -lodash.foreach@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" - integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= +"lodash.foreach@^4.3.0": + "integrity" "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + "resolved" "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz" + "version" "4.5.0" -lodash.map@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" - integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM= +"lodash.map@^4.4.0": + "integrity" "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" + "resolved" "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz" + "version" "4.6.0" -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +"lodash.memoize@^4.1.2": + "integrity" "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + "resolved" "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" + "version" "4.1.2" -lodash.merge@^4.4.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +"lodash.merge@^4.4.0": + "integrity" "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "resolved" "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + "version" "4.6.2" -lodash.pick@^4.2.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" - integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= +"lodash.pick@^4.2.1": + "integrity" "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" + "resolved" "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz" + "version" "4.4.0" -lodash.reduce@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" - integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs= +"lodash.reduce@^4.4.0": + "integrity" "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + "resolved" "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz" + "version" "4.6.0" -lodash.reject@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" - integrity sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU= +"lodash.reject@^4.4.0": + "integrity" "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" + "resolved" "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz" + "version" "4.6.0" -lodash.some@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" - integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= +"lodash.some@^4.4.0": + "integrity" "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" + "resolved" "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz" + "version" "4.6.0" -lodash.uniq@4.5.0, lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= +"lodash.uniq@^4.5.0", "lodash.uniq@4.5.0": + "integrity" "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + "resolved" "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" + "version" "4.5.0" -lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +"lodash@^4.17.14", "lodash@^4.17.19", "lodash@^4.17.20", "lodash@^4.17.21": + "integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "resolved" "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + "version" "4.17.21" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== +"loose-envify@^1.0.0", "loose-envify@^1.1.0", "loose-envify@^1.2.0", "loose-envify@^1.3.1", "loose-envify@^1.4.0": + "integrity" "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==" + "resolved" "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" + "version" "1.4.0" dependencies: - js-tokens "^3.0.0 || ^4.0.0" + "js-tokens" "^3.0.0 || ^4.0.0" -lower-case@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" - integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== +"lower-case@^2.0.2": + "integrity" "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==" + "resolved" "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" + "version" "2.0.2" dependencies: - tslib "^2.0.3" + "tslib" "^2.0.3" -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== +"lowercase-keys@^1.0.0", "lowercase-keys@^1.0.1": + "integrity" "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + "resolved" "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" + "version" "1.0.1" -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +"lowercase-keys@^2.0.0": + "integrity" "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + "resolved" "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" + "version" "2.0.0" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== +"lru-cache@^5.1.1": + "integrity" "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==" + "resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + "version" "5.1.1" dependencies: - yallist "^4.0.0" + "yallist" "^3.0.2" -magic-string@^0.25.3: - version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" - integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== +"lru-cache@^6.0.0": + "integrity" "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==" + "resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + "version" "6.0.0" dependencies: - sourcemap-codec "^1.4.4" + "yallist" "^4.0.0" -make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== +"magic-string@^0.25.3": + "integrity" "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==" + "resolved" "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz" + "version" "0.25.7" dependencies: - semver "^6.0.0" + "sourcemap-codec" "^1.4.4" -markdown-escapes@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" - integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== - -mdast-squeeze-paragraphs@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" - integrity sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ== +"make-dir@^3.0.0", "make-dir@^3.0.2", "make-dir@^3.1.0": + "integrity" "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==" + "resolved" "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" + "version" "3.1.0" dependencies: - unist-util-remove "^2.0.0" + "semver" "^6.0.0" -mdast-util-definitions@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" - integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== +"markdown-escapes@^1.0.0": + "integrity" "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==" + "resolved" "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz" + "version" "1.0.4" + +"mdast-squeeze-paragraphs@^4.0.0": + "integrity" "sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==" + "resolved" "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz" + "version" "4.0.0" dependencies: - unist-util-visit "^2.0.0" + "unist-util-remove" "^2.0.0" -mdast-util-to-hast@10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" - integrity sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA== +"mdast-util-definitions@^4.0.0": + "integrity" "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==" + "resolved" "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "unist-util-visit" "^2.0.0" + +"mdast-util-to-hast@10.0.1": + "integrity" "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==" + "resolved" "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz" + "version" "10.0.1" dependencies: "@types/mdast" "^3.0.0" "@types/unist" "^2.0.0" - mdast-util-definitions "^4.0.0" - mdurl "^1.0.0" - unist-builder "^2.0.0" - unist-util-generated "^1.0.0" - unist-util-position "^3.0.0" - unist-util-visit "^2.0.0" + "mdast-util-definitions" "^4.0.0" + "mdurl" "^1.0.0" + "unist-builder" "^2.0.0" + "unist-util-generated" "^1.0.0" + "unist-util-position" "^3.0.0" + "unist-util-visit" "^2.0.0" -mdast-util-to-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" - integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== +"mdast-util-to-string@^2.0.0": + "integrity" "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==" + "resolved" "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz" + "version" "2.0.0" -mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== +"mdn-data@2.0.14": + "integrity" "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + "resolved" "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz" + "version" "2.0.14" -mdurl@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= +"mdurl@^1.0.0": + "integrity" "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" + "resolved" "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz" + "version" "1.0.1" -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +"media-typer@0.3.0": + "integrity" "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "resolved" "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + "version" "0.3.0" -memfs@^3.1.2, memfs@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.1.tgz#b78092f466a0dce054d63d39275b24c71d3f1305" - integrity sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw== +"memfs@^3.1.2", "memfs@^3.4.1": + "integrity" "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==" + "resolved" "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz" + "version" "3.4.1" dependencies: - fs-monkey "1.0.3" + "fs-monkey" "1.0.3" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= +"merge-descriptors@1.0.1": + "integrity" "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "resolved" "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" + "version" "1.0.1" -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +"merge-stream@^2.0.0": + "integrity" "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "resolved" "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + "version" "2.0.0" -merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +"merge2@^1.3.0", "merge2@^1.4.1": + "integrity" "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + "resolved" "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + "version" "1.4.1" -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= +"methods@~1.1.2": + "integrity" "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "resolved" "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + "version" "1.1.2" -micromatch@^4.0.2, micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== +"micromatch@^4.0.2", "micromatch@^4.0.4": + "integrity" "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==" + "resolved" "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz" + "version" "4.0.4" dependencies: - braces "^3.0.1" - picomatch "^2.2.3" + "braces" "^3.0.1" + "picomatch" "^2.2.3" -mime-db@1.51.0, "mime-db@>= 1.43.0 < 2": - version "1.51.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" - integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== +"mime-db@>= 1.43.0 < 2", "mime-db@1.51.0": + "integrity" "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz" + "version" "1.51.0" -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== +"mime-db@~1.33.0": + "integrity" "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz" + "version" "1.33.0" -mime-types@2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== +"mime-types@^2.1.27", "mime-types@^2.1.31", "mime-types@~2.1.17", "mime-types@~2.1.24", "mime-types@~2.1.34": + "integrity" "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==" + "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz" + "version" "2.1.34" dependencies: - mime-db "~1.33.0" + "mime-db" "1.51.0" -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.34" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" - integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== +"mime-types@2.1.18": + "integrity" "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==" + "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz" + "version" "2.1.18" dependencies: - mime-db "1.51.0" + "mime-db" "~1.33.0" -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +"mime@1.6.0": + "integrity" "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "resolved" "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + "version" "1.6.0" -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +"mimic-fn@^2.1.0": + "integrity" "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + "resolved" "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + "version" "2.1.0" -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +"mimic-response@^1.0.0", "mimic-response@^1.0.1": + "integrity" "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + "resolved" "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" + "version" "1.0.1" -mini-create-react-context@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" - integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== +"mini-create-react-context@^0.4.0": + "integrity" "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==" + "resolved" "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz" + "version" "0.4.1" dependencies: "@babel/runtime" "^7.12.1" - tiny-warning "^1.0.3" + "tiny-warning" "^1.0.3" -mini-css-extract-plugin@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz#83172b4fd812f8fc4a09d6f6d16f924f53990ca8" - integrity sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q== +"mini-css-extract-plugin@^1.6.0": + "integrity" "sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==" + "resolved" "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz" + "version" "1.6.2" dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - webpack-sources "^1.1.0" + "loader-utils" "^2.0.0" + "schema-utils" "^3.0.0" + "webpack-sources" "^1.1.0" -minimalistic-assert@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== +"minimalistic-assert@^1.0.0": + "integrity" "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + "resolved" "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + "version" "1.0.1" -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +"minimatch@^3.0.4": + "integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + "version" "3.1.2" dependencies: - brace-expansion "^1.1.7" + "brace-expansion" "^1.1.7" -minimatch@^3.0.4: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== +"minimatch@3.0.4": + "integrity" "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + "version" "3.0.4" dependencies: - brace-expansion "^1.1.7" + "brace-expansion" "^1.1.7" -minimist@^1.2.0, minimist@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +"minimist@^1.2.0", "minimist@^1.2.5": + "integrity" "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" + "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz" + "version" "1.2.7" -mkdirp@^0.5.5: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== +"mkdirp@^0.5.5": + "integrity" "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==" + "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" + "version" "0.5.5" dependencies: - minimist "^1.2.5" + "minimist" "^1.2.5" -mrmime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.0.tgz#14d387f0585a5233d291baba339b063752a2398b" - integrity sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ== +"mrmime@^1.0.0": + "integrity" "sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ==" + "resolved" "https://registry.npmjs.org/mrmime/-/mrmime-1.0.0.tgz" + "version" "1.0.0" -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +"ms@^2.1.1", "ms@2.1.3": + "integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + "version" "2.1.3" -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +"ms@2.0.0": + "integrity" "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + "version" "2.0.0" -ms@2.1.3, ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +"ms@2.1.2": + "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + "version" "2.1.2" -multicast-dns-service-types@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" - integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= +"multicast-dns-service-types@^1.1.0": + "integrity" "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" + "resolved" "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz" + "version" "1.1.0" -multicast-dns@^6.0.1: - version "6.2.3" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" - integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== +"multicast-dns@^6.0.1": + "integrity" "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==" + "resolved" "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz" + "version" "6.2.3" dependencies: - dns-packet "^1.3.1" - thunky "^1.0.2" + "dns-packet" "^1.3.1" + "thunky" "^1.0.2" -nanoid@^3.2.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" - integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== +"nanoid@^3.2.0": + "integrity" "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==" + "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz" + "version" "3.3.1" -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +"negotiator@0.6.3": + "integrity" "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + "resolved" "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" + "version" "0.6.3" -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +"neo-async@^2.6.2": + "integrity" "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + "resolved" "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + "version" "2.6.2" -no-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" - integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== +"no-case@^3.0.4": + "integrity" "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==" + "resolved" "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" + "version" "3.0.4" dependencies: - lower-case "^2.0.2" - tslib "^2.0.3" + "lower-case" "^2.0.2" + "tslib" "^2.0.3" -node-emoji@^1.10.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" - integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== +"node-emoji@^1.10.0": + "integrity" "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==" + "resolved" "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz" + "version" "1.11.0" dependencies: - lodash "^4.17.21" + "lodash" "^4.17.21" -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== +"node-fetch@2.6.7": + "integrity" "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==" + "resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" + "version" "2.6.7" dependencies: - whatwg-url "^5.0.0" + "whatwg-url" "^5.0.0" -node-forge@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.0.tgz#37a874ea723855f37db091e6c186e5b67a01d4b2" - integrity sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA== +"node-forge@^1.2.0": + "integrity" "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==" + "resolved" "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz" + "version" "1.3.0" -node-releases@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" - integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== +"node-releases@^2.0.8": + "integrity" "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" + "resolved" "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz" + "version" "2.0.10" -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +"normalize-path@^3.0.0", "normalize-path@~3.0.0": + "integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + "version" "3.0.0" -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +"normalize-range@^0.1.2": + "integrity" "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + "resolved" "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" + "version" "0.1.2" -normalize-url@^4.1.0: - version "4.5.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" - integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== +"normalize-url@^4.1.0": + "integrity" "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + "resolved" "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz" + "version" "4.5.1" -normalize-url@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== +"normalize-url@^6.0.1": + "integrity" "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + "resolved" "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz" + "version" "6.1.0" -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== +"npm-run-path@^4.0.1": + "integrity" "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==" + "resolved" "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + "version" "4.0.1" dependencies: - path-key "^3.0.0" + "path-key" "^3.0.0" -nprogress@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" - integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E= +"nprogress@^0.2.0": + "integrity" "sha1-y480xTIT2JVyP8urkH6UIq28r7E=" + "resolved" "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz" + "version" "0.2.0" -nth-check@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" - integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== +"nth-check@^2.0.1": + "integrity" "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==" + "resolved" "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz" + "version" "2.0.1" dependencies: - boolbase "^1.0.0" + "boolbase" "^1.0.0" -nth-check@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== +"nth-check@~1.0.1": + "integrity" "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==" + "resolved" "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz" + "version" "1.0.2" dependencies: - boolbase "~1.0.0" + "boolbase" "~1.0.0" -object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +"object-assign@^4.1.0", "object-assign@^4.1.1": + "integrity" "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "resolved" "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + "version" "4.1.1" -object-is@^1.0.1: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== +"object-is@^1.0.1": + "integrity" "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==" + "resolved" "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" + "version" "1.1.5" dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" -object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +"object-keys@^1.0.12", "object-keys@^1.1.1": + "integrity" "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + "resolved" "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + "version" "1.1.1" -object.assign@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== +"object.assign@^4.1.0": + "integrity" "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==" + "resolved" "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" + "version" "4.1.2" dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" + "call-bind" "^1.0.0" + "define-properties" "^1.1.3" + "has-symbols" "^1.0.1" + "object-keys" "^1.1.1" -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== +"obuf@^1.0.0", "obuf@^1.1.2": + "integrity" "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + "resolved" "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" + "version" "1.1.2" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= +"on-finished@~2.3.0": + "integrity" "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=" + "resolved" "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" + "version" "2.3.0" dependencies: - ee-first "1.1.1" + "ee-first" "1.1.1" -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== +"on-headers@~1.0.2": + "integrity" "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + "resolved" "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" + "version" "1.0.2" -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= +"once@^1.3.0", "once@^1.3.1", "once@^1.4.0": + "integrity" "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" + "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + "version" "1.4.0" dependencies: - wrappy "1" + "wrappy" "1" -onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== +"onetime@^5.1.2": + "integrity" "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==" + "resolved" "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + "version" "5.1.2" dependencies: - mimic-fn "^2.1.0" + "mimic-fn" "^2.1.0" -open@^8.0.9, open@^8.4.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" - integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== +"open@^8.0.9", "open@^8.4.0": + "integrity" "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==" + "resolved" "https://registry.npmjs.org/open/-/open-8.4.0.tgz" + "version" "8.4.0" dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" + "define-lazy-prop" "^2.0.0" + "is-docker" "^2.1.1" + "is-wsl" "^2.2.0" -opener@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" - integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== +"opener@^1.5.2": + "integrity" "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" + "resolved" "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz" + "version" "1.5.2" -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +"p-cancelable@^1.0.0": + "integrity" "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + "resolved" "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz" + "version" "1.1.0" -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== +"p-limit@^2.0.0", "p-limit@^2.2.0": + "integrity" "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==" + "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + "version" "2.3.0" dependencies: - p-try "^2.0.0" + "p-try" "^2.0.0" -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== +"p-limit@^3.0.2": + "integrity" "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==" + "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + "version" "3.1.0" dependencies: - yocto-queue "^0.1.0" + "yocto-queue" "^0.1.0" -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== +"p-locate@^3.0.0": + "integrity" "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" + "version" "3.0.0" dependencies: - p-limit "^2.0.0" + "p-limit" "^2.0.0" -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== +"p-locate@^4.1.0": + "integrity" "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + "version" "4.1.0" dependencies: - p-limit "^2.2.0" + "p-limit" "^2.2.0" -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== +"p-locate@^5.0.0": + "integrity" "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + "version" "5.0.0" dependencies: - p-limit "^3.0.2" + "p-limit" "^3.0.2" -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== +"p-map@^4.0.0": + "integrity" "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==" + "resolved" "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" + "version" "4.0.0" dependencies: - aggregate-error "^3.0.0" + "aggregate-error" "^3.0.0" -p-retry@^4.5.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.1.tgz#8fcddd5cdf7a67a0911a9cf2ef0e5df7f602316c" - integrity sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA== +"p-retry@^4.5.0": + "integrity" "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==" + "resolved" "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz" + "version" "4.6.1" dependencies: "@types/retry" "^0.12.0" - retry "^0.13.1" + "retry" "^0.13.1" -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +"p-try@^2.0.0": + "integrity" "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + "resolved" "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + "version" "2.2.0" -package-json@^6.3.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" - integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== +"package-json@^6.3.0": + "integrity" "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==" + "resolved" "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz" + "version" "6.5.0" dependencies: - got "^9.6.0" - registry-auth-token "^4.0.0" - registry-url "^5.0.0" - semver "^6.2.0" + "got" "^9.6.0" + "registry-auth-token" "^4.0.0" + "registry-url" "^5.0.0" + "semver" "^6.2.0" -param-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" - integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== +"param-case@^3.0.4": + "integrity" "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==" + "resolved" "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" + "version" "3.0.4" dependencies: - dot-case "^3.0.4" - tslib "^2.0.3" + "dot-case" "^3.0.4" + "tslib" "^2.0.3" -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== +"parent-module@^1.0.0": + "integrity" "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==" + "resolved" "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + "version" "1.0.1" dependencies: - callsites "^3.0.0" + "callsites" "^3.0.0" -parse-entities@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" - integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== +"parse-entities@^2.0.0": + "integrity" "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==" + "resolved" "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz" + "version" "2.0.0" dependencies: - character-entities "^1.0.0" - character-entities-legacy "^1.0.0" - character-reference-invalid "^1.0.0" - is-alphanumerical "^1.0.0" - is-decimal "^1.0.0" - is-hexadecimal "^1.0.0" + "character-entities" "^1.0.0" + "character-entities-legacy" "^1.0.0" + "character-reference-invalid" "^1.0.0" + "is-alphanumerical" "^1.0.0" + "is-decimal" "^1.0.0" + "is-hexadecimal" "^1.0.0" -parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== +"parse-json@^5.0.0": + "integrity" "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==" + "resolved" "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + "version" "5.2.0" dependencies: "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" + "error-ex" "^1.3.1" + "json-parse-even-better-errors" "^2.3.0" + "lines-and-columns" "^1.1.6" -parse-numeric-range@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz#7c63b61190d61e4d53a1197f0c83c47bb670ffa3" - integrity sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ== +"parse-numeric-range@^1.3.0": + "integrity" "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" + "resolved" "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz" + "version" "1.3.0" -parse5-htmlparser2-tree-adapter@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" - integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== +"parse5-htmlparser2-tree-adapter@^6.0.1": + "integrity" "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==" + "resolved" "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz" + "version" "6.0.1" dependencies: - parse5 "^6.0.1" + "parse5" "^6.0.1" -parse5@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +"parse5@^5.0.0": + "integrity" "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + "resolved" "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz" + "version" "5.1.1" -parse5@^6.0.0, parse5@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +"parse5@^6.0.0", "parse5@^6.0.1": + "integrity" "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + "resolved" "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" + "version" "6.0.1" -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +"parseurl@~1.3.2", "parseurl@~1.3.3": + "integrity" "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "resolved" "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" + "version" "1.3.3" -pascal-case@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" - integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== +"pascal-case@^3.1.2": + "integrity" "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==" + "resolved" "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz" + "version" "3.1.2" dependencies: - no-case "^3.0.4" - tslib "^2.0.3" + "no-case" "^3.0.4" + "tslib" "^2.0.3" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= +"path-exists@^3.0.0": + "integrity" "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" + "version" "3.0.0" -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== +"path-exists@^4.0.0": + "integrity" "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + "version" "4.0.0" -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +"path-is-absolute@^1.0.0": + "integrity" "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "resolved" "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "version" "1.0.1" -path-is-inside@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= +"path-is-inside@1.0.2": + "integrity" "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + "resolved" "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz" + "version" "1.0.2" -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +"path-key@^3.0.0", "path-key@^3.1.0": + "integrity" "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + "resolved" "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + "version" "3.1.1" -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +"path-parse@^1.0.7": + "integrity" "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "resolved" "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + "version" "1.0.7" -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path-to-regexp@2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" - integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== - -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== +"path-to-regexp@^1.7.0": + "integrity" "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==" + "resolved" "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz" + "version" "1.8.0" dependencies: - isarray "0.0.1" + "isarray" "0.0.1" -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +"path-to-regexp@0.1.7": + "integrity" "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "resolved" "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + "version" "0.1.7" -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +"path-to-regexp@2.2.1": + "integrity" "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" + "resolved" "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz" + "version" "2.2.1" -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +"path-type@^4.0.0": + "integrity" "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + "resolved" "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + "version" "4.0.0" -pkg-dir@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== +"picocolors@^1.0.0": + "integrity" "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "resolved" "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + "version" "1.0.0" + +"picomatch@^2.0.4", "picomatch@^2.2.1", "picomatch@^2.2.3": + "integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + "version" "2.3.1" + +"pkg-dir@^4.1.0": + "integrity" "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==" + "resolved" "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + "version" "4.2.0" dependencies: - find-up "^4.0.0" + "find-up" "^4.0.0" -pkg-up@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" - integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== +"pkg-up@^3.1.0": + "integrity" "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==" + "resolved" "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz" + "version" "3.1.0" dependencies: - find-up "^3.0.0" + "find-up" "^3.0.0" -portfinder@^1.0.28: - version "1.0.28" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" - integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== +"portfinder@^1.0.28": + "integrity" "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==" + "resolved" "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz" + "version" "1.0.28" dependencies: - async "^2.6.2" - debug "^3.1.1" - mkdirp "^0.5.5" + "async" "^2.6.2" + "debug" "^3.1.1" + "mkdirp" "^0.5.5" -postcss-calc@^8.2.0: - version "8.2.4" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" - integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== +"postcss-calc@^8.2.0": + "integrity" "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==" + "resolved" "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz" + "version" "8.2.4" dependencies: - postcss-selector-parser "^6.0.9" - postcss-value-parser "^4.2.0" + "postcss-selector-parser" "^6.0.9" + "postcss-value-parser" "^4.2.0" -postcss-colormin@^5.2.5: - version "5.2.5" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.2.5.tgz#d1fc269ac2ad03fe641d462b5d1dada35c69968a" - integrity sha512-+X30aDaGYq81mFqwyPpnYInsZQnNpdxMX0ajlY7AExCexEFkPVV+KrO7kXwayqEWL2xwEbNQ4nUO0ZsRWGnevg== +"postcss-colormin@^5.2.5": + "integrity" "sha512-+X30aDaGYq81mFqwyPpnYInsZQnNpdxMX0ajlY7AExCexEFkPVV+KrO7kXwayqEWL2xwEbNQ4nUO0ZsRWGnevg==" + "resolved" "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.5.tgz" + "version" "5.2.5" dependencies: - browserslist "^4.16.6" - caniuse-api "^3.0.0" - colord "^2.9.1" - postcss-value-parser "^4.2.0" + "browserslist" "^4.16.6" + "caniuse-api" "^3.0.0" + "colord" "^2.9.1" + "postcss-value-parser" "^4.2.0" -postcss-convert-values@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.0.4.tgz#3e74dd97c581f475ae7b4500bc0a7c4fb3a6b1b6" - integrity sha512-bugzSAyjIexdObovsPZu/sBCTHccImJxLyFgeV0MmNBm/Lw5h5XnjfML6gzEmJ3A6nyfCW7hb1JXzcsA4Zfbdw== +"postcss-convert-values@^5.0.4": + "integrity" "sha512-bugzSAyjIexdObovsPZu/sBCTHccImJxLyFgeV0MmNBm/Lw5h5XnjfML6gzEmJ3A6nyfCW7hb1JXzcsA4Zfbdw==" + "resolved" "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-discard-comments@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.0.3.tgz#011acb63418d600fdbe18804e1bbecb543ad2f87" - integrity sha512-6W5BemziRoqIdAKT+1QjM4bNcJAQ7z7zk073730NHg4cUXh3/rQHHj7pmYxUB9aGhuRhBiUf0pXvIHkRwhQP0Q== +"postcss-discard-comments@^5.0.3": + "integrity" "sha512-6W5BemziRoqIdAKT+1QjM4bNcJAQ7z7zk073730NHg4cUXh3/rQHHj7pmYxUB9aGhuRhBiUf0pXvIHkRwhQP0Q==" + "resolved" "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.3.tgz" + "version" "5.0.3" -postcss-discard-duplicates@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.3.tgz#10f202a4cfe9d407b73dfea7a477054d21ea0c1f" - integrity sha512-vPtm1Mf+kp7iAENTG7jI1MN1lk+fBqL5y+qxyi4v3H+lzsXEdfS3dwUZD45KVhgzDEgduur8ycB4hMegyMTeRw== +"postcss-discard-duplicates@^5.0.3": + "integrity" "sha512-vPtm1Mf+kp7iAENTG7jI1MN1lk+fBqL5y+qxyi4v3H+lzsXEdfS3dwUZD45KVhgzDEgduur8ycB4hMegyMTeRw==" + "resolved" "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.3.tgz" + "version" "5.0.3" -postcss-discard-empty@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.0.3.tgz#ec185af4a3710b88933b0ff751aa157b6041dd6a" - integrity sha512-xGJugpaXKakwKI7sSdZjUuN4V3zSzb2Y0LOlmTajFbNinEjTfVs9PFW2lmKBaC/E64WwYppfqLD03P8l9BuueA== +"postcss-discard-empty@^5.0.3": + "integrity" "sha512-xGJugpaXKakwKI7sSdZjUuN4V3zSzb2Y0LOlmTajFbNinEjTfVs9PFW2lmKBaC/E64WwYppfqLD03P8l9BuueA==" + "resolved" "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.3.tgz" + "version" "5.0.3" -postcss-discard-overridden@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.0.4.tgz#cc999d6caf18ea16eff8b2b58f48ec3ddee35c9c" - integrity sha512-3j9QH0Qh1KkdxwiZOW82cId7zdwXVQv/gRXYDnwx5pBtR1sTkU4cXRK9lp5dSdiM0r0OICO/L8J6sV1/7m0kHg== +"postcss-discard-overridden@^5.0.4": + "integrity" "sha512-3j9QH0Qh1KkdxwiZOW82cId7zdwXVQv/gRXYDnwx5pBtR1sTkU4cXRK9lp5dSdiM0r0OICO/L8J6sV1/7m0kHg==" + "resolved" "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.4.tgz" + "version" "5.0.4" -postcss-discard-unused@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-5.0.3.tgz#89fd3ebdbed8320df77a4ad503bd83cff52409f5" - integrity sha512-WO6FJxL5fGnuE77ZbTcZ/nRZJ4+TOqNaqLBLWgkR4e+WdmHn77OHPyQmsRv7eOB2rLKL6tsq2bs1GwoKXD/++Q== +"postcss-discard-unused@^5.0.3": + "integrity" "sha512-WO6FJxL5fGnuE77ZbTcZ/nRZJ4+TOqNaqLBLWgkR4e+WdmHn77OHPyQmsRv7eOB2rLKL6tsq2bs1GwoKXD/++Q==" + "resolved" "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.0.3.tgz" + "version" "5.0.3" dependencies: - postcss-selector-parser "^6.0.5" + "postcss-selector-parser" "^6.0.5" -postcss-loader@^6.1.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.2.1.tgz#0895f7346b1702103d30fdc66e4d494a93c008ef" - integrity sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q== +"postcss-loader@^6.1.1": + "integrity" "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==" + "resolved" "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz" + "version" "6.2.1" dependencies: - cosmiconfig "^7.0.0" - klona "^2.0.5" - semver "^7.3.5" + "cosmiconfig" "^7.0.0" + "klona" "^2.0.5" + "semver" "^7.3.5" -postcss-merge-idents@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-5.0.3.tgz#04f333f32767bd7b7b002f0032da347ec3c8c484" - integrity sha512-Z4LCzh2WzMn69KaS2FaJcrIeDQ170V13QHq+0hnBEFKJJkD+y5qndZ/bl3AhpddrSrXWIVR+xAwjmHQIJI2Eog== +"postcss-merge-idents@^5.0.3": + "integrity" "sha512-Z4LCzh2WzMn69KaS2FaJcrIeDQ170V13QHq+0hnBEFKJJkD+y5qndZ/bl3AhpddrSrXWIVR+xAwjmHQIJI2Eog==" + "resolved" "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.0.3.tgz" + "version" "5.0.3" dependencies: - cssnano-utils "^3.0.2" - postcss-value-parser "^4.2.0" + "cssnano-utils" "^3.0.2" + "postcss-value-parser" "^4.2.0" -postcss-merge-longhand@^5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.0.6.tgz#090e60d5d3b3caad899f8774f8dccb33217d2166" - integrity sha512-rkmoPwQO6ymJSmWsX6l2hHeEBQa7C4kJb9jyi5fZB1sE8nSCv7sqchoYPixRwX/yvLoZP2y6FA5kcjiByeJqDg== +"postcss-merge-longhand@^5.0.6": + "integrity" "sha512-rkmoPwQO6ymJSmWsX6l2hHeEBQa7C4kJb9jyi5fZB1sE8nSCv7sqchoYPixRwX/yvLoZP2y6FA5kcjiByeJqDg==" + "resolved" "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.6.tgz" + "version" "5.0.6" dependencies: - postcss-value-parser "^4.2.0" - stylehacks "^5.0.3" + "postcss-value-parser" "^4.2.0" + "stylehacks" "^5.0.3" -postcss-merge-rules@^5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.6.tgz#26b37411fe1e80202fcef61cab027265b8925f2b" - integrity sha512-nzJWJ9yXWp8AOEpn/HFAW72WKVGD2bsLiAmgw4hDchSij27bt6TF+sIK0cJUBAYT3SGcjtGGsOR89bwkkMuMgQ== +"postcss-merge-rules@^5.0.6": + "integrity" "sha512-nzJWJ9yXWp8AOEpn/HFAW72WKVGD2bsLiAmgw4hDchSij27bt6TF+sIK0cJUBAYT3SGcjtGGsOR89bwkkMuMgQ==" + "resolved" "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.6.tgz" + "version" "5.0.6" dependencies: - browserslist "^4.16.6" - caniuse-api "^3.0.0" - cssnano-utils "^3.0.2" - postcss-selector-parser "^6.0.5" + "browserslist" "^4.16.6" + "caniuse-api" "^3.0.0" + "cssnano-utils" "^3.0.2" + "postcss-selector-parser" "^6.0.5" -postcss-minify-font-values@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.0.4.tgz#627d824406b0712243221891f40a44fffe1467fd" - integrity sha512-RN6q3tyuEesvyCYYFCRGJ41J1XFvgV+dvYGHr0CeHv8F00yILlN8Slf4t8XW4IghlfZYCeyRrANO6HpJ948ieA== +"postcss-minify-font-values@^5.0.4": + "integrity" "sha512-RN6q3tyuEesvyCYYFCRGJ41J1XFvgV+dvYGHr0CeHv8F00yILlN8Slf4t8XW4IghlfZYCeyRrANO6HpJ948ieA==" + "resolved" "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-minify-gradients@^5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.0.6.tgz#b07cef51a93f075e94053fd972ff1cba2eaf6503" - integrity sha512-E/dT6oVxB9nLGUTiY/rG5dX9taugv9cbLNTFad3dKxOO+BQg25Q/xo2z2ddG+ZB1CbkZYaVwx5blY8VC7R/43A== +"postcss-minify-gradients@^5.0.6": + "integrity" "sha512-E/dT6oVxB9nLGUTiY/rG5dX9taugv9cbLNTFad3dKxOO+BQg25Q/xo2z2ddG+ZB1CbkZYaVwx5blY8VC7R/43A==" + "resolved" "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.6.tgz" + "version" "5.0.6" dependencies: - colord "^2.9.1" - cssnano-utils "^3.0.2" - postcss-value-parser "^4.2.0" + "colord" "^2.9.1" + "cssnano-utils" "^3.0.2" + "postcss-value-parser" "^4.2.0" -postcss-minify-params@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.0.5.tgz#86cb624358cd45c21946f8c317893f0449396646" - integrity sha512-YBNuq3Rz5LfLFNHb9wrvm6t859b8qIqfXsWeK7wROm3jSKNpO1Y5e8cOyBv6Acji15TgSrAwb3JkVNCqNyLvBg== +"postcss-minify-params@^5.0.5": + "integrity" "sha512-YBNuq3Rz5LfLFNHb9wrvm6t859b8qIqfXsWeK7wROm3jSKNpO1Y5e8cOyBv6Acji15TgSrAwb3JkVNCqNyLvBg==" + "resolved" "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.5.tgz" + "version" "5.0.5" dependencies: - browserslist "^4.16.6" - cssnano-utils "^3.0.2" - postcss-value-parser "^4.2.0" + "browserslist" "^4.16.6" + "cssnano-utils" "^3.0.2" + "postcss-value-parser" "^4.2.0" -postcss-minify-selectors@^5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.1.3.tgz#6ac12d52aa661fd509469d87ab2cebb0a1e3a1b5" - integrity sha512-9RJfTiQEKA/kZhMaEXND893nBqmYQ8qYa/G+uPdVnXF6D/FzpfI6kwBtWEcHx5FqDbA79O9n6fQJfrIj6M8jvQ== +"postcss-minify-selectors@^5.1.3": + "integrity" "sha512-9RJfTiQEKA/kZhMaEXND893nBqmYQ8qYa/G+uPdVnXF6D/FzpfI6kwBtWEcHx5FqDbA79O9n6fQJfrIj6M8jvQ==" + "resolved" "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.3.tgz" + "version" "5.1.3" dependencies: - postcss-selector-parser "^6.0.5" + "postcss-selector-parser" "^6.0.5" -postcss-modules-extract-imports@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" - integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== +"postcss-modules-extract-imports@^3.0.0": + "integrity" "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" + "resolved" "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz" + "version" "3.0.0" -postcss-modules-local-by-default@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" - integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== +"postcss-modules-local-by-default@^4.0.0": + "integrity" "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==" + "resolved" "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz" + "version" "4.0.0" dependencies: - icss-utils "^5.0.0" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.1.0" + "icss-utils" "^5.0.0" + "postcss-selector-parser" "^6.0.2" + "postcss-value-parser" "^4.1.0" -postcss-modules-scope@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" - integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== +"postcss-modules-scope@^3.0.0": + "integrity" "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==" + "resolved" "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz" + "version" "3.0.0" dependencies: - postcss-selector-parser "^6.0.4" + "postcss-selector-parser" "^6.0.4" -postcss-modules-values@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" - integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== +"postcss-modules-values@^4.0.0": + "integrity" "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==" + "resolved" "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz" + "version" "4.0.0" dependencies: - icss-utils "^5.0.0" + "icss-utils" "^5.0.0" -postcss-normalize-charset@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.0.3.tgz#719fb9f9ca9835fcbd4fed8d6e0d72a79e7b5472" - integrity sha512-iKEplDBco9EfH7sx4ut7R2r/dwTnUqyfACf62Unc9UiyFuI7uUqZZtY+u+qp7g8Qszl/U28HIfcsI3pEABWFfA== +"postcss-normalize-charset@^5.0.3": + "integrity" "sha512-iKEplDBco9EfH7sx4ut7R2r/dwTnUqyfACf62Unc9UiyFuI7uUqZZtY+u+qp7g8Qszl/U28HIfcsI3pEABWFfA==" + "resolved" "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.3.tgz" + "version" "5.0.3" -postcss-normalize-display-values@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.3.tgz#94cc82e20c51cc4ffba6b36e9618adc1e50db8c1" - integrity sha512-FIV5FY/qs4Ja32jiDb5mVj5iWBlS3N8tFcw2yg98+8MkRgyhtnBgSC0lxU+16AMHbjX5fbSJgw5AXLMolonuRQ== +"postcss-normalize-display-values@^5.0.3": + "integrity" "sha512-FIV5FY/qs4Ja32jiDb5mVj5iWBlS3N8tFcw2yg98+8MkRgyhtnBgSC0lxU+16AMHbjX5fbSJgw5AXLMolonuRQ==" + "resolved" "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.3.tgz" + "version" "5.0.3" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-normalize-positions@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.0.4.tgz#4001f38c99675437b83277836fb4291887fcc6cc" - integrity sha512-qynirjBX0Lc73ROomZE3lzzmXXTu48/QiEzKgMeqh28+MfuHLsuqC9po4kj84igZqqFGovz8F8hf44hA3dPYmQ== +"postcss-normalize-positions@^5.0.4": + "integrity" "sha512-qynirjBX0Lc73ROomZE3lzzmXXTu48/QiEzKgMeqh28+MfuHLsuqC9po4kj84igZqqFGovz8F8hf44hA3dPYmQ==" + "resolved" "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-normalize-repeat-style@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.4.tgz#d005adf9ee45fae78b673031a376c0c871315145" - integrity sha512-Innt+wctD7YpfeDR7r5Ik6krdyppyAg2HBRpX88fo5AYzC1Ut/l3xaxACG0KsbX49cO2n5EB13clPwuYVt8cMA== +"postcss-normalize-repeat-style@^5.0.4": + "integrity" "sha512-Innt+wctD7YpfeDR7r5Ik6krdyppyAg2HBRpX88fo5AYzC1Ut/l3xaxACG0KsbX49cO2n5EB13clPwuYVt8cMA==" + "resolved" "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-normalize-string@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.0.4.tgz#b5e00a07597e7aa8a871817bfeac2bfaa59c3333" - integrity sha512-Dfk42l0+A1CDnVpgE606ENvdmksttLynEqTQf5FL3XGQOyqxjbo25+pglCUvziicTxjtI2NLUR6KkxyUWEVubQ== +"postcss-normalize-string@^5.0.4": + "integrity" "sha512-Dfk42l0+A1CDnVpgE606ENvdmksttLynEqTQf5FL3XGQOyqxjbo25+pglCUvziicTxjtI2NLUR6KkxyUWEVubQ==" + "resolved" "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-normalize-timing-functions@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.3.tgz#47210227bfcba5e52650d7a18654337090de7072" - integrity sha512-QRfjvFh11moN4PYnJ7hia4uJXeFotyK3t2jjg8lM9mswleGsNw2Lm3I5wO+l4k1FzK96EFwEVn8X8Ojrp2gP4g== +"postcss-normalize-timing-functions@^5.0.3": + "integrity" "sha512-QRfjvFh11moN4PYnJ7hia4uJXeFotyK3t2jjg8lM9mswleGsNw2Lm3I5wO+l4k1FzK96EFwEVn8X8Ojrp2gP4g==" + "resolved" "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.3.tgz" + "version" "5.0.3" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-normalize-unicode@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.4.tgz#02866096937005cdb2c17116c690f29505a1623d" - integrity sha512-W79Regn+a+eXTzB+oV/8XJ33s3pDyFTND2yDuUCo0Xa3QSy1HtNIfRVPXNubHxjhlqmMFADr3FSCHT84ITW3ig== +"postcss-normalize-unicode@^5.0.4": + "integrity" "sha512-W79Regn+a+eXTzB+oV/8XJ33s3pDyFTND2yDuUCo0Xa3QSy1HtNIfRVPXNubHxjhlqmMFADr3FSCHT84ITW3ig==" + "resolved" "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.4.tgz" + "version" "5.0.4" dependencies: - browserslist "^4.16.6" - postcss-value-parser "^4.2.0" + "browserslist" "^4.16.6" + "postcss-value-parser" "^4.2.0" -postcss-normalize-url@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.0.5.tgz#c39efc12ff119f6f45f0b4f516902b12c8080e3a" - integrity sha512-Ws3tX+PcekYlXh+ycAt0wyzqGthkvVtZ9SZLutMVvHARxcpu4o7vvXcNoiNKyjKuWecnjS6HDI3fjBuDr5MQxQ== +"postcss-normalize-url@^5.0.5": + "integrity" "sha512-Ws3tX+PcekYlXh+ycAt0wyzqGthkvVtZ9SZLutMVvHARxcpu4o7vvXcNoiNKyjKuWecnjS6HDI3fjBuDr5MQxQ==" + "resolved" "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.5.tgz" + "version" "5.0.5" dependencies: - normalize-url "^6.0.1" - postcss-value-parser "^4.2.0" + "normalize-url" "^6.0.1" + "postcss-value-parser" "^4.2.0" -postcss-normalize-whitespace@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.4.tgz#1d477e7da23fecef91fc4e37d462272c7b55c5ca" - integrity sha512-wsnuHolYZjMwWZJoTC9jeI2AcjA67v4UuidDrPN9RnX8KIZfE+r2Nd6XZRwHVwUiHmRvKQtxiqo64K+h8/imaw== +"postcss-normalize-whitespace@^5.0.4": + "integrity" "sha512-wsnuHolYZjMwWZJoTC9jeI2AcjA67v4UuidDrPN9RnX8KIZfE+r2Nd6XZRwHVwUiHmRvKQtxiqo64K+h8/imaw==" + "resolved" "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-ordered-values@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.0.5.tgz#e878af822a130c3f3709737e24cb815ca7c6d040" - integrity sha512-mfY7lXpq+8bDEHfP+muqibDPhZ5eP9zgBEF9XRvoQgXcQe2Db3G1wcvjbnfjXG6wYsl+0UIjikqq4ym1V2jGMQ== +"postcss-ordered-values@^5.0.5": + "integrity" "sha512-mfY7lXpq+8bDEHfP+muqibDPhZ5eP9zgBEF9XRvoQgXcQe2Db3G1wcvjbnfjXG6wYsl+0UIjikqq4ym1V2jGMQ==" + "resolved" "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.5.tgz" + "version" "5.0.5" dependencies: - cssnano-utils "^3.0.2" - postcss-value-parser "^4.2.0" + "cssnano-utils" "^3.0.2" + "postcss-value-parser" "^4.2.0" -postcss-reduce-idents@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-5.0.3.tgz#b632796275b4fa1a4040799969dd17167eaf4d8b" - integrity sha512-9bj9/Xhwiti0Z35kkguJX4G6yUYVw8S1kRLU4jFSCTEuHu4yJggf4rNUoVnT45lm/vU97Wd593CxspMDbHxy4w== +"postcss-reduce-idents@^5.0.3": + "integrity" "sha512-9bj9/Xhwiti0Z35kkguJX4G6yUYVw8S1kRLU4jFSCTEuHu4yJggf4rNUoVnT45lm/vU97Wd593CxspMDbHxy4w==" + "resolved" "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.0.3.tgz" + "version" "5.0.3" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-reduce-initial@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.3.tgz#68891594defd648253703bbd8f1093162f19568d" - integrity sha512-c88TkSnQ/Dnwgb4OZbKPOBbCaauwEjbECP5uAuFPOzQ+XdjNjRH7SG0dteXrpp1LlIFEKK76iUGgmw2V0xeieA== +"postcss-reduce-initial@^5.0.3": + "integrity" "sha512-c88TkSnQ/Dnwgb4OZbKPOBbCaauwEjbECP5uAuFPOzQ+XdjNjRH7SG0dteXrpp1LlIFEKK76iUGgmw2V0xeieA==" + "resolved" "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.3.tgz" + "version" "5.0.3" dependencies: - browserslist "^4.16.6" - caniuse-api "^3.0.0" + "browserslist" "^4.16.6" + "caniuse-api" "^3.0.0" -postcss-reduce-transforms@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.4.tgz#717e72d30befe857f7d2784dba10eb1157863712" - integrity sha512-VIJB9SFSaL8B/B7AXb7KHL6/GNNbbCHslgdzS9UDfBZYIA2nx8NLY7iD/BXFSO/1sRUILzBTfHCoW5inP37C5g== +"postcss-reduce-transforms@^5.0.4": + "integrity" "sha512-VIJB9SFSaL8B/B7AXb7KHL6/GNNbbCHslgdzS9UDfBZYIA2nx8NLY7iD/BXFSO/1sRUILzBTfHCoW5inP37C5g==" + "resolved" "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: - version "6.0.9" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" - integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== +"postcss-selector-parser@^6.0.2", "postcss-selector-parser@^6.0.4", "postcss-selector-parser@^6.0.5", "postcss-selector-parser@^6.0.9": + "integrity" "sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==" + "resolved" "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz" + "version" "6.0.9" dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" + "cssesc" "^3.0.0" + "util-deprecate" "^1.0.2" -postcss-sort-media-queries@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-4.2.1.tgz#a99bae69ef1098ee3b64a5fa94d258ec240d0355" - integrity sha512-9VYekQalFZ3sdgcTjXMa0dDjsfBVHXlraYJEMiOJ/2iMmI2JGCMavP16z3kWOaRu8NSaJCTgVpB/IVpH5yT9YQ== +"postcss-sort-media-queries@^4.1.0": + "integrity" "sha512-9VYekQalFZ3sdgcTjXMa0dDjsfBVHXlraYJEMiOJ/2iMmI2JGCMavP16z3kWOaRu8NSaJCTgVpB/IVpH5yT9YQ==" + "resolved" "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.2.1.tgz" + "version" "4.2.1" dependencies: - sort-css-media-queries "2.0.4" + "sort-css-media-queries" "2.0.4" -postcss-svgo@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.4.tgz#cfa8682f47b88f7cd75108ec499e133b43102abf" - integrity sha512-yDKHvULbnZtIrRqhZoA+rxreWpee28JSRH/gy9727u0UCgtpv1M/9WEWY3xySlFa0zQJcqf6oCBJPR5NwkmYpg== +"postcss-svgo@^5.0.4": + "integrity" "sha512-yDKHvULbnZtIrRqhZoA+rxreWpee28JSRH/gy9727u0UCgtpv1M/9WEWY3xySlFa0zQJcqf6oCBJPR5NwkmYpg==" + "resolved" "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" - svgo "^2.7.0" + "postcss-value-parser" "^4.2.0" + "svgo" "^2.7.0" -postcss-unique-selectors@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.0.4.tgz#08e188126b634ddfa615fb1d6c262bafdd64826e" - integrity sha512-5ampwoSDJCxDPoANBIlMgoBcYUHnhaiuLYJR5pj1DLnYQvMRVyFuTA5C3Bvt+aHtiqWpJkD/lXT50Vo1D0ZsAQ== +"postcss-unique-selectors@^5.0.4": + "integrity" "sha512-5ampwoSDJCxDPoANBIlMgoBcYUHnhaiuLYJR5pj1DLnYQvMRVyFuTA5C3Bvt+aHtiqWpJkD/lXT50Vo1D0ZsAQ==" + "resolved" "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-selector-parser "^6.0.5" + "postcss-selector-parser" "^6.0.5" -postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" - integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== +"postcss-value-parser@^4.1.0", "postcss-value-parser@^4.2.0": + "integrity" "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "resolved" "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + "version" "4.2.0" -postcss-zindex@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-5.0.2.tgz#7e48aee54062c93418593035229ea06b92381251" - integrity sha512-KPQFjQu73H35HLHmE8Wv31ygfQoucxD52oRm4FPFv1emYhFMzUQdF8adaXCevFLIHPRp2rRYfbaDiEqZ4YjVtw== +"postcss-zindex@^5.0.2": + "integrity" "sha512-KPQFjQu73H35HLHmE8Wv31ygfQoucxD52oRm4FPFv1emYhFMzUQdF8adaXCevFLIHPRp2rRYfbaDiEqZ4YjVtw==" + "resolved" "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.0.2.tgz" + "version" "5.0.2" -postcss@^8.3.11, postcss@^8.3.5, postcss@^8.3.7, postcss@^8.4.5: - version "8.4.6" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.6.tgz#c5ff3c3c457a23864f32cb45ac9b741498a09ae1" - integrity sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA== +"postcss@^7.0.0 || ^8.0.1", "postcss@^8.0.9", "postcss@^8.1.0", "postcss@^8.2.15", "postcss@^8.2.2", "postcss@^8.3.11", "postcss@^8.3.5", "postcss@^8.3.7", "postcss@^8.4.4", "postcss@^8.4.5": + "integrity" "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==" + "resolved" "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz" + "version" "8.4.6" dependencies: - nanoid "^3.2.0" - picocolors "^1.0.0" - source-map-js "^1.0.2" + "nanoid" "^3.2.0" + "picocolors" "^1.0.0" + "source-map-js" "^1.0.2" -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +"prepend-http@^2.0.0": + "integrity" "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + "resolved" "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" + "version" "2.0.0" -pretty-error@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" - integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== +"pretty-error@^4.0.0": + "integrity" "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==" + "resolved" "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz" + "version" "4.0.0" dependencies: - lodash "^4.17.20" - renderkid "^3.0.0" + "lodash" "^4.17.20" + "renderkid" "^3.0.0" -pretty-time@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" - integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== +"pretty-time@^1.1.0": + "integrity" "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" + "resolved" "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz" + "version" "1.1.0" -prism-react-renderer@^1.2.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.1.tgz#88fc9d0df6bed06ca2b9097421349f8c2f24e30d" - integrity sha512-xUeDMEz074d0zc5y6rxiMp/dlC7C+5IDDlaEUlcBOFE2wddz7hz5PNupb087mPwTt7T9BrFmewObfCBuf/LKwQ== +"prism-react-renderer@^1.2.1": + "integrity" "sha512-xUeDMEz074d0zc5y6rxiMp/dlC7C+5IDDlaEUlcBOFE2wddz7hz5PNupb087mPwTt7T9BrFmewObfCBuf/LKwQ==" + "resolved" "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.1.tgz" + "version" "1.3.1" -prismjs@^1.23.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057" - integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA== +"prismjs@^1.23.0": + "integrity" "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==" + "resolved" "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz" + "version" "1.27.0" -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +"process-nextick-args@~2.0.0": + "integrity" "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "resolved" "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + "version" "2.0.1" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== +"promise@^7.1.1": + "integrity" "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==" + "resolved" "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz" + "version" "7.3.1" dependencies: - asap "~2.0.3" + "asap" "~2.0.3" -prompts@^2.4.1, prompts@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" - integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== +"prompts@^2.4.1", "prompts@^2.4.2": + "integrity" "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==" + "resolved" "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" + "version" "2.4.2" dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" + "kleur" "^3.0.3" + "sisteransi" "^1.0.5" -prop-types@^15.6.2, prop-types@^15.7.2: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== +"prop-types@^15.0.0", "prop-types@^15.6.2", "prop-types@^15.7.2": + "integrity" "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==" + "resolved" "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" + "version" "15.8.1" dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" + "loose-envify" "^1.4.0" + "object-assign" "^4.1.1" + "react-is" "^16.13.1" -property-information@^5.0.0, property-information@^5.3.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" - integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== +"property-information@^5.0.0", "property-information@^5.3.0": + "integrity" "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==" + "resolved" "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz" + "version" "5.6.0" dependencies: - xtend "^4.0.0" + "xtend" "^4.0.0" -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== +"proxy-addr@~2.0.7": + "integrity" "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==" + "resolved" "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" + "version" "2.0.7" dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" + "forwarded" "0.2.0" + "ipaddr.js" "1.9.1" -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== +"pump@^3.0.0": + "integrity" "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==" + "resolved" "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" + "version" "3.0.0" dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" + "end-of-stream" "^1.1.0" + "once" "^1.3.1" -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= +"punycode@^1.3.2": + "integrity" "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "resolved" "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + "version" "1.4.1" -punycode@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= +"punycode@^2.1.0": + "integrity" "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "resolved" "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" + "version" "2.1.1" -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +"punycode@1.3.2": + "integrity" "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + "resolved" "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + "version" "1.3.2" -pupa@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" - integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== +"pupa@^2.1.1": + "integrity" "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==" + "resolved" "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz" + "version" "2.1.1" dependencies: - escape-goat "^2.0.0" + "escape-goat" "^2.0.0" -pure-color@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e" - integrity sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4= +"pure-color@^1.2.0": + "integrity" "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" + "resolved" "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz" + "version" "1.3.0" -qs@6.9.7: - version "6.9.7" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" - integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== +"qs@6.9.7": + "integrity" "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" + "resolved" "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz" + "version" "6.9.7" -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +"querystring@0.2.0": + "integrity" "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "resolved" "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" + "version" "0.2.0" -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +"queue-microtask@^1.2.2": + "integrity" "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + "resolved" "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + "version" "1.2.3" -queue@6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" - integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== +"queue@6.0.2": + "integrity" "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==" + "resolved" "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz" + "version" "6.0.2" dependencies: - inherits "~2.0.3" + "inherits" "~2.0.3" -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== +"randombytes@^2.1.0": + "integrity" "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==" + "resolved" "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + "version" "2.1.0" dependencies: - safe-buffer "^5.1.0" + "safe-buffer" "^5.1.0" -range-parser@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= +"range-parser@^1.2.1", "range-parser@~1.2.1": + "integrity" "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + "resolved" "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + "version" "1.2.1" -range-parser@^1.2.1, range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== +"range-parser@1.2.0": + "integrity" "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + "resolved" "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz" + "version" "1.2.0" -raw-body@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" - integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== +"raw-body@2.4.3": + "integrity" "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==" + "resolved" "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz" + "version" "2.4.3" dependencies: - bytes "3.1.2" - http-errors "1.8.1" - iconv-lite "0.4.24" - unpipe "1.0.0" + "bytes" "3.1.2" + "http-errors" "1.8.1" + "iconv-lite" "0.4.24" + "unpipe" "1.0.0" -rc@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== +"rc@^1.2.8": + "integrity" "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==" + "resolved" "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" + "version" "1.2.8" dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" + "deep-extend" "^0.6.0" + "ini" "~1.3.0" + "minimist" "^1.2.0" + "strip-json-comments" "~2.0.1" -react-base16-styling@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c" - integrity sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw= +"react-base16-styling@^0.6.0": + "integrity" "sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw=" + "resolved" "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz" + "version" "0.6.0" dependencies: - base16 "^1.0.0" - lodash.curry "^4.0.1" - lodash.flow "^3.3.0" - pure-color "^1.2.0" + "base16" "^1.0.0" + "lodash.curry" "^4.0.1" + "lodash.flow" "^3.3.0" + "pure-color" "^1.2.0" -react-dev-utils@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.0.tgz#4eab12cdb95692a077616770b5988f0adf806526" - integrity sha512-xBQkitdxozPxt1YZ9O1097EJiVpwHr9FoAuEVURCKV0Av8NBERovJauzP7bo1ThvuhZ4shsQ1AJiu4vQpoT1AQ== +"react-dev-utils@^12.0.0": + "integrity" "sha512-xBQkitdxozPxt1YZ9O1097EJiVpwHr9FoAuEVURCKV0Av8NBERovJauzP7bo1ThvuhZ4shsQ1AJiu4vQpoT1AQ==" + "resolved" "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz" + "version" "12.0.0" dependencies: "@babel/code-frame" "^7.16.0" - address "^1.1.2" - browserslist "^4.18.1" - chalk "^4.1.2" - cross-spawn "^7.0.3" - detect-port-alt "^1.1.6" - escape-string-regexp "^4.0.0" - filesize "^8.0.6" - find-up "^5.0.0" - fork-ts-checker-webpack-plugin "^6.5.0" - global-modules "^2.0.0" - globby "^11.0.4" - gzip-size "^6.0.0" - immer "^9.0.7" - is-root "^2.1.0" - loader-utils "^3.2.0" - open "^8.4.0" - pkg-up "^3.1.0" - prompts "^2.4.2" - react-error-overlay "^6.0.10" - recursive-readdir "^2.2.2" - shell-quote "^1.7.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" + "address" "^1.1.2" + "browserslist" "^4.18.1" + "chalk" "^4.1.2" + "cross-spawn" "^7.0.3" + "detect-port-alt" "^1.1.6" + "escape-string-regexp" "^4.0.0" + "filesize" "^8.0.6" + "find-up" "^5.0.0" + "fork-ts-checker-webpack-plugin" "^6.5.0" + "global-modules" "^2.0.0" + "globby" "^11.0.4" + "gzip-size" "^6.0.0" + "immer" "^9.0.7" + "is-root" "^2.1.0" + "loader-utils" "^3.2.0" + "open" "^8.4.0" + "pkg-up" "^3.1.0" + "prompts" "^2.4.2" + "react-error-overlay" "^6.0.10" + "recursive-readdir" "^2.2.2" + "shell-quote" "^1.7.3" + "strip-ansi" "^6.0.1" + "text-table" "^0.2.0" -react-dom@^16.10.2: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" - integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== +"react-dom@*", "react-dom@^16.10.2", "react-dom@^16.8.4 || ^17.0.0", "react-dom@^17.0.0 || ^16.3.0 || ^15.5.4", "react-dom@>= 16.8.0 < 18.0.0": + "integrity" "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==" + "resolved" "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz" + "version" "16.14.0" dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.19.1" + "loose-envify" "^1.1.0" + "object-assign" "^4.1.1" + "prop-types" "^15.6.2" + "scheduler" "^0.19.1" -react-error-overlay@^6.0.10: - version "6.0.10" - resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.10.tgz#0fe26db4fa85d9dbb8624729580e90e7159a59a6" - integrity sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA== +"react-error-overlay@^6.0.10": + "integrity" "sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA==" + "resolved" "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz" + "version" "6.0.10" -react-fast-compare@^3.1.1: - version "3.2.0" - resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" - integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== +"react-fast-compare@^3.1.1": + "integrity" "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + "resolved" "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz" + "version" "3.2.0" -react-helmet@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" - integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== +"react-helmet@^6.1.0": + "integrity" "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==" + "resolved" "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz" + "version" "6.1.0" dependencies: - object-assign "^4.1.1" - prop-types "^15.7.2" - react-fast-compare "^3.1.1" - react-side-effect "^2.1.0" + "object-assign" "^4.1.1" + "prop-types" "^15.7.2" + "react-fast-compare" "^3.1.1" + "react-side-effect" "^2.1.0" -react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +"react-is@^16.13.1", "react-is@^16.6.0", "react-is@^16.7.0": + "integrity" "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "resolved" "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" + "version" "16.13.1" -react-json-view@^1.21.3: - version "1.21.3" - resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475" - integrity sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw== +"react-json-view@^1.21.3": + "integrity" "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==" + "resolved" "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz" + "version" "1.21.3" dependencies: - flux "^4.0.1" - react-base16-styling "^0.6.0" - react-lifecycles-compat "^3.0.4" - react-textarea-autosize "^8.3.2" + "flux" "^4.0.1" + "react-base16-styling" "^0.6.0" + "react-lifecycles-compat" "^3.0.4" + "react-textarea-autosize" "^8.3.2" -react-lifecycles-compat@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" - integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +"react-lifecycles-compat@^3.0.4": + "integrity" "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + "resolved" "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz" + "version" "3.0.4" -react-loadable-ssr-addon-v5-slorber@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz#2cdc91e8a744ffdf9e3556caabeb6e4278689883" - integrity sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A== +"react-loadable-ssr-addon-v5-slorber@^1.0.1": + "integrity" "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==" + "resolved" "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz" + "version" "1.0.1" dependencies: "@babel/runtime" "^7.10.3" -react-popupbox@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/react-popupbox/-/react-popupbox-2.0.8.tgz#9e0c96dcf4ddbbea8d03c28ee6c0634f0d51b791" - integrity sha512-5DT0SxLMIchKgnUkdPwTzvFhtTL5SOQd6n5dzUnnELiimjFE8eaQwL1n58NZUxs9oJsHXF3qQNvcgwEfn8VHrw== +"react-loadable@*", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": + "integrity" "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz" + "version" "5.5.2" dependencies: - deepmerge "^1.3.2" - react "^16.3.1" + "@types/react" "*" + "prop-types" "^15.6.2" -react-router-config@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988" - integrity sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg== +"react-popupbox@^2.0.8": + "integrity" "sha512-5DT0SxLMIchKgnUkdPwTzvFhtTL5SOQd6n5dzUnnELiimjFE8eaQwL1n58NZUxs9oJsHXF3qQNvcgwEfn8VHrw==" + "resolved" "https://registry.npmjs.org/react-popupbox/-/react-popupbox-2.0.8.tgz" + "version" "2.0.8" + dependencies: + "deepmerge" "^1.3.2" + "react" "^16.3.1" + +"react-router-config@^5.1.1": + "integrity" "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==" + "resolved" "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz" + "version" "5.1.1" dependencies: "@babel/runtime" "^7.1.2" -react-router-dom@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.0.tgz#da1bfb535a0e89a712a93b97dd76f47ad1f32363" - integrity sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ== +"react-router-dom@^5.2.0": + "integrity" "sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==" + "resolved" "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.0.tgz" + "version" "5.3.0" dependencies: "@babel/runtime" "^7.12.13" - history "^4.9.0" - loose-envify "^1.3.1" - prop-types "^15.6.2" - react-router "5.2.1" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" + "history" "^4.9.0" + "loose-envify" "^1.3.1" + "prop-types" "^15.6.2" + "react-router" "5.2.1" + "tiny-invariant" "^1.0.2" + "tiny-warning" "^1.0.0" -react-router@5.2.1, react-router@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.1.tgz#4d2e4e9d5ae9425091845b8dbc6d9d276239774d" - integrity sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ== +"react-router@^5.2.0", "react-router@>=5", "react-router@5.2.1": + "integrity" "sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==" + "resolved" "https://registry.npmjs.org/react-router/-/react-router-5.2.1.tgz" + "version" "5.2.1" dependencies: "@babel/runtime" "^7.12.13" - history "^4.9.0" - hoist-non-react-statics "^3.1.0" - loose-envify "^1.3.1" - mini-create-react-context "^0.4.0" - path-to-regexp "^1.7.0" - prop-types "^15.6.2" - react-is "^16.6.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" + "history" "^4.9.0" + "hoist-non-react-statics" "^3.1.0" + "loose-envify" "^1.3.1" + "mini-create-react-context" "^0.4.0" + "path-to-regexp" "^1.7.0" + "prop-types" "^15.6.2" + "react-is" "^16.6.0" + "tiny-invariant" "^1.0.2" + "tiny-warning" "^1.0.0" -react-side-effect@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3" - integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ== +"react-side-effect@^2.1.0": + "integrity" "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==" + "resolved" "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz" + "version" "2.1.1" -react-textarea-autosize@^8.3.2: - version "8.3.3" - resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz#f70913945369da453fd554c168f6baacd1fa04d8" - integrity sha512-2XlHXK2TDxS6vbQaoPbMOfQ8GK7+irc2fVK6QFIcC8GOnH3zI/v481n+j1L0WaPVvKxwesnY93fEfH++sus2rQ== +"react-textarea-autosize@^8.3.2": + "integrity" "sha512-2XlHXK2TDxS6vbQaoPbMOfQ8GK7+irc2fVK6QFIcC8GOnH3zI/v481n+j1L0WaPVvKxwesnY93fEfH++sus2rQ==" + "resolved" "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz" + "version" "8.3.3" dependencies: "@babel/runtime" "^7.10.2" - use-composed-ref "^1.0.0" - use-latest "^1.0.0" + "use-composed-ref" "^1.0.0" + "use-latest" "^1.0.0" -react@^16.10.2, react@^16.3.1: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" - integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== +"react@*", "react@^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "react@^15.0.2 || ^16.0.0 || ^17.0.0", "react@^16.10.2", "react@^16.13.1", "react@^16.13.1 || ^17.0.0", "react@^16.14.0", "react@^16.3.0 || ^17.0.0", "react@^16.3.1", "react@^16.8.0 || ^17.0.0", "react@^16.8.4 || ^17.0.0", "react@^17.0.0 || ^16.3.0 || ^15.5.4", "react@>= 16.8.0 < 18.0.0", "react@>=0.14.9", "react@>=15", "react@>=16.3.0": + "integrity" "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==" + "resolved" "https://registry.npmjs.org/react/-/react-16.14.0.tgz" + "version" "16.14.0" dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" + "loose-envify" "^1.1.0" + "object-assign" "^4.1.1" + "prop-types" "^15.6.2" -readable-stream@^2.0.1: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== +"readable-stream@^2.0.1": + "integrity" "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==" + "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" + "version" "2.3.7" dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" + "core-util-is" "~1.0.0" + "inherits" "~2.0.3" + "isarray" "~1.0.0" + "process-nextick-args" "~2.0.0" + "safe-buffer" "~5.1.1" + "string_decoder" "~1.1.1" + "util-deprecate" "~1.0.1" -readable-stream@^3.0.6, readable-stream@^3.1.1: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== +"readable-stream@^3.0.6", "readable-stream@^3.1.1": + "integrity" "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==" + "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" + "version" "3.6.0" dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" + "inherits" "^2.0.3" + "string_decoder" "^1.1.1" + "util-deprecate" "^1.0.1" -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== +"readdirp@~3.6.0": + "integrity" "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==" + "resolved" "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + "version" "3.6.0" dependencies: - picomatch "^2.2.1" + "picomatch" "^2.2.1" -reading-time@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb" - integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg== +"reading-time@^1.5.0": + "integrity" "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" + "resolved" "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz" + "version" "1.5.0" -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= +"rechoir@^0.6.2": + "integrity" "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=" + "resolved" "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz" + "version" "0.6.2" dependencies: - resolve "^1.1.6" + "resolve" "^1.1.6" -recursive-readdir@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" - integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== +"recursive-readdir@^2.2.2": + "integrity" "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==" + "resolved" "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz" + "version" "2.2.2" dependencies: - minimatch "3.0.4" + "minimatch" "3.0.4" -regenerate-unicode-properties@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" - integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== +"regenerate-unicode-properties@^10.0.1": + "integrity" "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==" + "resolved" "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz" + "version" "10.0.1" dependencies: - regenerate "^1.4.2" + "regenerate" "^1.4.2" -regenerate-unicode-properties@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" - integrity sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA== +"regenerate-unicode-properties@^9.0.0": + "integrity" "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==" + "resolved" "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz" + "version" "9.0.0" dependencies: - regenerate "^1.4.2" + "regenerate" "^1.4.2" -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== +"regenerate@^1.4.2": + "integrity" "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + "resolved" "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" + "version" "1.4.2" -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== +"regenerator-runtime@^0.13.4": + "integrity" "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "resolved" "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz" + "version" "0.13.9" -regenerator-transform@^0.14.2: - version "0.14.5" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" - integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== +"regenerator-transform@^0.14.2": + "integrity" "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==" + "resolved" "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz" + "version" "0.14.5" dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" - integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== +"regexp.prototype.flags@^1.2.0": + "integrity" "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==" + "resolved" "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz" + "version" "1.4.1" dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" -regexpu-core@^4.5.4: - version "4.8.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" - integrity sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg== +"regexpu-core@^4.5.4": + "integrity" "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==" + "resolved" "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz" + "version" "4.8.0" dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^9.0.0" - regjsgen "^0.5.2" - regjsparser "^0.7.0" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" + "regenerate" "^1.4.2" + "regenerate-unicode-properties" "^9.0.0" + "regjsgen" "^0.5.2" + "regjsparser" "^0.7.0" + "unicode-match-property-ecmascript" "^2.0.0" + "unicode-match-property-value-ecmascript" "^2.0.0" -regexpu-core@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" - integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== +"regexpu-core@^5.0.1": + "integrity" "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==" + "resolved" "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz" + "version" "5.0.1" dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^10.0.1" - regjsgen "^0.6.0" - regjsparser "^0.8.2" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" + "regenerate" "^1.4.2" + "regenerate-unicode-properties" "^10.0.1" + "regjsgen" "^0.6.0" + "regjsparser" "^0.8.2" + "unicode-match-property-ecmascript" "^2.0.0" + "unicode-match-property-value-ecmascript" "^2.0.0" -registry-auth-token@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" - integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== +"registry-auth-token@^4.0.0": + "integrity" "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==" + "resolved" "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz" + "version" "4.2.1" dependencies: - rc "^1.2.8" + "rc" "^1.2.8" -registry-url@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" - integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== +"registry-url@^5.0.0": + "integrity" "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==" + "resolved" "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz" + "version" "5.1.0" dependencies: - rc "^1.2.8" + "rc" "^1.2.8" -regjsgen@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" - integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== +"regjsgen@^0.5.2": + "integrity" "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" + "resolved" "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz" + "version" "0.5.2" -regjsgen@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" - integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== +"regjsgen@^0.6.0": + "integrity" "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" + "resolved" "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz" + "version" "0.6.0" -regjsparser@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968" - integrity sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ== +"regjsparser@^0.7.0": + "integrity" "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==" + "resolved" "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz" + "version" "0.7.0" dependencies: - jsesc "~0.5.0" + "jsesc" "~0.5.0" -regjsparser@^0.8.2: - version "0.8.4" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" - integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== +"regjsparser@^0.8.2": + "integrity" "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==" + "resolved" "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz" + "version" "0.8.4" dependencies: - jsesc "~0.5.0" + "jsesc" "~0.5.0" -rehype-parse@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-6.0.2.tgz#aeb3fdd68085f9f796f1d3137ae2b85a98406964" - integrity sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug== +"rehype-parse@^6.0.2": + "integrity" "sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug==" + "resolved" "https://registry.npmjs.org/rehype-parse/-/rehype-parse-6.0.2.tgz" + "version" "6.0.2" dependencies: - hast-util-from-parse5 "^5.0.0" - parse5 "^5.0.0" - xtend "^4.0.0" + "hast-util-from-parse5" "^5.0.0" + "parse5" "^5.0.0" + "xtend" "^4.0.0" -relateurl@^0.2.7: - version "0.2.7" - resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= +"relateurl@^0.2.7": + "integrity" "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" + "resolved" "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz" + "version" "0.2.7" -remark-admonitions@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/remark-admonitions/-/remark-admonitions-1.2.1.tgz#87caa1a442aa7b4c0cafa04798ed58a342307870" - integrity sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow== +"remark-admonitions@^1.2.1": + "integrity" "sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow==" + "resolved" "https://registry.npmjs.org/remark-admonitions/-/remark-admonitions-1.2.1.tgz" + "version" "1.2.1" dependencies: - rehype-parse "^6.0.2" - unified "^8.4.2" - unist-util-visit "^2.0.1" + "rehype-parse" "^6.0.2" + "unified" "^8.4.2" + "unist-util-visit" "^2.0.1" -remark-emoji@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/remark-emoji/-/remark-emoji-2.2.0.tgz#1c702090a1525da5b80e15a8f963ef2c8236cac7" - integrity sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w== +"remark-emoji@^2.1.0": + "integrity" "sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w==" + "resolved" "https://registry.npmjs.org/remark-emoji/-/remark-emoji-2.2.0.tgz" + "version" "2.2.0" dependencies: - emoticon "^3.2.0" - node-emoji "^1.10.0" - unist-util-visit "^2.0.3" + "emoticon" "^3.2.0" + "node-emoji" "^1.10.0" + "unist-util-visit" "^2.0.3" -remark-footnotes@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-2.0.0.tgz#9001c4c2ffebba55695d2dd80ffb8b82f7e6303f" - integrity sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ== +"remark-footnotes@2.0.0": + "integrity" "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==" + "resolved" "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz" + "version" "2.0.0" -remark-mdx-remove-exports@^1.6.22: - version "1.6.22" - resolved "https://registry.yarnpkg.com/remark-mdx-remove-exports/-/remark-mdx-remove-exports-1.6.22.tgz#9e34f3d02c9c54b02ca0a1fde946449338d06ecb" - integrity sha512-7g2uiTmTGfz5QyVb+toeX25frbk1Y6yd03RXGPtqx0+DVh86Gb7MkNYbk7H2X27zdZ3CQv1W/JqlFO0Oo8IxVA== +"remark-mdx-remove-exports@^1.6.22": + "integrity" "sha512-7g2uiTmTGfz5QyVb+toeX25frbk1Y6yd03RXGPtqx0+DVh86Gb7MkNYbk7H2X27zdZ3CQv1W/JqlFO0Oo8IxVA==" + "resolved" "https://registry.npmjs.org/remark-mdx-remove-exports/-/remark-mdx-remove-exports-1.6.22.tgz" + "version" "1.6.22" dependencies: - unist-util-remove "2.0.0" + "unist-util-remove" "2.0.0" -remark-mdx-remove-imports@^1.6.22: - version "1.6.22" - resolved "https://registry.yarnpkg.com/remark-mdx-remove-imports/-/remark-mdx-remove-imports-1.6.22.tgz#79f711c95359cff437a120d1fbdc1326ec455826" - integrity sha512-lmjAXD8Ltw0TsvBzb45S+Dxx7LTJAtDaMneMAv8LAUIPEyYoKkmGbmVsiF0/pY6mhM1Q16swCmu1TN+ie/vn/A== +"remark-mdx-remove-imports@^1.6.22": + "integrity" "sha512-lmjAXD8Ltw0TsvBzb45S+Dxx7LTJAtDaMneMAv8LAUIPEyYoKkmGbmVsiF0/pY6mhM1Q16swCmu1TN+ie/vn/A==" + "resolved" "https://registry.npmjs.org/remark-mdx-remove-imports/-/remark-mdx-remove-imports-1.6.22.tgz" + "version" "1.6.22" dependencies: - unist-util-remove "2.0.0" + "unist-util-remove" "2.0.0" -remark-mdx@1.6.22: - version "1.6.22" - resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.6.22.tgz#06a8dab07dcfdd57f3373af7f86bd0e992108bbd" - integrity sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ== +"remark-mdx@1.6.22": + "integrity" "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==" + "resolved" "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz" + "version" "1.6.22" dependencies: "@babel/core" "7.12.9" "@babel/helper-plugin-utils" "7.10.4" "@babel/plugin-proposal-object-rest-spread" "7.12.1" "@babel/plugin-syntax-jsx" "7.12.1" "@mdx-js/util" "1.6.22" - is-alphabetical "1.0.4" - remark-parse "8.0.3" - unified "9.2.0" + "is-alphabetical" "1.0.4" + "remark-parse" "8.0.3" + "unified" "9.2.0" -remark-parse@8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-8.0.3.tgz#9c62aa3b35b79a486454c690472906075f40c7e1" - integrity sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q== +"remark-parse@8.0.3": + "integrity" "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==" + "resolved" "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz" + "version" "8.0.3" dependencies: - ccount "^1.0.0" - collapse-white-space "^1.0.2" - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - is-word-character "^1.0.0" - markdown-escapes "^1.0.0" - parse-entities "^2.0.0" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - trim "0.0.1" - trim-trailing-lines "^1.0.0" - unherit "^1.0.4" - unist-util-remove-position "^2.0.0" - vfile-location "^3.0.0" - xtend "^4.0.1" + "ccount" "^1.0.0" + "collapse-white-space" "^1.0.2" + "is-alphabetical" "^1.0.0" + "is-decimal" "^1.0.0" + "is-whitespace-character" "^1.0.0" + "is-word-character" "^1.0.0" + "markdown-escapes" "^1.0.0" + "parse-entities" "^2.0.0" + "repeat-string" "^1.5.4" + "state-toggle" "^1.0.0" + "trim" "0.0.1" + "trim-trailing-lines" "^1.0.0" + "unherit" "^1.0.4" + "unist-util-remove-position" "^2.0.0" + "vfile-location" "^3.0.0" + "xtend" "^4.0.1" -remark-squeeze-paragraphs@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz#76eb0e085295131c84748c8e43810159c5653ead" - integrity sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw== +"remark-squeeze-paragraphs@4.0.0": + "integrity" "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==" + "resolved" "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz" + "version" "4.0.0" dependencies: - mdast-squeeze-paragraphs "^4.0.0" + "mdast-squeeze-paragraphs" "^4.0.0" -remarkable-admonitions@^0.2.1: - version "0.2.2" - resolved "https://registry.yarnpkg.com/remarkable-admonitions/-/remarkable-admonitions-0.2.2.tgz#8765f9ec66be4f4c651a4e1cfb559dd7f920819c" - integrity sha512-CcMTEcLYmJLXX3IVMk4LyW4oFD2NQxh5FeLzn4k89TAPpyWIeVix/B/g/gDbZAUpCNY9l6heovR5NNIktf8X5A== +"remarkable-admonitions@^0.2.1": + "integrity" "sha512-CcMTEcLYmJLXX3IVMk4LyW4oFD2NQxh5FeLzn4k89TAPpyWIeVix/B/g/gDbZAUpCNY9l6heovR5NNIktf8X5A==" + "resolved" "https://registry.npmjs.org/remarkable-admonitions/-/remarkable-admonitions-0.2.2.tgz" + "version" "0.2.2" -renderkid@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" - integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== +"renderkid@^3.0.0": + "integrity" "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==" + "resolved" "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz" + "version" "3.0.0" dependencies: - css-select "^4.1.3" - dom-converter "^0.2.0" - htmlparser2 "^6.1.0" - lodash "^4.17.21" - strip-ansi "^6.0.1" + "css-select" "^4.1.3" + "dom-converter" "^0.2.0" + "htmlparser2" "^6.1.0" + "lodash" "^4.17.21" + "strip-ansi" "^6.0.1" -repeat-string@^1.5.4: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= +"repeat-string@^1.5.4": + "integrity" "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "resolved" "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + "version" "1.6.1" -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +"require-from-string@^2.0.2": + "integrity" "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + "resolved" "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + "version" "2.0.2" "require-like@>= 0.1.1": - version "0.1.2" - resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" - integrity sha1-rW8wwTvs15cBDEaK+ndcDAprR/o= + "integrity" "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=" + "resolved" "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" + "version" "0.1.2" -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +"requires-port@^1.0.0": + "integrity" "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + "resolved" "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + "version" "1.0.0" -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +"resolve-from@^4.0.0": + "integrity" "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + "resolved" "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + "version" "4.0.0" -resolve-pathname@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" - integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== +"resolve-pathname@^3.0.0": + "integrity" "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + "resolved" "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz" + "version" "3.0.0" -resolve@^1.1.6, resolve@^1.14.2, resolve@^1.3.2: - version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== +"resolve@^1.1.6", "resolve@^1.14.2", "resolve@^1.3.2": + "integrity" "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==" + "resolved" "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz" + "version" "1.22.0" dependencies: - is-core-module "^2.8.1" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" + "is-core-module" "^2.8.1" + "path-parse" "^1.0.7" + "supports-preserve-symlinks-flag" "^1.0.0" -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= +"responselike@^1.0.2": + "integrity" "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=" + "resolved" "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" + "version" "1.0.2" dependencies: - lowercase-keys "^1.0.0" + "lowercase-keys" "^1.0.0" -retry@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== +"retry@^0.13.1": + "integrity" "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + "resolved" "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" + "version" "0.13.1" -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +"reusify@^1.0.4": + "integrity" "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + "resolved" "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + "version" "1.0.4" -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== +"rimraf@^3.0.2": + "integrity" "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==" + "resolved" "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + "version" "3.0.2" dependencies: - glob "^7.1.3" + "glob" "^7.1.3" -rtl-detect@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.0.4.tgz#40ae0ea7302a150b96bc75af7d749607392ecac6" - integrity sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ== +"rtl-detect@^1.0.4": + "integrity" "sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==" + "resolved" "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.4.tgz" + "version" "1.0.4" -rtlcss@^3.3.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/rtlcss/-/rtlcss-3.5.0.tgz#c9eb91269827a102bac7ae3115dd5d049de636c3" - integrity sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A== +"rtlcss@^3.3.0": + "integrity" "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==" + "resolved" "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz" + "version" "3.5.0" dependencies: - find-up "^5.0.0" - picocolors "^1.0.0" - postcss "^8.3.11" - strip-json-comments "^3.1.1" + "find-up" "^5.0.0" + "picocolors" "^1.0.0" + "postcss" "^8.3.11" + "strip-json-comments" "^3.1.1" -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== +"run-parallel@^1.1.9": + "integrity" "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==" + "resolved" "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + "version" "1.2.0" dependencies: - queue-microtask "^1.2.2" + "queue-microtask" "^1.2.2" -rxjs@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.4.tgz#3d6bd407e6b7ce9a123e76b1e770dc5761aa368d" - integrity sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ== +"rxjs@^7.5.4": + "integrity" "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==" + "resolved" "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz" + "version" "7.5.4" dependencies: - tslib "^2.1.0" + "tslib" "^2.1.0" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +"safe-buffer@^5.0.1", "safe-buffer@^5.1.0", "safe-buffer@>=5.1.0", "safe-buffer@~5.2.0", "safe-buffer@5.2.1": + "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + "version" "5.2.1" -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +"safe-buffer@~5.1.0", "safe-buffer@~5.1.1": + "integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + "version" "5.1.2" + +"safe-buffer@5.1.2": + "integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + "version" "5.1.2" "safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + "integrity" "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "resolved" "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + "version" "2.1.2" -sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +"sax@^1.2.4": + "integrity" "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "resolved" "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" + "version" "1.2.4" -scheduler@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" - integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== +"scheduler@^0.19.1": + "integrity" "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==" + "resolved" "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz" + "version" "0.19.1" dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" + "loose-envify" "^1.1.0" + "object-assign" "^4.1.1" -schema-utils@2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== - dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" - -schema-utils@^2.6.5: - version "2.7.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" - integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== +"schema-utils@^2.6.5": + "integrity" "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz" + "version" "2.7.1" dependencies: "@types/json-schema" "^7.0.5" - ajv "^6.12.4" - ajv-keywords "^3.5.2" + "ajv" "^6.12.4" + "ajv-keywords" "^3.5.2" -schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== +"schema-utils@^3.0.0", "schema-utils@^3.1.0", "schema-utils@^3.1.1": + "integrity" "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz" + "version" "3.1.1" dependencies: "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" + "ajv" "^6.12.5" + "ajv-keywords" "^3.5.2" -schema-utils@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" - integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== +"schema-utils@^4.0.0": + "integrity" "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz" + "version" "4.0.0" dependencies: "@types/json-schema" "^7.0.9" - ajv "^8.8.0" - ajv-formats "^2.1.1" - ajv-keywords "^5.0.0" + "ajv" "^8.8.0" + "ajv-formats" "^2.1.1" + "ajv-keywords" "^5.0.0" -section-matter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" - integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== +"schema-utils@2.7.0": + "integrity" "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" + "version" "2.7.0" dependencies: - extend-shallow "^2.0.1" - kind-of "^6.0.0" + "@types/json-schema" "^7.0.4" + "ajv" "^6.12.2" + "ajv-keywords" "^3.4.1" -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= - -selfsigned@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.0.0.tgz#e927cd5377cbb0a1075302cff8df1042cc2bce5b" - integrity sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ== +"section-matter@^1.0.0": + "integrity" "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==" + "resolved" "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz" + "version" "1.0.0" dependencies: - node-forge "^1.2.0" + "extend-shallow" "^2.0.1" + "kind-of" "^6.0.0" -semver-diff@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" - integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== +"select-hose@^2.0.0": + "integrity" "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + "resolved" "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" + "version" "2.0.0" + +"selfsigned@^2.0.0": + "integrity" "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==" + "resolved" "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz" + "version" "2.0.0" dependencies: - semver "^6.3.0" + "node-forge" "^1.2.0" -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== +"semver-diff@^3.1.1": + "integrity" "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==" + "resolved" "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz" + "version" "3.1.1" dependencies: - lru-cache "^6.0.0" + "semver" "^6.3.0" -send@0.17.2: - version "0.17.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" - integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== +"semver@^5.4.1": + "integrity" "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "resolved" "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" + "version" "5.7.1" + +"semver@^6.0.0", "semver@^6.1.1", "semver@^6.1.2", "semver@^6.2.0", "semver@^6.3.0": + "integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + "version" "6.3.0" + +"semver@^7.3.2": + "integrity" "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" + "version" "7.3.5" dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "1.8.1" - mime "1.6.0" - ms "2.1.3" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" + "lru-cache" "^6.0.0" -serialize-javascript@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== +"semver@^7.3.4": + "integrity" "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" + "version" "7.3.5" dependencies: - randombytes "^2.1.0" + "lru-cache" "^6.0.0" -serve-handler@^6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.3.tgz#1bf8c5ae138712af55c758477533b9117f6435e8" - integrity sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w== +"semver@^7.3.5": + "integrity" "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" + "version" "7.3.5" dependencies: - bytes "3.0.0" - content-disposition "0.5.2" - fast-url-parser "1.1.3" - mime-types "2.1.18" - minimatch "3.0.4" - path-is-inside "1.0.2" - path-to-regexp "2.2.1" - range-parser "1.2.0" + "lru-cache" "^6.0.0" -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= +"semver@7.0.0": + "integrity" "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz" + "version" "7.0.0" + +"send@0.17.2": + "integrity" "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==" + "resolved" "https://registry.npmjs.org/send/-/send-0.17.2.tgz" + "version" "0.17.2" dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" + "debug" "2.6.9" + "depd" "~1.1.2" + "destroy" "~1.0.4" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "etag" "~1.8.1" + "fresh" "0.5.2" + "http-errors" "1.8.1" + "mime" "1.6.0" + "ms" "2.1.3" + "on-finished" "~2.3.0" + "range-parser" "~1.2.1" + "statuses" "~1.5.0" -serve-static@1.14.2: - version "1.14.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" - integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== +"serialize-javascript@^6.0.0": + "integrity" "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==" + "resolved" "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" + "version" "6.0.0" dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.2" + "randombytes" "^2.1.0" -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== +"serve-handler@^6.1.3": + "integrity" "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==" + "resolved" "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz" + "version" "6.1.3" dependencies: - kind-of "^6.0.2" + "bytes" "3.0.0" + "content-disposition" "0.5.2" + "fast-url-parser" "1.1.3" + "mime-types" "2.1.18" + "minimatch" "3.0.4" + "path-is-inside" "1.0.2" + "path-to-regexp" "2.2.1" + "range-parser" "1.2.0" -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== +"serve-index@^1.9.1": + "integrity" "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=" + "resolved" "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" + "version" "1.9.1" dependencies: - shebang-regex "^3.0.0" + "accepts" "~1.3.4" + "batch" "0.6.1" + "debug" "2.6.9" + "escape-html" "~1.0.3" + "http-errors" "~1.6.2" + "mime-types" "~2.1.17" + "parseurl" "~1.3.2" -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shell-quote@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" - integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== - -shelljs@^0.8.4: - version "0.8.5" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" - integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== +"serve-static@1.14.2": + "integrity" "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==" + "resolved" "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz" + "version" "1.14.2" dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "parseurl" "~1.3.3" + "send" "0.17.2" -signal-exit@^3.0.2, signal-exit@^3.0.3: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +"setimmediate@^1.0.5": + "integrity" "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + "resolved" "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" + "version" "1.0.5" -sirv@^1.0.7: - version "1.0.19" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" - integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== +"setprototypeof@1.1.0": + "integrity" "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + "resolved" "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" + "version" "1.1.0" + +"setprototypeof@1.2.0": + "integrity" "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "resolved" "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + "version" "1.2.0" + +"shallow-clone@^3.0.0": + "integrity" "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==" + "resolved" "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "kind-of" "^6.0.2" + +"shebang-command@^2.0.0": + "integrity" "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==" + "resolved" "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "shebang-regex" "^3.0.0" + +"shebang-regex@^3.0.0": + "integrity" "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + "resolved" "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + "version" "3.0.0" + +"shell-quote@^1.7.3": + "integrity" "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + "resolved" "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz" + "version" "1.7.3" + +"shelljs@^0.8.4": + "integrity" "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==" + "resolved" "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz" + "version" "0.8.5" + dependencies: + "glob" "^7.0.0" + "interpret" "^1.0.0" + "rechoir" "^0.6.2" + +"signal-exit@^3.0.2", "signal-exit@^3.0.3": + "integrity" "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "resolved" "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + "version" "3.0.7" + +"sirv@^1.0.7": + "integrity" "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==" + "resolved" "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz" + "version" "1.0.19" dependencies: "@polka/url" "^1.0.0-next.20" - mrmime "^1.0.0" - totalist "^1.0.0" + "mrmime" "^1.0.0" + "totalist" "^1.0.0" -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +"sisteransi@^1.0.5": + "integrity" "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + "resolved" "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" + "version" "1.0.5" -sitemap@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-7.1.1.tgz#eeed9ad6d95499161a3eadc60f8c6dce4bea2bef" - integrity sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg== +"sitemap@^7.0.0": + "integrity" "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==" + "resolved" "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz" + "version" "7.1.1" dependencies: "@types/node" "^17.0.5" "@types/sax" "^1.2.1" - arg "^5.0.0" - sax "^1.2.4" + "arg" "^5.0.0" + "sax" "^1.2.4" -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +"slash@^3.0.0": + "integrity" "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + "resolved" "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + "version" "3.0.0" -slash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" - integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +"slash@^4.0.0": + "integrity" "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + "resolved" "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" + "version" "4.0.0" -sockjs@^0.3.21: - version "0.3.24" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" - integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== +"sockjs@^0.3.21": + "integrity" "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==" + "resolved" "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" + "version" "0.3.24" dependencies: - faye-websocket "^0.11.3" - uuid "^8.3.2" - websocket-driver "^0.7.4" + "faye-websocket" "^0.11.3" + "uuid" "^8.3.2" + "websocket-driver" "^0.7.4" -sort-css-media-queries@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.0.4.tgz#b2badfa519cb4a938acbc6d3aaa913d4949dc908" - integrity sha512-PAIsEK/XupCQwitjv7XxoMvYhT7EAfyzI3hsy/MyDgTvc+Ft55ctdkctJLOy6cQejaIC+zjpUL4djFVm2ivOOw== +"sort-css-media-queries@2.0.4": + "integrity" "sha512-PAIsEK/XupCQwitjv7XxoMvYhT7EAfyzI3hsy/MyDgTvc+Ft55ctdkctJLOy6cQejaIC+zjpUL4djFVm2ivOOw==" + "resolved" "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.0.4.tgz" + "version" "2.0.4" -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +"source-list-map@^2.0.0": + "integrity" "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + "resolved" "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz" + "version" "2.0.1" -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +"source-map-js@^1.0.2": + "integrity" "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + "resolved" "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" + "version" "1.0.2" -source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== +"source-map-support@~0.5.20": + "integrity" "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==" + "resolved" "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + "version" "0.5.21" dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" + "buffer-from" "^1.0.0" + "source-map" "^0.6.0" -source-map@^0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= +"source-map@^0.5.0": + "integrity" "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + "version" "0.5.7" -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +"source-map@^0.6.0", "source-map@^0.6.1", "source-map@~0.6.0", "source-map@~0.6.1": + "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + "version" "0.6.1" -sourcemap-codec@^1.4.4: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +"sourcemap-codec@^1.4.4": + "integrity" "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "resolved" "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" + "version" "1.4.8" -space-separated-tokens@^1.0.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" - integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== +"space-separated-tokens@^1.0.0": + "integrity" "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" + "resolved" "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz" + "version" "1.1.5" -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== +"spdy-transport@^3.0.0": + "integrity" "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==" + "resolved" "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz" + "version" "3.0.0" dependencies: - debug "^4.1.0" - detect-node "^2.0.4" - hpack.js "^2.1.6" - obuf "^1.1.2" - readable-stream "^3.0.6" - wbuf "^1.7.3" + "debug" "^4.1.0" + "detect-node" "^2.0.4" + "hpack.js" "^2.1.6" + "obuf" "^1.1.2" + "readable-stream" "^3.0.6" + "wbuf" "^1.7.3" -spdy@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" - integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== +"spdy@^4.0.2": + "integrity" "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==" + "resolved" "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz" + "version" "4.0.2" dependencies: - debug "^4.1.0" - handle-thing "^2.0.0" - http-deceiver "^1.2.7" - select-hose "^2.0.0" - spdy-transport "^3.0.0" + "debug" "^4.1.0" + "handle-thing" "^2.0.0" + "http-deceiver" "^1.2.7" + "select-hose" "^2.0.0" + "spdy-transport" "^3.0.0" -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +"sprintf-js@~1.0.2": + "integrity" "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "resolved" "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + "version" "1.0.3" -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +"stable@^0.1.8": + "integrity" "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + "resolved" "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" + "version" "0.1.8" -state-toggle@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" - integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== +"state-toggle@^1.0.0": + "integrity" "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==" + "resolved" "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz" + "version" "1.0.3" -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", "statuses@~1.5.0": + "integrity" "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + "resolved" "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + "version" "1.5.0" -std-env@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.0.1.tgz#bc4cbc0e438610197e34c2d79c3df30b491f5182" - integrity sha512-mC1Ps9l77/97qeOZc+HrOL7TIaOboHqMZ24dGVQrlxFcpPpfCHpH+qfUT7Dz+6mlG8+JPA1KfBQo19iC/+Ngcw== +"std-env@^3.0.1": + "integrity" "sha512-mC1Ps9l77/97qeOZc+HrOL7TIaOboHqMZ24dGVQrlxFcpPpfCHpH+qfUT7Dz+6mlG8+JPA1KfBQo19iC/+Ngcw==" + "resolved" "https://registry.npmjs.org/std-env/-/std-env-3.0.1.tgz" + "version" "3.0.1" -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== +"string_decoder@^1.1.1": + "integrity" "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==" + "resolved" "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + "version" "1.3.0" dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" + "safe-buffer" "~5.2.0" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== +"string_decoder@~1.1.1": + "integrity" "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==" + "resolved" "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + "version" "1.1.1" dependencies: - safe-buffer "~5.2.0" + "safe-buffer" "~5.1.0" -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== +"string-width@^4.0.0", "string-width@^4.1.0", "string-width@^4.2.2": + "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" + "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + "version" "4.2.3" dependencies: - safe-buffer "~5.1.0" + "emoji-regex" "^8.0.0" + "is-fullwidth-code-point" "^3.0.0" + "strip-ansi" "^6.0.1" -stringify-object@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" - integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== +"stringify-object@^3.3.0": + "integrity" "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==" + "resolved" "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz" + "version" "3.3.0" dependencies: - get-own-enumerable-property-symbols "^3.0.0" - is-obj "^1.0.1" - is-regexp "^1.0.0" + "get-own-enumerable-property-symbols" "^3.0.0" + "is-obj" "^1.0.1" + "is-regexp" "^1.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== +"strip-ansi@^6.0.0", "strip-ansi@^6.0.1": + "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + "version" "6.0.1" dependencies: - ansi-regex "^5.0.1" + "ansi-regex" "^5.0.1" -strip-ansi@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" - integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== +"strip-ansi@^7.0.0": + "integrity" "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz" + "version" "7.0.1" dependencies: - ansi-regex "^6.0.1" + "ansi-regex" "^6.0.1" -strip-bom-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" - integrity sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI= +"strip-bom-string@^1.0.0": + "integrity" "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=" + "resolved" "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz" + "version" "1.0.0" -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +"strip-final-newline@^2.0.0": + "integrity" "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + "resolved" "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + "version" "2.0.0" -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +"strip-json-comments@^3.1.1": + "integrity" "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + "version" "3.1.1" -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +"strip-json-comments@~2.0.1": + "integrity" "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + "version" "2.0.1" -style-to-object@0.3.0, style-to-object@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" - integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== +"style-to-object@^0.3.0", "style-to-object@0.3.0": + "integrity" "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==" + "resolved" "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz" + "version" "0.3.0" dependencies: - inline-style-parser "0.1.1" + "inline-style-parser" "0.1.1" -stylehacks@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.0.3.tgz#2ef3de567bfa2be716d29a93bf3d208c133e8d04" - integrity sha512-ENcUdpf4yO0E1rubu8rkxI+JGQk4CgjchynZ4bDBJDfqdy+uhTRSWb8/F3Jtu+Bw5MW45Po3/aQGeIyyxgQtxg== +"stylehacks@^5.0.3": + "integrity" "sha512-ENcUdpf4yO0E1rubu8rkxI+JGQk4CgjchynZ4bDBJDfqdy+uhTRSWb8/F3Jtu+Bw5MW45Po3/aQGeIyyxgQtxg==" + "resolved" "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.3.tgz" + "version" "5.0.3" dependencies: - browserslist "^4.16.6" - postcss-selector-parser "^6.0.4" + "browserslist" "^4.16.6" + "postcss-selector-parser" "^6.0.4" -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== +"supports-color@^5.3.0": + "integrity" "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + "version" "5.5.0" dependencies: - has-flag "^3.0.0" + "has-flag" "^3.0.0" -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== +"supports-color@^7.1.0": + "integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + "version" "7.2.0" dependencies: - has-flag "^4.0.0" + "has-flag" "^4.0.0" -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== +"supports-color@^8.0.0": + "integrity" "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + "version" "8.1.1" dependencies: - has-flag "^4.0.0" + "has-flag" "^4.0.0" -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +"supports-preserve-symlinks-flag@^1.0.0": + "integrity" "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "resolved" "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + "version" "1.0.0" -svg-parser@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" - integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== +"svg-parser@^2.0.2": + "integrity" "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + "resolved" "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz" + "version" "2.0.4" -svgo@^2.5.0, svgo@^2.7.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" - integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== +"svgo@^2.5.0", "svgo@^2.7.0": + "integrity" "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==" + "resolved" "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz" + "version" "2.8.0" dependencies: "@trysound/sax" "0.2.0" - commander "^7.2.0" - css-select "^4.1.3" - css-tree "^1.1.3" - csso "^4.2.0" - picocolors "^1.0.0" - stable "^0.1.8" + "commander" "^7.2.0" + "css-select" "^4.1.3" + "css-tree" "^1.1.3" + "csso" "^4.2.0" + "picocolors" "^1.0.0" + "stable" "^0.1.8" -tapable@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +"tapable@^1.0.0": + "integrity" "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + "resolved" "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz" + "version" "1.1.3" -tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== +"tapable@^2.0.0", "tapable@^2.1.1", "tapable@^2.2.0": + "integrity" "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + "resolved" "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + "version" "2.2.1" -terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.2.4: - version "5.3.1" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz#0320dcc270ad5372c1e8993fabbd927929773e54" - integrity sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g== +"terser-webpack-plugin@^5.1.3", "terser-webpack-plugin@^5.2.4": + "integrity" "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==" + "resolved" "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz" + "version" "5.3.1" dependencies: - jest-worker "^27.4.5" - schema-utils "^3.1.1" - serialize-javascript "^6.0.0" - source-map "^0.6.1" - terser "^5.7.2" + "jest-worker" "^27.4.5" + "schema-utils" "^3.1.1" + "serialize-javascript" "^6.0.0" + "source-map" "^0.6.1" + "terser" "^5.7.2" -terser@^5.10.0, terser@^5.7.2: - version "5.14.2" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10" - integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA== +"terser@^5.10.0", "terser@^5.7.2": + "integrity" "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==" + "resolved" "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz" + "version" "5.14.2" dependencies: "@jridgewell/source-map" "^0.3.2" - acorn "^8.5.0" - commander "^2.20.0" - source-map-support "~0.5.20" + "acorn" "^8.5.0" + "commander" "^2.20.0" + "source-map-support" "~0.5.20" -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +"text-table@^0.2.0": + "integrity" "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + "resolved" "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + "version" "0.2.0" -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +"thunky@^1.0.2": + "integrity" "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + "resolved" "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" + "version" "1.1.0" -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" - integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= +"timsort@^0.3.0": + "integrity" "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + "resolved" "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz" + "version" "0.3.0" -tiny-invariant@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" - integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== +"tiny-invariant@^1.0.2": + "integrity" "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" + "resolved" "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz" + "version" "1.2.0" -tiny-warning@^1.0.0, tiny-warning@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" - integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +"tiny-warning@^1.0.0", "tiny-warning@^1.0.3": + "integrity" "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + "resolved" "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz" + "version" "1.0.3" -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= +"to-fast-properties@^2.0.0": + "integrity" "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "resolved" "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + "version" "2.0.0" -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== +"to-readable-stream@^1.0.0": + "integrity" "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + "resolved" "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" + "version" "1.0.0" -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== +"to-regex-range@^5.0.1": + "integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==" + "resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + "version" "5.0.1" dependencies: - is-number "^7.0.0" + "is-number" "^7.0.0" -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +"toidentifier@1.0.1": + "integrity" "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + "resolved" "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + "version" "1.0.1" -totalist@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" - integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +"totalist@^1.0.0": + "integrity" "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==" + "resolved" "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz" + "version" "1.1.0" -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +"tr46@~0.0.3": + "integrity" "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "resolved" "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + "version" "0.0.3" -trim-trailing-lines@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" - integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== +"trim-trailing-lines@^1.0.0": + "integrity" "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==" + "resolved" "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz" + "version" "1.1.4" -trim@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" - integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= +"trim@0.0.1": + "integrity" "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" + "resolved" "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz" + "version" "0.0.1" -trough@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" - integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== +"trough@^1.0.0": + "integrity" "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" + "resolved" "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz" + "version" "1.0.5" -tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +"tslib@^2.0.3", "tslib@^2.1.0", "tslib@^2.2.0", "tslib@^2.3.1": + "integrity" "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" + "version" "2.3.1" -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +"type-fest@^0.20.2": + "integrity" "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + "version" "0.20.2" -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== +"type-is@~1.6.18": + "integrity" "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==" + "resolved" "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" + "version" "1.6.18" dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" + "media-typer" "0.3.0" + "mime-types" "~2.1.24" -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== +"typedarray-to-buffer@^3.1.5": + "integrity" "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==" + "resolved" "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" + "version" "3.1.5" dependencies: - is-typedarray "^1.0.0" + "is-typedarray" "^1.0.0" -ua-parser-js@^0.7.30: - version "0.7.33" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" - integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== +"typescript@>= 2.7": + "integrity" "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" + "resolved" "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + "version" "4.9.5" -unherit@^1.0.4: - version "1.1.3" - resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" - integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== +"ua-parser-js@^0.7.30": + "integrity" "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==" + "resolved" "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz" + "version" "0.7.33" + +"unherit@^1.0.4": + "integrity" "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==" + "resolved" "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz" + "version" "1.1.3" dependencies: - inherits "^2.0.0" - xtend "^4.0.0" + "inherits" "^2.0.0" + "xtend" "^4.0.0" -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== +"unicode-canonical-property-names-ecmascript@^2.0.0": + "integrity" "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + "resolved" "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz" + "version" "2.0.0" -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== +"unicode-match-property-ecmascript@^2.0.0": + "integrity" "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==" + "resolved" "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz" + "version" "2.0.0" dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" + "unicode-canonical-property-names-ecmascript" "^2.0.0" + "unicode-property-aliases-ecmascript" "^2.0.0" -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== +"unicode-match-property-value-ecmascript@^2.0.0": + "integrity" "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" + "resolved" "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz" + "version" "2.0.0" -unicode-property-aliases-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" - integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== +"unicode-property-aliases-ecmascript@^2.0.0": + "integrity" "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" + "resolved" "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz" + "version" "2.0.0" -unified@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8" - integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== +"unified@^8.4.2": + "integrity" "sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA==" + "resolved" "https://registry.npmjs.org/unified/-/unified-8.4.2.tgz" + "version" "8.4.2" dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-buffer "^2.0.0" - is-plain-obj "^2.0.0" - trough "^1.0.0" - vfile "^4.0.0" + "bail" "^1.0.0" + "extend" "^3.0.0" + "is-plain-obj" "^2.0.0" + "trough" "^1.0.0" + "vfile" "^4.0.0" -unified@^8.4.2: - version "8.4.2" - resolved "https://registry.yarnpkg.com/unified/-/unified-8.4.2.tgz#13ad58b4a437faa2751a4a4c6a16f680c500fff1" - integrity sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA== +"unified@9.2.0": + "integrity" "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==" + "resolved" "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz" + "version" "9.2.0" dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-plain-obj "^2.0.0" - trough "^1.0.0" - vfile "^4.0.0" + "bail" "^1.0.0" + "extend" "^3.0.0" + "is-buffer" "^2.0.0" + "is-plain-obj" "^2.0.0" + "trough" "^1.0.0" + "vfile" "^4.0.0" -unique-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== +"unique-string@^2.0.0": + "integrity" "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==" + "resolved" "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz" + "version" "2.0.0" dependencies: - crypto-random-string "^2.0.0" + "crypto-random-string" "^2.0.0" -unist-builder@2.0.3, unist-builder@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" - integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== +"unist-builder@^2.0.0", "unist-builder@2.0.3": + "integrity" "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==" + "resolved" "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz" + "version" "2.0.3" -unist-util-generated@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" - integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== +"unist-util-generated@^1.0.0": + "integrity" "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==" + "resolved" "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz" + "version" "1.1.6" -unist-util-is@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" - integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== +"unist-util-is@^4.0.0": + "integrity" "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==" + "resolved" "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz" + "version" "4.1.0" -unist-util-position@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" - integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== +"unist-util-position@^3.0.0": + "integrity" "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==" + "resolved" "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz" + "version" "3.1.0" -unist-util-remove-position@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" - integrity sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA== +"unist-util-remove-position@^2.0.0": + "integrity" "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==" + "resolved" "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz" + "version" "2.0.1" dependencies: - unist-util-visit "^2.0.0" + "unist-util-visit" "^2.0.0" -unist-util-remove@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.0.0.tgz#32c2ad5578802f2ca62ab808173d505b2c898488" - integrity sha512-HwwWyNHKkeg/eXRnE11IpzY8JT55JNM1YCwwU9YNCnfzk6s8GhPXrVBBZWiwLeATJbI7euvoGSzcy9M29UeW3g== +"unist-util-remove@^2.0.0": + "integrity" "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==" + "resolved" "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.1.0.tgz" + "version" "2.1.0" dependencies: - unist-util-is "^4.0.0" + "unist-util-is" "^4.0.0" -unist-util-remove@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.1.0.tgz#b0b4738aa7ee445c402fda9328d604a02d010588" - integrity sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q== +"unist-util-remove@2.0.0": + "integrity" "sha512-HwwWyNHKkeg/eXRnE11IpzY8JT55JNM1YCwwU9YNCnfzk6s8GhPXrVBBZWiwLeATJbI7euvoGSzcy9M29UeW3g==" + "resolved" "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.0.0.tgz" + "version" "2.0.0" dependencies: - unist-util-is "^4.0.0" + "unist-util-is" "^4.0.0" -unist-util-stringify-position@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" - integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== +"unist-util-stringify-position@^2.0.0": + "integrity" "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==" + "resolved" "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz" + "version" "2.0.3" dependencies: "@types/unist" "^2.0.2" -unist-util-visit-parents@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" - integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== +"unist-util-visit-parents@^3.0.0": + "integrity" "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==" + "resolved" "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz" + "version" "3.1.1" dependencies: "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" + "unist-util-is" "^4.0.0" -unist-util-visit@2.0.3, unist-util-visit@^2.0.0, unist-util-visit@^2.0.1, unist-util-visit@^2.0.2, unist-util-visit@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" - integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== +"unist-util-visit@^2.0.0", "unist-util-visit@^2.0.1", "unist-util-visit@^2.0.2", "unist-util-visit@^2.0.3", "unist-util-visit@2.0.3": + "integrity" "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==" + "resolved" "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz" + "version" "2.0.3" dependencies: "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" - unist-util-visit-parents "^3.0.0" + "unist-util-is" "^4.0.0" + "unist-util-visit-parents" "^3.0.0" -universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +"universalify@^2.0.0": + "integrity" "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + "resolved" "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" + "version" "2.0.0" -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= +"unpipe@~1.0.0", "unpipe@1.0.0": + "integrity" "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "resolved" "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + "version" "1.0.0" -update-notifier@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" - integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== +"update-browserslist-db@^1.0.10": + "integrity" "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==" + "resolved" "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz" + "version" "1.0.10" dependencies: - boxen "^5.0.0" - chalk "^4.1.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.4.0" - is-npm "^5.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.1.0" - pupa "^2.1.1" - semver "^7.3.4" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" + "escalade" "^3.1.1" + "picocolors" "^1.0.0" -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== +"update-notifier@^5.1.0": + "integrity" "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==" + "resolved" "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz" + "version" "5.1.0" dependencies: - punycode "^2.1.0" + "boxen" "^5.0.0" + "chalk" "^4.1.0" + "configstore" "^5.0.1" + "has-yarn" "^2.1.0" + "import-lazy" "^2.1.0" + "is-ci" "^2.0.0" + "is-installed-globally" "^0.4.0" + "is-npm" "^5.0.0" + "is-yarn-global" "^0.3.0" + "latest-version" "^5.1.0" + "pupa" "^2.1.1" + "semver" "^7.3.4" + "semver-diff" "^3.1.1" + "xdg-basedir" "^4.0.0" -url-loader@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" - integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== +"uri-js@^4.2.2": + "integrity" "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==" + "resolved" "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + "version" "4.4.1" dependencies: - loader-utils "^2.0.0" - mime-types "^2.1.27" - schema-utils "^3.0.0" + "punycode" "^2.1.0" -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= +"url-loader@^4.1.1": + "integrity" "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==" + "resolved" "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz" + "version" "4.1.1" dependencies: - prepend-http "^2.0.0" + "loader-utils" "^2.0.0" + "mime-types" "^2.1.27" + "schema-utils" "^3.0.0" -url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= +"url-parse-lax@^3.0.0": + "integrity" "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=" + "resolved" "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" + "version" "3.0.0" dependencies: - punycode "1.3.2" - querystring "0.2.0" + "prepend-http" "^2.0.0" -use-composed-ref@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.2.1.tgz#9bdcb5ccd894289105da2325e1210079f56bf849" - integrity sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw== - -use-isomorphic-layout-effect@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz#7bb6589170cd2987a152042f9084f9effb75c225" - integrity sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ== - -use-latest@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.2.0.tgz#a44f6572b8288e0972ec411bdd0840ada366f232" - integrity sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw== +"url@^0.11.0": + "integrity" "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=" + "resolved" "https://registry.npmjs.org/url/-/url-0.11.0.tgz" + "version" "0.11.0" dependencies: - use-isomorphic-layout-effect "^1.0.0" + "punycode" "1.3.2" + "querystring" "0.2.0" -util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +"use-composed-ref@^1.0.0": + "integrity" "sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw==" + "resolved" "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.2.1.tgz" + "version" "1.2.1" -utila@~0.4: - version "0.4.0" - resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" - integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= +"use-isomorphic-layout-effect@^1.0.0": + "integrity" "sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ==" + "resolved" "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz" + "version" "1.1.1" -utility-types@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" - integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== +"use-latest@^1.0.0": + "integrity" "sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw==" + "resolved" "https://registry.npmjs.org/use-latest/-/use-latest-1.2.0.tgz" + "version" "1.2.0" + dependencies: + "use-isomorphic-layout-effect" "^1.0.0" -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= +"util-deprecate@^1.0.1", "util-deprecate@^1.0.2", "util-deprecate@~1.0.1": + "integrity" "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "resolved" "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "version" "1.0.2" -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +"utila@~0.4": + "integrity" "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" + "resolved" "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz" + "version" "0.4.0" -value-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" - integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== +"utility-types@^3.10.0": + "integrity" "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==" + "resolved" "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz" + "version" "3.10.0" -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +"utils-merge@1.0.1": + "integrity" "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "resolved" "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" + "version" "1.0.1" -vfile-location@^3.0.0, vfile-location@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" - integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== +"uuid@^8.3.2": + "integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "resolved" "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + "version" "8.3.2" -vfile-message@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" - integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== +"value-equal@^1.0.1": + "integrity" "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + "resolved" "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz" + "version" "1.0.1" + +"vary@~1.1.2": + "integrity" "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "resolved" "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + "version" "1.1.2" + +"vfile-location@^3.0.0", "vfile-location@^3.2.0": + "integrity" "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==" + "resolved" "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz" + "version" "3.2.0" + +"vfile-message@^2.0.0": + "integrity" "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==" + "resolved" "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz" + "version" "2.0.4" dependencies: "@types/unist" "^2.0.0" - unist-util-stringify-position "^2.0.0" + "unist-util-stringify-position" "^2.0.0" -vfile@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" - integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== +"vfile@^4.0.0": + "integrity" "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==" + "resolved" "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz" + "version" "4.2.1" dependencies: "@types/unist" "^2.0.0" - is-buffer "^2.0.0" - unist-util-stringify-position "^2.0.0" - vfile-message "^2.0.0" + "is-buffer" "^2.0.0" + "unist-util-stringify-position" "^2.0.0" + "vfile-message" "^2.0.0" -wait-on@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.1.tgz#16bbc4d1e4ebdd41c5b4e63a2e16dbd1f4e5601e" - integrity sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw== +"wait-on@^6.0.0": + "integrity" "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==" + "resolved" "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz" + "version" "6.0.1" dependencies: - axios "^0.25.0" - joi "^17.6.0" - lodash "^4.17.21" - minimist "^1.2.5" - rxjs "^7.5.4" + "axios" "^0.25.0" + "joi" "^17.6.0" + "lodash" "^4.17.21" + "minimist" "^1.2.5" + "rxjs" "^7.5.4" -watchpack@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.1.tgz#4200d9447b401156eeca7767ee610f8809bc9d25" - integrity sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA== +"watchpack@^2.3.1": + "integrity" "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==" + "resolved" "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz" + "version" "2.3.1" dependencies: - glob-to-regexp "^0.4.1" - graceful-fs "^4.1.2" + "glob-to-regexp" "^0.4.1" + "graceful-fs" "^4.1.2" -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== +"wbuf@^1.1.0", "wbuf@^1.7.3": + "integrity" "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==" + "resolved" "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz" + "version" "1.7.3" dependencies: - minimalistic-assert "^1.0.0" + "minimalistic-assert" "^1.0.0" -web-namespaces@^1.0.0, web-namespaces@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" - integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== +"web-namespaces@^1.0.0", "web-namespaces@^1.1.2": + "integrity" "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==" + "resolved" "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz" + "version" "1.1.4" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= +"webidl-conversions@^3.0.0": + "integrity" "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + "version" "3.0.1" -webpack-bundle-analyzer@^4.4.2: - version "4.5.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5" - integrity sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ== +"webpack-bundle-analyzer@^4.4.2": + "integrity" "sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==" + "resolved" "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz" + "version" "4.5.0" dependencies: - acorn "^8.0.4" - acorn-walk "^8.0.0" - chalk "^4.1.0" - commander "^7.2.0" - gzip-size "^6.0.0" - lodash "^4.17.20" - opener "^1.5.2" - sirv "^1.0.7" - ws "^7.3.1" + "acorn" "^8.0.4" + "acorn-walk" "^8.0.0" + "chalk" "^4.1.0" + "commander" "^7.2.0" + "gzip-size" "^6.0.0" + "lodash" "^4.17.20" + "opener" "^1.5.2" + "sirv" "^1.0.7" + "ws" "^7.3.1" -webpack-dev-middleware@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz#aa079a8dedd7e58bfeab358a9af7dab304cee57f" - integrity sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg== +"webpack-dev-middleware@^5.3.1": + "integrity" "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==" + "resolved" "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz" + "version" "5.3.1" dependencies: - colorette "^2.0.10" - memfs "^3.4.1" - mime-types "^2.1.31" - range-parser "^1.2.1" - schema-utils "^4.0.0" + "colorette" "^2.0.10" + "memfs" "^3.4.1" + "mime-types" "^2.1.31" + "range-parser" "^1.2.1" + "schema-utils" "^4.0.0" -webpack-dev-server@^4.7.1: - version "4.7.4" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz#d0ef7da78224578384e795ac228d8efb63d5f945" - integrity sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A== +"webpack-dev-server@^4.7.1": + "integrity" "sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A==" + "resolved" "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz" + "version" "4.7.4" dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" @@ -7519,212 +7582,217 @@ webpack-dev-server@^4.7.1: "@types/serve-index" "^1.9.1" "@types/sockjs" "^0.3.33" "@types/ws" "^8.2.2" - ansi-html-community "^0.0.8" - bonjour "^3.5.0" - chokidar "^3.5.3" - colorette "^2.0.10" - compression "^1.7.4" - connect-history-api-fallback "^1.6.0" - default-gateway "^6.0.3" - del "^6.0.0" - express "^4.17.1" - graceful-fs "^4.2.6" - html-entities "^2.3.2" - http-proxy-middleware "^2.0.0" - ipaddr.js "^2.0.1" - open "^8.0.9" - p-retry "^4.5.0" - portfinder "^1.0.28" - schema-utils "^4.0.0" - selfsigned "^2.0.0" - serve-index "^1.9.1" - sockjs "^0.3.21" - spdy "^4.0.2" - strip-ansi "^7.0.0" - webpack-dev-middleware "^5.3.1" - ws "^8.4.2" + "ansi-html-community" "^0.0.8" + "bonjour" "^3.5.0" + "chokidar" "^3.5.3" + "colorette" "^2.0.10" + "compression" "^1.7.4" + "connect-history-api-fallback" "^1.6.0" + "default-gateway" "^6.0.3" + "del" "^6.0.0" + "express" "^4.17.1" + "graceful-fs" "^4.2.6" + "html-entities" "^2.3.2" + "http-proxy-middleware" "^2.0.0" + "ipaddr.js" "^2.0.1" + "open" "^8.0.9" + "p-retry" "^4.5.0" + "portfinder" "^1.0.28" + "schema-utils" "^4.0.0" + "selfsigned" "^2.0.0" + "serve-index" "^1.9.1" + "sockjs" "^0.3.21" + "spdy" "^4.0.2" + "strip-ansi" "^7.0.0" + "webpack-dev-middleware" "^5.3.1" + "ws" "^8.4.2" -webpack-merge@^5.8.0: - version "5.8.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" - integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== +"webpack-merge@^5.8.0": + "integrity" "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==" + "resolved" "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz" + "version" "5.8.0" dependencies: - clone-deep "^4.0.1" - wildcard "^2.0.0" + "clone-deep" "^4.0.1" + "wildcard" "^2.0.0" -webpack-sources@^1.1.0, webpack-sources@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== +"webpack-sources@^1.1.0", "webpack-sources@^1.4.3": + "integrity" "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==" + "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz" + "version" "1.4.3" dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" + "source-list-map" "^2.0.0" + "source-map" "~0.6.1" -webpack-sources@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" - integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== +"webpack-sources@^3.2.3": + "integrity" "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" + "version" "3.2.3" -webpack@^5.61.0: - version "5.69.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.69.1.tgz#8cfd92c192c6a52c99ab00529b5a0d33aa848dc5" - integrity sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A== +"webpack@^4.0.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", "webpack@^4.4.0 || ^5.0.0", "webpack@^5.0.0", "webpack@^5.1.0", "webpack@^5.20.0", "webpack@^5.61.0", "webpack@>= 4", "webpack@>=2", "webpack@>=4.41.1 || 5.x", "webpack@3 || 4 || 5", "webpack@5.x": + "integrity" "sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==" + "resolved" "https://registry.npmjs.org/webpack/-/webpack-5.69.1.tgz" + "version" "5.69.1" dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/wasm-edit" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" - acorn "^8.4.1" - acorn-import-assertions "^1.7.6" - browserslist "^4.14.5" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.8.3" - es-module-lexer "^0.9.0" - eslint-scope "5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" - json-parse-better-errors "^1.0.2" - loader-runner "^4.2.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - schema-utils "^3.1.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.1.3" - watchpack "^2.3.1" - webpack-sources "^3.2.3" + "acorn" "^8.4.1" + "acorn-import-assertions" "^1.7.6" + "browserslist" "^4.14.5" + "chrome-trace-event" "^1.0.2" + "enhanced-resolve" "^5.8.3" + "es-module-lexer" "^0.9.0" + "eslint-scope" "5.1.1" + "events" "^3.2.0" + "glob-to-regexp" "^0.4.1" + "graceful-fs" "^4.2.9" + "json-parse-better-errors" "^1.0.2" + "loader-runner" "^4.2.0" + "mime-types" "^2.1.27" + "neo-async" "^2.6.2" + "schema-utils" "^3.1.0" + "tapable" "^2.1.1" + "terser-webpack-plugin" "^5.1.3" + "watchpack" "^2.3.1" + "webpack-sources" "^3.2.3" -webpackbar@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-5.0.2.tgz#d3dd466211c73852741dfc842b7556dcbc2b0570" - integrity sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ== +"webpackbar@^5.0.2": + "integrity" "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==" + "resolved" "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz" + "version" "5.0.2" dependencies: - chalk "^4.1.0" - consola "^2.15.3" - pretty-time "^1.1.0" - std-env "^3.0.1" + "chalk" "^4.1.0" + "consola" "^2.15.3" + "pretty-time" "^1.1.0" + "std-env" "^3.0.1" -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== +"websocket-driver@^0.7.4", "websocket-driver@>=0.5.1": + "integrity" "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==" + "resolved" "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" + "version" "0.7.4" dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" + "http-parser-js" ">=0.5.1" + "safe-buffer" ">=5.1.0" + "websocket-extensions" ">=0.1.1" -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== +"websocket-extensions@>=0.1.1": + "integrity" "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + "resolved" "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" + "version" "0.1.4" -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= +"whatwg-url@^5.0.0": + "integrity" "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=" + "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + "version" "5.0.0" dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" + "tr46" "~0.0.3" + "webidl-conversions" "^3.0.0" -which@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== +"which@^1.3.1": + "integrity" "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==" + "resolved" "https://registry.npmjs.org/which/-/which-1.3.1.tgz" + "version" "1.3.1" dependencies: - isexe "^2.0.0" + "isexe" "^2.0.0" -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== +"which@^2.0.1": + "integrity" "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==" + "resolved" "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + "version" "2.0.2" dependencies: - isexe "^2.0.0" + "isexe" "^2.0.0" -widest-line@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" - integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== +"widest-line@^3.1.0": + "integrity" "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==" + "resolved" "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz" + "version" "3.1.0" dependencies: - string-width "^4.0.0" + "string-width" "^4.0.0" -wildcard@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" - integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== +"wildcard@^2.0.0": + "integrity" "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" + "resolved" "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz" + "version" "2.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +"wrap-ansi@^7.0.0": + "integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==" + "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + "version" "7.0.0" dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" + "ansi-styles" "^4.0.0" + "string-width" "^4.1.0" + "strip-ansi" "^6.0.0" -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +"wrappy@1": + "integrity" "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "version" "1.0.2" -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== +"write-file-atomic@^3.0.0": + "integrity" "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==" + "resolved" "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" + "version" "3.0.3" dependencies: - imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" + "imurmurhash" "^0.1.4" + "is-typedarray" "^1.0.0" + "signal-exit" "^3.0.2" + "typedarray-to-buffer" "^3.1.5" -ws@^7.3.1: - version "7.5.7" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" - integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== +"ws@^7.3.1": + "integrity" "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==" + "resolved" "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz" + "version" "7.5.7" -ws@^8.4.2: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== +"ws@^8.4.2": + "integrity" "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==" + "resolved" "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz" + "version" "8.5.0" -xdg-basedir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" - integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== +"xdg-basedir@^4.0.0": + "integrity" "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + "resolved" "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz" + "version" "4.0.0" -xml-js@^1.6.11: - version "1.6.11" - resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" - integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== +"xml-js@^1.6.11": + "integrity" "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==" + "resolved" "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz" + "version" "1.6.11" dependencies: - sax "^1.2.4" + "sax" "^1.2.4" -xtend@^4.0.0, xtend@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +"xtend@^4.0.0", "xtend@^4.0.1": + "integrity" "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + "resolved" "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" + "version" "4.0.2" -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +"yallist@^3.0.2": + "integrity" "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "resolved" "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + "version" "3.1.1" -yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +"yallist@^4.0.0": + "integrity" "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "resolved" "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + "version" "4.0.0" -yarn@^1.17.3: - version "1.22.17" - resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.17.tgz#bf910747d22497b573131f7341c0e1d15c74036c" - integrity sha512-H0p241BXaH0UN9IeH//RT82tl5PfNraVpSpEoW+ET7lmopNC61eZ+A+IDvU8FM6Go5vx162SncDL8J1ZjRBriQ== +"yaml@^1.10.0", "yaml@^1.10.2", "yaml@^1.7.2": + "integrity" "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + "resolved" "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + "version" "1.10.2" -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +"yarn@^1.17.3": + "integrity" "sha512-H0p241BXaH0UN9IeH//RT82tl5PfNraVpSpEoW+ET7lmopNC61eZ+A+IDvU8FM6Go5vx162SncDL8J1ZjRBriQ==" + "resolved" "https://registry.npmjs.org/yarn/-/yarn-1.22.17.tgz" + "version" "1.22.17" -zwitch@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" - integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== +"yocto-queue@^0.1.0": + "integrity" "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + "resolved" "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + "version" "0.1.0" + +"zwitch@^1.0.0": + "integrity" "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==" + "resolved" "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz" + "version" "1.0.5" From 66e05849da02a463791301a3ed6eddfe23e3341c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 13 Mar 2023 16:22:14 +0100 Subject: [PATCH 1267/1271] replace last usages of Qt module --- openpype/hosts/maya/plugins/inventory/connect_geometry.py | 2 +- openpype/hosts/maya/plugins/inventory/connect_xgen.py | 2 +- openpype/hosts/maya/plugins/load/load_xgen.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/inventory/connect_geometry.py b/openpype/hosts/maya/plugins/inventory/connect_geometry.py index a12487cf7e..03154b7afe 100644 --- a/openpype/hosts/maya/plugins/inventory/connect_geometry.py +++ b/openpype/hosts/maya/plugins/inventory/connect_geometry.py @@ -134,7 +134,7 @@ class ConnectGeometry(InventoryAction): bool """ - from Qt import QtWidgets + from qtpy import QtWidgets accept = QtWidgets.QMessageBox.Ok if show_cancel: diff --git a/openpype/hosts/maya/plugins/inventory/connect_xgen.py b/openpype/hosts/maya/plugins/inventory/connect_xgen.py index 933a1b4025..177971f176 100644 --- a/openpype/hosts/maya/plugins/inventory/connect_xgen.py +++ b/openpype/hosts/maya/plugins/inventory/connect_xgen.py @@ -149,7 +149,7 @@ class ConnectXgen(InventoryAction): bool """ - from Qt import QtWidgets + from qtpy import QtWidgets accept = QtWidgets.QMessageBox.Ok if show_cancel: diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 1600cd49bd..7e6cabc77c 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -3,7 +3,7 @@ import os import maya.cmds as cmds import xgenm -from Qt import QtWidgets +from qtpy import QtWidgets import openpype.hosts.maya.api.plugin from openpype.hosts.maya.api.lib import ( From efc9c794748c38daac60d3e2e14e39eea16b6ec3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 14 Mar 2023 16:23:55 +0100 Subject: [PATCH 1268/1271] updating website yarn.lock --- website/yarn.lock | 10884 ++++++++++++++++++++++---------------------- 1 file changed, 5476 insertions(+), 5408 deletions(-) diff --git a/website/yarn.lock b/website/yarn.lock index 559c58f931..2edf57abf4 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -3,56 +3,56 @@ "@algolia/autocomplete-core@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.5.2.tgz#ec0178e07b44fd74a057728ac157291b26cecf37" - integrity sha512-DY0bhyczFSS1b/CqJlTE/nQRtnTAHl6IemIkBy0nEWnhDzRDdtdx4p5Uuk3vwAFxwEEgi1WqKwgSSMx6DpNL4A== + "integrity" "sha512-DY0bhyczFSS1b/CqJlTE/nQRtnTAHl6IemIkBy0nEWnhDzRDdtdx4p5Uuk3vwAFxwEEgi1WqKwgSSMx6DpNL4A==" + "resolved" "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.5.2.tgz" + "version" "1.5.2" dependencies: "@algolia/autocomplete-shared" "1.5.2" "@algolia/autocomplete-preset-algolia@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.5.2.tgz#36c5638cc6dba6ea46a86e5a0314637ca40a77ca" - integrity sha512-3MRYnYQFJyovANzSX2CToS6/5cfVjbLLqFsZTKcvF3abhQzxbqwwaMBlJtt620uBUOeMzhdfasKhCc40+RHiZw== + "integrity" "sha512-3MRYnYQFJyovANzSX2CToS6/5cfVjbLLqFsZTKcvF3abhQzxbqwwaMBlJtt620uBUOeMzhdfasKhCc40+RHiZw==" + "resolved" "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.5.2.tgz" + "version" "1.5.2" dependencies: "@algolia/autocomplete-shared" "1.5.2" "@algolia/autocomplete-shared@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.5.2.tgz#e157f9ad624ab8fd940ff28bd2094cdf199cdd79" - integrity sha512-ylQAYv5H0YKMfHgVWX0j0NmL8XBcAeeeVQUmppnnMtzDbDnca6CzhKj3Q8eF9cHCgcdTDdb5K+3aKyGWA0obug== + "integrity" "sha512-ylQAYv5H0YKMfHgVWX0j0NmL8XBcAeeeVQUmppnnMtzDbDnca6CzhKj3Q8eF9cHCgcdTDdb5K+3aKyGWA0obug==" + "resolved" "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.5.2.tgz" + "version" "1.5.2" "@algolia/cache-browser-local-storage@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.12.1.tgz#23f4f219963b96918d0524acd09d4d646541d888" - integrity sha512-ERFFOnC9740xAkuO0iZTQqm2AzU7Dpz/s+g7o48GlZgx5p9GgNcsuK5eS0GoW/tAK+fnKlizCtlFHNuIWuvfsg== + "integrity" "sha512-ERFFOnC9740xAkuO0iZTQqm2AzU7Dpz/s+g7o48GlZgx5p9GgNcsuK5eS0GoW/tAK+fnKlizCtlFHNuIWuvfsg==" + "resolved" "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/cache-common" "4.12.1" "@algolia/cache-common@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.12.1.tgz#d3f1676ca9c404adce0f78d68f6381bedb44cd9c" - integrity sha512-UugTER3V40jT+e19Dmph5PKMeliYKxycNPwrPNADin0RcWNfT2QksK9Ff2N2W7UKraqMOzoeDb4LAJtxcK1a8Q== + "integrity" "sha512-UugTER3V40jT+e19Dmph5PKMeliYKxycNPwrPNADin0RcWNfT2QksK9Ff2N2W7UKraqMOzoeDb4LAJtxcK1a8Q==" + "resolved" "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.12.1.tgz" + "version" "4.12.1" "@algolia/cache-in-memory@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.12.1.tgz#0ef6aac2f8feab5b46fc130beb682bbd21b55244" - integrity sha512-U6iaunaxK1lHsAf02UWF58foKFEcrVLsHwN56UkCtwn32nlP9rz52WOcHsgk6TJrL8NDcO5swMjtOQ5XHESFLw== + "integrity" "sha512-U6iaunaxK1lHsAf02UWF58foKFEcrVLsHwN56UkCtwn32nlP9rz52WOcHsgk6TJrL8NDcO5swMjtOQ5XHESFLw==" + "resolved" "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/cache-common" "4.12.1" "@algolia/client-account@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.12.1.tgz#e838c9283db2fab32a425dd13c77da321d48fd8b" - integrity sha512-jGo4ConJNoMdTCR2zouO0jO/JcJmzOK6crFxMMLvdnB1JhmMbuIKluOTJVlBWeivnmcsqb7r0v7qTCPW5PAyxQ== + "integrity" "sha512-jGo4ConJNoMdTCR2zouO0jO/JcJmzOK6crFxMMLvdnB1JhmMbuIKluOTJVlBWeivnmcsqb7r0v7qTCPW5PAyxQ==" + "resolved" "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/client-common" "4.12.1" "@algolia/client-search" "4.12.1" "@algolia/transporter" "4.12.1" "@algolia/client-analytics@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.12.1.tgz#2976d658655a1590cf84cfb596aa75a204f6dec4" - integrity sha512-h1It7KXzIthlhuhfBk7LteYq72tym9maQDUsyRW0Gft8b6ZQahnRak9gcCvKwhcJ1vJoP7T7JrNYGiYSicTD9g== + "integrity" "sha512-h1It7KXzIthlhuhfBk7LteYq72tym9maQDUsyRW0Gft8b6ZQahnRak9gcCvKwhcJ1vJoP7T7JrNYGiYSicTD9g==" + "resolved" "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/client-common" "4.12.1" "@algolia/client-search" "4.12.1" @@ -60,99 +60,121 @@ "@algolia/transporter" "4.12.1" "@algolia/client-common@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.12.1.tgz#104ccefe96bda3ff926bc70c31ff6d17c41b6107" - integrity sha512-obnJ8eSbv+h94Grk83DTGQ3bqhViSWureV6oK1s21/KMGWbb3DkduHm+lcwFrMFkjSUSzosLBHV9EQUIBvueTw== + "integrity" "sha512-obnJ8eSbv+h94Grk83DTGQ3bqhViSWureV6oK1s21/KMGWbb3DkduHm+lcwFrMFkjSUSzosLBHV9EQUIBvueTw==" + "resolved" "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/requester-common" "4.12.1" "@algolia/transporter" "4.12.1" "@algolia/client-personalization@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.12.1.tgz#f63d1890f95de850e1c8e41c1d57adda521d9e7f" - integrity sha512-sMSnjjPjRgByGHYygV+5L/E8a6RgU7l2GbpJukSzJ9GRY37tHmBHuvahv8JjdCGJ2p7QDYLnQy5bN5Z02qjc7Q== + "integrity" "sha512-sMSnjjPjRgByGHYygV+5L/E8a6RgU7l2GbpJukSzJ9GRY37tHmBHuvahv8JjdCGJ2p7QDYLnQy5bN5Z02qjc7Q==" + "resolved" "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/client-common" "4.12.1" "@algolia/requester-common" "4.12.1" "@algolia/transporter" "4.12.1" -"@algolia/client-search@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.12.1.tgz#fcd7a974be5d39d5c336d7f2e89577ffa66aefdd" - integrity sha512-MwwKKprfY6X2nJ5Ki/ccXM2GDEePvVjZnnoOB2io3dLKW4fTqeSRlC5DRXeFD7UM0vOPPHr4ItV2aj19APKNVQ== +"@algolia/client-search@^4.9.1", "@algolia/client-search@4.12.1": + "integrity" "sha512-MwwKKprfY6X2nJ5Ki/ccXM2GDEePvVjZnnoOB2io3dLKW4fTqeSRlC5DRXeFD7UM0vOPPHr4ItV2aj19APKNVQ==" + "resolved" "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/client-common" "4.12.1" "@algolia/requester-common" "4.12.1" "@algolia/transporter" "4.12.1" "@algolia/events@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@algolia/events/-/events-4.0.1.tgz#fd39e7477e7bc703d7f893b556f676c032af3950" - integrity sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ== + "integrity" "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + "resolved" "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz" + "version" "4.0.1" "@algolia/logger-common@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.12.1.tgz#d6501b4d9d242956257ba8e10f6b4bbf6863baa4" - integrity sha512-fCgrzlXGATNqdFTxwx0GsyPXK+Uqrx1SZ3iuY2VGPPqdt1a20clAG2n2OcLHJpvaa6vMFPlJyWvbqAgzxdxBlQ== + "integrity" "sha512-fCgrzlXGATNqdFTxwx0GsyPXK+Uqrx1SZ3iuY2VGPPqdt1a20clAG2n2OcLHJpvaa6vMFPlJyWvbqAgzxdxBlQ==" + "resolved" "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.12.1.tgz" + "version" "4.12.1" "@algolia/logger-console@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.12.1.tgz#841edd39dd5c5530a69fc66084bfee3254dd0807" - integrity sha512-0owaEnq/davngQMYqxLA4KrhWHiXujQ1CU3FFnyUcMyBR7rGHI48zSOUpqnsAXrMBdSH6rH5BDkSUUFwsh8RkQ== + "integrity" "sha512-0owaEnq/davngQMYqxLA4KrhWHiXujQ1CU3FFnyUcMyBR7rGHI48zSOUpqnsAXrMBdSH6rH5BDkSUUFwsh8RkQ==" + "resolved" "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/logger-common" "4.12.1" "@algolia/requester-browser-xhr@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.12.1.tgz#2d0c18ee188d7cae0e4a930e5e89989e3c4a816b" - integrity sha512-OaMxDyG0TZG0oqz1lQh9e3woantAG1bLnuwq3fmypsrQxra4IQZiyn1x+kEb69D2TcXApI5gOgrD4oWhtEVMtw== + "integrity" "sha512-OaMxDyG0TZG0oqz1lQh9e3woantAG1bLnuwq3fmypsrQxra4IQZiyn1x+kEb69D2TcXApI5gOgrD4oWhtEVMtw==" + "resolved" "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/requester-common" "4.12.1" "@algolia/requester-common@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.12.1.tgz#95bb6539da7199da3e205341cea8f27267f7af29" - integrity sha512-XWIrWQNJ1vIrSuL/bUk3ZwNMNxl+aWz6dNboRW6+lGTcMIwc3NBFE90ogbZKhNrFRff8zI4qCF15tjW+Fyhpow== + "integrity" "sha512-XWIrWQNJ1vIrSuL/bUk3ZwNMNxl+aWz6dNboRW6+lGTcMIwc3NBFE90ogbZKhNrFRff8zI4qCF15tjW+Fyhpow==" + "resolved" "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.12.1.tgz" + "version" "4.12.1" "@algolia/requester-node-http@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.12.1.tgz#c9df97ff1daa7e58c5c2b1f28cf7163005edccb0" - integrity sha512-awBtwaD+s0hxkA1aehYn8F0t9wqGoBVWgY4JPHBmp1ChO3pK7RKnnvnv7QQa9vTlllX29oPt/BBVgMo1Z3n1Qg== + "integrity" "sha512-awBtwaD+s0hxkA1aehYn8F0t9wqGoBVWgY4JPHBmp1ChO3pK7RKnnvnv7QQa9vTlllX29oPt/BBVgMo1Z3n1Qg==" + "resolved" "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/requester-common" "4.12.1" "@algolia/transporter@4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.12.1.tgz#61b9829916c474f42e2d4a6eada0d6c138379945" - integrity sha512-BGeNgdEHc6dXIk2g8kdlOoQ6fQ6OIaKQcplEj7HPoi+XZUeAvRi3Pff3QWd7YmybWkjzd9AnTzieTASDWhL+sQ== + "integrity" "sha512-BGeNgdEHc6dXIk2g8kdlOoQ6fQ6OIaKQcplEj7HPoi+XZUeAvRi3Pff3QWd7YmybWkjzd9AnTzieTASDWhL+sQ==" + "resolved" "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/cache-common" "4.12.1" "@algolia/logger-common" "4.12.1" "@algolia/requester-common" "4.12.1" -"@ampproject/remapping@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" - integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== +"@ampproject/remapping@^2.2.0": + "integrity" "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==" + "resolved" "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz" + "version" "2.2.0" dependencies: - "@jridgewell/trace-mapping" "^0.3.0" + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.8.3": + "integrity" "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==" + "resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/highlight" "^7.16.7" + "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4", "@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.0.tgz#86850b8597ea6962089770952075dcaabb8dba34" - integrity sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.20.5": + "integrity" "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==" + "resolved" "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz" + "version" "7.21.0" -"@babel/core@7.12.9": - version "7.12.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" - integrity sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ== +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.15.5", "@babel/core@^7.16.0", "@babel/core@^7.4.0-0": + "integrity" "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==" + "resolved" "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz" + "version" "7.21.3" + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.21.3" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.3" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.3" + "@babel/types" "^7.21.3" + "convert-source-map" "^1.7.0" + "debug" "^4.1.0" + "gensync" "^1.0.0-beta.2" + "json5" "^2.2.2" + "semver" "^6.3.0" + +"@babel/core@^7.11.6", "@babel/core@7.12.9": + "integrity" "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==" + "resolved" "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz" + "version" "7.12.9" dependencies: "@babel/code-frame" "^7.10.4" "@babel/generator" "^7.12.5" @@ -162,74 +184,55 @@ "@babel/template" "^7.12.7" "@babel/traverse" "^7.12.9" "@babel/types" "^7.12.7" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" + "convert-source-map" "^1.7.0" + "debug" "^4.1.0" + "gensync" "^1.0.0-beta.1" + "json5" "^2.1.2" + "lodash" "^4.17.19" + "resolve" "^1.3.2" + "semver" "^5.4.1" + "source-map" "^0.5.0" -"@babel/core@^7.15.5", "@babel/core@^7.16.0": - version "7.17.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.5.tgz#6cd2e836058c28f06a4ca8ee7ed955bbf37c8225" - integrity sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA== +"@babel/generator@^7.12.5", "@babel/generator@^7.16.0", "@babel/generator@^7.21.3": + "integrity" "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==" + "resolved" "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz" + "version" "7.21.3" dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.3" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-module-transforms" "^7.16.7" - "@babel/helpers" "^7.17.2" - "@babel/parser" "^7.17.3" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" - "@babel/types" "^7.17.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - -"@babel/generator@^7.12.5", "@babel/generator@^7.16.0", "@babel/generator@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.3.tgz#a2c30b0c4f89858cb87050c3ffdfd36bdf443200" - integrity sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg== - dependencies: - "@babel/types" "^7.17.0" - jsesc "^2.5.1" - source-map "^0.5.0" + "@babel/types" "^7.21.3" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + "jsesc" "^2.5.1" "@babel/helper-annotate-as-pure@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" - integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== + "integrity" "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==" + "resolved" "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/types" "^7.16.7" "@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" - integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== + "integrity" "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==" + "resolved" "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-explode-assignable-expression" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" - integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.20.7": + "integrity" "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz" + "version" "7.20.7" dependencies: - "@babel/compat-data" "^7.16.4" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.17.5" - semver "^6.3.0" + "@babel/compat-data" "^7.20.5" + "@babel/helper-validator-option" "^7.18.6" + "browserslist" "^4.21.3" + "lru-cache" "^5.1.1" + "semver" "^6.3.0" "@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7": - version "7.17.1" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz#9699f14a88833a7e055ce57dcd3ffdcd25186b21" - integrity sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ== + "integrity" "sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz" + "version" "7.17.1" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-environment-visitor" "^7.16.7" @@ -240,122 +243,112 @@ "@babel/helper-split-export-declaration" "^7.16.7" "@babel/helper-create-regexp-features-plugin@^7.16.7": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" - integrity sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA== + "integrity" "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==" + "resolved" "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz" + "version" "7.17.0" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" - regexpu-core "^5.0.1" + "regexpu-core" "^5.0.1" "@babel/helper-define-polyfill-provider@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" - integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== + "integrity" "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==" + "resolved" "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz" + "version" "0.3.1" dependencies: "@babel/helper-compilation-targets" "^7.13.0" "@babel/helper-module-imports" "^7.12.13" "@babel/helper-plugin-utils" "^7.13.0" "@babel/traverse" "^7.13.0" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" + "debug" "^4.1.1" + "lodash.debounce" "^4.0.8" + "resolve" "^1.14.2" + "semver" "^6.1.2" -"@babel/helper-environment-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" - integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== - dependencies: - "@babel/types" "^7.16.7" +"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.9": + "integrity" "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" + "resolved" "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" + "version" "7.18.9" "@babel/helper-explode-assignable-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" - integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== + "integrity" "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/types" "^7.16.7" -"@babel/helper-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" - integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== +"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.21.0": + "integrity" "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==" + "resolved" "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz" + "version" "7.21.0" dependencies: - "@babel/helper-get-function-arity" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" -"@babel/helper-get-function-arity@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" - integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== +"@babel/helper-hoist-variables@^7.16.7", "@babel/helper-hoist-variables@^7.18.6": + "integrity" "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==" + "resolved" "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-hoist-variables@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" - integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== - dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" "@babel/helper-member-expression-to-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" - integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== + "integrity" "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==" + "resolved" "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" - integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6": + "integrity" "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==" + "resolved" "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" - integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.7", "@babel/helper-module-transforms@^7.21.2": + "integrity" "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz" + "version" "7.21.2" dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" "@babel/helper-optimise-call-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" - integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== + "integrity" "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==" + "resolved" "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/types" "^7.16.7" -"@babel/helper-plugin-utils@7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" - integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== - "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" - integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== + "integrity" "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==" + "resolved" "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz" + "version" "7.16.7" + +"@babel/helper-plugin-utils@7.10.4": + "integrity" "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + "resolved" "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz" + "version" "7.10.4" "@babel/helper-remap-async-to-generator@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" - integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw== + "integrity" "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==" + "resolved" "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-wrap-function" "^7.16.8" "@babel/types" "^7.16.8" "@babel/helper-replace-supers@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" - integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== + "integrity" "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==" + "resolved" "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-environment-visitor" "^7.16.7" "@babel/helper-member-expression-to-functions" "^7.16.7" @@ -363,173 +356,169 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-simple-access@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" - integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== +"@babel/helper-simple-access@^7.16.7", "@babel/helper-simple-access@^7.20.2": + "integrity" "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==" + "resolved" "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz" + "version" "7.20.2" dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.20.2" "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" - integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== + "integrity" "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==" + "resolved" "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz" + "version" "7.16.0" dependencies: "@babel/types" "^7.16.0" -"@babel/helper-split-export-declaration@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" - integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== +"@babel/helper-split-export-declaration@^7.16.7", "@babel/helper-split-export-declaration@^7.18.6": + "integrity" "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==" + "resolved" "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-string-parser@^7.19.4": + "integrity" "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" + "resolved" "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz" + "version" "7.19.4" -"@babel/helper-validator-option@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" - integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== +"@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + "integrity" "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + "resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" + "version" "7.19.1" + +"@babel/helper-validator-option@^7.16.7", "@babel/helper-validator-option@^7.18.6": + "integrity" "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz" + "version" "7.21.0" "@babel/helper-wrap-function@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" - integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw== + "integrity" "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==" + "resolved" "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-function-name" "^7.16.7" "@babel/template" "^7.16.7" "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helpers@^7.12.5", "@babel/helpers@^7.17.2": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417" - integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ== +"@babel/helpers@^7.12.5", "@babel/helpers@^7.21.0": + "integrity" "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==" + "resolved" "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz" + "version" "7.21.0" dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.0" - "@babel/types" "^7.17.0" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" -"@babel/highlight@^7.16.7": - version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" - integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== +"@babel/highlight@^7.18.6": + "integrity" "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==" + "resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - chalk "^2.0.0" - js-tokens "^4.0.0" + "@babel/helper-validator-identifier" "^7.18.6" + "chalk" "^2.0.0" + "js-tokens" "^4.0.0" -"@babel/parser@^7.12.7", "@babel/parser@^7.16.4", "@babel/parser@^7.16.7", "@babel/parser@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.3.tgz#b07702b982990bf6fdc1da5049a23fece4c5c3d0" - integrity sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA== +"@babel/parser@^7.12.7", "@babel/parser@^7.16.4", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3": + "integrity" "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==" + "resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz" + "version" "7.21.3" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050" - integrity sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg== + "integrity" "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz#cc001234dfc139ac45f6bcf801866198c8c72ff9" - integrity sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw== + "integrity" "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-proposal-optional-chaining" "^7.16.7" "@babel/plugin-proposal-async-generator-functions@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8" - integrity sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ== + "integrity" "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-proposal-class-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" - integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== + "integrity" "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-create-class-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-proposal-class-static-block@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz#712357570b612106ef5426d13dc433ce0f200c2a" - integrity sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw== + "integrity" "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-create-class-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-dynamic-import@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" - integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg== + "integrity" "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-proposal-export-namespace-from@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz#09de09df18445a5786a305681423ae63507a6163" - integrity sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA== + "integrity" "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-proposal-json-strings@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz#9732cb1d17d9a2626a08c5be25186c195b6fa6e8" - integrity sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ== + "integrity" "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-proposal-logical-assignment-operators@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea" - integrity sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg== + "integrity" "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" - integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== + "integrity" "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-numeric-separator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" - integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw== + "integrity" "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" - integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-transform-parameters" "^7.12.1" - "@babel/plugin-proposal-object-rest-spread@^7.16.7": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" - integrity sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw== + "integrity" "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz" + "version" "7.17.3" dependencies: "@babel/compat-data" "^7.17.0" "@babel/helper-compilation-targets" "^7.16.7" @@ -537,35 +526,44 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.16.7" +"@babel/plugin-proposal-object-rest-spread@7.12.1": + "integrity" "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz" + "version" "7.12.1" + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-proposal-optional-catch-binding@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" - integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== + "integrity" "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" - integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== + "integrity" "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-proposal-private-methods@^7.16.11": - version "7.16.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz#e8df108288555ff259f4527dbe84813aac3a1c50" - integrity sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw== + "integrity" "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz" + "version" "7.16.11" dependencies: "@babel/helper-create-class-features-plugin" "^7.16.10" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-proposal-private-property-in-object@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz#b0b8cef543c2c3d57e59e2c611994861d46a3fce" - integrity sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ== + "integrity" "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-create-class-features-plugin" "^7.16.7" @@ -573,166 +571,166 @@ "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2" - integrity sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg== + "integrity" "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + "integrity" "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" + "version" "7.8.4" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + "integrity" "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" + "version" "7.12.13" dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + "integrity" "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz" + "version" "7.14.5" dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + "integrity" "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + "integrity" "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + "integrity" "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" - integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-jsx@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" - integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== + "integrity" "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-syntax-jsx@7.12.1": + "integrity" "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz" + "version" "7.12.1" + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + "integrity" "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" + "version" "7.10.4" dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + "integrity" "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + "integrity" "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" + "version" "7.10.4" dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-object-rest-spread@7.8.3", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== +"@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3", "@babel/plugin-syntax-object-rest-spread@7.8.3": + "integrity" "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + "integrity" "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + "integrity" "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" + "version" "7.8.3" dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + "integrity" "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz" + "version" "7.14.5" dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + "integrity" "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" + "version" "7.14.5" dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" - integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== + "integrity" "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-arrow-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" - integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== + "integrity" "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-async-to-generator@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808" - integrity sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg== + "integrity" "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-module-imports" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-transform-block-scoped-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" - integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== + "integrity" "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoping@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" - integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== + "integrity" "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-classes@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" - integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== + "integrity" "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-environment-visitor" "^7.16.7" @@ -741,174 +739,174 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" - globals "^11.1.0" + "globals" "^11.1.0" "@babel/plugin-transform-computed-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" - integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== + "integrity" "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-destructuring@^7.16.7": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz#c445f75819641788a27a0a3a759d9df911df6abc" - integrity sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg== + "integrity" "sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz" + "version" "7.17.3" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" - integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== + "integrity" "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-duplicate-keys@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz#2207e9ca8f82a0d36a5a67b6536e7ef8b08823c9" - integrity sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw== + "integrity" "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-exponentiation-operator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" - integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== + "integrity" "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-for-of@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" - integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== + "integrity" "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" - integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== + "integrity" "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-compilation-targets" "^7.16.7" "@babel/helper-function-name" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" - integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== + "integrity" "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-member-expression-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" - integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== + "integrity" "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-modules-amd@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz#b28d323016a7daaae8609781d1f8c9da42b13186" - integrity sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g== + "integrity" "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" + "babel-plugin-dynamic-import-node" "^2.3.3" "@babel/plugin-transform-modules-commonjs@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" - integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== + "integrity" "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-simple-access" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" + "babel-plugin-dynamic-import-node" "^2.3.3" "@babel/plugin-transform-modules-systemjs@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz#887cefaef88e684d29558c2b13ee0563e287c2d7" - integrity sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw== + "integrity" "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-hoist-variables" "^7.16.7" "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-validator-identifier" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" + "babel-plugin-dynamic-import-node" "^2.3.3" "@babel/plugin-transform-modules-umd@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz#23dad479fa585283dbd22215bff12719171e7618" - integrity sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ== + "integrity" "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-named-capturing-groups-regex@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz#7f860e0e40d844a02c9dcf9d84965e7dfd666252" - integrity sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw== + "integrity" "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/plugin-transform-new-target@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz#9967d89a5c243818e0800fdad89db22c5f514244" - integrity sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg== + "integrity" "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-object-super@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" - integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== + "integrity" "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" "@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" - integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== + "integrity" "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-property-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" - integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== + "integrity" "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-constant-elements@^7.14.5": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.16.7.tgz#19e9e4c2df2f6c3e6b3aea11778297d81db8df62" - integrity sha512-lF+cfsyTgwWkcw715J88JhMYJ5GpysYNLhLP1PkvkhTRN7B3e74R/1KsDxFxhRpSn0UUD3IWM4GvdBR2PEbbQQ== + "integrity" "sha512-lF+cfsyTgwWkcw715J88JhMYJ5GpysYNLhLP1PkvkhTRN7B3e74R/1KsDxFxhRpSn0UUD3IWM4GvdBR2PEbbQQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-display-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" - integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== + "integrity" "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx-development@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8" - integrity sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A== + "integrity" "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/plugin-transform-react-jsx" "^7.16.7" "@babel/plugin-transform-react-jsx@^7.16.7": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1" - integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ== + "integrity" "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz" + "version" "7.17.3" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-module-imports" "^7.16.7" @@ -917,103 +915,103 @@ "@babel/types" "^7.17.0" "@babel/plugin-transform-react-pure-annotations@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz#232bfd2f12eb551d6d7d01d13fe3f86b45eb9c67" - integrity sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA== + "integrity" "sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-regenerator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" - integrity sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q== + "integrity" "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz" + "version" "7.16.7" dependencies: - regenerator-transform "^0.14.2" + "regenerator-transform" "^0.14.2" "@babel/plugin-transform-reserved-words@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz#1d798e078f7c5958eec952059c460b220a63f586" - integrity sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg== + "integrity" "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-runtime@^7.16.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz#0a2e08b5e2b2d95c4b1d3b3371a2180617455b70" - integrity sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A== + "integrity" "sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz" + "version" "7.17.0" dependencies: "@babel/helper-module-imports" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" - semver "^6.3.0" + "babel-plugin-polyfill-corejs2" "^0.3.0" + "babel-plugin-polyfill-corejs3" "^0.5.0" + "babel-plugin-polyfill-regenerator" "^0.3.0" + "semver" "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" - integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== + "integrity" "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-spread@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" - integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== + "integrity" "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-transform-sticky-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" - integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== + "integrity" "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-template-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" - integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== + "integrity" "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-typeof-symbol@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz#9cdbe622582c21368bd482b660ba87d5545d4f7e" - integrity sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ== + "integrity" "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-typescript@^7.16.7": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" - integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== + "integrity" "sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz" + "version" "7.16.8" dependencies: "@babel/helper-create-class-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-typescript" "^7.16.7" "@babel/plugin-transform-unicode-escapes@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" - integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q== + "integrity" "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-unicode-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" - integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== + "integrity" "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/preset-env@^7.15.6", "@babel/preset-env@^7.16.4": - version "7.16.11" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.16.11.tgz#5dd88fd885fae36f88fd7c8342475c9f0abe2982" - integrity sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g== + "integrity" "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==" + "resolved" "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz" + "version" "7.16.11" dependencies: "@babel/compat-data" "^7.16.8" "@babel/helper-compilation-targets" "^7.16.7" @@ -1084,27 +1082,27 @@ "@babel/plugin-transform-unicode-regex" "^7.16.7" "@babel/preset-modules" "^0.1.5" "@babel/types" "^7.16.8" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" - core-js-compat "^3.20.2" - semver "^6.3.0" + "babel-plugin-polyfill-corejs2" "^0.3.0" + "babel-plugin-polyfill-corejs3" "^0.5.0" + "babel-plugin-polyfill-regenerator" "^0.3.0" + "core-js-compat" "^3.20.2" + "semver" "^6.3.0" "@babel/preset-modules@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" - integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + "integrity" "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==" + "resolved" "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz" + "version" "0.1.5" dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" "@babel/plugin-transform-dotall-regex" "^7.4.4" "@babel/types" "^7.4.4" - esutils "^2.0.2" + "esutils" "^2.0.2" "@babel/preset-react@^7.14.5", "@babel/preset-react@^7.16.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.16.7.tgz#4c18150491edc69c183ff818f9f2aecbe5d93852" - integrity sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA== + "integrity" "sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA==" + "resolved" "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-validator-option" "^7.16.7" @@ -1114,81 +1112,82 @@ "@babel/plugin-transform-react-pure-annotations" "^7.16.7" "@babel/preset-typescript@^7.15.0", "@babel/preset-typescript@^7.16.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" - integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== + "integrity" "sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ==" + "resolved" "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz" + "version" "7.16.7" dependencies: "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.16.7" "@babel/runtime-corejs3@^7.16.3": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz#fdca2cd05fba63388babe85d349b6801b008fd13" - integrity sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg== + "integrity" "sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==" + "resolved" "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz" + "version" "7.17.2" dependencies: - core-js-pure "^3.20.2" - regenerator-runtime "^0.13.4" + "core-js-pure" "^3.20.2" + "regenerator-runtime" "^0.13.4" "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.16.3", "@babel/runtime@^7.8.4": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941" - integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw== + "integrity" "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==" + "resolved" "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz" + "version" "7.17.2" dependencies: - regenerator-runtime "^0.13.4" + "regenerator-runtime" "^0.13.4" -"@babel/template@^7.12.7", "@babel/template@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== +"@babel/template@^7.12.7", "@babel/template@^7.16.7", "@babel/template@^7.20.7": + "integrity" "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==" + "resolved" "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" + "version" "7.20.7" dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" -"@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.3", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.0", "@babel/traverse@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" - integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== +"@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.3", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3": + "integrity" "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==" + "resolved" "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz" + "version" "7.21.3" dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.3" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.3" - "@babel/types" "^7.17.0" - debug "^4.1.0" - globals "^11.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.21.3" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.3" + "@babel/types" "^7.21.3" + "debug" "^4.1.0" + "globals" "^11.1.0" -"@babel/types@^7.12.7", "@babel/types@^7.15.6", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.4.4": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" - integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== +"@babel/types@^7.12.7", "@babel/types@^7.15.6", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.6", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.4.4": + "integrity" "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==" + "resolved" "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz" + "version" "7.21.3" dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - to-fast-properties "^2.0.0" + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + "to-fast-properties" "^2.0.0" "@docsearch/css@3.0.0-alpha.50": - version "3.0.0-alpha.50" - resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.0.0-alpha.50.tgz#794c6a8d301840a49b55f5b331c7be84b9723643" - integrity sha512-QeWFCQOtS9D+Fi20liKsPXF2j/xWKh52e+P2Z1UATIdPMqmH6zoB2lcUz+cgv6PPVgWUtECeR6VSSUm71LT94w== + "integrity" "sha512-QeWFCQOtS9D+Fi20liKsPXF2j/xWKh52e+P2Z1UATIdPMqmH6zoB2lcUz+cgv6PPVgWUtECeR6VSSUm71LT94w==" + "resolved" "https://registry.npmjs.org/@docsearch/css/-/css-3.0.0-alpha.50.tgz" + "version" "3.0.0-alpha.50" "@docsearch/react@^3.0.0-alpha.39": - version "3.0.0-alpha.50" - resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.0.0-alpha.50.tgz#a7dc547836c2b221fd3aa8eb87bfb47a579ef141" - integrity sha512-oDGV1zZCRYv7MWsh6CyQVthYTRc3b4q+6kKwNYb1/g/Wf/4nJHutpxolFLHdEUDhrJ4Xi8wxwQG+lEwAVBTHPg== + "integrity" "sha512-oDGV1zZCRYv7MWsh6CyQVthYTRc3b4q+6kKwNYb1/g/Wf/4nJHutpxolFLHdEUDhrJ4Xi8wxwQG+lEwAVBTHPg==" + "resolved" "https://registry.npmjs.org/@docsearch/react/-/react-3.0.0-alpha.50.tgz" + "version" "3.0.0-alpha.50" dependencies: "@algolia/autocomplete-core" "1.5.2" "@algolia/autocomplete-preset-algolia" "1.5.2" "@docsearch/css" "3.0.0-alpha.50" - algoliasearch "^4.0.0" + "algoliasearch" "^4.0.0" "@docusaurus/core@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.0.0-beta.15.tgz#1a3f8361803767072e56c77d60332c87e59f1ad0" - integrity sha512-zXhhD0fApMSvq/9Pkm9DQxa//hGOXVCq9yMHiXOkI5D1tLec7PxtnaC5cLfGHljkN9cKIfRDYUVcG1gHymVfpA== + "integrity" "sha512-zXhhD0fApMSvq/9Pkm9DQxa//hGOXVCq9yMHiXOkI5D1tLec7PxtnaC5cLfGHljkN9cKIfRDYUVcG1gHymVfpA==" + "resolved" "https://registry.npmjs.org/@docusaurus/core/-/core-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@babel/core" "^7.16.0" "@babel/generator" "^7.16.0" @@ -1209,103 +1208,103 @@ "@docusaurus/utils-validation" "2.0.0-beta.15" "@slorber/static-site-generator-webpack-plugin" "^4.0.0" "@svgr/webpack" "^6.0.0" - autoprefixer "^10.3.5" - babel-loader "^8.2.2" - babel-plugin-dynamic-import-node "2.3.0" - boxen "^5.0.1" - chokidar "^3.5.2" - clean-css "^5.1.5" - commander "^5.1.0" - copy-webpack-plugin "^10.2.0" - core-js "^3.18.0" - css-loader "^6.5.1" - css-minimizer-webpack-plugin "^3.3.1" - cssnano "^5.0.8" - del "^6.0.0" - detect-port "^1.3.0" - escape-html "^1.0.3" - eta "^1.12.3" - file-loader "^6.2.0" - fs-extra "^10.0.0" - html-minifier-terser "^6.0.2" - html-tags "^3.1.0" - html-webpack-plugin "^5.4.0" - import-fresh "^3.3.0" - is-root "^2.1.0" - leven "^3.1.0" - lodash "^4.17.20" - mini-css-extract-plugin "^1.6.0" - nprogress "^0.2.0" - postcss "^8.3.7" - postcss-loader "^6.1.1" - prompts "^2.4.1" - react-dev-utils "^12.0.0" - react-helmet "^6.1.0" - react-loadable "npm:@docusaurus/react-loadable@5.5.2" - react-loadable-ssr-addon-v5-slorber "^1.0.1" - react-router "^5.2.0" - react-router-config "^5.1.1" - react-router-dom "^5.2.0" - remark-admonitions "^1.2.1" - rtl-detect "^1.0.4" - semver "^7.3.4" - serve-handler "^6.1.3" - shelljs "^0.8.4" - strip-ansi "^6.0.0" - terser-webpack-plugin "^5.2.4" - tslib "^2.3.1" - update-notifier "^5.1.0" - url-loader "^4.1.1" - wait-on "^6.0.0" - webpack "^5.61.0" - webpack-bundle-analyzer "^4.4.2" - webpack-dev-server "^4.7.1" - webpack-merge "^5.8.0" - webpackbar "^5.0.2" + "autoprefixer" "^10.3.5" + "babel-loader" "^8.2.2" + "babel-plugin-dynamic-import-node" "2.3.0" + "boxen" "^5.0.1" + "chokidar" "^3.5.2" + "clean-css" "^5.1.5" + "commander" "^5.1.0" + "copy-webpack-plugin" "^10.2.0" + "core-js" "^3.18.0" + "css-loader" "^6.5.1" + "css-minimizer-webpack-plugin" "^3.3.1" + "cssnano" "^5.0.8" + "del" "^6.0.0" + "detect-port" "^1.3.0" + "escape-html" "^1.0.3" + "eta" "^1.12.3" + "file-loader" "^6.2.0" + "fs-extra" "^10.0.0" + "html-minifier-terser" "^6.0.2" + "html-tags" "^3.1.0" + "html-webpack-plugin" "^5.4.0" + "import-fresh" "^3.3.0" + "is-root" "^2.1.0" + "leven" "^3.1.0" + "lodash" "^4.17.20" + "mini-css-extract-plugin" "^1.6.0" + "nprogress" "^0.2.0" + "postcss" "^8.3.7" + "postcss-loader" "^6.1.1" + "prompts" "^2.4.1" + "react-dev-utils" "^12.0.0" + "react-helmet" "^6.1.0" + "react-loadable" "npm:@docusaurus/react-loadable@5.5.2" + "react-loadable-ssr-addon-v5-slorber" "^1.0.1" + "react-router" "^5.2.0" + "react-router-config" "^5.1.1" + "react-router-dom" "^5.2.0" + "remark-admonitions" "^1.2.1" + "rtl-detect" "^1.0.4" + "semver" "^7.3.4" + "serve-handler" "^6.1.3" + "shelljs" "^0.8.4" + "strip-ansi" "^6.0.0" + "terser-webpack-plugin" "^5.2.4" + "tslib" "^2.3.1" + "update-notifier" "^5.1.0" + "url-loader" "^4.1.1" + "wait-on" "^6.0.0" + "webpack" "^5.61.0" + "webpack-bundle-analyzer" "^4.4.2" + "webpack-dev-server" "^4.7.1" + "webpack-merge" "^5.8.0" + "webpackbar" "^5.0.2" "@docusaurus/cssnano-preset@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-beta.15.tgz#033c52815c428f0f66c87eaff93ea12554ea89df" - integrity sha512-55aYURbB5dqrx64lStNcZxDx5R6bKkAawlCB7mDKx3r+Qnp3ofGW7UExLQSCbTu3axT1vJCF5D7H6ljTRYJLtA== + "integrity" "sha512-55aYURbB5dqrx64lStNcZxDx5R6bKkAawlCB7mDKx3r+Qnp3ofGW7UExLQSCbTu3axT1vJCF5D7H6ljTRYJLtA==" + "resolved" "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: - cssnano-preset-advanced "^5.1.4" - postcss "^8.3.7" - postcss-sort-media-queries "^4.1.0" + "cssnano-preset-advanced" "^5.1.4" + "postcss" "^8.3.7" + "postcss-sort-media-queries" "^4.1.0" "@docusaurus/logger@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.0.0-beta.15.tgz#6d17a05fb292d15fdc43b5fa90fd2a49ad5d40ce" - integrity sha512-5bDSHCyLfMtz6QnFfICdL5mgxbGfC7DW1V+/Q17nRdpZSPZgsNKK/Esp0zdDi1oxAyEpXMXx64nLaHL7joJxIg== + "integrity" "sha512-5bDSHCyLfMtz6QnFfICdL5mgxbGfC7DW1V+/Q17nRdpZSPZgsNKK/Esp0zdDi1oxAyEpXMXx64nLaHL7joJxIg==" + "resolved" "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: - chalk "^4.1.2" - tslib "^2.3.1" + "chalk" "^4.1.2" + "tslib" "^2.3.1" "@docusaurus/mdx-loader@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-beta.15.tgz#da23745bc73c93338dd330dad6bbc9d9fe325553" - integrity sha512-MVpytjDDao7hmPF1QSs9B5zoTgevZjiqjnX3FM1yjqdCv+chyUo0gnmYHjeG/4Gqu7jucp+dDdp6yQpzs4g09A== + "integrity" "sha512-MVpytjDDao7hmPF1QSs9B5zoTgevZjiqjnX3FM1yjqdCv+chyUo0gnmYHjeG/4Gqu7jucp+dDdp6yQpzs4g09A==" + "resolved" "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@babel/parser" "^7.16.4" "@babel/traverse" "^7.16.3" "@docusaurus/logger" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" "@mdx-js/mdx" "^1.6.21" - escape-html "^1.0.3" - file-loader "^6.2.0" - fs-extra "^10.0.0" - image-size "^1.0.1" - mdast-util-to-string "^2.0.0" - remark-emoji "^2.1.0" - stringify-object "^3.3.0" - tslib "^2.3.1" - unist-util-visit "^2.0.2" - url-loader "^4.1.1" - webpack "^5.61.0" + "escape-html" "^1.0.3" + "file-loader" "^6.2.0" + "fs-extra" "^10.0.0" + "image-size" "^1.0.1" + "mdast-util-to-string" "^2.0.0" + "remark-emoji" "^2.1.0" + "stringify-object" "^3.3.0" + "tslib" "^2.3.1" + "unist-util-visit" "^2.0.2" + "url-loader" "^4.1.1" + "webpack" "^5.61.0" "@docusaurus/plugin-content-blog@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-beta.15.tgz#6d4bf532ad3dedb4f9fd6398b0fbe481af5b77a9" - integrity sha512-VtEwkgkoNIS8JFPe+huBeBuJ8HG8Lq1JNYM/ItwQg/cwGAgP8EgwbEuKDn428oZKEI2PpgAuf5Gv4AzJWIes9A== + "integrity" "sha512-VtEwkgkoNIS8JFPe+huBeBuJ8HG8Lq1JNYM/ItwQg/cwGAgP8EgwbEuKDn428oZKEI2PpgAuf5Gv4AzJWIes9A==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/logger" "2.0.0-beta.15" @@ -1313,98 +1312,98 @@ "@docusaurus/utils" "2.0.0-beta.15" "@docusaurus/utils-common" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - cheerio "^1.0.0-rc.10" - feed "^4.2.2" - fs-extra "^10.0.0" - lodash "^4.17.20" - reading-time "^1.5.0" - remark-admonitions "^1.2.1" - tslib "^2.3.1" - utility-types "^3.10.0" - webpack "^5.61.0" + "cheerio" "^1.0.0-rc.10" + "feed" "^4.2.2" + "fs-extra" "^10.0.0" + "lodash" "^4.17.20" + "reading-time" "^1.5.0" + "remark-admonitions" "^1.2.1" + "tslib" "^2.3.1" + "utility-types" "^3.10.0" + "webpack" "^5.61.0" "@docusaurus/plugin-content-docs@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-beta.15.tgz#9486bba8abd2a6284e749718bf56743d8e4446f1" - integrity sha512-HSwNZdUKz4rpJiGbFjl/OFhSleeZUSZ6E6lk98i4iL1A5u6fIm4CHsT53yp4UUOse+lFrePTFZsyqwMA4nZZYA== + "integrity" "sha512-HSwNZdUKz4rpJiGbFjl/OFhSleeZUSZ6E6lk98i4iL1A5u6fIm4CHsT53yp4UUOse+lFrePTFZsyqwMA4nZZYA==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/logger" "2.0.0-beta.15" "@docusaurus/mdx-loader" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - combine-promises "^1.1.0" - fs-extra "^10.0.0" - import-fresh "^3.2.2" - js-yaml "^4.0.0" - lodash "^4.17.20" - remark-admonitions "^1.2.1" - shelljs "^0.8.4" - tslib "^2.3.1" - utility-types "^3.10.0" - webpack "^5.61.0" + "combine-promises" "^1.1.0" + "fs-extra" "^10.0.0" + "import-fresh" "^3.2.2" + "js-yaml" "^4.0.0" + "lodash" "^4.17.20" + "remark-admonitions" "^1.2.1" + "shelljs" "^0.8.4" + "tslib" "^2.3.1" + "utility-types" "^3.10.0" + "webpack" "^5.61.0" "@docusaurus/plugin-content-pages@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-beta.15.tgz#e488f7dcdd45cd1d46e8c2c5ff5275327a6a3c65" - integrity sha512-N7YhW5RiOY6J228z4lOoP//qX0Q48cRtxDONZ/Ohd9C5OI2vS6TD8iQuDqOIYHxH+BshjNSsKvbJ+SMIQDwysg== + "integrity" "sha512-N7YhW5RiOY6J228z4lOoP//qX0Q48cRtxDONZ/Ohd9C5OI2vS6TD8iQuDqOIYHxH+BshjNSsKvbJ+SMIQDwysg==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/mdx-loader" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - fs-extra "^10.0.0" - globby "^11.0.2" - remark-admonitions "^1.2.1" - tslib "^2.3.1" - webpack "^5.61.0" + "fs-extra" "^10.0.0" + "globby" "^11.0.2" + "remark-admonitions" "^1.2.1" + "tslib" "^2.3.1" + "webpack" "^5.61.0" "@docusaurus/plugin-debug@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-beta.15.tgz#b75d706d4f9fc4146f84015097bd837d1afb7c6b" - integrity sha512-Jth11jB/rVqPwCGdkVKSUWeXZPAr/NyPn+yeknTBk2LgQKBJ3YU5dNG0uyt0Ay+UYT01TkousPJkXhLuy4Qrsw== + "integrity" "sha512-Jth11jB/rVqPwCGdkVKSUWeXZPAr/NyPn+yeknTBk2LgQKBJ3YU5dNG0uyt0Ay+UYT01TkousPJkXhLuy4Qrsw==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" - fs-extra "^10.0.0" - react-json-view "^1.21.3" - tslib "^2.3.1" + "fs-extra" "^10.0.0" + "react-json-view" "^1.21.3" + "tslib" "^2.3.1" "@docusaurus/plugin-google-analytics@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.15.tgz#6ffebe76d9caac5383cfb78d2baa5883c9c2df6c" - integrity sha512-ELAnxNYiC2i7gfu/ViurNIdm1/DdnbEfVDmpffS9niQhOREM1U3jpxkz/ff1GIC6heOLyHTtini/CZBDoroVGw== + "integrity" "sha512-ELAnxNYiC2i7gfu/ViurNIdm1/DdnbEfVDmpffS9niQhOREM1U3jpxkz/ff1GIC6heOLyHTtini/CZBDoroVGw==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - tslib "^2.3.1" + "tslib" "^2.3.1" "@docusaurus/plugin-google-gtag@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-beta.15.tgz#4db3330d302653e8541dc3cb86a4dbfef0cc96f8" - integrity sha512-E5Rm3+dN7i3A9V5uq5sl9xTNA3aXsLwTZEA2SpOkY571dCpd+sfVvz1lR+KRY9Fy6ZHk8PqrNImgCWfIerRuZQ== + "integrity" "sha512-E5Rm3+dN7i3A9V5uq5sl9xTNA3aXsLwTZEA2SpOkY571dCpd+sfVvz1lR+KRY9Fy6ZHk8PqrNImgCWfIerRuZQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - tslib "^2.3.1" + "tslib" "^2.3.1" "@docusaurus/plugin-sitemap@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-beta.15.tgz#0cc083d9e76041897e81b4b82bcd0ccbfa65d6e5" - integrity sha512-PBjeQb2Qpe4uPdRefWL/eXCeYjrgNB/UArExYeUuP4wiY1dpw2unGNCvFUxv4hzJGmARoTLsnRkeYkUim809LQ== + "integrity" "sha512-PBjeQb2Qpe4uPdRefWL/eXCeYjrgNB/UArExYeUuP4wiY1dpw2unGNCvFUxv4hzJGmARoTLsnRkeYkUim809LQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" "@docusaurus/utils-common" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - fs-extra "^10.0.0" - sitemap "^7.0.0" - tslib "^2.3.1" + "fs-extra" "^10.0.0" + "sitemap" "^7.0.0" + "tslib" "^2.3.1" "@docusaurus/preset-classic@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.0.0-beta.15.tgz#13d2f3c4fa7c055af35541ae5e93453450efb208" - integrity sha512-3NZIXWTAzk+kOgiB8uAbD+FZv3VFR1qkU6+TW24DRenjRnXof3CkRuldhI1QI0hILm1fuJ319QRkakV8FFtXyA== + "integrity" "sha512-3NZIXWTAzk+kOgiB8uAbD+FZv3VFR1qkU6+TW24DRenjRnXof3CkRuldhI1QI0hILm1fuJ319QRkakV8FFtXyA==" + "resolved" "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/plugin-content-blog" "2.0.0-beta.15" @@ -1418,18 +1417,18 @@ "@docusaurus/theme-common" "2.0.0-beta.15" "@docusaurus/theme-search-algolia" "2.0.0-beta.15" -"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": - version "5.5.2" - resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" - integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== +"@docusaurus/react-loadable@5.5.2": + "integrity" "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz" + "version" "5.5.2" dependencies: "@types/react" "*" - prop-types "^15.6.2" + "prop-types" "^15.6.2" "@docusaurus/theme-classic@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.0.0-beta.15.tgz#35d04232f2d5fcb2007675339b0e6d0e8681be95" - integrity sha512-WwNRcQvMtQ7KDhOEHFKFHxXCdoZwLg66hT3vhqNIFMfGQuPzOP91MX5LUSo1QWHhlrD3H3Og+r7Ik/fy2bf5lQ== + "integrity" "sha512-WwNRcQvMtQ7KDhOEHFKFHxXCdoZwLg66hT3vhqNIFMfGQuPzOP91MX5LUSo1QWHhlrD3H3Og+r7Ik/fy2bf5lQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/core" "2.0.0-beta.15" "@docusaurus/plugin-content-blog" "2.0.0-beta.15" @@ -1441,33 +1440,33 @@ "@docusaurus/utils-common" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" "@mdx-js/react" "^1.6.21" - clsx "^1.1.1" - copy-text-to-clipboard "^3.0.1" - infima "0.2.0-alpha.37" - lodash "^4.17.20" - postcss "^8.3.7" - prism-react-renderer "^1.2.1" - prismjs "^1.23.0" - react-router-dom "^5.2.0" - rtlcss "^3.3.0" + "clsx" "^1.1.1" + "copy-text-to-clipboard" "^3.0.1" + "infima" "0.2.0-alpha.37" + "lodash" "^4.17.20" + "postcss" "^8.3.7" + "prism-react-renderer" "^1.2.1" + "prismjs" "^1.23.0" + "react-router-dom" "^5.2.0" + "rtlcss" "^3.3.0" "@docusaurus/theme-common@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.0.0-beta.15.tgz#5bd338d483e2c19d6d74d133572988241518398a" - integrity sha512-+pvarmzcyECE4nWxw+dCMKRIoes0NegrRuM9+nRsUrS/E5ywsF539kpupKIEqaMjq6AuM0CJtDoHxHHPNe0KaQ== + "integrity" "sha512-+pvarmzcyECE4nWxw+dCMKRIoes0NegrRuM9+nRsUrS/E5ywsF539kpupKIEqaMjq6AuM0CJtDoHxHHPNe0KaQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/plugin-content-blog" "2.0.0-beta.15" "@docusaurus/plugin-content-docs" "2.0.0-beta.15" "@docusaurus/plugin-content-pages" "2.0.0-beta.15" - clsx "^1.1.1" - parse-numeric-range "^1.3.0" - tslib "^2.3.1" - utility-types "^3.10.0" + "clsx" "^1.1.1" + "parse-numeric-range" "^1.3.0" + "tslib" "^2.3.1" + "utility-types" "^3.10.0" "@docusaurus/theme-search-algolia@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-beta.15.tgz#c3ad7fd8e27fcb3e072990031c08768c602cb9a4" - integrity sha512-XrrQKyjOPzmEuOcdsaAn1tzNJkNMA3PC86PwPZUaah0cYPpBGptcJYDlIW4VHIrCBfkQvhvmg/B3qKF6bMMi8g== + "integrity" "sha512-XrrQKyjOPzmEuOcdsaAn1tzNJkNMA3PC86PwPZUaah0cYPpBGptcJYDlIW4VHIrCBfkQvhvmg/B3qKF6bMMi8g==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docsearch/react" "^3.0.0-alpha.39" "@docusaurus/core" "2.0.0-beta.15" @@ -1476,268 +1475,268 @@ "@docusaurus/theme-translations" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" "@docusaurus/utils-validation" "2.0.0-beta.15" - algoliasearch "^4.10.5" - algoliasearch-helper "^3.5.5" - clsx "^1.1.1" - eta "^1.12.3" - lodash "^4.17.20" - tslib "^2.3.1" - utility-types "^3.10.0" + "algoliasearch" "^4.10.5" + "algoliasearch-helper" "^3.5.5" + "clsx" "^1.1.1" + "eta" "^1.12.3" + "lodash" "^4.17.20" + "tslib" "^2.3.1" + "utility-types" "^3.10.0" "@docusaurus/theme-translations@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.0.0-beta.15.tgz#658397ab4c0d7784043e3cec52cef7ae09d2fb59" - integrity sha512-Lu2JDsnZaB2BcJe8Hpq5nrbS7+7bd09jT08b9vztQyvzR8PgzsthnzlLN4ilOeamRIuYJKo1pUGm0EsQBOP6Nw== + "integrity" "sha512-Lu2JDsnZaB2BcJe8Hpq5nrbS7+7bd09jT08b9vztQyvzR8PgzsthnzlLN4ilOeamRIuYJKo1pUGm0EsQBOP6Nw==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: - fs-extra "^10.0.0" - tslib "^2.3.1" + "fs-extra" "^10.0.0" + "tslib" "^2.3.1" "@docusaurus/utils-common@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.0.0-beta.15.tgz#5549b329fc750bd5e9f24952c9e3ff7cf1f63e08" - integrity sha512-kIGlSIvbE/oniUpUjI8GOkSpH8o4NXbYqAh9dqPn+TJ0KbEFY3fc80gzZQU+9SunCwJMJbIxIGevX9Ry+nackw== + "integrity" "sha512-kIGlSIvbE/oniUpUjI8GOkSpH8o4NXbYqAh9dqPn+TJ0KbEFY3fc80gzZQU+9SunCwJMJbIxIGevX9Ry+nackw==" + "resolved" "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: - tslib "^2.3.1" + "tslib" "^2.3.1" "@docusaurus/utils-validation@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.0.0-beta.15.tgz#c664bc021194db9254eb45e6b48cb7c2af269041" - integrity sha512-1oOVBCkRrsTXSYrBTsMdnj3a/R56zrx11rjF4xo0+dmm8C01Xw4msFtc3uA7VLX0HQvgHsk8xPzU5GERNdsNpg== + "integrity" "sha512-1oOVBCkRrsTXSYrBTsMdnj3a/R56zrx11rjF4xo0+dmm8C01Xw4msFtc3uA7VLX0HQvgHsk8xPzU5GERNdsNpg==" + "resolved" "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/logger" "2.0.0-beta.15" "@docusaurus/utils" "2.0.0-beta.15" - joi "^17.4.2" - tslib "^2.3.1" + "joi" "^17.4.2" + "tslib" "^2.3.1" "@docusaurus/utils@2.0.0-beta.15": - version "2.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.0.0-beta.15.tgz#60868046700d5585cfa6ffc57c5f3fbed00b61fc" - integrity sha512-xkoPmFxCBkDqbZR4U3SE752OcXtWTGgZnc/pZWxItzb1IYRGNZHrzdIr7CnI7rppriuZzsyivDGiC4Ud9MWhkA== + "integrity" "sha512-xkoPmFxCBkDqbZR4U3SE752OcXtWTGgZnc/pZWxItzb1IYRGNZHrzdIr7CnI7rppriuZzsyivDGiC4Ud9MWhkA==" + "resolved" "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.0.0-beta.15.tgz" + "version" "2.0.0-beta.15" dependencies: "@docusaurus/logger" "2.0.0-beta.15" "@mdx-js/runtime" "^1.6.22" "@svgr/webpack" "^6.0.0" - file-loader "^6.2.0" - fs-extra "^10.0.0" - github-slugger "^1.4.0" - globby "^11.0.4" - gray-matter "^4.0.3" - js-yaml "^4.0.0" - lodash "^4.17.20" - micromatch "^4.0.4" - remark-mdx-remove-exports "^1.6.22" - remark-mdx-remove-imports "^1.6.22" - resolve-pathname "^3.0.0" - tslib "^2.3.1" - url-loader "^4.1.1" + "file-loader" "^6.2.0" + "fs-extra" "^10.0.0" + "github-slugger" "^1.4.0" + "globby" "^11.0.4" + "gray-matter" "^4.0.3" + "js-yaml" "^4.0.0" + "lodash" "^4.17.20" + "micromatch" "^4.0.4" + "remark-mdx-remove-exports" "^1.6.22" + "remark-mdx-remove-imports" "^1.6.22" + "resolve-pathname" "^3.0.0" + "tslib" "^2.3.1" + "url-loader" "^4.1.1" "@hapi/hoek@^9.0.0": - version "9.2.1" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" - integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== + "integrity" "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw==" + "resolved" "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz" + "version" "9.2.1" "@hapi/topo@^5.0.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" - integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + "integrity" "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==" + "resolved" "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz" + "version" "5.1.0" dependencies: "@hapi/hoek" "^9.0.0" -"@jridgewell/gen-mapping@^0.3.0": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== +"@jridgewell/gen-mapping@^0.1.0": + "integrity" "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==" + "resolved" "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz" + "version" "0.1.1" + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + "integrity" "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==" + "resolved" "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" + "version" "0.3.2" dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@3.1.0": + "integrity" "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + "resolved" "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" + "version" "3.1.0" -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + "integrity" "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + "resolved" "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + "version" "1.1.2" "@jridgewell/source-map@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" - integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + "integrity" "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==" + "resolved" "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz" + "version" "0.3.2" dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@1.4.14": + "integrity" "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" + "version" "1.4.14" -"@jridgewell/trace-mapping@^0.3.0": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" - integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + "integrity" "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==" + "resolved" "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz" + "version" "0.3.17" dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" - integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@mdx-js/mdx@1.6.22", "@mdx-js/mdx@^1.6.21": - version "1.6.22" - resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" - integrity sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA== +"@mdx-js/mdx@^1.6.21", "@mdx-js/mdx@1.6.22": + "integrity" "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==" + "resolved" "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz" + "version" "1.6.22" dependencies: "@babel/core" "7.12.9" "@babel/plugin-syntax-jsx" "7.12.1" "@babel/plugin-syntax-object-rest-spread" "7.8.3" "@mdx-js/util" "1.6.22" - babel-plugin-apply-mdx-type-prop "1.6.22" - babel-plugin-extract-import-names "1.6.22" - camelcase-css "2.0.1" - detab "2.0.4" - hast-util-raw "6.0.1" - lodash.uniq "4.5.0" - mdast-util-to-hast "10.0.1" - remark-footnotes "2.0.0" - remark-mdx "1.6.22" - remark-parse "8.0.3" - remark-squeeze-paragraphs "4.0.0" - style-to-object "0.3.0" - unified "9.2.0" - unist-builder "2.0.3" - unist-util-visit "2.0.3" + "babel-plugin-apply-mdx-type-prop" "1.6.22" + "babel-plugin-extract-import-names" "1.6.22" + "camelcase-css" "2.0.1" + "detab" "2.0.4" + "hast-util-raw" "6.0.1" + "lodash.uniq" "4.5.0" + "mdast-util-to-hast" "10.0.1" + "remark-footnotes" "2.0.0" + "remark-mdx" "1.6.22" + "remark-parse" "8.0.3" + "remark-squeeze-paragraphs" "4.0.0" + "style-to-object" "0.3.0" + "unified" "9.2.0" + "unist-builder" "2.0.3" + "unist-util-visit" "2.0.3" -"@mdx-js/react@1.6.22", "@mdx-js/react@^1.6.21": - version "1.6.22" - resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.22.tgz#ae09b4744fddc74714ee9f9d6f17a66e77c43573" - integrity sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg== +"@mdx-js/react@^1.6.21", "@mdx-js/react@1.6.22": + "integrity" "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==" + "resolved" "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz" + "version" "1.6.22" "@mdx-js/runtime@^1.6.22": - version "1.6.22" - resolved "https://registry.yarnpkg.com/@mdx-js/runtime/-/runtime-1.6.22.tgz#3edd388bf68a519ffa1aaf9c446b548165102345" - integrity sha512-p17spaO2+55VLCuxXA3LVHC4phRx60NR2XMdZ+qgVU1lKvEX4y88dmFNOzGDCPLJ03IZyKrJ/rPWWRiBrd9JrQ== + "integrity" "sha512-p17spaO2+55VLCuxXA3LVHC4phRx60NR2XMdZ+qgVU1lKvEX4y88dmFNOzGDCPLJ03IZyKrJ/rPWWRiBrd9JrQ==" + "resolved" "https://registry.npmjs.org/@mdx-js/runtime/-/runtime-1.6.22.tgz" + "version" "1.6.22" dependencies: "@mdx-js/mdx" "1.6.22" "@mdx-js/react" "1.6.22" - buble-jsx-only "^0.19.8" + "buble-jsx-only" "^0.19.8" "@mdx-js/util@1.6.22": - version "1.6.22" - resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" - integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== + "integrity" "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==" + "resolved" "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz" + "version" "1.6.22" "@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + "integrity" "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + "version" "2.1.5" dependencies: "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" + "run-parallel" "^1.1.9" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + "integrity" "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + "version" "2.0.5" "@nodelib/fs.walk@^1.2.3": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + "integrity" "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + "version" "1.2.8" dependencies: "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" + "fastq" "^1.6.0" "@polka/url@^1.0.0-next.20": - version "1.0.0-next.21" - resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" - integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== + "integrity" "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==" + "resolved" "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz" + "version" "1.0.0-next.21" "@sideway/address@^4.1.3": - version "4.1.3" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27" - integrity sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ== + "integrity" "sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ==" + "resolved" "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz" + "version" "4.1.3" dependencies: "@hapi/hoek" "^9.0.0" "@sideway/formula@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" - integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + "integrity" "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + "resolved" "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz" + "version" "3.0.1" "@sideway/pinpoint@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" - integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "integrity" "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + "resolved" "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz" + "version" "2.0.0" "@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + "integrity" "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + "resolved" "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" + "version" "0.14.0" "@slorber/static-site-generator-webpack-plugin@^4.0.0": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.1.tgz#0c8852146441aaa683693deaa5aee2f991d94841" - integrity sha512-PSv4RIVO1Y3kvHxjvqeVisk3E9XFoO04uwYBDWe217MFqKspplYswTuKLiJu0aLORQWzuQjfVsSlLPojwfYsLw== + "integrity" "sha512-PSv4RIVO1Y3kvHxjvqeVisk3E9XFoO04uwYBDWe217MFqKspplYswTuKLiJu0aLORQWzuQjfVsSlLPojwfYsLw==" + "resolved" "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.1.tgz" + "version" "4.0.1" dependencies: - bluebird "^3.7.1" - cheerio "^0.22.0" - eval "^0.1.4" - url "^0.11.0" - webpack-sources "^1.4.3" + "bluebird" "^3.7.1" + "cheerio" "^0.22.0" + "eval" "^0.1.4" + "url" "^0.11.0" + "webpack-sources" "^1.4.3" "@svgr/babel-plugin-add-jsx-attribute@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz#bd6d1ff32a31b82b601e73672a789cc41e84fe18" - integrity sha512-MdPdhdWLtQsjd29Wa4pABdhWbaRMACdM1h31BY+c6FghTZqNGT7pEYdBoaGeKtdTOBC/XNFQaKVj+r/Ei2ryWA== + "integrity" "sha512-MdPdhdWLtQsjd29Wa4pABdhWbaRMACdM1h31BY+c6FghTZqNGT7pEYdBoaGeKtdTOBC/XNFQaKVj+r/Ei2ryWA==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-remove-jsx-attribute@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.0.0.tgz#58654908beebfa069681a83332544b17e5237e89" - integrity sha512-aVdtfx9jlaaxc3unA6l+M9YRnKIZjOhQPthLKqmTXC8UVkBLDRGwPKo+r8n3VZN8B34+yVajzPTZ+ptTSuZZCw== + "integrity" "sha512-aVdtfx9jlaaxc3unA6l+M9YRnKIZjOhQPthLKqmTXC8UVkBLDRGwPKo+r8n3VZN8B34+yVajzPTZ+ptTSuZZCw==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-remove-jsx-empty-expression@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.0.0.tgz#d06dd6e8a8f603f92f9979bb9990a1f85a4f57ba" - integrity sha512-Ccj42ApsePD451AZJJf1QzTD1B/BOU392URJTeXFxSK709i0KUsGtbwyiqsKu7vsYxpTM0IA5clAKDyf9RCZyA== + "integrity" "sha512-Ccj42ApsePD451AZJJf1QzTD1B/BOU392URJTeXFxSK709i0KUsGtbwyiqsKu7vsYxpTM0IA5clAKDyf9RCZyA==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-replace-jsx-attribute-value@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.0.0.tgz#0b85837577b02c31c09c758a12932820f5245cee" - integrity sha512-88V26WGyt1Sfd1emBYmBJRWMmgarrExpKNVmI9vVozha4kqs6FzQJ/Kp5+EYli1apgX44518/0+t9+NU36lThQ== + "integrity" "sha512-88V26WGyt1Sfd1emBYmBJRWMmgarrExpKNVmI9vVozha4kqs6FzQJ/Kp5+EYli1apgX44518/0+t9+NU36lThQ==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-svg-dynamic-title@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.0.0.tgz#28236ec26f7ab9d486a487d36ae52d58ba15676f" - integrity sha512-F7YXNLfGze+xv0KMQxrl2vkNbI9kzT9oDK55/kUuymh1ACyXkMV+VZWX1zEhSTfEKh7VkHVZGmVtHg8eTZ6PRg== + "integrity" "sha512-F7YXNLfGze+xv0KMQxrl2vkNbI9kzT9oDK55/kUuymh1ACyXkMV+VZWX1zEhSTfEKh7VkHVZGmVtHg8eTZ6PRg==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-svg-em-dimensions@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.0.0.tgz#40267c5dea1b43c4f83a0eb6169e08b43d8bafce" - integrity sha512-+rghFXxdIqJNLQK08kwPBD3Z22/0b2tEZ9lKiL/yTfuyj1wW8HUXu4bo/XkogATIYuXSghVQOOCwURXzHGKyZA== + "integrity" "sha512-+rghFXxdIqJNLQK08kwPBD3Z22/0b2tEZ9lKiL/yTfuyj1wW8HUXu4bo/XkogATIYuXSghVQOOCwURXzHGKyZA==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-transform-react-native-svg@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.0.0.tgz#eb688d0a5f539e34d268d8a516e81f5d7fede7c9" - integrity sha512-VaphyHZ+xIKv5v0K0HCzyfAaLhPGJXSk2HkpYfXIOKb7DjLBv0soHDxNv6X0vr2titsxE7klb++u7iOf7TSrFQ== + "integrity" "sha512-VaphyHZ+xIKv5v0K0HCzyfAaLhPGJXSk2HkpYfXIOKb7DjLBv0soHDxNv6X0vr2titsxE7klb++u7iOf7TSrFQ==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.0.0.tgz" + "version" "6.0.0" "@svgr/babel-plugin-transform-svg-component@^6.2.0": - version "6.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.2.0.tgz#7ba61d9fc1fb42b0ba1a04e4630019fa7e993c4f" - integrity sha512-bhYIpsORb++wpsp91fymbFkf09Z/YEKR0DnFjxvN+8JHeCUD2unnh18jIMKnDJTWtvpTaGYPXELVe4OOzFI0xg== + "integrity" "sha512-bhYIpsORb++wpsp91fymbFkf09Z/YEKR0DnFjxvN+8JHeCUD2unnh18jIMKnDJTWtvpTaGYPXELVe4OOzFI0xg==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.2.0.tgz" + "version" "6.2.0" "@svgr/babel-preset@^6.2.0": - version "6.2.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.2.0.tgz#1d3ad8c7664253a4be8e4a0f0e6872f30d8af627" - integrity sha512-4WQNY0J71JIaL03DRn0vLiz87JXx0b9dYm2aA8XHlQJQoixMl4r/soYHm8dsaJZ3jWtkCiOYy48dp9izvXhDkQ== + "integrity" "sha512-4WQNY0J71JIaL03DRn0vLiz87JXx0b9dYm2aA8XHlQJQoixMl4r/soYHm8dsaJZ3jWtkCiOYy48dp9izvXhDkQ==" + "resolved" "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.2.0.tgz" + "version" "6.2.0" dependencies: "@svgr/babel-plugin-add-jsx-attribute" "^6.0.0" "@svgr/babel-plugin-remove-jsx-attribute" "^6.0.0" @@ -1748,46 +1747,46 @@ "@svgr/babel-plugin-transform-react-native-svg" "^6.0.0" "@svgr/babel-plugin-transform-svg-component" "^6.2.0" -"@svgr/core@^6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.2.1.tgz#195de807a9f27f9e0e0d678e01084b05c54fdf61" - integrity sha512-NWufjGI2WUyrg46mKuySfviEJ6IxHUOm/8a3Ph38VCWSp+83HBraCQrpEM3F3dB6LBs5x8OElS8h3C0oOJaJAA== +"@svgr/core@^6.0.0", "@svgr/core@^6.2.1": + "integrity" "sha512-NWufjGI2WUyrg46mKuySfviEJ6IxHUOm/8a3Ph38VCWSp+83HBraCQrpEM3F3dB6LBs5x8OElS8h3C0oOJaJAA==" + "resolved" "https://registry.npmjs.org/@svgr/core/-/core-6.2.1.tgz" + "version" "6.2.1" dependencies: "@svgr/plugin-jsx" "^6.2.1" - camelcase "^6.2.0" - cosmiconfig "^7.0.1" + "camelcase" "^6.2.0" + "cosmiconfig" "^7.0.1" "@svgr/hast-util-to-babel-ast@^6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.2.1.tgz#ae065567b74cbe745afae617053adf9a764bea25" - integrity sha512-pt7MMkQFDlWJVy9ULJ1h+hZBDGFfSCwlBNW1HkLnVi7jUhyEXUaGYWi1x6bM2IXuAR9l265khBT4Av4lPmaNLQ== + "integrity" "sha512-pt7MMkQFDlWJVy9ULJ1h+hZBDGFfSCwlBNW1HkLnVi7jUhyEXUaGYWi1x6bM2IXuAR9l265khBT4Av4lPmaNLQ==" + "resolved" "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.2.1.tgz" + "version" "6.2.1" dependencies: "@babel/types" "^7.15.6" - entities "^3.0.1" + "entities" "^3.0.1" "@svgr/plugin-jsx@^6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.2.1.tgz#5668f1d2aa18c2f1bb7a1fc9f682d3f9aed263bd" - integrity sha512-u+MpjTsLaKo6r3pHeeSVsh9hmGRag2L7VzApWIaS8imNguqoUwDq/u6U/NDmYs/KAsrmtBjOEaAAPbwNGXXp1g== + "integrity" "sha512-u+MpjTsLaKo6r3pHeeSVsh9hmGRag2L7VzApWIaS8imNguqoUwDq/u6U/NDmYs/KAsrmtBjOEaAAPbwNGXXp1g==" + "resolved" "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.2.1.tgz" + "version" "6.2.1" dependencies: "@babel/core" "^7.15.5" "@svgr/babel-preset" "^6.2.0" "@svgr/hast-util-to-babel-ast" "^6.2.1" - svg-parser "^2.0.2" + "svg-parser" "^2.0.2" "@svgr/plugin-svgo@^6.2.0": - version "6.2.0" - resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-6.2.0.tgz#4cbe6a33ccccdcae4e3b63ded64cc1cbe1faf48c" - integrity sha512-oDdMQONKOJEbuKwuy4Np6VdV6qoaLLvoY86hjvQEgU82Vx1MSWRyYms6Sl0f+NtqxLI/rDVufATbP/ev996k3Q== + "integrity" "sha512-oDdMQONKOJEbuKwuy4Np6VdV6qoaLLvoY86hjvQEgU82Vx1MSWRyYms6Sl0f+NtqxLI/rDVufATbP/ev996k3Q==" + "resolved" "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.2.0.tgz" + "version" "6.2.0" dependencies: - cosmiconfig "^7.0.1" - deepmerge "^4.2.2" - svgo "^2.5.0" + "cosmiconfig" "^7.0.1" + "deepmerge" "^4.2.2" + "svgo" "^2.5.0" "@svgr/webpack@^6.0.0": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.2.1.tgz#ef5d51c1b6be4e7537fb9f76b3f2b2e22b63c58d" - integrity sha512-h09ngMNd13hnePwgXa+Y5CgOjzlCvfWLHg+MBnydEedAnuLRzUHUJmGS3o2OsrhxTOOqEsPOFt5v/f6C5Qulcw== + "integrity" "sha512-h09ngMNd13hnePwgXa+Y5CgOjzlCvfWLHg+MBnydEedAnuLRzUHUJmGS3o2OsrhxTOOqEsPOFt5v/f6C5Qulcw==" + "resolved" "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.2.1.tgz" + "version" "6.2.1" dependencies: "@babel/core" "^7.15.5" "@babel/plugin-transform-react-constant-elements" "^7.14.5" @@ -1799,81 +1798,81 @@ "@svgr/plugin-svgo" "^6.2.0" "@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + "integrity" "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==" + "resolved" "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" + "version" "1.1.2" dependencies: - defer-to-connect "^1.0.1" + "defer-to-connect" "^1.0.1" "@trysound/sax@0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" - integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + "integrity" "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + "resolved" "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz" + "version" "0.2.0" "@types/body-parser@*": - version "1.19.2" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" - integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + "integrity" "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==" + "resolved" "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz" + "version" "1.19.2" dependencies: "@types/connect" "*" "@types/node" "*" "@types/bonjour@^3.5.9": - version "3.5.10" - resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" - integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== + "integrity" "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==" + "resolved" "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz" + "version" "3.5.10" dependencies: "@types/node" "*" "@types/connect-history-api-fallback@^1.3.5": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" - integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== + "integrity" "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==" + "resolved" "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz" + "version" "1.3.5" dependencies: "@types/express-serve-static-core" "*" "@types/node" "*" "@types/connect@*": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + "integrity" "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==" + "resolved" "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" + "version" "3.4.35" dependencies: "@types/node" "*" "@types/eslint-scope@^3.7.3": - version "3.7.3" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" - integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g== + "integrity" "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==" + "resolved" "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz" + "version" "3.7.3" dependencies: "@types/eslint" "*" "@types/estree" "*" "@types/eslint@*": - version "8.4.1" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.1.tgz#c48251553e8759db9e656de3efc846954ac32304" - integrity sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA== + "integrity" "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==" + "resolved" "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz" + "version" "8.4.1" dependencies: "@types/estree" "*" "@types/json-schema" "*" "@types/estree@*", "@types/estree@^0.0.51": - version "0.0.51" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" - integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + "integrity" "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + "resolved" "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz" + "version" "0.0.51" "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": - version "4.17.28" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" - integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== + "integrity" "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==" + "resolved" "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz" + "version" "4.17.28" dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" "@types/express@*", "@types/express@^4.17.13": - version "4.17.13" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" - integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + "integrity" "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==" + "resolved" "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz" + "version" "4.17.13" dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "^4.17.18" @@ -1881,172 +1880,172 @@ "@types/serve-static" "*" "@types/hast@^2.0.0": - version "2.3.4" - resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" - integrity sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g== + "integrity" "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==" + "resolved" "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz" + "version" "2.3.4" dependencies: "@types/unist" "*" "@types/html-minifier-terser@^6.0.0": - version "6.1.0" - resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" - integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + "integrity" "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + "resolved" "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + "version" "6.1.0" "@types/http-proxy@^1.17.8": - version "1.17.8" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.8.tgz#968c66903e7e42b483608030ee85800f22d03f55" - integrity sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA== + "integrity" "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==" + "resolved" "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz" + "version" "1.17.8" dependencies: "@types/node" "*" "@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": - version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" - integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + "integrity" "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" + "resolved" "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz" + "version" "7.0.9" "@types/mdast@^3.0.0": - version "3.0.10" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" - integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA== + "integrity" "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==" + "resolved" "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz" + "version" "3.0.10" dependencies: "@types/unist" "*" "@types/mime@^1": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" - integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + "integrity" "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + "resolved" "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz" + "version" "1.3.2" "@types/node@*", "@types/node@^17.0.5": - version "17.0.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074" - integrity sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA== + "integrity" "sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==" + "resolved" "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz" + "version" "17.0.18" "@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + "integrity" "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + "resolved" "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" + "version" "4.0.0" "@types/parse5@^5.0.0": - version "5.0.3" - resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" - integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + "integrity" "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" + "resolved" "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz" + "version" "5.0.3" "@types/prop-types@*": - version "15.7.4" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" - integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== + "integrity" "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==" + "resolved" "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz" + "version" "15.7.4" "@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + "integrity" "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "resolved" "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" + "version" "6.9.7" "@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + "integrity" "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "resolved" "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" + "version" "1.2.4" -"@types/react@*": - version "17.0.39" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce" - integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug== +"@types/react@*", "@types/react@>= 16.8.0 < 18.0.0": + "integrity" "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==" + "resolved" "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz" + "version" "17.0.39" dependencies: "@types/prop-types" "*" "@types/scheduler" "*" - csstype "^3.0.2" + "csstype" "^3.0.2" "@types/retry@^0.12.0": - version "0.12.1" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065" - integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g== + "integrity" "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" + "resolved" "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz" + "version" "0.12.1" "@types/sax@^1.2.1": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.4.tgz#8221affa7f4f3cb21abd22f244cfabfa63e6a69e" - integrity sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw== + "integrity" "sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==" + "resolved" "https://registry.npmjs.org/@types/sax/-/sax-1.2.4.tgz" + "version" "1.2.4" dependencies: "@types/node" "*" "@types/scheduler@*": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" - integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + "integrity" "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + "resolved" "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz" + "version" "0.16.2" "@types/serve-index@^1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" - integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== + "integrity" "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==" + "resolved" "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz" + "version" "1.9.1" dependencies: "@types/express" "*" "@types/serve-static@*": - version "1.13.10" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" - integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + "integrity" "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==" + "resolved" "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz" + "version" "1.13.10" dependencies: "@types/mime" "^1" "@types/node" "*" "@types/sockjs@^0.3.33": - version "0.3.33" - resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" - integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== + "integrity" "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==" + "resolved" "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz" + "version" "0.3.33" dependencies: "@types/node" "*" "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" - integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== + "integrity" "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + "resolved" "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz" + "version" "2.0.6" "@types/ws@^8.2.2": - version "8.2.2" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21" - integrity sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg== + "integrity" "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==" + "resolved" "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz" + "version" "8.2.2" dependencies: "@types/node" "*" "@webassemblyjs/ast@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" - integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + "integrity" "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/helper-numbers" "1.11.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.1" "@webassemblyjs/floating-point-hex-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" - integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + "integrity" "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz" + "version" "1.11.1" "@webassemblyjs/helper-api-error@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" - integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + "integrity" "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz" + "version" "1.11.1" "@webassemblyjs/helper-buffer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" - integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + "integrity" "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz" + "version" "1.11.1" "@webassemblyjs/helper-numbers@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" - integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + "integrity" "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/floating-point-hex-parser" "1.11.1" "@webassemblyjs/helper-api-error" "1.11.1" "@xtuc/long" "4.2.2" "@webassemblyjs/helper-wasm-bytecode@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" - integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + "integrity" "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz" + "version" "1.11.1" "@webassemblyjs/helper-wasm-section@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" - integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + "integrity" "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-buffer" "1.11.1" @@ -2054,28 +2053,28 @@ "@webassemblyjs/wasm-gen" "1.11.1" "@webassemblyjs/ieee754@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" - integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + "integrity" "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz" + "version" "1.11.1" dependencies: "@xtuc/ieee754" "^1.2.0" "@webassemblyjs/leb128@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" - integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + "integrity" "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz" + "version" "1.11.1" dependencies: "@xtuc/long" "4.2.2" "@webassemblyjs/utf8@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" - integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + "integrity" "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz" + "version" "1.11.1" "@webassemblyjs/wasm-edit@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" - integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + "integrity" "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-buffer" "1.11.1" @@ -2087,9 +2086,9 @@ "@webassemblyjs/wast-printer" "1.11.1" "@webassemblyjs/wasm-gen@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" - integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + "integrity" "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.1" @@ -2098,9 +2097,9 @@ "@webassemblyjs/utf8" "1.11.1" "@webassemblyjs/wasm-opt@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" - integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + "integrity" "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-buffer" "1.11.1" @@ -2108,9 +2107,9 @@ "@webassemblyjs/wasm-parser" "1.11.1" "@webassemblyjs/wasm-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" - integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + "integrity" "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-api-error" "1.11.1" @@ -2120,124 +2119,124 @@ "@webassemblyjs/utf8" "1.11.1" "@webassemblyjs/wast-printer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" - integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + "integrity" "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz" + "version" "1.11.1" dependencies: "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + "integrity" "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + "resolved" "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" + "version" "1.2.0" "@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + "integrity" "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + "resolved" "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" + "version" "4.2.2" -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== +"accepts@~1.3.4", "accepts@~1.3.5", "accepts@~1.3.8": + "integrity" "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==" + "resolved" "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" + "version" "1.3.8" dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" + "mime-types" "~2.1.34" + "negotiator" "0.6.3" -acorn-dynamic-import@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948" - integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw== +"acorn-dynamic-import@^4.0.0": + "integrity" "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==" + "resolved" "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz" + "version" "4.0.0" -acorn-import-assertions@^1.7.6: - version "1.8.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" - integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== +"acorn-import-assertions@^1.7.6": + "integrity" "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" + "resolved" "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz" + "version" "1.8.0" -acorn-jsx@^5.0.1: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== +"acorn-jsx@^5.0.1": + "integrity" "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" + "resolved" "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + "version" "5.3.2" -acorn-walk@^8.0.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== +"acorn-walk@^8.0.0": + "integrity" "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + "resolved" "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" + "version" "8.2.0" -acorn@^6.1.1: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== +"acorn@^6.0.0", "acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^6.1.1": + "integrity" "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz" + "version" "6.4.2" -acorn@^8.0.4, acorn@^8.4.1, acorn@^8.5.0: - version "8.7.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +"acorn@^8", "acorn@^8.0.4", "acorn@^8.4.1", "acorn@^8.5.0": + "integrity" "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz" + "version" "8.7.1" -address@^1.0.1, address@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" - integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== +"address@^1.0.1", "address@^1.1.2": + "integrity" "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==" + "resolved" "https://registry.npmjs.org/address/-/address-1.1.2.tgz" + "version" "1.1.2" -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== +"aggregate-error@^3.0.0": + "integrity" "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==" + "resolved" "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" + "version" "3.1.0" dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" + "clean-stack" "^2.0.0" + "indent-string" "^4.0.0" -ajv-formats@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== +"ajv-formats@^2.1.1": + "integrity" "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==" + "resolved" "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" + "version" "2.1.1" dependencies: - ajv "^8.0.0" + "ajv" "^8.0.0" -ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== +"ajv-keywords@^3.4.1", "ajv-keywords@^3.5.2": + "integrity" "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" + "resolved" "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" + "version" "3.5.2" -ajv-keywords@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" - integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== +"ajv-keywords@^5.0.0": + "integrity" "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==" + "resolved" "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz" + "version" "5.1.0" dependencies: - fast-deep-equal "^3.1.3" + "fast-deep-equal" "^3.1.3" -ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== +"ajv@^6.12.2", "ajv@^6.12.4", "ajv@^6.12.5", "ajv@^6.9.1": + "integrity" "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + "version" "6.12.6" dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" + "fast-deep-equal" "^3.1.1" + "fast-json-stable-stringify" "^2.0.0" + "json-schema-traverse" "^0.4.1" + "uri-js" "^4.2.2" -ajv@^8.0.0, ajv@^8.8.0: - version "8.10.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d" - integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw== +"ajv@^8.0.0", "ajv@^8.8.0", "ajv@^8.8.2": + "integrity" "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz" + "version" "8.10.0" dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" + "fast-deep-equal" "^3.1.1" + "json-schema-traverse" "^1.0.0" + "require-from-string" "^2.0.2" + "uri-js" "^4.2.2" -algoliasearch-helper@^3.5.5: - version "3.7.0" - resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.7.0.tgz#c0a0493df84d850360f664ad7a9d4fc78a94fd78" - integrity sha512-XJ3QfERBLfeVCyTVx80gon7r3/rgm/CE8Ha1H7cbablRe/X7SfYQ14g/eO+MhjVKIQp+gy9oC6G5ilmLwS1k6w== +"algoliasearch-helper@^3.5.5": + "integrity" "sha512-XJ3QfERBLfeVCyTVx80gon7r3/rgm/CE8Ha1H7cbablRe/X7SfYQ14g/eO+MhjVKIQp+gy9oC6G5ilmLwS1k6w==" + "resolved" "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.7.0.tgz" + "version" "3.7.0" dependencies: "@algolia/events" "^4.0.1" -algoliasearch@^4.0.0, algoliasearch@^4.10.5: - version "4.12.1" - resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.12.1.tgz#574a2c5424c4b6681c026928fb810be2d2ec3924" - integrity sha512-c0dM1g3zZBJrkzE5GA/Nu1y3fFxx3LCzxKzcmp2dgGS8P4CjszB/l3lsSh2MSrrK1Hn/KV4BlbBMXtYgG1Bfrw== +"algoliasearch@^4.0.0", "algoliasearch@^4.10.5", "algoliasearch@^4.9.1", "algoliasearch@>= 3.1 < 5": + "integrity" "sha512-c0dM1g3zZBJrkzE5GA/Nu1y3fFxx3LCzxKzcmp2dgGS8P4CjszB/l3lsSh2MSrrK1Hn/KV4BlbBMXtYgG1Bfrw==" + "resolved" "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.12.1.tgz" + "version" "4.12.1" dependencies: "@algolia/cache-browser-local-storage" "4.12.1" "@algolia/cache-common" "4.12.1" @@ -2254,2429 +2253,2448 @@ algoliasearch@^4.0.0, algoliasearch@^4.10.5: "@algolia/requester-node-http" "4.12.1" "@algolia/transporter" "4.12.1" -ansi-align@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" - integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== +"ansi-align@^3.0.0": + "integrity" "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==" + "resolved" "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz" + "version" "3.0.1" dependencies: - string-width "^4.1.0" + "string-width" "^4.1.0" -ansi-html-community@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" - integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== +"ansi-html-community@^0.0.8": + "integrity" "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" + "resolved" "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz" + "version" "0.0.8" -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +"ansi-regex@^5.0.1": + "integrity" "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + "version" "5.0.1" -ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== +"ansi-regex@^6.0.1": + "integrity" "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + "version" "6.0.1" -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== +"ansi-styles@^3.2.1": + "integrity" "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + "version" "3.2.1" dependencies: - color-convert "^1.9.0" + "color-convert" "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== +"ansi-styles@^4.0.0", "ansi-styles@^4.1.0": + "integrity" "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + "version" "4.3.0" dependencies: - color-convert "^2.0.1" + "color-convert" "^2.0.1" -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== +"anymatch@~3.1.2": + "integrity" "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==" + "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" + "version" "3.1.2" dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" + "normalize-path" "^3.0.0" + "picomatch" "^2.0.4" -arg@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.1.tgz#eb0c9a8f77786cad2af8ff2b862899842d7b6adb" - integrity sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA== +"arg@^5.0.0": + "integrity" "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==" + "resolved" "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz" + "version" "5.0.1" -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== +"argparse@^1.0.7": + "integrity" "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==" + "resolved" "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + "version" "1.0.10" dependencies: - sprintf-js "~1.0.2" + "sprintf-js" "~1.0.2" -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +"argparse@^2.0.1": + "integrity" "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "resolved" "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + "version" "2.0.1" -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= +"array-flatten@^2.1.0": + "integrity" "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + "resolved" "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz" + "version" "2.1.2" -array-flatten@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== +"array-flatten@1.1.1": + "integrity" "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "resolved" "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + "version" "1.1.1" -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +"array-union@^2.1.0": + "integrity" "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + "resolved" "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + "version" "2.1.0" -array-union@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-3.0.1.tgz#da52630d327f8b88cfbfb57728e2af5cd9b6b975" - integrity sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw== +"array-union@^3.0.1": + "integrity" "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==" + "resolved" "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz" + "version" "3.0.1" -asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= +"asap@~2.0.3": + "integrity" "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + "resolved" "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" + "version" "2.0.6" -async@^2.6.2: - version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== +"async@^2.6.2": + "integrity" "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==" + "resolved" "https://registry.npmjs.org/async/-/async-2.6.4.tgz" + "version" "2.6.4" dependencies: - lodash "^4.17.14" + "lodash" "^4.17.14" -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +"at-least-node@^1.0.0": + "integrity" "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + "resolved" "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" + "version" "1.0.0" -autoprefixer@^10.3.5, autoprefixer@^10.3.7: - version "10.4.2" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.2.tgz#25e1df09a31a9fba5c40b578936b90d35c9d4d3b" - integrity sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ== +"autoprefixer@^10.3.5", "autoprefixer@^10.3.7": + "integrity" "sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==" + "resolved" "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz" + "version" "10.4.2" dependencies: - browserslist "^4.19.1" - caniuse-lite "^1.0.30001297" - fraction.js "^4.1.2" - normalize-range "^0.1.2" - picocolors "^1.0.0" - postcss-value-parser "^4.2.0" + "browserslist" "^4.19.1" + "caniuse-lite" "^1.0.30001297" + "fraction.js" "^4.1.2" + "normalize-range" "^0.1.2" + "picocolors" "^1.0.0" + "postcss-value-parser" "^4.2.0" -axios@^0.25.0: - version "0.25.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" - integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== +"axios@^0.25.0": + "integrity" "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==" + "resolved" "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz" + "version" "0.25.0" dependencies: - follow-redirects "^1.14.7" + "follow-redirects" "^1.14.7" -babel-loader@^8.2.2: - version "8.2.3" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.3.tgz#8986b40f1a64cacfcb4b8429320085ef68b1342d" - integrity sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw== +"babel-loader@^8.2.2": + "integrity" "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==" + "resolved" "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz" + "version" "8.2.3" dependencies: - find-cache-dir "^3.3.1" - loader-utils "^1.4.0" - make-dir "^3.1.0" - schema-utils "^2.6.5" + "find-cache-dir" "^3.3.1" + "loader-utils" "^1.4.0" + "make-dir" "^3.1.0" + "schema-utils" "^2.6.5" -babel-plugin-apply-mdx-type-prop@1.6.22: - version "1.6.22" - resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz#d216e8fd0de91de3f1478ef3231e05446bc8705b" - integrity sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ== +"babel-plugin-apply-mdx-type-prop@1.6.22": + "integrity" "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz" + "version" "1.6.22" dependencies: "@babel/helper-plugin-utils" "7.10.4" "@mdx-js/util" "1.6.22" -babel-plugin-dynamic-import-node@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" - integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== +"babel-plugin-dynamic-import-node@^2.3.3": + "integrity" "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz" + "version" "2.3.3" dependencies: - object.assign "^4.1.0" + "object.assign" "^4.1.0" -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== +"babel-plugin-dynamic-import-node@2.3.0": + "integrity" "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz" + "version" "2.3.0" dependencies: - object.assign "^4.1.0" + "object.assign" "^4.1.0" -babel-plugin-extract-import-names@1.6.22: - version "1.6.22" - resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz#de5f9a28eb12f3eb2578bf74472204e66d1a13dc" - integrity sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ== +"babel-plugin-extract-import-names@1.6.22": + "integrity" "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz" + "version" "1.6.22" dependencies: "@babel/helper-plugin-utils" "7.10.4" -babel-plugin-polyfill-corejs2@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" - integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== +"babel-plugin-polyfill-corejs2@^0.3.0": + "integrity" "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz" + "version" "0.3.1" dependencies: "@babel/compat-data" "^7.13.11" "@babel/helper-define-polyfill-provider" "^0.3.1" - semver "^6.1.1" + "semver" "^6.1.1" -babel-plugin-polyfill-corejs3@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" - integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== +"babel-plugin-polyfill-corejs3@^0.5.0": + "integrity" "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz" + "version" "0.5.2" dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" - core-js-compat "^3.21.0" + "core-js-compat" "^3.21.0" -babel-plugin-polyfill-regenerator@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" - integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== +"babel-plugin-polyfill-regenerator@^0.3.0": + "integrity" "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz" + "version" "0.3.1" dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" -bail@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" - integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== +"bail@^1.0.0": + "integrity" "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==" + "resolved" "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz" + "version" "1.0.5" -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +"balanced-match@^1.0.0": + "integrity" "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + "version" "1.0.2" -base16@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70" - integrity sha1-4pf2DX7BAUp6lxo568ipjAtoHnA= +"base16@^1.0.0": + "integrity" "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" + "resolved" "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz" + "version" "1.0.0" -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= +"batch@0.6.1": + "integrity" "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + "resolved" "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" + "version" "0.6.1" -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +"big.js@^5.2.2": + "integrity" "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + "resolved" "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" + "version" "5.2.2" -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +"binary-extensions@^2.0.0": + "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + "version" "2.2.0" -bluebird@^3.7.1: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +"bluebird@^3.7.1": + "integrity" "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + "resolved" "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" + "version" "3.7.2" -body-parser@1.19.2: - version "1.19.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" - integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== +"body-parser@1.19.2": + "integrity" "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==" + "resolved" "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz" + "version" "1.19.2" dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.8.1" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.9.7" - raw-body "2.4.3" - type-is "~1.6.18" + "bytes" "3.1.2" + "content-type" "~1.0.4" + "debug" "2.6.9" + "depd" "~1.1.2" + "http-errors" "1.8.1" + "iconv-lite" "0.4.24" + "on-finished" "~2.3.0" + "qs" "6.9.7" + "raw-body" "2.4.3" + "type-is" "~1.6.18" -bonjour@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" - integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= +"bonjour@^3.5.0": + "integrity" "sha1-jokKGD2O6aI5OzhExpGkK897yfU=" + "resolved" "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz" + "version" "3.5.0" dependencies: - array-flatten "^2.1.0" - deep-equal "^1.0.1" - dns-equal "^1.0.0" - dns-txt "^2.0.2" - multicast-dns "^6.0.1" - multicast-dns-service-types "^1.1.0" + "array-flatten" "^2.1.0" + "deep-equal" "^1.0.1" + "dns-equal" "^1.0.0" + "dns-txt" "^2.0.2" + "multicast-dns" "^6.0.1" + "multicast-dns-service-types" "^1.1.0" -boolbase@^1.0.0, boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +"boolbase@^1.0.0", "boolbase@~1.0.0": + "integrity" "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + "resolved" "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" + "version" "1.0.0" -boxen@^5.0.0, boxen@^5.0.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" - integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== +"boxen@^5.0.0", "boxen@^5.0.1": + "integrity" "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==" + "resolved" "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz" + "version" "5.1.2" dependencies: - ansi-align "^3.0.0" - camelcase "^6.2.0" - chalk "^4.1.0" - cli-boxes "^2.2.1" - string-width "^4.2.2" - type-fest "^0.20.2" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" + "ansi-align" "^3.0.0" + "camelcase" "^6.2.0" + "chalk" "^4.1.0" + "cli-boxes" "^2.2.1" + "string-width" "^4.2.2" + "type-fest" "^0.20.2" + "widest-line" "^3.1.0" + "wrap-ansi" "^7.0.0" -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== +"brace-expansion@^1.1.7": + "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" + "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + "version" "1.1.11" dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" + "balanced-match" "^1.0.0" + "concat-map" "0.0.1" -braces@^3.0.1, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +"braces@^3.0.1", "braces@~3.0.2": + "integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==" + "resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + "version" "3.0.2" dependencies: - fill-range "^7.0.1" + "fill-range" "^7.0.1" -browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.18.1, browserslist@^4.19.1: - version "4.19.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" - integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== +"browserslist@^4.0.0", "browserslist@^4.14.5", "browserslist@^4.16.6", "browserslist@^4.18.1", "browserslist@^4.19.1", "browserslist@^4.21.3", "browserslist@>= 4.21.0": + "integrity" "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==" + "resolved" "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz" + "version" "4.21.5" dependencies: - caniuse-lite "^1.0.30001286" - electron-to-chromium "^1.4.17" - escalade "^3.1.1" - node-releases "^2.0.1" - picocolors "^1.0.0" + "caniuse-lite" "^1.0.30001449" + "electron-to-chromium" "^1.4.284" + "node-releases" "^2.0.8" + "update-browserslist-db" "^1.0.10" -buble-jsx-only@^0.19.8: - version "0.19.8" - resolved "https://registry.yarnpkg.com/buble-jsx-only/-/buble-jsx-only-0.19.8.tgz#6e3524aa0f1c523de32496ac9aceb9cc2b493867" - integrity sha512-7AW19pf7PrKFnGTEDzs6u9+JZqQwM1VnLS19OlqYDhXomtFFknnoQJAPHeg84RMFWAvOhYrG7harizJNwUKJsA== +"buble-jsx-only@^0.19.8": + "integrity" "sha512-7AW19pf7PrKFnGTEDzs6u9+JZqQwM1VnLS19OlqYDhXomtFFknnoQJAPHeg84RMFWAvOhYrG7harizJNwUKJsA==" + "resolved" "https://registry.npmjs.org/buble-jsx-only/-/buble-jsx-only-0.19.8.tgz" + "version" "0.19.8" dependencies: - acorn "^6.1.1" - acorn-dynamic-import "^4.0.0" - acorn-jsx "^5.0.1" - chalk "^2.4.2" - magic-string "^0.25.3" - minimist "^1.2.0" - regexpu-core "^4.5.4" + "acorn" "^6.1.1" + "acorn-dynamic-import" "^4.0.0" + "acorn-jsx" "^5.0.1" + "chalk" "^2.4.2" + "magic-string" "^0.25.3" + "minimist" "^1.2.0" + "regexpu-core" "^4.5.4" -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +"buffer-from@^1.0.0": + "integrity" "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "resolved" "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + "version" "1.1.2" -buffer-indexof@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" - integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== +"buffer-indexof@^1.0.0": + "integrity" "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + "resolved" "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz" + "version" "1.1.1" -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= +"bytes@3.0.0": + "integrity" "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + "resolved" "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" + "version" "3.0.0" -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +"bytes@3.1.2": + "integrity" "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + "resolved" "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + "version" "3.1.2" -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== +"cacheable-request@^6.0.0": + "integrity" "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==" + "resolved" "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz" + "version" "6.1.0" dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" + "clone-response" "^1.0.2" + "get-stream" "^5.1.0" + "http-cache-semantics" "^4.0.0" + "keyv" "^3.0.0" + "lowercase-keys" "^2.0.0" + "normalize-url" "^4.1.0" + "responselike" "^1.0.2" -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== +"call-bind@^1.0.0", "call-bind@^1.0.2": + "integrity" "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==" + "resolved" "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + "version" "1.0.2" dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + "function-bind" "^1.1.1" + "get-intrinsic" "^1.0.2" -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +"callsites@^3.0.0": + "integrity" "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + "resolved" "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + "version" "3.1.0" -camel-case@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" - integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== +"camel-case@^4.1.2": + "integrity" "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==" + "resolved" "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" + "version" "4.1.2" dependencies: - pascal-case "^3.1.2" - tslib "^2.0.3" + "pascal-case" "^3.1.2" + "tslib" "^2.0.3" -camelcase-css@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" - integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== +"camelcase-css@2.0.1": + "integrity" "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + "resolved" "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" + "version" "2.0.1" -camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +"camelcase@^6.2.0": + "integrity" "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + "version" "6.3.0" -caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== +"caniuse-api@^3.0.0": + "integrity" "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==" + "resolved" "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz" + "version" "3.0.0" dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" + "browserslist" "^4.0.0" + "caniuse-lite" "^1.0.0" + "lodash.memoize" "^4.1.2" + "lodash.uniq" "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001297: - version "1.0.30001312" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz#e11eba4b87e24d22697dae05455d5aea28550d5f" - integrity sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ== +"caniuse-lite@^1.0.0", "caniuse-lite@^1.0.30001297", "caniuse-lite@^1.0.30001449": + "integrity" "sha512-ewtFBSfWjEmxUgNBSZItFSmVtvk9zkwkl1OfRZlKA8slltRN+/C/tuGVrF9styXkN36Yu3+SeJ1qkXxDEyNZ5w==" + "resolved" "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001466.tgz" + "version" "1.0.30001466" -ccount@^1.0.0, ccount@^1.0.3: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" - integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== +"ccount@^1.0.0", "ccount@^1.0.3": + "integrity" "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==" + "resolved" "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz" + "version" "1.1.0" -chalk@^2.0.0, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== +"chalk@^2.0.0": + "integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + "version" "2.4.2" dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" + "ansi-styles" "^3.2.1" + "escape-string-regexp" "^1.0.5" + "supports-color" "^5.3.0" -chalk@^4.1.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== +"chalk@^2.4.2": + "integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + "version" "2.4.2" dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" + "ansi-styles" "^3.2.1" + "escape-string-regexp" "^1.0.5" + "supports-color" "^5.3.0" -character-entities-legacy@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" - integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== - -character-entities@^1.0.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" - integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== - -character-reference-invalid@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" - integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== - -cheerio-select@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823" - integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg== +"chalk@^4.1.0", "chalk@^4.1.2": + "integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + "version" "4.1.2" dependencies: - css-select "^4.1.3" - css-what "^5.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" - domutils "^2.7.0" + "ansi-styles" "^4.1.0" + "supports-color" "^7.1.0" -cheerio@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" - integrity sha1-qbqoYKP5tZWmuBsahocxIe06Jp4= - dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.0" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash.assignin "^4.0.9" - lodash.bind "^4.1.4" - lodash.defaults "^4.0.1" - lodash.filter "^4.4.0" - lodash.flatten "^4.2.0" - lodash.foreach "^4.3.0" - lodash.map "^4.4.0" - lodash.merge "^4.4.0" - lodash.pick "^4.2.1" - lodash.reduce "^4.4.0" - lodash.reject "^4.4.0" - lodash.some "^4.4.0" +"character-entities-legacy@^1.0.0": + "integrity" "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" + "resolved" "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz" + "version" "1.1.4" -cheerio@^1.0.0-rc.10: - version "1.0.0-rc.10" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e" - integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw== - dependencies: - cheerio-select "^1.5.0" - dom-serializer "^1.3.2" - domhandler "^4.2.0" - htmlparser2 "^6.1.0" - parse5 "^6.0.1" - parse5-htmlparser2-tree-adapter "^6.0.1" - tslib "^2.2.0" +"character-entities@^1.0.0": + "integrity" "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" + "resolved" "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz" + "version" "1.2.4" -chokidar@^3.4.2, chokidar@^3.5.2, chokidar@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== +"character-reference-invalid@^1.0.0": + "integrity" "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" + "resolved" "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz" + "version" "1.1.4" + +"cheerio-select@^1.5.0": + "integrity" "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==" + "resolved" "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz" + "version" "1.5.0" dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" + "css-select" "^4.1.3" + "css-what" "^5.0.1" + "domelementtype" "^2.2.0" + "domhandler" "^4.2.0" + "domutils" "^2.7.0" + +"cheerio@^0.22.0": + "integrity" "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=" + "resolved" "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz" + "version" "0.22.0" + dependencies: + "css-select" "~1.2.0" + "dom-serializer" "~0.1.0" + "entities" "~1.1.1" + "htmlparser2" "^3.9.1" + "lodash.assignin" "^4.0.9" + "lodash.bind" "^4.1.4" + "lodash.defaults" "^4.0.1" + "lodash.filter" "^4.4.0" + "lodash.flatten" "^4.2.0" + "lodash.foreach" "^4.3.0" + "lodash.map" "^4.4.0" + "lodash.merge" "^4.4.0" + "lodash.pick" "^4.2.1" + "lodash.reduce" "^4.4.0" + "lodash.reject" "^4.4.0" + "lodash.some" "^4.4.0" + +"cheerio@^1.0.0-rc.10": + "integrity" "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==" + "resolved" "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz" + "version" "1.0.0-rc.10" + dependencies: + "cheerio-select" "^1.5.0" + "dom-serializer" "^1.3.2" + "domhandler" "^4.2.0" + "htmlparser2" "^6.1.0" + "parse5" "^6.0.1" + "parse5-htmlparser2-tree-adapter" "^6.0.1" + "tslib" "^2.2.0" + +"chokidar@^3.4.2", "chokidar@^3.5.2", "chokidar@^3.5.3": + "integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==" + "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + "version" "3.5.3" + dependencies: + "anymatch" "~3.1.2" + "braces" "~3.0.2" + "glob-parent" "~5.1.2" + "is-binary-path" "~2.1.0" + "is-glob" "~4.0.1" + "normalize-path" "~3.0.0" + "readdirp" "~3.6.0" optionalDependencies: - fsevents "~2.3.2" + "fsevents" "~2.3.2" -chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== +"chrome-trace-event@^1.0.2": + "integrity" "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + "resolved" "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz" + "version" "1.0.3" -ci-info@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +"ci-info@^2.0.0": + "integrity" "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + "resolved" "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" + "version" "2.0.0" -classnames@^2.2.6: - version "2.3.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" - integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== +"classnames@^2.2.6": + "integrity" "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + "resolved" "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz" + "version" "2.3.1" -clean-css@^5.1.5, clean-css@^5.2.2: - version "5.2.4" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.2.4.tgz#982b058f8581adb2ae062520808fb2429bd487a4" - integrity sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg== +"clean-css@^5.1.5", "clean-css@^5.2.2": + "integrity" "sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg==" + "resolved" "https://registry.npmjs.org/clean-css/-/clean-css-5.2.4.tgz" + "version" "5.2.4" dependencies: - source-map "~0.6.0" + "source-map" "~0.6.0" -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +"clean-stack@^2.0.0": + "integrity" "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + "resolved" "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" + "version" "2.2.0" -cli-boxes@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" - integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== +"cli-boxes@^2.2.1": + "integrity" "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" + "resolved" "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz" + "version" "2.2.1" -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== +"clone-deep@^4.0.1": + "integrity" "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==" + "resolved" "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" + "version" "4.0.1" dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" + "is-plain-object" "^2.0.4" + "kind-of" "^6.0.2" + "shallow-clone" "^3.0.0" -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= +"clone-response@^1.0.2": + "integrity" "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=" + "resolved" "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" + "version" "1.0.2" dependencies: - mimic-response "^1.0.0" + "mimic-response" "^1.0.0" -clsx@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" - integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== +"clsx@^1.1.1": + "integrity" "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" + "resolved" "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz" + "version" "1.1.1" -collapse-white-space@^1.0.2: - version "1.0.6" - resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" - integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== +"collapse-white-space@^1.0.2": + "integrity" "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==" + "resolved" "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz" + "version" "1.0.6" -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== +"color-convert@^1.9.0": + "integrity" "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==" + "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + "version" "1.9.3" dependencies: - color-name "1.1.3" + "color-name" "1.1.3" -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== +"color-convert@^2.0.1": + "integrity" "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==" + "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + "version" "2.0.1" dependencies: - color-name "~1.1.4" + "color-name" "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +"color-name@~1.1.4": + "integrity" "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + "version" "1.1.4" -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +"color-name@1.1.3": + "integrity" "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + "version" "1.1.3" -colord@^2.9.1: - version "2.9.2" - resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1" - integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ== +"colord@^2.9.1": + "integrity" "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==" + "resolved" "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz" + "version" "2.9.2" -colorette@^2.0.10: - version "2.0.16" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" - integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== +"colorette@^2.0.10": + "integrity" "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" + "resolved" "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz" + "version" "2.0.16" -combine-promises@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/combine-promises/-/combine-promises-1.1.0.tgz#72db90743c0ca7aab7d0d8d2052fd7b0f674de71" - integrity sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg== +"combine-promises@^1.1.0": + "integrity" "sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg==" + "resolved" "https://registry.npmjs.org/combine-promises/-/combine-promises-1.1.0.tgz" + "version" "1.1.0" -comma-separated-tokens@^1.0.0: - version "1.0.8" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" - integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== +"comma-separated-tokens@^1.0.0": + "integrity" "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==" + "resolved" "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz" + "version" "1.0.8" -commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +"commander@^2.20.0": + "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + "version" "2.20.3" -commander@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== +"commander@^5.1.0": + "integrity" "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + "resolved" "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz" + "version" "5.1.0" -commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +"commander@^7.2.0": + "integrity" "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + "resolved" "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" + "version" "7.2.0" -commander@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +"commander@^8.3.0": + "integrity" "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + "resolved" "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" + "version" "8.3.0" -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +"commondir@^1.0.1": + "integrity" "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + "resolved" "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" + "version" "1.0.1" -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== +"compressible@~2.0.16": + "integrity" "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==" + "resolved" "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" + "version" "2.0.18" dependencies: - mime-db ">= 1.43.0 < 2" + "mime-db" ">= 1.43.0 < 2" -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== +"compression@^1.7.4": + "integrity" "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==" + "resolved" "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz" + "version" "1.7.4" dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" + "accepts" "~1.3.5" + "bytes" "3.0.0" + "compressible" "~2.0.16" + "debug" "2.6.9" + "on-headers" "~1.0.2" + "safe-buffer" "5.1.2" + "vary" "~1.1.2" -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +"concat-map@0.0.1": + "integrity" "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "version" "0.0.1" -configstore@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== +"configstore@^5.0.1": + "integrity" "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==" + "resolved" "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz" + "version" "5.0.1" dependencies: - dot-prop "^5.2.0" - graceful-fs "^4.1.2" - make-dir "^3.0.0" - unique-string "^2.0.0" - write-file-atomic "^3.0.0" - xdg-basedir "^4.0.0" + "dot-prop" "^5.2.0" + "graceful-fs" "^4.1.2" + "make-dir" "^3.0.0" + "unique-string" "^2.0.0" + "write-file-atomic" "^3.0.0" + "xdg-basedir" "^4.0.0" -connect-history-api-fallback@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" - integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== +"connect-history-api-fallback@^1.6.0": + "integrity" "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + "resolved" "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz" + "version" "1.6.0" -consola@^2.15.3: - version "2.15.3" - resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" - integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== +"consola@^2.15.3": + "integrity" "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + "resolved" "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz" + "version" "2.15.3" -content-disposition@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= +"content-disposition@0.5.2": + "integrity" "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + "resolved" "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz" + "version" "0.5.2" -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== +"content-disposition@0.5.4": + "integrity" "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==" + "resolved" "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" + "version" "0.5.4" dependencies: - safe-buffer "5.2.1" + "safe-buffer" "5.2.1" -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== +"content-type@~1.0.4": + "integrity" "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "resolved" "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" + "version" "1.0.4" -convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== +"convert-source-map@^1.7.0": + "integrity" "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==" + "resolved" "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz" + "version" "1.8.0" dependencies: - safe-buffer "~5.1.1" + "safe-buffer" "~5.1.1" -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= +"cookie-signature@1.0.6": + "integrity" "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "resolved" "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + "version" "1.0.6" -cookie@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +"cookie@0.4.2": + "integrity" "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + "resolved" "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" + "version" "0.4.2" -copy-text-to-clipboard@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz#8cbf8f90e0a47f12e4a24743736265d157bce69c" - integrity sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q== +"copy-text-to-clipboard@^3.0.1": + "integrity" "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==" + "resolved" "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz" + "version" "3.0.1" -copy-webpack-plugin@^10.2.0: - version "10.2.4" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz#6c854be3fdaae22025da34b9112ccf81c63308fe" - integrity sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg== +"copy-webpack-plugin@^10.2.0": + "integrity" "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==" + "resolved" "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz" + "version" "10.2.4" dependencies: - fast-glob "^3.2.7" - glob-parent "^6.0.1" - globby "^12.0.2" - normalize-path "^3.0.0" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" + "fast-glob" "^3.2.7" + "glob-parent" "^6.0.1" + "globby" "^12.0.2" + "normalize-path" "^3.0.0" + "schema-utils" "^4.0.0" + "serialize-javascript" "^6.0.0" -core-js-compat@^3.20.2, core-js-compat@^3.21.0: - version "3.21.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.21.1.tgz#cac369f67c8d134ff8f9bd1623e3bc2c42068c82" - integrity sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g== +"core-js-compat@^3.20.2", "core-js-compat@^3.21.0": + "integrity" "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==" + "resolved" "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz" + "version" "3.21.1" dependencies: - browserslist "^4.19.1" - semver "7.0.0" + "browserslist" "^4.19.1" + "semver" "7.0.0" -core-js-pure@^3.20.2: - version "3.21.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.21.1.tgz#8c4d1e78839f5f46208de7230cebfb72bc3bdb51" - integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ== +"core-js-pure@^3.20.2": + "integrity" "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==" + "resolved" "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz" + "version" "3.21.1" -core-js@^3.18.0: - version "3.21.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94" - integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig== +"core-js@^3.18.0": + "integrity" "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==" + "resolved" "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz" + "version" "3.21.1" -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +"core-util-is@~1.0.0": + "integrity" "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "resolved" "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + "version" "1.0.3" -cosmiconfig@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" - integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== +"cosmiconfig@^6.0.0": + "integrity" "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==" + "resolved" "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" + "version" "6.0.0" dependencies: "@types/parse-json" "^4.0.0" - import-fresh "^3.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.7.2" + "import-fresh" "^3.1.0" + "parse-json" "^5.0.0" + "path-type" "^4.0.0" + "yaml" "^1.7.2" -cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== +"cosmiconfig@^7.0.0", "cosmiconfig@^7.0.1": + "integrity" "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==" + "resolved" "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz" + "version" "7.0.1" dependencies: "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" + "import-fresh" "^3.2.1" + "parse-json" "^5.0.0" + "path-type" "^4.0.0" + "yaml" "^1.10.0" -cross-fetch@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== +"cross-fetch@^3.1.5": + "integrity" "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==" + "resolved" "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz" + "version" "3.1.5" dependencies: - node-fetch "2.6.7" + "node-fetch" "2.6.7" -cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== +"cross-spawn@^7.0.3": + "integrity" "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==" + "resolved" "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + "version" "7.0.3" dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" + "path-key" "^3.1.0" + "shebang-command" "^2.0.0" + "which" "^2.0.1" -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== +"crypto-random-string@^2.0.0": + "integrity" "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + "resolved" "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz" + "version" "2.0.0" -css-declaration-sorter@^6.0.3: - version "6.1.4" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz#b9bfb4ed9a41f8dcca9bf7184d849ea94a8294b4" - integrity sha512-lpfkqS0fctcmZotJGhnxkIyJWvBXgpyi2wsFd4J8VB7wzyrT6Ch/3Q+FMNJpjK4gu1+GN5khOnpU2ZVKrLbhCw== +"css-declaration-sorter@^6.0.3": + "integrity" "sha512-lpfkqS0fctcmZotJGhnxkIyJWvBXgpyi2wsFd4J8VB7wzyrT6Ch/3Q+FMNJpjK4gu1+GN5khOnpU2ZVKrLbhCw==" + "resolved" "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz" + "version" "6.1.4" dependencies: - timsort "^0.3.0" + "timsort" "^0.3.0" -css-loader@^6.5.1: - version "6.6.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.6.0.tgz#c792ad5510bd1712618b49381bd0310574fafbd3" - integrity sha512-FK7H2lisOixPT406s5gZM1S3l8GrfhEBT3ZiL2UX1Ng1XWs0y2GPllz/OTyvbaHe12VgQrIXIzuEGVlbUhodqg== +"css-loader@^6.5.1": + "integrity" "sha512-FK7H2lisOixPT406s5gZM1S3l8GrfhEBT3ZiL2UX1Ng1XWs0y2GPllz/OTyvbaHe12VgQrIXIzuEGVlbUhodqg==" + "resolved" "https://registry.npmjs.org/css-loader/-/css-loader-6.6.0.tgz" + "version" "6.6.0" dependencies: - icss-utils "^5.1.0" - postcss "^8.4.5" - postcss-modules-extract-imports "^3.0.0" - postcss-modules-local-by-default "^4.0.0" - postcss-modules-scope "^3.0.0" - postcss-modules-values "^4.0.0" - postcss-value-parser "^4.2.0" - semver "^7.3.5" + "icss-utils" "^5.1.0" + "postcss" "^8.4.5" + "postcss-modules-extract-imports" "^3.0.0" + "postcss-modules-local-by-default" "^4.0.0" + "postcss-modules-scope" "^3.0.0" + "postcss-modules-values" "^4.0.0" + "postcss-value-parser" "^4.2.0" + "semver" "^7.3.5" -css-minimizer-webpack-plugin@^3.3.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz#ab78f781ced9181992fe7b6e4f3422e76429878f" - integrity sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q== +"css-minimizer-webpack-plugin@^3.3.1": + "integrity" "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==" + "resolved" "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz" + "version" "3.4.1" dependencies: - cssnano "^5.0.6" - jest-worker "^27.0.2" - postcss "^8.3.5" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" - source-map "^0.6.1" + "cssnano" "^5.0.6" + "jest-worker" "^27.0.2" + "postcss" "^8.3.5" + "schema-utils" "^4.0.0" + "serialize-javascript" "^6.0.0" + "source-map" "^0.6.1" -css-select@^4.1.3: - version "4.2.1" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.2.1.tgz#9e665d6ae4c7f9d65dbe69d0316e3221fb274cdd" - integrity sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ== +"css-select@^4.1.3": + "integrity" "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==" + "resolved" "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz" + "version" "4.2.1" dependencies: - boolbase "^1.0.0" - css-what "^5.1.0" - domhandler "^4.3.0" - domutils "^2.8.0" - nth-check "^2.0.1" + "boolbase" "^1.0.0" + "css-what" "^5.1.0" + "domhandler" "^4.3.0" + "domutils" "^2.8.0" + "nth-check" "^2.0.1" -css-select@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" - integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= +"css-select@~1.2.0": + "integrity" "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=" + "resolved" "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz" + "version" "1.2.0" dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" + "boolbase" "~1.0.0" + "css-what" "2.1" + "domutils" "1.5.1" + "nth-check" "~1.0.1" -css-tree@^1.1.2, css-tree@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== +"css-tree@^1.1.2", "css-tree@^1.1.3": + "integrity" "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==" + "resolved" "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + "version" "1.1.3" dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" + "mdn-data" "2.0.14" + "source-map" "^0.6.1" -css-what@2.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== +"css-what@^5.0.1", "css-what@^5.1.0": + "integrity" "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==" + "resolved" "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz" + "version" "5.1.0" -css-what@^5.0.1, css-what@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" - integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== +"css-what@2.1": + "integrity" "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" + "resolved" "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz" + "version" "2.1.3" -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +"cssesc@^3.0.0": + "integrity" "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + "resolved" "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + "version" "3.0.0" -cssnano-preset-advanced@^5.1.4: - version "5.1.12" - resolved "https://registry.yarnpkg.com/cssnano-preset-advanced/-/cssnano-preset-advanced-5.1.12.tgz#11f5b0c4e3c32bcfd475465a283fa14dec8df972" - integrity sha512-5WWV9mbqVNwH4nRjs5UbhNl7eKo+16eYNzGogmz0Sa6iqWUeLdN8oo83WuTTqz5vjEKhTbRM5oX6WV1i6ees6g== +"cssnano-preset-advanced@^5.1.4": + "integrity" "sha512-5WWV9mbqVNwH4nRjs5UbhNl7eKo+16eYNzGogmz0Sa6iqWUeLdN8oo83WuTTqz5vjEKhTbRM5oX6WV1i6ees6g==" + "resolved" "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.1.12.tgz" + "version" "5.1.12" dependencies: - autoprefixer "^10.3.7" - cssnano-preset-default "^5.1.12" - postcss-discard-unused "^5.0.3" - postcss-merge-idents "^5.0.3" - postcss-reduce-idents "^5.0.3" - postcss-zindex "^5.0.2" + "autoprefixer" "^10.3.7" + "cssnano-preset-default" "^5.1.12" + "postcss-discard-unused" "^5.0.3" + "postcss-merge-idents" "^5.0.3" + "postcss-reduce-idents" "^5.0.3" + "postcss-zindex" "^5.0.2" -cssnano-preset-default@^5.1.12: - version "5.1.12" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.12.tgz#64e2ad8e27a279e1413d2d2383ef89a41c909be9" - integrity sha512-rO/JZYyjW1QNkWBxMGV28DW7d98UDLaF759frhli58QFehZ+D/LSmwQ2z/ylBAe2hUlsIWTq6NYGfQPq65EF9w== +"cssnano-preset-default@^5.1.12": + "integrity" "sha512-rO/JZYyjW1QNkWBxMGV28DW7d98UDLaF759frhli58QFehZ+D/LSmwQ2z/ylBAe2hUlsIWTq6NYGfQPq65EF9w==" + "resolved" "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.12.tgz" + "version" "5.1.12" dependencies: - css-declaration-sorter "^6.0.3" - cssnano-utils "^3.0.2" - postcss-calc "^8.2.0" - postcss-colormin "^5.2.5" - postcss-convert-values "^5.0.4" - postcss-discard-comments "^5.0.3" - postcss-discard-duplicates "^5.0.3" - postcss-discard-empty "^5.0.3" - postcss-discard-overridden "^5.0.4" - postcss-merge-longhand "^5.0.6" - postcss-merge-rules "^5.0.6" - postcss-minify-font-values "^5.0.4" - postcss-minify-gradients "^5.0.6" - postcss-minify-params "^5.0.5" - postcss-minify-selectors "^5.1.3" - postcss-normalize-charset "^5.0.3" - postcss-normalize-display-values "^5.0.3" - postcss-normalize-positions "^5.0.4" - postcss-normalize-repeat-style "^5.0.4" - postcss-normalize-string "^5.0.4" - postcss-normalize-timing-functions "^5.0.3" - postcss-normalize-unicode "^5.0.4" - postcss-normalize-url "^5.0.5" - postcss-normalize-whitespace "^5.0.4" - postcss-ordered-values "^5.0.5" - postcss-reduce-initial "^5.0.3" - postcss-reduce-transforms "^5.0.4" - postcss-svgo "^5.0.4" - postcss-unique-selectors "^5.0.4" + "css-declaration-sorter" "^6.0.3" + "cssnano-utils" "^3.0.2" + "postcss-calc" "^8.2.0" + "postcss-colormin" "^5.2.5" + "postcss-convert-values" "^5.0.4" + "postcss-discard-comments" "^5.0.3" + "postcss-discard-duplicates" "^5.0.3" + "postcss-discard-empty" "^5.0.3" + "postcss-discard-overridden" "^5.0.4" + "postcss-merge-longhand" "^5.0.6" + "postcss-merge-rules" "^5.0.6" + "postcss-minify-font-values" "^5.0.4" + "postcss-minify-gradients" "^5.0.6" + "postcss-minify-params" "^5.0.5" + "postcss-minify-selectors" "^5.1.3" + "postcss-normalize-charset" "^5.0.3" + "postcss-normalize-display-values" "^5.0.3" + "postcss-normalize-positions" "^5.0.4" + "postcss-normalize-repeat-style" "^5.0.4" + "postcss-normalize-string" "^5.0.4" + "postcss-normalize-timing-functions" "^5.0.3" + "postcss-normalize-unicode" "^5.0.4" + "postcss-normalize-url" "^5.0.5" + "postcss-normalize-whitespace" "^5.0.4" + "postcss-ordered-values" "^5.0.5" + "postcss-reduce-initial" "^5.0.3" + "postcss-reduce-transforms" "^5.0.4" + "postcss-svgo" "^5.0.4" + "postcss-unique-selectors" "^5.0.4" -cssnano-utils@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.0.2.tgz#d82b4991a27ba6fec644b39bab35fe027137f516" - integrity sha512-KhprijuQv2sP4kT92sSQwhlK3SJTbDIsxcfIEySB0O+3m9esFOai7dP9bMx5enHAh2MwarVIcnwiWoOm01RIbQ== +"cssnano-utils@^3.0.2": + "integrity" "sha512-KhprijuQv2sP4kT92sSQwhlK3SJTbDIsxcfIEySB0O+3m9esFOai7dP9bMx5enHAh2MwarVIcnwiWoOm01RIbQ==" + "resolved" "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.0.2.tgz" + "version" "3.0.2" -cssnano@^5.0.6, cssnano@^5.0.8: - version "5.0.17" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.17.tgz#ff45713c05cfc780a1aeb3e663b6f224d091cabf" - integrity sha512-fmjLP7k8kL18xSspeXTzRhaFtRI7DL9b8IcXR80JgtnWBpvAzHT7sCR/6qdn0tnxIaINUN6OEQu83wF57Gs3Xw== +"cssnano@^5.0.6", "cssnano@^5.0.8": + "integrity" "sha512-fmjLP7k8kL18xSspeXTzRhaFtRI7DL9b8IcXR80JgtnWBpvAzHT7sCR/6qdn0tnxIaINUN6OEQu83wF57Gs3Xw==" + "resolved" "https://registry.npmjs.org/cssnano/-/cssnano-5.0.17.tgz" + "version" "5.0.17" dependencies: - cssnano-preset-default "^5.1.12" - lilconfig "^2.0.3" - yaml "^1.10.2" + "cssnano-preset-default" "^5.1.12" + "lilconfig" "^2.0.3" + "yaml" "^1.10.2" -csso@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== +"csso@^4.2.0": + "integrity" "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==" + "resolved" "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz" + "version" "4.2.0" dependencies: - css-tree "^1.1.2" + "css-tree" "^1.1.2" -csstype@^3.0.2: - version "3.0.10" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" - integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== +"csstype@^3.0.2": + "integrity" "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==" + "resolved" "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz" + "version" "3.0.10" -debug@2.6.9, debug@^2.6.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== +"debug@^2.6.0", "debug@2.6.9": + "integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==" + "resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + "version" "2.6.9" dependencies: - ms "2.0.0" + "ms" "2.0.0" -debug@^3.1.1: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== +"debug@^3.1.1": + "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==" + "resolved" "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + "version" "3.2.7" dependencies: - ms "^2.1.1" + "ms" "^2.1.1" -debug@^4.1.0, debug@^4.1.1: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== +"debug@^4.1.0": + "integrity" "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" + "version" "4.3.3" dependencies: - ms "2.1.2" + "ms" "2.1.2" -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= +"debug@^4.1.1": + "integrity" "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" + "version" "4.3.3" dependencies: - mimic-response "^1.0.0" + "ms" "2.1.2" -deep-equal@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" - integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== +"decompress-response@^3.3.0": + "integrity" "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=" + "resolved" "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" + "version" "3.3.0" dependencies: - is-arguments "^1.0.4" - is-date-object "^1.0.1" - is-regex "^1.0.4" - object-is "^1.0.1" - object-keys "^1.1.1" - regexp.prototype.flags "^1.2.0" + "mimic-response" "^1.0.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -deepmerge@^1.3.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" - integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== - -deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - -default-gateway@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" - integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== +"deep-equal@^1.0.1": + "integrity" "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==" + "resolved" "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz" + "version" "1.1.1" dependencies: - execa "^5.0.0" + "is-arguments" "^1.0.4" + "is-date-object" "^1.0.1" + "is-regex" "^1.0.4" + "object-is" "^1.0.1" + "object-keys" "^1.1.1" + "regexp.prototype.flags" "^1.2.0" -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +"deep-extend@^0.6.0": + "integrity" "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + "resolved" "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" + "version" "0.6.0" -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +"deepmerge@^1.3.2": + "integrity" "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==" + "resolved" "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz" + "version" "1.5.2" -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== +"deepmerge@^4.2.2": + "integrity" "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + "resolved" "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz" + "version" "4.2.2" + +"default-gateway@^6.0.3": + "integrity" "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==" + "resolved" "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" + "version" "6.0.3" dependencies: - object-keys "^1.0.12" + "execa" "^5.0.0" -del@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" - integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ== +"defer-to-connect@^1.0.1": + "integrity" "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + "resolved" "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz" + "version" "1.1.3" + +"define-lazy-prop@^2.0.0": + "integrity" "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + "resolved" "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" + "version" "2.0.0" + +"define-properties@^1.1.3": + "integrity" "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==" + "resolved" "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" + "version" "1.1.3" dependencies: - globby "^11.0.1" - graceful-fs "^4.2.4" - is-glob "^4.0.1" - is-path-cwd "^2.2.0" - is-path-inside "^3.0.2" - p-map "^4.0.0" - rimraf "^3.0.2" - slash "^3.0.0" + "object-keys" "^1.0.12" -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -detab@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.4.tgz#b927892069aff405fbb9a186fe97a44a92a94b43" - integrity sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g== +"del@^6.0.0": + "integrity" "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==" + "resolved" "https://registry.npmjs.org/del/-/del-6.0.0.tgz" + "version" "6.0.0" dependencies: - repeat-string "^1.5.4" + "globby" "^11.0.1" + "graceful-fs" "^4.2.4" + "is-glob" "^4.0.1" + "is-path-cwd" "^2.2.0" + "is-path-inside" "^3.0.2" + "p-map" "^4.0.0" + "rimraf" "^3.0.2" + "slash" "^3.0.0" -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== +"depd@~1.1.2": + "integrity" "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "resolved" "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" + "version" "1.1.2" -detect-port-alt@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" - integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== +"destroy@~1.0.4": + "integrity" "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "resolved" "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" + "version" "1.0.4" + +"detab@2.0.4": + "integrity" "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==" + "resolved" "https://registry.npmjs.org/detab/-/detab-2.0.4.tgz" + "version" "2.0.4" dependencies: - address "^1.0.1" - debug "^2.6.0" + "repeat-string" "^1.5.4" -detect-port@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" - integrity sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ== +"detect-node@^2.0.4": + "integrity" "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + "resolved" "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" + "version" "2.1.0" + +"detect-port-alt@^1.1.6": + "integrity" "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==" + "resolved" "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz" + "version" "1.1.6" dependencies: - address "^1.0.1" - debug "^2.6.0" + "address" "^1.0.1" + "debug" "^2.6.0" -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== +"detect-port@^1.3.0": + "integrity" "sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==" + "resolved" "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz" + "version" "1.3.0" dependencies: - path-type "^4.0.0" + "address" "^1.0.1" + "debug" "^2.6.0" -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= - -dns-packet@^1.3.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" - integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== +"dir-glob@^3.0.1": + "integrity" "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==" + "resolved" "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + "version" "3.0.1" dependencies: - ip "^1.1.0" - safe-buffer "^5.0.1" + "path-type" "^4.0.0" -dns-txt@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" - integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= +"dns-equal@^1.0.0": + "integrity" "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + "resolved" "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz" + "version" "1.0.0" + +"dns-packet@^1.3.1": + "integrity" "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==" + "resolved" "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz" + "version" "1.3.4" dependencies: - buffer-indexof "^1.0.0" + "ip" "^1.1.0" + "safe-buffer" "^5.0.1" -dom-converter@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" - integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== +"dns-txt@^2.0.2": + "integrity" "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=" + "resolved" "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz" + "version" "2.0.2" dependencies: - utila "~0.4" + "buffer-indexof" "^1.0.0" -dom-serializer@0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== +"dom-converter@^0.2.0": + "integrity" "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==" + "resolved" "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz" + "version" "0.2.0" dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" + "utila" "~0.4" -dom-serializer@^1.0.1, dom-serializer@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" - integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== +"dom-serializer@^1.0.1": + "integrity" "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz" + "version" "1.3.2" dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.0" - entities "^2.0.0" + "domelementtype" "^2.0.1" + "domhandler" "^4.2.0" + "entities" "^2.0.0" -dom-serializer@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" - integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== +"dom-serializer@^1.3.2": + "integrity" "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz" + "version" "1.3.2" dependencies: - domelementtype "^1.3.0" - entities "^1.1.1" + "domelementtype" "^2.0.1" + "domhandler" "^4.2.0" + "entities" "^2.0.0" -domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1, domelementtype@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" - integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== - -domhandler@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" - integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== +"dom-serializer@~0.1.0": + "integrity" "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz" + "version" "0.1.1" dependencies: - domelementtype "1" + "domelementtype" "^1.3.0" + "entities" "^1.1.1" -domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" - integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== +"dom-serializer@0": + "integrity" "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" + "version" "0.2.2" dependencies: - domelementtype "^2.2.0" + "domelementtype" "^2.0.1" + "entities" "^2.0.0" -domutils@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= +"domelementtype@^1.3.0", "domelementtype@^1.3.1", "domelementtype@1": + "integrity" "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" + "version" "1.3.1" + +"domelementtype@^2.0.1", "domelementtype@^2.2.0": + "integrity" "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" + "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz" + "version" "2.2.0" + +"domhandler@^2.3.0": + "integrity" "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==" + "resolved" "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz" + "version" "2.4.2" dependencies: - dom-serializer "0" - domelementtype "1" + "domelementtype" "1" -domutils@^1.5.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== +"domhandler@^4.0.0", "domhandler@^4.2.0", "domhandler@^4.3.0": + "integrity" "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==" + "resolved" "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz" + "version" "4.3.0" dependencies: - dom-serializer "0" - domelementtype "1" + "domelementtype" "^2.2.0" -domutils@^2.5.2, domutils@^2.7.0, domutils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" - integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== +"domutils@^1.5.1": + "integrity" "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==" + "resolved" "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" + "version" "1.7.0" dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" + "dom-serializer" "0" + "domelementtype" "1" -dot-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" - integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== +"domutils@^2.5.2", "domutils@^2.7.0", "domutils@^2.8.0": + "integrity" "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==" + "resolved" "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz" + "version" "2.8.0" dependencies: - no-case "^3.0.4" - tslib "^2.0.3" + "dom-serializer" "^1.0.1" + "domelementtype" "^2.2.0" + "domhandler" "^4.2.0" -dot-prop@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" - integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== +"domutils@1.5.1": + "integrity" "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=" + "resolved" "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz" + "version" "1.5.1" dependencies: - is-obj "^2.0.0" + "dom-serializer" "0" + "domelementtype" "1" -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -duplexer@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -electron-to-chromium@^1.4.17: - version "1.4.71" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.71.tgz#17056914465da0890ce00351a3b946fd4cd51ff6" - integrity sha512-Hk61vXXKRb2cd3znPE9F+2pLWdIOmP7GjiTj45y6L3W/lO+hSnUSUhq+6lEaERWBdZOHbk2s3YV5c9xVl3boVw== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - -emoticon@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-3.2.0.tgz#c008ca7d7620fac742fe1bf4af8ff8fed154ae7f" - integrity sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== +"dot-case@^3.0.4": + "integrity" "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==" + "resolved" "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" + "version" "3.0.4" dependencies: - once "^1.4.0" + "no-case" "^3.0.4" + "tslib" "^2.0.3" -enhanced-resolve@^5.8.3: - version "5.9.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz#49ac24953ac8452ed8fed2ef1340fc8e043667ee" - integrity sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA== +"dot-prop@^5.2.0": + "integrity" "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==" + "resolved" "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" + "version" "5.3.0" dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" + "is-obj" "^2.0.0" -entities@^1.1.1, entities@~1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" - integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== +"duplexer@^0.1.2": + "integrity" "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + "resolved" "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" + "version" "0.1.2" -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +"duplexer3@^0.1.4": + "integrity" "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + "resolved" "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" + "version" "0.1.4" -entities@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" - integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== +"ee-first@1.1.1": + "integrity" "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "resolved" "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + "version" "1.1.1" -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== +"electron-to-chromium@^1.4.284": + "integrity" "sha512-DE9tTy2PNmy1v55AZAO542ui+MLC2cvINMK4P2LXGsJdput/ThVG9t+QGecPuAZZSgC8XoI+Jh9M1OG9IoNSCw==" + "resolved" "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.328.tgz" + "version" "1.4.328" + +"emoji-regex@^8.0.0": + "integrity" "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + "version" "8.0.0" + +"emojis-list@^3.0.0": + "integrity" "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + "resolved" "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" + "version" "3.0.0" + +"emoticon@^3.2.0": + "integrity" "sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg==" + "resolved" "https://registry.npmjs.org/emoticon/-/emoticon-3.2.0.tgz" + "version" "3.2.0" + +"encodeurl@~1.0.2": + "integrity" "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "resolved" "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" + "version" "1.0.2" + +"end-of-stream@^1.1.0": + "integrity" "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==" + "resolved" "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" + "version" "1.4.4" dependencies: - is-arrayish "^0.2.1" + "once" "^1.4.0" -es-module-lexer@^0.9.0: - version "0.9.3" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" - integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== - -escape-html@^1.0.3, escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-scope@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== +"enhanced-resolve@^5.8.3": + "integrity" "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==" + "resolved" "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz" + "version" "5.9.0" dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" + "graceful-fs" "^4.2.4" + "tapable" "^2.2.0" -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +"entities@^1.1.1", "entities@~1.1.1": + "integrity" "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + "resolved" "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz" + "version" "1.1.2" -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== +"entities@^2.0.0": + "integrity" "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + "resolved" "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" + "version" "2.2.0" + +"entities@^3.0.1": + "integrity" "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==" + "resolved" "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz" + "version" "3.0.1" + +"error-ex@^1.3.1": + "integrity" "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==" + "resolved" "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + "version" "1.3.2" dependencies: - estraverse "^5.2.0" + "is-arrayish" "^0.2.1" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +"es-module-lexer@^0.9.0": + "integrity" "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + "resolved" "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz" + "version" "0.9.3" -estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +"escalade@^3.1.1": + "integrity" "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "resolved" "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + "version" "3.1.1" -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +"escape-goat@^2.0.0": + "integrity" "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" + "resolved" "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz" + "version" "2.1.1" -eta@^1.12.3: - version "1.12.3" - resolved "https://registry.yarnpkg.com/eta/-/eta-1.12.3.tgz#2982d08adfbef39f9fa50e2fbd42d7337e7338b1" - integrity sha512-qHixwbDLtekO/d51Yr4glcaUJCIjGVJyTzuqV4GPlgZo1YpgOKG+avQynErZIYrfM6JIJdtiG2Kox8tbb+DoGg== +"escape-html@^1.0.3", "escape-html@~1.0.3": + "integrity" "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "resolved" "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + "version" "1.0.3" -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +"escape-string-regexp@^1.0.5": + "integrity" "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + "version" "1.0.5" -eval@^0.1.4: - version "0.1.6" - resolved "https://registry.yarnpkg.com/eval/-/eval-0.1.6.tgz#9620d7d8c85515e97e6b47c5814f46ae381cb3cc" - integrity sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ== +"escape-string-regexp@^4.0.0": + "integrity" "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + "version" "4.0.0" + +"eslint-scope@5.1.1": + "integrity" "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==" + "resolved" "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + "version" "5.1.1" dependencies: - require-like ">= 0.1.1" + "esrecurse" "^4.3.0" + "estraverse" "^4.1.1" -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +"esprima@^4.0.0": + "integrity" "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "resolved" "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + "version" "4.0.1" -events@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== +"esrecurse@^4.3.0": + "integrity" "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==" + "resolved" "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + "version" "4.3.0" dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" + "estraverse" "^5.2.0" -express@^4.17.1: - version "4.17.3" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" - integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== +"estraverse@^4.1.1": + "integrity" "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + "version" "4.3.0" + +"estraverse@^5.2.0": + "integrity" "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + "version" "5.3.0" + +"esutils@^2.0.2": + "integrity" "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "resolved" "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + "version" "2.0.3" + +"eta@^1.12.3": + "integrity" "sha512-qHixwbDLtekO/d51Yr4glcaUJCIjGVJyTzuqV4GPlgZo1YpgOKG+avQynErZIYrfM6JIJdtiG2Kox8tbb+DoGg==" + "resolved" "https://registry.npmjs.org/eta/-/eta-1.12.3.tgz" + "version" "1.12.3" + +"etag@~1.8.1": + "integrity" "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "resolved" "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" + "version" "1.8.1" + +"eval@^0.1.4": + "integrity" "sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ==" + "resolved" "https://registry.npmjs.org/eval/-/eval-0.1.6.tgz" + "version" "0.1.6" dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.19.2" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.4.2" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.9.7" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.17.2" - serve-static "1.14.2" - setprototypeof "1.2.0" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" + "require-like" ">= 0.1.1" -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= +"eventemitter3@^4.0.0": + "integrity" "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "resolved" "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + "version" "4.0.7" + +"events@^3.2.0": + "integrity" "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + "resolved" "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + "version" "3.3.0" + +"execa@^5.0.0": + "integrity" "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==" + "resolved" "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + "version" "5.1.1" dependencies: - is-extendable "^0.1.0" + "cross-spawn" "^7.0.3" + "get-stream" "^6.0.0" + "human-signals" "^2.1.0" + "is-stream" "^2.0.0" + "merge-stream" "^2.0.0" + "npm-run-path" "^4.0.1" + "onetime" "^5.1.2" + "signal-exit" "^3.0.3" + "strip-final-newline" "^2.0.0" -extend@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +"express@^4.17.1": + "integrity" "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==" + "resolved" "https://registry.npmjs.org/express/-/express-4.17.3.tgz" + "version" "4.17.3" + dependencies: + "accepts" "~1.3.8" + "array-flatten" "1.1.1" + "body-parser" "1.19.2" + "content-disposition" "0.5.4" + "content-type" "~1.0.4" + "cookie" "0.4.2" + "cookie-signature" "1.0.6" + "debug" "2.6.9" + "depd" "~1.1.2" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "etag" "~1.8.1" + "finalhandler" "~1.1.2" + "fresh" "0.5.2" + "merge-descriptors" "1.0.1" + "methods" "~1.1.2" + "on-finished" "~2.3.0" + "parseurl" "~1.3.3" + "path-to-regexp" "0.1.7" + "proxy-addr" "~2.0.7" + "qs" "6.9.7" + "range-parser" "~1.2.1" + "safe-buffer" "5.2.1" + "send" "0.17.2" + "serve-static" "1.14.2" + "setprototypeof" "1.2.0" + "statuses" "~1.5.0" + "type-is" "~1.6.18" + "utils-merge" "1.0.1" + "vary" "~1.1.2" -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +"extend-shallow@^2.0.1": + "integrity" "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" + "resolved" "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "is-extendable" "^0.1.0" -fast-glob@^3.2.7, fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== +"extend@^3.0.0": + "integrity" "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "resolved" "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" + "version" "3.0.2" + +"fast-deep-equal@^3.1.1", "fast-deep-equal@^3.1.3": + "integrity" "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "resolved" "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + "version" "3.1.3" + +"fast-glob@^3.2.7", "fast-glob@^3.2.9": + "integrity" "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==" + "resolved" "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz" + "version" "3.2.11" dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" + "glob-parent" "^5.1.2" + "merge2" "^1.3.0" + "micromatch" "^4.0.4" -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +"fast-json-stable-stringify@^2.0.0": + "integrity" "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "resolved" "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + "version" "2.1.0" -fast-url-parser@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" - integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= +"fast-url-parser@1.1.3": + "integrity" "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=" + "resolved" "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz" + "version" "1.1.3" dependencies: - punycode "^1.3.2" + "punycode" "^1.3.2" -fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== +"fastq@^1.6.0": + "integrity" "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==" + "resolved" "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" + "version" "1.13.0" dependencies: - reusify "^1.0.4" + "reusify" "^1.0.4" -faye-websocket@^0.11.3: - version "0.11.4" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== +"faye-websocket@^0.11.3": + "integrity" "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==" + "resolved" "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" + "version" "0.11.4" dependencies: - websocket-driver ">=0.5.1" + "websocket-driver" ">=0.5.1" -fbemitter@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-3.0.0.tgz#00b2a1af5411254aab416cd75f9e6289bee4bff3" - integrity sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw== +"fbemitter@^3.0.0": + "integrity" "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==" + "resolved" "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz" + "version" "3.0.0" dependencies: - fbjs "^3.0.0" + "fbjs" "^3.0.0" -fbjs-css-vars@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" - integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== +"fbjs-css-vars@^1.0.0": + "integrity" "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + "resolved" "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz" + "version" "1.0.2" -fbjs@^3.0.0, fbjs@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.4.tgz#e1871c6bd3083bac71ff2da868ad5067d37716c6" - integrity sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ== +"fbjs@^3.0.0", "fbjs@^3.0.1": + "integrity" "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==" + "resolved" "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz" + "version" "3.0.4" dependencies: - cross-fetch "^3.1.5" - fbjs-css-vars "^1.0.0" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.30" + "cross-fetch" "^3.1.5" + "fbjs-css-vars" "^1.0.0" + "loose-envify" "^1.0.0" + "object-assign" "^4.1.0" + "promise" "^7.1.1" + "setimmediate" "^1.0.5" + "ua-parser-js" "^0.7.30" -feed@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/feed/-/feed-4.2.2.tgz#865783ef6ed12579e2c44bbef3c9113bc4956a7e" - integrity sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ== +"feed@^4.2.2": + "integrity" "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==" + "resolved" "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz" + "version" "4.2.2" dependencies: - xml-js "^1.6.11" + "xml-js" "^1.6.11" -file-loader@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" - integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== +"file-loader@*", "file-loader@^6.2.0": + "integrity" "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==" + "resolved" "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz" + "version" "6.2.0" dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" + "loader-utils" "^2.0.0" + "schema-utils" "^3.0.0" -filesize@^8.0.6: - version "8.0.7" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8" - integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ== +"filesize@^8.0.6": + "integrity" "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" + "resolved" "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz" + "version" "8.0.7" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +"fill-range@^7.0.1": + "integrity" "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==" + "resolved" "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + "version" "7.0.1" dependencies: - to-regex-range "^5.0.1" + "to-regex-range" "^5.0.1" -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== +"finalhandler@~1.1.2": + "integrity" "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==" + "resolved" "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" + "version" "1.1.2" dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" + "debug" "2.6.9" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "on-finished" "~2.3.0" + "parseurl" "~1.3.3" + "statuses" "~1.5.0" + "unpipe" "~1.0.0" -find-cache-dir@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" - integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== +"find-cache-dir@^3.3.1": + "integrity" "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==" + "resolved" "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" + "version" "3.3.2" dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" + "commondir" "^1.0.1" + "make-dir" "^3.0.2" + "pkg-dir" "^4.1.0" -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== +"find-up@^3.0.0": + "integrity" "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" + "version" "3.0.0" dependencies: - locate-path "^3.0.0" + "locate-path" "^3.0.0" -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== +"find-up@^4.0.0": + "integrity" "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + "version" "4.1.0" dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" + "locate-path" "^5.0.0" + "path-exists" "^4.0.0" -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== +"find-up@^5.0.0": + "integrity" "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + "version" "5.0.0" dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" + "locate-path" "^6.0.0" + "path-exists" "^4.0.0" -flux@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/flux/-/flux-4.0.3.tgz#573b504a24982c4768fdfb59d8d2ea5637d72ee7" - integrity sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw== +"flux@^4.0.1": + "integrity" "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==" + "resolved" "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz" + "version" "4.0.3" dependencies: - fbemitter "^3.0.0" - fbjs "^3.0.1" + "fbemitter" "^3.0.0" + "fbjs" "^3.0.1" -follow-redirects@^1.0.0, follow-redirects@^1.14.7: - version "1.14.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" - integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== +"follow-redirects@^1.0.0", "follow-redirects@^1.14.7": + "integrity" "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz" + "version" "1.14.9" -fork-ts-checker-webpack-plugin@^6.5.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.0.tgz#0282b335fa495a97e167f69018f566ea7d2a2b5e" - integrity sha512-cS178Y+xxtIjEUorcHddKS7yCMlrDPV31mt47blKKRfMd70Kxu5xruAFE2o9sDY6wVC5deuob/u/alD04YYHnw== +"fork-ts-checker-webpack-plugin@^6.5.0": + "integrity" "sha512-cS178Y+xxtIjEUorcHddKS7yCMlrDPV31mt47blKKRfMd70Kxu5xruAFE2o9sDY6wVC5deuob/u/alD04YYHnw==" + "resolved" "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.0.tgz" + "version" "6.5.0" dependencies: "@babel/code-frame" "^7.8.3" "@types/json-schema" "^7.0.5" - chalk "^4.1.0" - chokidar "^3.4.2" - cosmiconfig "^6.0.0" - deepmerge "^4.2.2" - fs-extra "^9.0.0" - glob "^7.1.6" - memfs "^3.1.2" - minimatch "^3.0.4" - schema-utils "2.7.0" - semver "^7.3.2" - tapable "^1.0.0" + "chalk" "^4.1.0" + "chokidar" "^3.4.2" + "cosmiconfig" "^6.0.0" + "deepmerge" "^4.2.2" + "fs-extra" "^9.0.0" + "glob" "^7.1.6" + "memfs" "^3.1.2" + "minimatch" "^3.0.4" + "schema-utils" "2.7.0" + "semver" "^7.3.2" + "tapable" "^1.0.0" -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +"forwarded@0.2.0": + "integrity" "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "resolved" "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" + "version" "0.2.0" -fraction.js@^4.1.2: - version "4.1.3" - resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.1.3.tgz#be65b0f20762ef27e1e793860bc2dfb716e99e65" - integrity sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg== +"fraction.js@^4.1.2": + "integrity" "sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg==" + "resolved" "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.3.tgz" + "version" "4.1.3" -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +"fresh@0.5.2": + "integrity" "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "resolved" "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" + "version" "0.5.2" -fs-extra@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" - integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== +"fs-extra@^10.0.0": + "integrity" "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==" + "resolved" "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz" + "version" "10.0.0" dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" + "graceful-fs" "^4.2.0" + "jsonfile" "^6.0.1" + "universalify" "^2.0.0" -fs-extra@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== +"fs-extra@^9.0.0": + "integrity" "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==" + "resolved" "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" + "version" "9.1.0" dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" + "at-least-node" "^1.0.0" + "graceful-fs" "^4.2.0" + "jsonfile" "^6.0.1" + "universalify" "^2.0.0" -fs-monkey@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" - integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== +"fs-monkey@1.0.3": + "integrity" "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + "resolved" "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz" + "version" "1.0.3" -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +"fs.realpath@^1.0.0": + "integrity" "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + "version" "1.0.0" -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +"function-bind@^1.1.1": + "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + "version" "1.1.1" -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +"gensync@^1.0.0-beta.1", "gensync@^1.0.0-beta.2": + "integrity" "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + "resolved" "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + "version" "1.0.0-beta.2" -gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-intrinsic@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== +"get-intrinsic@^1.0.2": + "integrity" "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==" + "resolved" "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" + "version" "1.1.1" dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" + "function-bind" "^1.1.1" + "has" "^1.0.3" + "has-symbols" "^1.0.1" -get-own-enumerable-property-symbols@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" - integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== +"get-own-enumerable-property-symbols@^3.0.0": + "integrity" "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + "resolved" "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz" + "version" "3.0.2" -get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== +"get-stream@^4.1.0": + "integrity" "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==" + "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" + "version" "4.1.0" dependencies: - pump "^3.0.0" + "pump" "^3.0.0" -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== +"get-stream@^5.1.0": + "integrity" "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==" + "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" + "version" "5.2.0" dependencies: - pump "^3.0.0" + "pump" "^3.0.0" -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +"get-stream@^6.0.0": + "integrity" "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + "version" "6.0.1" -github-slugger@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e" - integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ== +"github-slugger@^1.4.0": + "integrity" "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==" + "resolved" "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz" + "version" "1.4.0" -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== +"glob-parent@^5.1.2", "glob-parent@~5.1.2": + "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + "version" "5.1.2" dependencies: - is-glob "^4.0.1" + "is-glob" "^4.0.1" -glob-parent@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== +"glob-parent@^6.0.1": + "integrity" "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + "version" "6.0.2" dependencies: - is-glob "^4.0.3" + "is-glob" "^4.0.3" -glob-to-regexp@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" - integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +"glob-to-regexp@^0.4.1": + "integrity" "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + "resolved" "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" + "version" "0.4.1" -glob@^7.0.0, glob@^7.1.3, glob@^7.1.6: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +"glob@^7.0.0", "glob@^7.1.3", "glob@^7.1.6": + "integrity" "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==" + "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" + "version" "7.2.0" dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" + "fs.realpath" "^1.0.0" + "inflight" "^1.0.4" + "inherits" "2" + "minimatch" "^3.0.4" + "once" "^1.3.0" + "path-is-absolute" "^1.0.0" -global-dirs@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" - integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== +"global-dirs@^3.0.0": + "integrity" "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==" + "resolved" "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz" + "version" "3.0.0" dependencies: - ini "2.0.0" + "ini" "2.0.0" -global-modules@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== +"global-modules@^2.0.0": + "integrity" "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==" + "resolved" "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz" + "version" "2.0.0" dependencies: - global-prefix "^3.0.0" + "global-prefix" "^3.0.0" -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== +"global-prefix@^3.0.0": + "integrity" "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==" + "resolved" "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz" + "version" "3.0.0" dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" + "ini" "^1.3.5" + "kind-of" "^6.0.2" + "which" "^1.3.1" -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +"globals@^11.1.0": + "integrity" "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + "resolved" "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + "version" "11.12.0" -globby@^11.0.1, globby@^11.0.2, globby@^11.0.4: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== +"globby@^11.0.1", "globby@^11.0.2", "globby@^11.0.4": + "integrity" "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==" + "resolved" "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + "version" "11.1.0" dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" + "array-union" "^2.1.0" + "dir-glob" "^3.0.1" + "fast-glob" "^3.2.9" + "ignore" "^5.2.0" + "merge2" "^1.4.1" + "slash" "^3.0.0" -globby@^12.0.2: - version "12.2.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-12.2.0.tgz#2ab8046b4fba4ff6eede835b29f678f90e3d3c22" - integrity sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA== +"globby@^12.0.2": + "integrity" "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==" + "resolved" "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz" + "version" "12.2.0" dependencies: - array-union "^3.0.1" - dir-glob "^3.0.1" - fast-glob "^3.2.7" - ignore "^5.1.9" - merge2 "^1.4.1" - slash "^4.0.0" + "array-union" "^3.0.1" + "dir-glob" "^3.0.1" + "fast-glob" "^3.2.7" + "ignore" "^5.1.9" + "merge2" "^1.4.1" + "slash" "^4.0.0" -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== +"got@^9.6.0": + "integrity" "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==" + "resolved" "https://registry.npmjs.org/got/-/got-9.6.0.tgz" + "version" "9.6.0" dependencies: "@sindresorhus/is" "^0.14.0" "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" + "cacheable-request" "^6.0.0" + "decompress-response" "^3.3.0" + "duplexer3" "^0.1.4" + "get-stream" "^4.1.0" + "lowercase-keys" "^1.0.1" + "mimic-response" "^1.0.1" + "p-cancelable" "^1.0.0" + "to-readable-stream" "^1.0.0" + "url-parse-lax" "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: - version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" - integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== +"graceful-fs@^4.1.2", "graceful-fs@^4.1.6", "graceful-fs@^4.2.0", "graceful-fs@^4.2.4", "graceful-fs@^4.2.6", "graceful-fs@^4.2.9": + "integrity" "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + "resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" + "version" "4.2.9" -gray-matter@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" - integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== +"gray-matter@^4.0.3": + "integrity" "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==" + "resolved" "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz" + "version" "4.0.3" dependencies: - js-yaml "^3.13.1" - kind-of "^6.0.2" - section-matter "^1.0.0" - strip-bom-string "^1.0.0" + "js-yaml" "^3.13.1" + "kind-of" "^6.0.2" + "section-matter" "^1.0.0" + "strip-bom-string" "^1.0.0" -gzip-size@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" - integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== +"gzip-size@^6.0.0": + "integrity" "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==" + "resolved" "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz" + "version" "6.0.0" dependencies: - duplexer "^0.1.2" + "duplexer" "^0.1.2" -handle-thing@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" - integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== +"handle-thing@^2.0.0": + "integrity" "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + "resolved" "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz" + "version" "2.0.1" -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +"has-flag@^3.0.0": + "integrity" "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + "version" "3.0.0" -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +"has-flag@^4.0.0": + "integrity" "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + "version" "4.0.0" -has-symbols@^1.0.1, has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +"has-symbols@^1.0.1", "has-symbols@^1.0.2": + "integrity" "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + "resolved" "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz" + "version" "1.0.2" -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== +"has-tostringtag@^1.0.0": + "integrity" "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==" + "resolved" "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" + "version" "1.0.0" dependencies: - has-symbols "^1.0.2" + "has-symbols" "^1.0.2" -has-yarn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" - integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== +"has-yarn@^2.1.0": + "integrity" "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + "resolved" "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz" + "version" "2.1.0" -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +"has@^1.0.3": + "integrity" "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==" + "resolved" "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + "version" "1.0.3" dependencies: - function-bind "^1.1.1" + "function-bind" "^1.1.1" -hast-to-hyperscript@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d" - integrity sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA== +"hast-to-hyperscript@^9.0.0": + "integrity" "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==" + "resolved" "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz" + "version" "9.0.1" dependencies: "@types/unist" "^2.0.3" - comma-separated-tokens "^1.0.0" - property-information "^5.3.0" - space-separated-tokens "^1.0.0" - style-to-object "^0.3.0" - unist-util-is "^4.0.0" - web-namespaces "^1.0.0" + "comma-separated-tokens" "^1.0.0" + "property-information" "^5.3.0" + "space-separated-tokens" "^1.0.0" + "style-to-object" "^0.3.0" + "unist-util-is" "^4.0.0" + "web-namespaces" "^1.0.0" -hast-util-from-parse5@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz#3089dc0ee2ccf6ec8bc416919b51a54a589e097c" - integrity sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA== +"hast-util-from-parse5@^5.0.0": + "integrity" "sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA==" + "resolved" "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz" + "version" "5.0.3" dependencies: - ccount "^1.0.3" - hastscript "^5.0.0" - property-information "^5.0.0" - web-namespaces "^1.1.2" - xtend "^4.0.1" + "ccount" "^1.0.3" + "hastscript" "^5.0.0" + "property-information" "^5.0.0" + "web-namespaces" "^1.1.2" + "xtend" "^4.0.1" -hast-util-from-parse5@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" - integrity sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA== +"hast-util-from-parse5@^6.0.0": + "integrity" "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==" + "resolved" "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz" + "version" "6.0.1" dependencies: "@types/parse5" "^5.0.0" - hastscript "^6.0.0" - property-information "^5.0.0" - vfile "^4.0.0" - vfile-location "^3.2.0" - web-namespaces "^1.0.0" + "hastscript" "^6.0.0" + "property-information" "^5.0.0" + "vfile" "^4.0.0" + "vfile-location" "^3.2.0" + "web-namespaces" "^1.0.0" -hast-util-parse-selector@^2.0.0: - version "2.2.5" - resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" - integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== +"hast-util-parse-selector@^2.0.0": + "integrity" "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" + "resolved" "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz" + "version" "2.2.5" -hast-util-raw@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-6.0.1.tgz#973b15930b7529a7b66984c98148b46526885977" - integrity sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig== +"hast-util-raw@6.0.1": + "integrity" "sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==" + "resolved" "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-6.0.1.tgz" + "version" "6.0.1" dependencies: "@types/hast" "^2.0.0" - hast-util-from-parse5 "^6.0.0" - hast-util-to-parse5 "^6.0.0" - html-void-elements "^1.0.0" - parse5 "^6.0.0" - unist-util-position "^3.0.0" - vfile "^4.0.0" - web-namespaces "^1.0.0" - xtend "^4.0.0" - zwitch "^1.0.0" + "hast-util-from-parse5" "^6.0.0" + "hast-util-to-parse5" "^6.0.0" + "html-void-elements" "^1.0.0" + "parse5" "^6.0.0" + "unist-util-position" "^3.0.0" + "vfile" "^4.0.0" + "web-namespaces" "^1.0.0" + "xtend" "^4.0.0" + "zwitch" "^1.0.0" -hast-util-to-parse5@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" - integrity sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ== +"hast-util-to-parse5@^6.0.0": + "integrity" "sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==" + "resolved" "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz" + "version" "6.0.0" dependencies: - hast-to-hyperscript "^9.0.0" - property-information "^5.0.0" - web-namespaces "^1.0.0" - xtend "^4.0.0" - zwitch "^1.0.0" + "hast-to-hyperscript" "^9.0.0" + "property-information" "^5.0.0" + "web-namespaces" "^1.0.0" + "xtend" "^4.0.0" + "zwitch" "^1.0.0" -hastscript@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-5.1.2.tgz#bde2c2e56d04c62dd24e8c5df288d050a355fb8a" - integrity sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ== +"hastscript@^5.0.0": + "integrity" "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==" + "resolved" "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz" + "version" "5.1.2" dependencies: - comma-separated-tokens "^1.0.0" - hast-util-parse-selector "^2.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" + "comma-separated-tokens" "^1.0.0" + "hast-util-parse-selector" "^2.0.0" + "property-information" "^5.0.0" + "space-separated-tokens" "^1.0.0" -hastscript@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" - integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== +"hastscript@^6.0.0": + "integrity" "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==" + "resolved" "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz" + "version" "6.0.0" dependencies: "@types/hast" "^2.0.0" - comma-separated-tokens "^1.0.0" - hast-util-parse-selector "^2.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" + "comma-separated-tokens" "^1.0.0" + "hast-util-parse-selector" "^2.0.0" + "property-information" "^5.0.0" + "space-separated-tokens" "^1.0.0" -he@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +"he@^1.2.0": + "integrity" "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + "resolved" "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + "version" "1.2.0" -history@^4.9.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" - integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== +"history@^4.9.0": + "integrity" "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==" + "resolved" "https://registry.npmjs.org/history/-/history-4.10.1.tgz" + "version" "4.10.1" dependencies: "@babel/runtime" "^7.1.2" - loose-envify "^1.2.0" - resolve-pathname "^3.0.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - value-equal "^1.0.1" + "loose-envify" "^1.2.0" + "resolve-pathname" "^3.0.0" + "tiny-invariant" "^1.0.2" + "tiny-warning" "^1.0.0" + "value-equal" "^1.0.1" -hoist-non-react-statics@^3.1.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== +"hoist-non-react-statics@^3.1.0": + "integrity" "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==" + "resolved" "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" + "version" "3.3.2" dependencies: - react-is "^16.7.0" + "react-is" "^16.7.0" -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= +"hpack.js@^2.1.6": + "integrity" "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=" + "resolved" "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz" + "version" "2.1.6" dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" + "inherits" "^2.0.1" + "obuf" "^1.0.0" + "readable-stream" "^2.0.1" + "wbuf" "^1.1.0" -html-entities@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.2.tgz#760b404685cb1d794e4f4b744332e3b00dcfe488" - integrity sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ== +"html-entities@^2.3.2": + "integrity" "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==" + "resolved" "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz" + "version" "2.3.2" -html-minifier-terser@^6.0.2: - version "6.1.0" - resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" - integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== +"html-minifier-terser@^6.0.2": + "integrity" "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==" + "resolved" "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + "version" "6.1.0" dependencies: - camel-case "^4.1.2" - clean-css "^5.2.2" - commander "^8.3.0" - he "^1.2.0" - param-case "^3.0.4" - relateurl "^0.2.7" - terser "^5.10.0" + "camel-case" "^4.1.2" + "clean-css" "^5.2.2" + "commander" "^8.3.0" + "he" "^1.2.0" + "param-case" "^3.0.4" + "relateurl" "^0.2.7" + "terser" "^5.10.0" -html-tags@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" - integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== +"html-tags@^3.1.0": + "integrity" "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==" + "resolved" "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz" + "version" "3.1.0" -html-void-elements@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" - integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== +"html-void-elements@^1.0.0": + "integrity" "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==" + "resolved" "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz" + "version" "1.0.5" -html-webpack-plugin@^5.4.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" - integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw== +"html-webpack-plugin@^5.4.0": + "integrity" "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==" + "resolved" "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz" + "version" "5.5.0" dependencies: "@types/html-minifier-terser" "^6.0.0" - html-minifier-terser "^6.0.2" - lodash "^4.17.21" - pretty-error "^4.0.0" - tapable "^2.0.0" + "html-minifier-terser" "^6.0.2" + "lodash" "^4.17.21" + "pretty-error" "^4.0.0" + "tapable" "^2.0.0" -htmlparser2@^3.9.1: - version "3.10.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" - integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== +"htmlparser2@^3.9.1": + "integrity" "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==" + "resolved" "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz" + "version" "3.10.1" dependencies: - domelementtype "^1.3.1" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^3.1.1" + "domelementtype" "^1.3.1" + "domhandler" "^2.3.0" + "domutils" "^1.5.1" + "entities" "^1.1.1" + "inherits" "^2.0.1" + "readable-stream" "^3.1.1" -htmlparser2@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" - integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== +"htmlparser2@^6.1.0": + "integrity" "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==" + "resolved" "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz" + "version" "6.1.0" dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.5.2" - entities "^2.0.0" + "domelementtype" "^2.0.1" + "domhandler" "^4.0.0" + "domutils" "^2.5.2" + "entities" "^2.0.0" -http-cache-semantics@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" - integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== +"http-cache-semantics@^4.0.0": + "integrity" "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + "resolved" "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz" + "version" "4.1.1" -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= +"http-deceiver@^1.2.7": + "integrity" "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + "resolved" "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" + "version" "1.2.7" -http-errors@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" - integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== +"http-errors@~1.6.2": + "integrity" "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=" + "resolved" "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + "version" "1.6.3" dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.1" + "depd" "~1.1.2" + "inherits" "2.0.3" + "setprototypeof" "1.1.0" + "statuses" ">= 1.4.0 < 2" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= +"http-errors@1.8.1": + "integrity" "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==" + "resolved" "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz" + "version" "1.8.1" dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" + "depd" "~1.1.2" + "inherits" "2.0.4" + "setprototypeof" "1.2.0" + "statuses" ">= 1.5.0 < 2" + "toidentifier" "1.0.1" -http-parser-js@>=0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.5.tgz#d7c30d5d3c90d865b4a2e870181f9d6f22ac7ac5" - integrity sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA== +"http-parser-js@>=0.5.1": + "integrity" "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==" + "resolved" "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz" + "version" "0.5.5" -http-proxy-middleware@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.3.tgz#5df04f69a89f530c2284cd71eeaa51ba52243289" - integrity sha512-1bloEwnrHMnCoO/Gcwbz7eSVvW50KPES01PecpagI+YLNLci4AcuKJrujW4Mc3sBLpFxMSlsLNHS5Nl/lvrTPA== +"http-proxy-middleware@^2.0.0": + "integrity" "sha512-1bloEwnrHMnCoO/Gcwbz7eSVvW50KPES01PecpagI+YLNLci4AcuKJrujW4Mc3sBLpFxMSlsLNHS5Nl/lvrTPA==" + "resolved" "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.3.tgz" + "version" "2.0.3" dependencies: "@types/http-proxy" "^1.17.8" - http-proxy "^1.18.1" - is-glob "^4.0.1" - is-plain-obj "^3.0.0" - micromatch "^4.0.2" + "http-proxy" "^1.18.1" + "is-glob" "^4.0.1" + "is-plain-obj" "^3.0.0" + "micromatch" "^4.0.2" -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== +"http-proxy@^1.18.1": + "integrity" "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==" + "resolved" "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz" + "version" "1.18.1" dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" + "eventemitter3" "^4.0.0" + "follow-redirects" "^1.0.0" + "requires-port" "^1.0.0" -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +"human-signals@^2.1.0": + "integrity" "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + "resolved" "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + "version" "2.1.0" -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== +"iconv-lite@0.4.24": + "integrity" "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==" + "resolved" "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + "version" "0.4.24" dependencies: - safer-buffer ">= 2.1.2 < 3" + "safer-buffer" ">= 2.1.2 < 3" -icss-utils@^5.0.0, icss-utils@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" - integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== +"icss-utils@^5.0.0", "icss-utils@^5.1.0": + "integrity" "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" + "resolved" "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" + "version" "5.1.0" -ignore@^5.1.9, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +"ignore@^5.1.9", "ignore@^5.2.0": + "integrity" "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + "resolved" "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" + "version" "5.2.0" -image-size@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.1.tgz#86d6cfc2b1d19eab5d2b368d4b9194d9e48541c5" - integrity sha512-VAwkvNSNGClRw9mDHhc5Efax8PLlsOGcUTh0T/LIriC8vPA3U5PdqXWqkz406MoYHMKW8Uf9gWr05T/rYB44kQ== +"image-size@^1.0.1": + "integrity" "sha512-VAwkvNSNGClRw9mDHhc5Efax8PLlsOGcUTh0T/LIriC8vPA3U5PdqXWqkz406MoYHMKW8Uf9gWr05T/rYB44kQ==" + "resolved" "https://registry.npmjs.org/image-size/-/image-size-1.0.1.tgz" + "version" "1.0.1" dependencies: - queue "6.0.2" + "queue" "6.0.2" -immer@^9.0.7: - version "9.0.12" - resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.12.tgz#2d33ddf3ee1d247deab9d707ca472c8c942a0f20" - integrity sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA== +"immer@^9.0.7": + "integrity" "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==" + "resolved" "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz" + "version" "9.0.12" -import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.2.2, import-fresh@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== +"import-fresh@^3.1.0", "import-fresh@^3.2.1", "import-fresh@^3.2.2", "import-fresh@^3.3.0": + "integrity" "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==" + "resolved" "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + "version" "3.3.0" dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" + "parent-module" "^1.0.0" + "resolve-from" "^4.0.0" -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= +"import-lazy@^2.1.0": + "integrity" "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + "resolved" "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz" + "version" "2.1.0" -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +"imurmurhash@^0.1.4": + "integrity" "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + "resolved" "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + "version" "0.1.4" -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== +"indent-string@^4.0.0": + "integrity" "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + "resolved" "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" + "version" "4.0.0" -infima@0.2.0-alpha.37: - version "0.2.0-alpha.37" - resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.37.tgz#b87ff42d528d6d050098a560f0294fbdd12adb78" - integrity sha512-4GX7Baw+/lwS4PPW/UJNY89tWSvYG1DL6baKVdpK6mC593iRgMssxNtORMTFArLPJ/A/lzsGhRmx+z6MaMxj0Q== +"infima@0.2.0-alpha.37": + "integrity" "sha512-4GX7Baw+/lwS4PPW/UJNY89tWSvYG1DL6baKVdpK6mC593iRgMssxNtORMTFArLPJ/A/lzsGhRmx+z6MaMxj0Q==" + "resolved" "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.37.tgz" + "version" "0.2.0-alpha.37" -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= +"inflight@^1.0.4": + "integrity" "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" + "resolved" "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + "version" "1.0.6" dependencies: - once "^1.3.0" - wrappy "1" + "once" "^1.3.0" + "wrappy" "1" -inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +"inherits@^2.0.0", "inherits@^2.0.1", "inherits@^2.0.3", "inherits@~2.0.3", "inherits@2", "inherits@2.0.4": + "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + "version" "2.0.4" -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +"inherits@2.0.3": + "integrity" "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "version" "2.0.3" -ini@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== +"ini@^1.3.5", "ini@~1.3.0": + "integrity" "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "resolved" "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + "version" "1.3.8" -ini@^1.3.5, ini@~1.3.0: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +"ini@2.0.0": + "integrity" "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" + "resolved" "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" + "version" "2.0.0" -inline-style-parser@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" - integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== +"inline-style-parser@0.1.1": + "integrity" "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + "resolved" "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz" + "version" "0.1.1" -interpret@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +"interpret@^1.0.0": + "integrity" "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + "resolved" "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" + "version" "1.4.0" -ip@^1.1.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= +"ip@^1.1.0": + "integrity" "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + "resolved" "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz" + "version" "1.1.5" -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +"ipaddr.js@^2.0.1": + "integrity" "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" + "resolved" "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz" + "version" "2.0.1" -ipaddr.js@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" - integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== +"ipaddr.js@1.9.1": + "integrity" "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "resolved" "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + "version" "1.9.1" -is-alphabetical@1.0.4, is-alphabetical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" - integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== +"is-alphabetical@^1.0.0", "is-alphabetical@1.0.4": + "integrity" "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" + "resolved" "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz" + "version" "1.0.4" -is-alphanumerical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" - integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== +"is-alphanumerical@^1.0.0": + "integrity" "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==" + "resolved" "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz" + "version" "1.0.4" dependencies: - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" + "is-alphabetical" "^1.0.0" + "is-decimal" "^1.0.0" -is-arguments@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== +"is-arguments@^1.0.4": + "integrity" "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==" + "resolved" "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" + "version" "1.1.1" dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" + "call-bind" "^1.0.2" + "has-tostringtag" "^1.0.0" -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +"is-arrayish@^0.2.1": + "integrity" "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "resolved" "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + "version" "0.2.1" -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== +"is-binary-path@~2.1.0": + "integrity" "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==" + "resolved" "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + "version" "2.1.0" dependencies: - binary-extensions "^2.0.0" + "binary-extensions" "^2.0.0" -is-buffer@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== +"is-buffer@^2.0.0": + "integrity" "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + "resolved" "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" + "version" "2.0.5" -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== +"is-ci@^2.0.0": + "integrity" "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==" + "resolved" "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz" + "version" "2.0.0" dependencies: - ci-info "^2.0.0" + "ci-info" "^2.0.0" -is-core-module@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== +"is-core-module@^2.8.1": + "integrity" "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==" + "resolved" "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz" + "version" "2.8.1" dependencies: - has "^1.0.3" + "has" "^1.0.3" -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== +"is-date-object@^1.0.1": + "integrity" "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==" + "resolved" "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + "version" "1.0.5" dependencies: - has-tostringtag "^1.0.0" + "has-tostringtag" "^1.0.0" -is-decimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" - integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== +"is-decimal@^1.0.0": + "integrity" "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" + "resolved" "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz" + "version" "1.0.4" -is-docker@^2.0.0, is-docker@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +"is-docker@^2.0.0", "is-docker@^2.1.1": + "integrity" "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + "resolved" "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" + "version" "2.2.1" -is-extendable@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= +"is-extendable@^0.1.0": + "integrity" "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + "resolved" "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + "version" "0.1.1" -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +"is-extglob@^2.1.1": + "integrity" "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + "version" "2.1.1" -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +"is-fullwidth-code-point@^3.0.0": + "integrity" "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "resolved" "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + "version" "3.0.0" -is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== +"is-glob@^4.0.1", "is-glob@^4.0.3", "is-glob@~4.0.1": + "integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==" + "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + "version" "4.0.3" dependencies: - is-extglob "^2.1.1" + "is-extglob" "^2.1.1" -is-hexadecimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" - integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== +"is-hexadecimal@^1.0.0": + "integrity" "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" + "resolved" "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz" + "version" "1.0.4" -is-installed-globally@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" - integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== +"is-installed-globally@^0.4.0": + "integrity" "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==" + "resolved" "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz" + "version" "0.4.0" dependencies: - global-dirs "^3.0.0" - is-path-inside "^3.0.2" + "global-dirs" "^3.0.0" + "is-path-inside" "^3.0.2" -is-npm@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" - integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== +"is-npm@^5.0.0": + "integrity" "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==" + "resolved" "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz" + "version" "5.0.0" -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +"is-number@^7.0.0": + "integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + "version" "7.0.0" -is-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= +"is-obj@^1.0.1": + "integrity" "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + "resolved" "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz" + "version" "1.0.1" -is-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" - integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +"is-obj@^2.0.0": + "integrity" "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + "resolved" "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" + "version" "2.0.0" -is-path-cwd@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== +"is-path-cwd@^2.2.0": + "integrity" "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + "resolved" "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz" + "version" "2.2.0" -is-path-inside@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +"is-path-inside@^3.0.2": + "integrity" "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + "resolved" "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" + "version" "3.0.3" -is-plain-obj@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +"is-plain-obj@^2.0.0": + "integrity" "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" + "resolved" "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" + "version" "2.1.0" -is-plain-obj@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" - integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== +"is-plain-obj@^3.0.0": + "integrity" "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + "resolved" "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz" + "version" "3.0.0" -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== +"is-plain-object@^2.0.4": + "integrity" "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==" + "resolved" "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + "version" "2.0.4" dependencies: - isobject "^3.0.1" + "isobject" "^3.0.1" -is-regex@^1.0.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== +"is-regex@^1.0.4": + "integrity" "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==" + "resolved" "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + "version" "1.1.4" dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" + "call-bind" "^1.0.2" + "has-tostringtag" "^1.0.0" -is-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" - integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= +"is-regexp@^1.0.0": + "integrity" "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" + "resolved" "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz" + "version" "1.0.0" -is-root@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" - integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== +"is-root@^2.1.0": + "integrity" "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + "resolved" "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz" + "version" "2.1.0" -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +"is-stream@^2.0.0": + "integrity" "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + "resolved" "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + "version" "2.0.1" -is-typedarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +"is-typedarray@^1.0.0": + "integrity" "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "resolved" "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + "version" "1.0.0" -is-whitespace-character@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" - integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== +"is-whitespace-character@^1.0.0": + "integrity" "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==" + "resolved" "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz" + "version" "1.0.4" -is-word-character@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" - integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== +"is-word-character@^1.0.0": + "integrity" "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==" + "resolved" "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz" + "version" "1.0.4" -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== +"is-wsl@^2.2.0": + "integrity" "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==" + "resolved" "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + "version" "2.2.0" dependencies: - is-docker "^2.0.0" + "is-docker" "^2.0.0" -is-yarn-global@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" - integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== +"is-yarn-global@^0.3.0": + "integrity" "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + "resolved" "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz" + "version" "0.3.0" -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= +"isarray@~1.0.0": + "integrity" "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "resolved" "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "version" "1.0.0" -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +"isarray@0.0.1": + "integrity" "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "resolved" "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + "version" "0.0.1" -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +"isexe@^2.0.0": + "integrity" "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "resolved" "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + "version" "2.0.0" -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +"isobject@^3.0.1": + "integrity" "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "resolved" "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + "version" "3.0.1" -jest-worker@^27.0.2, jest-worker@^27.4.5: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== +"jest-worker@^27.0.2", "jest-worker@^27.4.5": + "integrity" "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==" + "resolved" "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" + "version" "27.5.1" dependencies: "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" + "merge-stream" "^2.0.0" + "supports-color" "^8.0.0" -joi@^17.4.2, joi@^17.6.0: - version "17.6.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" - integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw== +"joi@^17.4.2", "joi@^17.6.0": + "integrity" "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==" + "resolved" "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz" + "version" "17.6.0" dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -4684,2834 +4702,2879 @@ joi@^17.4.2, joi@^17.6.0: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +"js-tokens@^3.0.0 || ^4.0.0", "js-tokens@^4.0.0": + "integrity" "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "resolved" "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + "version" "4.0.0" -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== +"js-yaml@^3.13.1": + "integrity" "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==" + "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + "version" "3.14.1" dependencies: - argparse "^1.0.7" - esprima "^4.0.0" + "argparse" "^1.0.7" + "esprima" "^4.0.0" -js-yaml@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== +"js-yaml@^4.0.0": + "integrity" "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==" + "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + "version" "4.1.0" dependencies: - argparse "^2.0.1" + "argparse" "^2.0.1" -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +"jsesc@^2.5.1": + "integrity" "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + "resolved" "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" + "version" "2.5.2" -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= +"jsesc@~0.5.0": + "integrity" "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + "resolved" "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + "version" "0.5.0" -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +"json-buffer@3.0.0": + "integrity" "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + "resolved" "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" + "version" "3.0.0" -json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +"json-parse-better-errors@^1.0.2": + "integrity" "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + "resolved" "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" + "version" "1.0.2" -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +"json-parse-even-better-errors@^2.3.0": + "integrity" "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "resolved" "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + "version" "2.3.1" -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +"json-schema-traverse@^0.4.1": + "integrity" "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + "version" "0.4.1" -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +"json-schema-traverse@^1.0.0": + "integrity" "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + "version" "1.0.0" -json5@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== +"json5@^1.0.1": + "integrity" "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==" + "resolved" "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" + "version" "1.0.2" dependencies: - minimist "^1.2.0" + "minimist" "^1.2.0" -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" +"json5@^2.1.2", "json5@^2.2.2": + "integrity" "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + "resolved" "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + "version" "2.2.3" -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== +"jsonfile@^6.0.1": + "integrity" "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==" + "resolved" "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" + "version" "6.1.0" dependencies: - universalify "^2.0.0" + "universalify" "^2.0.0" optionalDependencies: - graceful-fs "^4.1.6" + "graceful-fs" "^4.1.6" -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== +"keyv@^3.0.0": + "integrity" "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==" + "resolved" "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz" + "version" "3.1.0" dependencies: - json-buffer "3.0.0" + "json-buffer" "3.0.0" -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +"kind-of@^6.0.0", "kind-of@^6.0.2": + "integrity" "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + "resolved" "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + "version" "6.0.3" -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +"kleur@^3.0.3": + "integrity" "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + "resolved" "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" + "version" "3.0.3" -klona@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" - integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== +"klona@^2.0.5": + "integrity" "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" + "resolved" "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz" + "version" "2.0.5" -latest-version@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" - integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== +"latest-version@^5.1.0": + "integrity" "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==" + "resolved" "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz" + "version" "5.1.0" dependencies: - package-json "^6.3.0" + "package-json" "^6.3.0" -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +"leven@^3.1.0": + "integrity" "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + "resolved" "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" + "version" "3.1.0" -lilconfig@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" - integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== +"lilconfig@^2.0.3": + "integrity" "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==" + "resolved" "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz" + "version" "2.0.4" -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +"lines-and-columns@^1.1.6": + "integrity" "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "resolved" "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + "version" "1.2.4" -loader-runner@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" - integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== +"loader-runner@^4.2.0": + "integrity" "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==" + "resolved" "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz" + "version" "4.2.0" -loader-utils@^1.4.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" - integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== +"loader-utils@^1.4.0": + "integrity" "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==" + "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz" + "version" "1.4.2" dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" + "big.js" "^5.2.2" + "emojis-list" "^3.0.0" + "json5" "^1.0.1" -loader-utils@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" - integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== +"loader-utils@^2.0.0": + "integrity" "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==" + "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz" + "version" "2.0.2" dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" + "big.js" "^5.2.2" + "emojis-list" "^3.0.0" + "json5" "^2.1.2" -loader-utils@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.0.tgz#bcecc51a7898bee7473d4bc6b845b23af8304d4f" - integrity sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ== +"loader-utils@^3.2.0": + "integrity" "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==" + "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz" + "version" "3.2.0" -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== +"locate-path@^3.0.0": + "integrity" "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" + "version" "3.0.0" dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" + "p-locate" "^3.0.0" + "path-exists" "^3.0.0" -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== +"locate-path@^5.0.0": + "integrity" "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + "version" "5.0.0" dependencies: - p-locate "^4.1.0" + "p-locate" "^4.1.0" -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== +"locate-path@^6.0.0": + "integrity" "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + "version" "6.0.0" dependencies: - p-locate "^5.0.0" + "p-locate" "^5.0.0" -lodash.assignin@^4.0.9: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" - integrity sha1-uo31+4QesKPoBEIysOJjqNxqKKI= +"lodash.assignin@^4.0.9": + "integrity" "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" + "resolved" "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz" + "version" "4.2.0" -lodash.bind@^4.1.4: - version "4.2.1" - resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" - integrity sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU= +"lodash.bind@^4.1.4": + "integrity" "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" + "resolved" "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz" + "version" "4.2.1" -lodash.curry@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" - integrity sha1-JI42By7ekGUB11lmIAqG2riyMXA= +"lodash.curry@^4.0.1": + "integrity" "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" + "resolved" "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz" + "version" "4.1.1" -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= +"lodash.debounce@^4.0.8": + "integrity" "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "resolved" "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" + "version" "4.0.8" -lodash.defaults@^4.0.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= +"lodash.defaults@^4.0.1": + "integrity" "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + "resolved" "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" + "version" "4.2.0" -lodash.filter@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" - integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4= +"lodash.filter@^4.4.0": + "integrity" "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" + "resolved" "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz" + "version" "4.6.0" -lodash.flatten@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" - integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= +"lodash.flatten@^4.2.0": + "integrity" "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + "resolved" "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" + "version" "4.4.0" -lodash.flow@^3.3.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a" - integrity sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o= +"lodash.flow@^3.3.0": + "integrity" "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" + "resolved" "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz" + "version" "3.5.0" -lodash.foreach@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" - integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= +"lodash.foreach@^4.3.0": + "integrity" "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + "resolved" "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz" + "version" "4.5.0" -lodash.map@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" - integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM= +"lodash.map@^4.4.0": + "integrity" "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" + "resolved" "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz" + "version" "4.6.0" -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +"lodash.memoize@^4.1.2": + "integrity" "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + "resolved" "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" + "version" "4.1.2" -lodash.merge@^4.4.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +"lodash.merge@^4.4.0": + "integrity" "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "resolved" "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + "version" "4.6.2" -lodash.pick@^4.2.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" - integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= +"lodash.pick@^4.2.1": + "integrity" "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" + "resolved" "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz" + "version" "4.4.0" -lodash.reduce@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" - integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs= +"lodash.reduce@^4.4.0": + "integrity" "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + "resolved" "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz" + "version" "4.6.0" -lodash.reject@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" - integrity sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU= +"lodash.reject@^4.4.0": + "integrity" "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" + "resolved" "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz" + "version" "4.6.0" -lodash.some@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" - integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= +"lodash.some@^4.4.0": + "integrity" "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" + "resolved" "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz" + "version" "4.6.0" -lodash.uniq@4.5.0, lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= +"lodash.uniq@^4.5.0", "lodash.uniq@4.5.0": + "integrity" "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + "resolved" "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" + "version" "4.5.0" -lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +"lodash@^4.17.14", "lodash@^4.17.19", "lodash@^4.17.20", "lodash@^4.17.21": + "integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "resolved" "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + "version" "4.17.21" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== +"loose-envify@^1.0.0", "loose-envify@^1.1.0", "loose-envify@^1.2.0", "loose-envify@^1.3.1", "loose-envify@^1.4.0": + "integrity" "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==" + "resolved" "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" + "version" "1.4.0" dependencies: - js-tokens "^3.0.0 || ^4.0.0" + "js-tokens" "^3.0.0 || ^4.0.0" -lower-case@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" - integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== +"lower-case@^2.0.2": + "integrity" "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==" + "resolved" "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" + "version" "2.0.2" dependencies: - tslib "^2.0.3" + "tslib" "^2.0.3" -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== +"lowercase-keys@^1.0.0", "lowercase-keys@^1.0.1": + "integrity" "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + "resolved" "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" + "version" "1.0.1" -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +"lowercase-keys@^2.0.0": + "integrity" "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + "resolved" "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" + "version" "2.0.0" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== +"lru-cache@^5.1.1": + "integrity" "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==" + "resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + "version" "5.1.1" dependencies: - yallist "^4.0.0" + "yallist" "^3.0.2" -magic-string@^0.25.3: - version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" - integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== +"lru-cache@^6.0.0": + "integrity" "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==" + "resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + "version" "6.0.0" dependencies: - sourcemap-codec "^1.4.4" + "yallist" "^4.0.0" -make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== +"magic-string@^0.25.3": + "integrity" "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==" + "resolved" "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz" + "version" "0.25.7" dependencies: - semver "^6.0.0" + "sourcemap-codec" "^1.4.4" -markdown-escapes@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" - integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== - -mdast-squeeze-paragraphs@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" - integrity sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ== +"make-dir@^3.0.0", "make-dir@^3.0.2", "make-dir@^3.1.0": + "integrity" "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==" + "resolved" "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" + "version" "3.1.0" dependencies: - unist-util-remove "^2.0.0" + "semver" "^6.0.0" -mdast-util-definitions@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" - integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== +"markdown-escapes@^1.0.0": + "integrity" "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==" + "resolved" "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz" + "version" "1.0.4" + +"mdast-squeeze-paragraphs@^4.0.0": + "integrity" "sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==" + "resolved" "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz" + "version" "4.0.0" dependencies: - unist-util-visit "^2.0.0" + "unist-util-remove" "^2.0.0" -mdast-util-to-hast@10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" - integrity sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA== +"mdast-util-definitions@^4.0.0": + "integrity" "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==" + "resolved" "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "unist-util-visit" "^2.0.0" + +"mdast-util-to-hast@10.0.1": + "integrity" "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==" + "resolved" "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz" + "version" "10.0.1" dependencies: "@types/mdast" "^3.0.0" "@types/unist" "^2.0.0" - mdast-util-definitions "^4.0.0" - mdurl "^1.0.0" - unist-builder "^2.0.0" - unist-util-generated "^1.0.0" - unist-util-position "^3.0.0" - unist-util-visit "^2.0.0" + "mdast-util-definitions" "^4.0.0" + "mdurl" "^1.0.0" + "unist-builder" "^2.0.0" + "unist-util-generated" "^1.0.0" + "unist-util-position" "^3.0.0" + "unist-util-visit" "^2.0.0" -mdast-util-to-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" - integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== +"mdast-util-to-string@^2.0.0": + "integrity" "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==" + "resolved" "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz" + "version" "2.0.0" -mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== +"mdn-data@2.0.14": + "integrity" "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + "resolved" "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz" + "version" "2.0.14" -mdurl@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= +"mdurl@^1.0.0": + "integrity" "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" + "resolved" "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz" + "version" "1.0.1" -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +"media-typer@0.3.0": + "integrity" "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "resolved" "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + "version" "0.3.0" -memfs@^3.1.2, memfs@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.1.tgz#b78092f466a0dce054d63d39275b24c71d3f1305" - integrity sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw== +"memfs@^3.1.2", "memfs@^3.4.1": + "integrity" "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==" + "resolved" "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz" + "version" "3.4.1" dependencies: - fs-monkey "1.0.3" + "fs-monkey" "1.0.3" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= +"merge-descriptors@1.0.1": + "integrity" "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "resolved" "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" + "version" "1.0.1" -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +"merge-stream@^2.0.0": + "integrity" "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "resolved" "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + "version" "2.0.0" -merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +"merge2@^1.3.0", "merge2@^1.4.1": + "integrity" "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + "resolved" "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + "version" "1.4.1" -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= +"methods@~1.1.2": + "integrity" "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "resolved" "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + "version" "1.1.2" -micromatch@^4.0.2, micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== +"micromatch@^4.0.2", "micromatch@^4.0.4": + "integrity" "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==" + "resolved" "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz" + "version" "4.0.4" dependencies: - braces "^3.0.1" - picomatch "^2.2.3" + "braces" "^3.0.1" + "picomatch" "^2.2.3" -mime-db@1.51.0, "mime-db@>= 1.43.0 < 2": - version "1.51.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" - integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== +"mime-db@>= 1.43.0 < 2", "mime-db@1.51.0": + "integrity" "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz" + "version" "1.51.0" -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== +"mime-db@~1.33.0": + "integrity" "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz" + "version" "1.33.0" -mime-types@2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== +"mime-types@^2.1.27", "mime-types@^2.1.31", "mime-types@~2.1.17", "mime-types@~2.1.24", "mime-types@~2.1.34": + "integrity" "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==" + "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz" + "version" "2.1.34" dependencies: - mime-db "~1.33.0" + "mime-db" "1.51.0" -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.34" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" - integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== +"mime-types@2.1.18": + "integrity" "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==" + "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz" + "version" "2.1.18" dependencies: - mime-db "1.51.0" + "mime-db" "~1.33.0" -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +"mime@1.6.0": + "integrity" "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "resolved" "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + "version" "1.6.0" -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +"mimic-fn@^2.1.0": + "integrity" "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + "resolved" "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + "version" "2.1.0" -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +"mimic-response@^1.0.0", "mimic-response@^1.0.1": + "integrity" "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + "resolved" "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" + "version" "1.0.1" -mini-create-react-context@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" - integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== +"mini-create-react-context@^0.4.0": + "integrity" "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==" + "resolved" "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz" + "version" "0.4.1" dependencies: "@babel/runtime" "^7.12.1" - tiny-warning "^1.0.3" + "tiny-warning" "^1.0.3" -mini-css-extract-plugin@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz#83172b4fd812f8fc4a09d6f6d16f924f53990ca8" - integrity sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q== +"mini-css-extract-plugin@^1.6.0": + "integrity" "sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==" + "resolved" "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz" + "version" "1.6.2" dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - webpack-sources "^1.1.0" + "loader-utils" "^2.0.0" + "schema-utils" "^3.0.0" + "webpack-sources" "^1.1.0" -minimalistic-assert@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== +"minimalistic-assert@^1.0.0": + "integrity" "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + "resolved" "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + "version" "1.0.1" -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +"minimatch@^3.0.4": + "integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + "version" "3.1.2" dependencies: - brace-expansion "^1.1.7" + "brace-expansion" "^1.1.7" -minimatch@^3.0.4: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== +"minimatch@3.0.4": + "integrity" "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + "version" "3.0.4" dependencies: - brace-expansion "^1.1.7" + "brace-expansion" "^1.1.7" -minimist@^1.2.0, minimist@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +"minimist@^1.2.0", "minimist@^1.2.5": + "integrity" "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" + "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz" + "version" "1.2.7" -mkdirp@^0.5.5: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== +"mkdirp@^0.5.5": + "integrity" "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==" + "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" + "version" "0.5.5" dependencies: - minimist "^1.2.5" + "minimist" "^1.2.5" -mrmime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.0.tgz#14d387f0585a5233d291baba339b063752a2398b" - integrity sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ== +"mrmime@^1.0.0": + "integrity" "sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ==" + "resolved" "https://registry.npmjs.org/mrmime/-/mrmime-1.0.0.tgz" + "version" "1.0.0" -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +"ms@^2.1.1", "ms@2.1.3": + "integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + "version" "2.1.3" -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +"ms@2.0.0": + "integrity" "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + "version" "2.0.0" -ms@2.1.3, ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +"ms@2.1.2": + "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + "version" "2.1.2" -multicast-dns-service-types@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" - integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= +"multicast-dns-service-types@^1.1.0": + "integrity" "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" + "resolved" "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz" + "version" "1.1.0" -multicast-dns@^6.0.1: - version "6.2.3" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" - integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== +"multicast-dns@^6.0.1": + "integrity" "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==" + "resolved" "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz" + "version" "6.2.3" dependencies: - dns-packet "^1.3.1" - thunky "^1.0.2" + "dns-packet" "^1.3.1" + "thunky" "^1.0.2" -nanoid@^3.2.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" - integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== +"nanoid@^3.2.0": + "integrity" "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==" + "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz" + "version" "3.3.1" -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +"negotiator@0.6.3": + "integrity" "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + "resolved" "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" + "version" "0.6.3" -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +"neo-async@^2.6.2": + "integrity" "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + "resolved" "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + "version" "2.6.2" -no-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" - integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== +"no-case@^3.0.4": + "integrity" "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==" + "resolved" "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" + "version" "3.0.4" dependencies: - lower-case "^2.0.2" - tslib "^2.0.3" + "lower-case" "^2.0.2" + "tslib" "^2.0.3" -node-emoji@^1.10.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" - integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== +"node-emoji@^1.10.0": + "integrity" "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==" + "resolved" "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz" + "version" "1.11.0" dependencies: - lodash "^4.17.21" + "lodash" "^4.17.21" -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== +"node-fetch@2.6.7": + "integrity" "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==" + "resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" + "version" "2.6.7" dependencies: - whatwg-url "^5.0.0" + "whatwg-url" "^5.0.0" -node-forge@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.0.tgz#37a874ea723855f37db091e6c186e5b67a01d4b2" - integrity sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA== +"node-forge@^1.2.0": + "integrity" "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==" + "resolved" "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz" + "version" "1.3.0" -node-releases@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" - integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== +"node-releases@^2.0.8": + "integrity" "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" + "resolved" "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz" + "version" "2.0.10" -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +"normalize-path@^3.0.0", "normalize-path@~3.0.0": + "integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + "version" "3.0.0" -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +"normalize-range@^0.1.2": + "integrity" "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + "resolved" "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" + "version" "0.1.2" -normalize-url@^4.1.0: - version "4.5.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" - integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== +"normalize-url@^4.1.0": + "integrity" "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + "resolved" "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz" + "version" "4.5.1" -normalize-url@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== +"normalize-url@^6.0.1": + "integrity" "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + "resolved" "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz" + "version" "6.1.0" -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== +"npm-run-path@^4.0.1": + "integrity" "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==" + "resolved" "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + "version" "4.0.1" dependencies: - path-key "^3.0.0" + "path-key" "^3.0.0" -nprogress@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" - integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E= +"nprogress@^0.2.0": + "integrity" "sha1-y480xTIT2JVyP8urkH6UIq28r7E=" + "resolved" "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz" + "version" "0.2.0" -nth-check@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" - integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== +"nth-check@^2.0.1": + "integrity" "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==" + "resolved" "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz" + "version" "2.0.1" dependencies: - boolbase "^1.0.0" + "boolbase" "^1.0.0" -nth-check@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== +"nth-check@~1.0.1": + "integrity" "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==" + "resolved" "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz" + "version" "1.0.2" dependencies: - boolbase "~1.0.0" + "boolbase" "~1.0.0" -object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +"object-assign@^4.1.0", "object-assign@^4.1.1": + "integrity" "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "resolved" "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + "version" "4.1.1" -object-is@^1.0.1: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== +"object-is@^1.0.1": + "integrity" "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==" + "resolved" "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" + "version" "1.1.5" dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" -object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +"object-keys@^1.0.12", "object-keys@^1.1.1": + "integrity" "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + "resolved" "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + "version" "1.1.1" -object.assign@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== +"object.assign@^4.1.0": + "integrity" "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==" + "resolved" "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" + "version" "4.1.2" dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" + "call-bind" "^1.0.0" + "define-properties" "^1.1.3" + "has-symbols" "^1.0.1" + "object-keys" "^1.1.1" -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== +"obuf@^1.0.0", "obuf@^1.1.2": + "integrity" "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + "resolved" "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" + "version" "1.1.2" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= +"on-finished@~2.3.0": + "integrity" "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=" + "resolved" "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" + "version" "2.3.0" dependencies: - ee-first "1.1.1" + "ee-first" "1.1.1" -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== +"on-headers@~1.0.2": + "integrity" "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + "resolved" "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" + "version" "1.0.2" -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= +"once@^1.3.0", "once@^1.3.1", "once@^1.4.0": + "integrity" "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" + "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + "version" "1.4.0" dependencies: - wrappy "1" + "wrappy" "1" -onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== +"onetime@^5.1.2": + "integrity" "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==" + "resolved" "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + "version" "5.1.2" dependencies: - mimic-fn "^2.1.0" + "mimic-fn" "^2.1.0" -open@^8.0.9, open@^8.4.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" - integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== +"open@^8.0.9", "open@^8.4.0": + "integrity" "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==" + "resolved" "https://registry.npmjs.org/open/-/open-8.4.0.tgz" + "version" "8.4.0" dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" + "define-lazy-prop" "^2.0.0" + "is-docker" "^2.1.1" + "is-wsl" "^2.2.0" -opener@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" - integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== +"opener@^1.5.2": + "integrity" "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" + "resolved" "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz" + "version" "1.5.2" -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +"p-cancelable@^1.0.0": + "integrity" "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + "resolved" "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz" + "version" "1.1.0" -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== +"p-limit@^2.0.0", "p-limit@^2.2.0": + "integrity" "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==" + "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + "version" "2.3.0" dependencies: - p-try "^2.0.0" + "p-try" "^2.0.0" -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== +"p-limit@^3.0.2": + "integrity" "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==" + "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + "version" "3.1.0" dependencies: - yocto-queue "^0.1.0" + "yocto-queue" "^0.1.0" -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== +"p-locate@^3.0.0": + "integrity" "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" + "version" "3.0.0" dependencies: - p-limit "^2.0.0" + "p-limit" "^2.0.0" -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== +"p-locate@^4.1.0": + "integrity" "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + "version" "4.1.0" dependencies: - p-limit "^2.2.0" + "p-limit" "^2.2.0" -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== +"p-locate@^5.0.0": + "integrity" "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + "version" "5.0.0" dependencies: - p-limit "^3.0.2" + "p-limit" "^3.0.2" -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== +"p-map@^4.0.0": + "integrity" "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==" + "resolved" "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" + "version" "4.0.0" dependencies: - aggregate-error "^3.0.0" + "aggregate-error" "^3.0.0" -p-retry@^4.5.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.1.tgz#8fcddd5cdf7a67a0911a9cf2ef0e5df7f602316c" - integrity sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA== +"p-retry@^4.5.0": + "integrity" "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==" + "resolved" "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz" + "version" "4.6.1" dependencies: "@types/retry" "^0.12.0" - retry "^0.13.1" + "retry" "^0.13.1" -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +"p-try@^2.0.0": + "integrity" "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + "resolved" "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + "version" "2.2.0" -package-json@^6.3.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" - integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== +"package-json@^6.3.0": + "integrity" "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==" + "resolved" "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz" + "version" "6.5.0" dependencies: - got "^9.6.0" - registry-auth-token "^4.0.0" - registry-url "^5.0.0" - semver "^6.2.0" + "got" "^9.6.0" + "registry-auth-token" "^4.0.0" + "registry-url" "^5.0.0" + "semver" "^6.2.0" -param-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" - integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== +"param-case@^3.0.4": + "integrity" "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==" + "resolved" "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" + "version" "3.0.4" dependencies: - dot-case "^3.0.4" - tslib "^2.0.3" + "dot-case" "^3.0.4" + "tslib" "^2.0.3" -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== +"parent-module@^1.0.0": + "integrity" "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==" + "resolved" "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + "version" "1.0.1" dependencies: - callsites "^3.0.0" + "callsites" "^3.0.0" -parse-entities@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" - integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== +"parse-entities@^2.0.0": + "integrity" "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==" + "resolved" "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz" + "version" "2.0.0" dependencies: - character-entities "^1.0.0" - character-entities-legacy "^1.0.0" - character-reference-invalid "^1.0.0" - is-alphanumerical "^1.0.0" - is-decimal "^1.0.0" - is-hexadecimal "^1.0.0" + "character-entities" "^1.0.0" + "character-entities-legacy" "^1.0.0" + "character-reference-invalid" "^1.0.0" + "is-alphanumerical" "^1.0.0" + "is-decimal" "^1.0.0" + "is-hexadecimal" "^1.0.0" -parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== +"parse-json@^5.0.0": + "integrity" "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==" + "resolved" "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + "version" "5.2.0" dependencies: "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" + "error-ex" "^1.3.1" + "json-parse-even-better-errors" "^2.3.0" + "lines-and-columns" "^1.1.6" -parse-numeric-range@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz#7c63b61190d61e4d53a1197f0c83c47bb670ffa3" - integrity sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ== +"parse-numeric-range@^1.3.0": + "integrity" "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" + "resolved" "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz" + "version" "1.3.0" -parse5-htmlparser2-tree-adapter@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" - integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== +"parse5-htmlparser2-tree-adapter@^6.0.1": + "integrity" "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==" + "resolved" "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz" + "version" "6.0.1" dependencies: - parse5 "^6.0.1" + "parse5" "^6.0.1" -parse5@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +"parse5@^5.0.0": + "integrity" "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + "resolved" "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz" + "version" "5.1.1" -parse5@^6.0.0, parse5@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +"parse5@^6.0.0", "parse5@^6.0.1": + "integrity" "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + "resolved" "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" + "version" "6.0.1" -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +"parseurl@~1.3.2", "parseurl@~1.3.3": + "integrity" "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "resolved" "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" + "version" "1.3.3" -pascal-case@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" - integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== +"pascal-case@^3.1.2": + "integrity" "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==" + "resolved" "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz" + "version" "3.1.2" dependencies: - no-case "^3.0.4" - tslib "^2.0.3" + "no-case" "^3.0.4" + "tslib" "^2.0.3" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= +"path-exists@^3.0.0": + "integrity" "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" + "version" "3.0.0" -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== +"path-exists@^4.0.0": + "integrity" "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + "version" "4.0.0" -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +"path-is-absolute@^1.0.0": + "integrity" "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "resolved" "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "version" "1.0.1" -path-is-inside@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= +"path-is-inside@1.0.2": + "integrity" "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + "resolved" "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz" + "version" "1.0.2" -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +"path-key@^3.0.0", "path-key@^3.1.0": + "integrity" "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + "resolved" "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + "version" "3.1.1" -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +"path-parse@^1.0.7": + "integrity" "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "resolved" "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + "version" "1.0.7" -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path-to-regexp@2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" - integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== - -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== +"path-to-regexp@^1.7.0": + "integrity" "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==" + "resolved" "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz" + "version" "1.8.0" dependencies: - isarray "0.0.1" + "isarray" "0.0.1" -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +"path-to-regexp@0.1.7": + "integrity" "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "resolved" "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + "version" "0.1.7" -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +"path-to-regexp@2.2.1": + "integrity" "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" + "resolved" "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz" + "version" "2.2.1" -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +"path-type@^4.0.0": + "integrity" "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + "resolved" "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + "version" "4.0.0" -pkg-dir@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== +"picocolors@^1.0.0": + "integrity" "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "resolved" "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + "version" "1.0.0" + +"picomatch@^2.0.4", "picomatch@^2.2.1", "picomatch@^2.2.3": + "integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + "version" "2.3.1" + +"pkg-dir@^4.1.0": + "integrity" "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==" + "resolved" "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + "version" "4.2.0" dependencies: - find-up "^4.0.0" + "find-up" "^4.0.0" -pkg-up@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" - integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== +"pkg-up@^3.1.0": + "integrity" "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==" + "resolved" "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz" + "version" "3.1.0" dependencies: - find-up "^3.0.0" + "find-up" "^3.0.0" -portfinder@^1.0.28: - version "1.0.28" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" - integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== +"portfinder@^1.0.28": + "integrity" "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==" + "resolved" "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz" + "version" "1.0.28" dependencies: - async "^2.6.2" - debug "^3.1.1" - mkdirp "^0.5.5" + "async" "^2.6.2" + "debug" "^3.1.1" + "mkdirp" "^0.5.5" -postcss-calc@^8.2.0: - version "8.2.4" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" - integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== +"postcss-calc@^8.2.0": + "integrity" "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==" + "resolved" "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz" + "version" "8.2.4" dependencies: - postcss-selector-parser "^6.0.9" - postcss-value-parser "^4.2.0" + "postcss-selector-parser" "^6.0.9" + "postcss-value-parser" "^4.2.0" -postcss-colormin@^5.2.5: - version "5.2.5" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.2.5.tgz#d1fc269ac2ad03fe641d462b5d1dada35c69968a" - integrity sha512-+X30aDaGYq81mFqwyPpnYInsZQnNpdxMX0ajlY7AExCexEFkPVV+KrO7kXwayqEWL2xwEbNQ4nUO0ZsRWGnevg== +"postcss-colormin@^5.2.5": + "integrity" "sha512-+X30aDaGYq81mFqwyPpnYInsZQnNpdxMX0ajlY7AExCexEFkPVV+KrO7kXwayqEWL2xwEbNQ4nUO0ZsRWGnevg==" + "resolved" "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.5.tgz" + "version" "5.2.5" dependencies: - browserslist "^4.16.6" - caniuse-api "^3.0.0" - colord "^2.9.1" - postcss-value-parser "^4.2.0" + "browserslist" "^4.16.6" + "caniuse-api" "^3.0.0" + "colord" "^2.9.1" + "postcss-value-parser" "^4.2.0" -postcss-convert-values@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.0.4.tgz#3e74dd97c581f475ae7b4500bc0a7c4fb3a6b1b6" - integrity sha512-bugzSAyjIexdObovsPZu/sBCTHccImJxLyFgeV0MmNBm/Lw5h5XnjfML6gzEmJ3A6nyfCW7hb1JXzcsA4Zfbdw== +"postcss-convert-values@^5.0.4": + "integrity" "sha512-bugzSAyjIexdObovsPZu/sBCTHccImJxLyFgeV0MmNBm/Lw5h5XnjfML6gzEmJ3A6nyfCW7hb1JXzcsA4Zfbdw==" + "resolved" "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-discard-comments@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.0.3.tgz#011acb63418d600fdbe18804e1bbecb543ad2f87" - integrity sha512-6W5BemziRoqIdAKT+1QjM4bNcJAQ7z7zk073730NHg4cUXh3/rQHHj7pmYxUB9aGhuRhBiUf0pXvIHkRwhQP0Q== +"postcss-discard-comments@^5.0.3": + "integrity" "sha512-6W5BemziRoqIdAKT+1QjM4bNcJAQ7z7zk073730NHg4cUXh3/rQHHj7pmYxUB9aGhuRhBiUf0pXvIHkRwhQP0Q==" + "resolved" "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.3.tgz" + "version" "5.0.3" -postcss-discard-duplicates@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.3.tgz#10f202a4cfe9d407b73dfea7a477054d21ea0c1f" - integrity sha512-vPtm1Mf+kp7iAENTG7jI1MN1lk+fBqL5y+qxyi4v3H+lzsXEdfS3dwUZD45KVhgzDEgduur8ycB4hMegyMTeRw== +"postcss-discard-duplicates@^5.0.3": + "integrity" "sha512-vPtm1Mf+kp7iAENTG7jI1MN1lk+fBqL5y+qxyi4v3H+lzsXEdfS3dwUZD45KVhgzDEgduur8ycB4hMegyMTeRw==" + "resolved" "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.3.tgz" + "version" "5.0.3" -postcss-discard-empty@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.0.3.tgz#ec185af4a3710b88933b0ff751aa157b6041dd6a" - integrity sha512-xGJugpaXKakwKI7sSdZjUuN4V3zSzb2Y0LOlmTajFbNinEjTfVs9PFW2lmKBaC/E64WwYppfqLD03P8l9BuueA== +"postcss-discard-empty@^5.0.3": + "integrity" "sha512-xGJugpaXKakwKI7sSdZjUuN4V3zSzb2Y0LOlmTajFbNinEjTfVs9PFW2lmKBaC/E64WwYppfqLD03P8l9BuueA==" + "resolved" "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.3.tgz" + "version" "5.0.3" -postcss-discard-overridden@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.0.4.tgz#cc999d6caf18ea16eff8b2b58f48ec3ddee35c9c" - integrity sha512-3j9QH0Qh1KkdxwiZOW82cId7zdwXVQv/gRXYDnwx5pBtR1sTkU4cXRK9lp5dSdiM0r0OICO/L8J6sV1/7m0kHg== +"postcss-discard-overridden@^5.0.4": + "integrity" "sha512-3j9QH0Qh1KkdxwiZOW82cId7zdwXVQv/gRXYDnwx5pBtR1sTkU4cXRK9lp5dSdiM0r0OICO/L8J6sV1/7m0kHg==" + "resolved" "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.4.tgz" + "version" "5.0.4" -postcss-discard-unused@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-5.0.3.tgz#89fd3ebdbed8320df77a4ad503bd83cff52409f5" - integrity sha512-WO6FJxL5fGnuE77ZbTcZ/nRZJ4+TOqNaqLBLWgkR4e+WdmHn77OHPyQmsRv7eOB2rLKL6tsq2bs1GwoKXD/++Q== +"postcss-discard-unused@^5.0.3": + "integrity" "sha512-WO6FJxL5fGnuE77ZbTcZ/nRZJ4+TOqNaqLBLWgkR4e+WdmHn77OHPyQmsRv7eOB2rLKL6tsq2bs1GwoKXD/++Q==" + "resolved" "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.0.3.tgz" + "version" "5.0.3" dependencies: - postcss-selector-parser "^6.0.5" + "postcss-selector-parser" "^6.0.5" -postcss-loader@^6.1.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.2.1.tgz#0895f7346b1702103d30fdc66e4d494a93c008ef" - integrity sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q== +"postcss-loader@^6.1.1": + "integrity" "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==" + "resolved" "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz" + "version" "6.2.1" dependencies: - cosmiconfig "^7.0.0" - klona "^2.0.5" - semver "^7.3.5" + "cosmiconfig" "^7.0.0" + "klona" "^2.0.5" + "semver" "^7.3.5" -postcss-merge-idents@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-5.0.3.tgz#04f333f32767bd7b7b002f0032da347ec3c8c484" - integrity sha512-Z4LCzh2WzMn69KaS2FaJcrIeDQ170V13QHq+0hnBEFKJJkD+y5qndZ/bl3AhpddrSrXWIVR+xAwjmHQIJI2Eog== +"postcss-merge-idents@^5.0.3": + "integrity" "sha512-Z4LCzh2WzMn69KaS2FaJcrIeDQ170V13QHq+0hnBEFKJJkD+y5qndZ/bl3AhpddrSrXWIVR+xAwjmHQIJI2Eog==" + "resolved" "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.0.3.tgz" + "version" "5.0.3" dependencies: - cssnano-utils "^3.0.2" - postcss-value-parser "^4.2.0" + "cssnano-utils" "^3.0.2" + "postcss-value-parser" "^4.2.0" -postcss-merge-longhand@^5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.0.6.tgz#090e60d5d3b3caad899f8774f8dccb33217d2166" - integrity sha512-rkmoPwQO6ymJSmWsX6l2hHeEBQa7C4kJb9jyi5fZB1sE8nSCv7sqchoYPixRwX/yvLoZP2y6FA5kcjiByeJqDg== +"postcss-merge-longhand@^5.0.6": + "integrity" "sha512-rkmoPwQO6ymJSmWsX6l2hHeEBQa7C4kJb9jyi5fZB1sE8nSCv7sqchoYPixRwX/yvLoZP2y6FA5kcjiByeJqDg==" + "resolved" "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.6.tgz" + "version" "5.0.6" dependencies: - postcss-value-parser "^4.2.0" - stylehacks "^5.0.3" + "postcss-value-parser" "^4.2.0" + "stylehacks" "^5.0.3" -postcss-merge-rules@^5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.6.tgz#26b37411fe1e80202fcef61cab027265b8925f2b" - integrity sha512-nzJWJ9yXWp8AOEpn/HFAW72WKVGD2bsLiAmgw4hDchSij27bt6TF+sIK0cJUBAYT3SGcjtGGsOR89bwkkMuMgQ== +"postcss-merge-rules@^5.0.6": + "integrity" "sha512-nzJWJ9yXWp8AOEpn/HFAW72WKVGD2bsLiAmgw4hDchSij27bt6TF+sIK0cJUBAYT3SGcjtGGsOR89bwkkMuMgQ==" + "resolved" "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.6.tgz" + "version" "5.0.6" dependencies: - browserslist "^4.16.6" - caniuse-api "^3.0.0" - cssnano-utils "^3.0.2" - postcss-selector-parser "^6.0.5" + "browserslist" "^4.16.6" + "caniuse-api" "^3.0.0" + "cssnano-utils" "^3.0.2" + "postcss-selector-parser" "^6.0.5" -postcss-minify-font-values@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.0.4.tgz#627d824406b0712243221891f40a44fffe1467fd" - integrity sha512-RN6q3tyuEesvyCYYFCRGJ41J1XFvgV+dvYGHr0CeHv8F00yILlN8Slf4t8XW4IghlfZYCeyRrANO6HpJ948ieA== +"postcss-minify-font-values@^5.0.4": + "integrity" "sha512-RN6q3tyuEesvyCYYFCRGJ41J1XFvgV+dvYGHr0CeHv8F00yILlN8Slf4t8XW4IghlfZYCeyRrANO6HpJ948ieA==" + "resolved" "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-minify-gradients@^5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.0.6.tgz#b07cef51a93f075e94053fd972ff1cba2eaf6503" - integrity sha512-E/dT6oVxB9nLGUTiY/rG5dX9taugv9cbLNTFad3dKxOO+BQg25Q/xo2z2ddG+ZB1CbkZYaVwx5blY8VC7R/43A== +"postcss-minify-gradients@^5.0.6": + "integrity" "sha512-E/dT6oVxB9nLGUTiY/rG5dX9taugv9cbLNTFad3dKxOO+BQg25Q/xo2z2ddG+ZB1CbkZYaVwx5blY8VC7R/43A==" + "resolved" "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.6.tgz" + "version" "5.0.6" dependencies: - colord "^2.9.1" - cssnano-utils "^3.0.2" - postcss-value-parser "^4.2.0" + "colord" "^2.9.1" + "cssnano-utils" "^3.0.2" + "postcss-value-parser" "^4.2.0" -postcss-minify-params@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.0.5.tgz#86cb624358cd45c21946f8c317893f0449396646" - integrity sha512-YBNuq3Rz5LfLFNHb9wrvm6t859b8qIqfXsWeK7wROm3jSKNpO1Y5e8cOyBv6Acji15TgSrAwb3JkVNCqNyLvBg== +"postcss-minify-params@^5.0.5": + "integrity" "sha512-YBNuq3Rz5LfLFNHb9wrvm6t859b8qIqfXsWeK7wROm3jSKNpO1Y5e8cOyBv6Acji15TgSrAwb3JkVNCqNyLvBg==" + "resolved" "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.5.tgz" + "version" "5.0.5" dependencies: - browserslist "^4.16.6" - cssnano-utils "^3.0.2" - postcss-value-parser "^4.2.0" + "browserslist" "^4.16.6" + "cssnano-utils" "^3.0.2" + "postcss-value-parser" "^4.2.0" -postcss-minify-selectors@^5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.1.3.tgz#6ac12d52aa661fd509469d87ab2cebb0a1e3a1b5" - integrity sha512-9RJfTiQEKA/kZhMaEXND893nBqmYQ8qYa/G+uPdVnXF6D/FzpfI6kwBtWEcHx5FqDbA79O9n6fQJfrIj6M8jvQ== +"postcss-minify-selectors@^5.1.3": + "integrity" "sha512-9RJfTiQEKA/kZhMaEXND893nBqmYQ8qYa/G+uPdVnXF6D/FzpfI6kwBtWEcHx5FqDbA79O9n6fQJfrIj6M8jvQ==" + "resolved" "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.3.tgz" + "version" "5.1.3" dependencies: - postcss-selector-parser "^6.0.5" + "postcss-selector-parser" "^6.0.5" -postcss-modules-extract-imports@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" - integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== +"postcss-modules-extract-imports@^3.0.0": + "integrity" "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" + "resolved" "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz" + "version" "3.0.0" -postcss-modules-local-by-default@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" - integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== +"postcss-modules-local-by-default@^4.0.0": + "integrity" "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==" + "resolved" "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz" + "version" "4.0.0" dependencies: - icss-utils "^5.0.0" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.1.0" + "icss-utils" "^5.0.0" + "postcss-selector-parser" "^6.0.2" + "postcss-value-parser" "^4.1.0" -postcss-modules-scope@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" - integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== +"postcss-modules-scope@^3.0.0": + "integrity" "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==" + "resolved" "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz" + "version" "3.0.0" dependencies: - postcss-selector-parser "^6.0.4" + "postcss-selector-parser" "^6.0.4" -postcss-modules-values@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" - integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== +"postcss-modules-values@^4.0.0": + "integrity" "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==" + "resolved" "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz" + "version" "4.0.0" dependencies: - icss-utils "^5.0.0" + "icss-utils" "^5.0.0" -postcss-normalize-charset@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.0.3.tgz#719fb9f9ca9835fcbd4fed8d6e0d72a79e7b5472" - integrity sha512-iKEplDBco9EfH7sx4ut7R2r/dwTnUqyfACf62Unc9UiyFuI7uUqZZtY+u+qp7g8Qszl/U28HIfcsI3pEABWFfA== +"postcss-normalize-charset@^5.0.3": + "integrity" "sha512-iKEplDBco9EfH7sx4ut7R2r/dwTnUqyfACf62Unc9UiyFuI7uUqZZtY+u+qp7g8Qszl/U28HIfcsI3pEABWFfA==" + "resolved" "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.3.tgz" + "version" "5.0.3" -postcss-normalize-display-values@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.3.tgz#94cc82e20c51cc4ffba6b36e9618adc1e50db8c1" - integrity sha512-FIV5FY/qs4Ja32jiDb5mVj5iWBlS3N8tFcw2yg98+8MkRgyhtnBgSC0lxU+16AMHbjX5fbSJgw5AXLMolonuRQ== +"postcss-normalize-display-values@^5.0.3": + "integrity" "sha512-FIV5FY/qs4Ja32jiDb5mVj5iWBlS3N8tFcw2yg98+8MkRgyhtnBgSC0lxU+16AMHbjX5fbSJgw5AXLMolonuRQ==" + "resolved" "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.3.tgz" + "version" "5.0.3" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-normalize-positions@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.0.4.tgz#4001f38c99675437b83277836fb4291887fcc6cc" - integrity sha512-qynirjBX0Lc73ROomZE3lzzmXXTu48/QiEzKgMeqh28+MfuHLsuqC9po4kj84igZqqFGovz8F8hf44hA3dPYmQ== +"postcss-normalize-positions@^5.0.4": + "integrity" "sha512-qynirjBX0Lc73ROomZE3lzzmXXTu48/QiEzKgMeqh28+MfuHLsuqC9po4kj84igZqqFGovz8F8hf44hA3dPYmQ==" + "resolved" "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-normalize-repeat-style@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.4.tgz#d005adf9ee45fae78b673031a376c0c871315145" - integrity sha512-Innt+wctD7YpfeDR7r5Ik6krdyppyAg2HBRpX88fo5AYzC1Ut/l3xaxACG0KsbX49cO2n5EB13clPwuYVt8cMA== +"postcss-normalize-repeat-style@^5.0.4": + "integrity" "sha512-Innt+wctD7YpfeDR7r5Ik6krdyppyAg2HBRpX88fo5AYzC1Ut/l3xaxACG0KsbX49cO2n5EB13clPwuYVt8cMA==" + "resolved" "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-normalize-string@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.0.4.tgz#b5e00a07597e7aa8a871817bfeac2bfaa59c3333" - integrity sha512-Dfk42l0+A1CDnVpgE606ENvdmksttLynEqTQf5FL3XGQOyqxjbo25+pglCUvziicTxjtI2NLUR6KkxyUWEVubQ== +"postcss-normalize-string@^5.0.4": + "integrity" "sha512-Dfk42l0+A1CDnVpgE606ENvdmksttLynEqTQf5FL3XGQOyqxjbo25+pglCUvziicTxjtI2NLUR6KkxyUWEVubQ==" + "resolved" "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-normalize-timing-functions@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.3.tgz#47210227bfcba5e52650d7a18654337090de7072" - integrity sha512-QRfjvFh11moN4PYnJ7hia4uJXeFotyK3t2jjg8lM9mswleGsNw2Lm3I5wO+l4k1FzK96EFwEVn8X8Ojrp2gP4g== +"postcss-normalize-timing-functions@^5.0.3": + "integrity" "sha512-QRfjvFh11moN4PYnJ7hia4uJXeFotyK3t2jjg8lM9mswleGsNw2Lm3I5wO+l4k1FzK96EFwEVn8X8Ojrp2gP4g==" + "resolved" "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.3.tgz" + "version" "5.0.3" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-normalize-unicode@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.4.tgz#02866096937005cdb2c17116c690f29505a1623d" - integrity sha512-W79Regn+a+eXTzB+oV/8XJ33s3pDyFTND2yDuUCo0Xa3QSy1HtNIfRVPXNubHxjhlqmMFADr3FSCHT84ITW3ig== +"postcss-normalize-unicode@^5.0.4": + "integrity" "sha512-W79Regn+a+eXTzB+oV/8XJ33s3pDyFTND2yDuUCo0Xa3QSy1HtNIfRVPXNubHxjhlqmMFADr3FSCHT84ITW3ig==" + "resolved" "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.4.tgz" + "version" "5.0.4" dependencies: - browserslist "^4.16.6" - postcss-value-parser "^4.2.0" + "browserslist" "^4.16.6" + "postcss-value-parser" "^4.2.0" -postcss-normalize-url@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.0.5.tgz#c39efc12ff119f6f45f0b4f516902b12c8080e3a" - integrity sha512-Ws3tX+PcekYlXh+ycAt0wyzqGthkvVtZ9SZLutMVvHARxcpu4o7vvXcNoiNKyjKuWecnjS6HDI3fjBuDr5MQxQ== +"postcss-normalize-url@^5.0.5": + "integrity" "sha512-Ws3tX+PcekYlXh+ycAt0wyzqGthkvVtZ9SZLutMVvHARxcpu4o7vvXcNoiNKyjKuWecnjS6HDI3fjBuDr5MQxQ==" + "resolved" "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.5.tgz" + "version" "5.0.5" dependencies: - normalize-url "^6.0.1" - postcss-value-parser "^4.2.0" + "normalize-url" "^6.0.1" + "postcss-value-parser" "^4.2.0" -postcss-normalize-whitespace@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.4.tgz#1d477e7da23fecef91fc4e37d462272c7b55c5ca" - integrity sha512-wsnuHolYZjMwWZJoTC9jeI2AcjA67v4UuidDrPN9RnX8KIZfE+r2Nd6XZRwHVwUiHmRvKQtxiqo64K+h8/imaw== +"postcss-normalize-whitespace@^5.0.4": + "integrity" "sha512-wsnuHolYZjMwWZJoTC9jeI2AcjA67v4UuidDrPN9RnX8KIZfE+r2Nd6XZRwHVwUiHmRvKQtxiqo64K+h8/imaw==" + "resolved" "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-ordered-values@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.0.5.tgz#e878af822a130c3f3709737e24cb815ca7c6d040" - integrity sha512-mfY7lXpq+8bDEHfP+muqibDPhZ5eP9zgBEF9XRvoQgXcQe2Db3G1wcvjbnfjXG6wYsl+0UIjikqq4ym1V2jGMQ== +"postcss-ordered-values@^5.0.5": + "integrity" "sha512-mfY7lXpq+8bDEHfP+muqibDPhZ5eP9zgBEF9XRvoQgXcQe2Db3G1wcvjbnfjXG6wYsl+0UIjikqq4ym1V2jGMQ==" + "resolved" "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.5.tgz" + "version" "5.0.5" dependencies: - cssnano-utils "^3.0.2" - postcss-value-parser "^4.2.0" + "cssnano-utils" "^3.0.2" + "postcss-value-parser" "^4.2.0" -postcss-reduce-idents@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-5.0.3.tgz#b632796275b4fa1a4040799969dd17167eaf4d8b" - integrity sha512-9bj9/Xhwiti0Z35kkguJX4G6yUYVw8S1kRLU4jFSCTEuHu4yJggf4rNUoVnT45lm/vU97Wd593CxspMDbHxy4w== +"postcss-reduce-idents@^5.0.3": + "integrity" "sha512-9bj9/Xhwiti0Z35kkguJX4G6yUYVw8S1kRLU4jFSCTEuHu4yJggf4rNUoVnT45lm/vU97Wd593CxspMDbHxy4w==" + "resolved" "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.0.3.tgz" + "version" "5.0.3" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-reduce-initial@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.3.tgz#68891594defd648253703bbd8f1093162f19568d" - integrity sha512-c88TkSnQ/Dnwgb4OZbKPOBbCaauwEjbECP5uAuFPOzQ+XdjNjRH7SG0dteXrpp1LlIFEKK76iUGgmw2V0xeieA== +"postcss-reduce-initial@^5.0.3": + "integrity" "sha512-c88TkSnQ/Dnwgb4OZbKPOBbCaauwEjbECP5uAuFPOzQ+XdjNjRH7SG0dteXrpp1LlIFEKK76iUGgmw2V0xeieA==" + "resolved" "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.3.tgz" + "version" "5.0.3" dependencies: - browserslist "^4.16.6" - caniuse-api "^3.0.0" + "browserslist" "^4.16.6" + "caniuse-api" "^3.0.0" -postcss-reduce-transforms@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.4.tgz#717e72d30befe857f7d2784dba10eb1157863712" - integrity sha512-VIJB9SFSaL8B/B7AXb7KHL6/GNNbbCHslgdzS9UDfBZYIA2nx8NLY7iD/BXFSO/1sRUILzBTfHCoW5inP37C5g== +"postcss-reduce-transforms@^5.0.4": + "integrity" "sha512-VIJB9SFSaL8B/B7AXb7KHL6/GNNbbCHslgdzS9UDfBZYIA2nx8NLY7iD/BXFSO/1sRUILzBTfHCoW5inP37C5g==" + "resolved" "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" + "postcss-value-parser" "^4.2.0" -postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: - version "6.0.9" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" - integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== +"postcss-selector-parser@^6.0.2", "postcss-selector-parser@^6.0.4", "postcss-selector-parser@^6.0.5", "postcss-selector-parser@^6.0.9": + "integrity" "sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==" + "resolved" "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz" + "version" "6.0.9" dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" + "cssesc" "^3.0.0" + "util-deprecate" "^1.0.2" -postcss-sort-media-queries@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-4.2.1.tgz#a99bae69ef1098ee3b64a5fa94d258ec240d0355" - integrity sha512-9VYekQalFZ3sdgcTjXMa0dDjsfBVHXlraYJEMiOJ/2iMmI2JGCMavP16z3kWOaRu8NSaJCTgVpB/IVpH5yT9YQ== +"postcss-sort-media-queries@^4.1.0": + "integrity" "sha512-9VYekQalFZ3sdgcTjXMa0dDjsfBVHXlraYJEMiOJ/2iMmI2JGCMavP16z3kWOaRu8NSaJCTgVpB/IVpH5yT9YQ==" + "resolved" "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.2.1.tgz" + "version" "4.2.1" dependencies: - sort-css-media-queries "2.0.4" + "sort-css-media-queries" "2.0.4" -postcss-svgo@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.4.tgz#cfa8682f47b88f7cd75108ec499e133b43102abf" - integrity sha512-yDKHvULbnZtIrRqhZoA+rxreWpee28JSRH/gy9727u0UCgtpv1M/9WEWY3xySlFa0zQJcqf6oCBJPR5NwkmYpg== +"postcss-svgo@^5.0.4": + "integrity" "sha512-yDKHvULbnZtIrRqhZoA+rxreWpee28JSRH/gy9727u0UCgtpv1M/9WEWY3xySlFa0zQJcqf6oCBJPR5NwkmYpg==" + "resolved" "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-value-parser "^4.2.0" - svgo "^2.7.0" + "postcss-value-parser" "^4.2.0" + "svgo" "^2.7.0" -postcss-unique-selectors@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.0.4.tgz#08e188126b634ddfa615fb1d6c262bafdd64826e" - integrity sha512-5ampwoSDJCxDPoANBIlMgoBcYUHnhaiuLYJR5pj1DLnYQvMRVyFuTA5C3Bvt+aHtiqWpJkD/lXT50Vo1D0ZsAQ== +"postcss-unique-selectors@^5.0.4": + "integrity" "sha512-5ampwoSDJCxDPoANBIlMgoBcYUHnhaiuLYJR5pj1DLnYQvMRVyFuTA5C3Bvt+aHtiqWpJkD/lXT50Vo1D0ZsAQ==" + "resolved" "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.4.tgz" + "version" "5.0.4" dependencies: - postcss-selector-parser "^6.0.5" + "postcss-selector-parser" "^6.0.5" -postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" - integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== +"postcss-value-parser@^4.1.0", "postcss-value-parser@^4.2.0": + "integrity" "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "resolved" "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + "version" "4.2.0" -postcss-zindex@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-5.0.2.tgz#7e48aee54062c93418593035229ea06b92381251" - integrity sha512-KPQFjQu73H35HLHmE8Wv31ygfQoucxD52oRm4FPFv1emYhFMzUQdF8adaXCevFLIHPRp2rRYfbaDiEqZ4YjVtw== +"postcss-zindex@^5.0.2": + "integrity" "sha512-KPQFjQu73H35HLHmE8Wv31ygfQoucxD52oRm4FPFv1emYhFMzUQdF8adaXCevFLIHPRp2rRYfbaDiEqZ4YjVtw==" + "resolved" "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.0.2.tgz" + "version" "5.0.2" -postcss@^8.3.11, postcss@^8.3.5, postcss@^8.3.7, postcss@^8.4.5: - version "8.4.6" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.6.tgz#c5ff3c3c457a23864f32cb45ac9b741498a09ae1" - integrity sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA== +"postcss@^7.0.0 || ^8.0.1", "postcss@^8.0.9", "postcss@^8.1.0", "postcss@^8.2.15", "postcss@^8.2.2", "postcss@^8.3.11", "postcss@^8.3.5", "postcss@^8.3.7", "postcss@^8.4.4", "postcss@^8.4.5": + "integrity" "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==" + "resolved" "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz" + "version" "8.4.6" dependencies: - nanoid "^3.2.0" - picocolors "^1.0.0" - source-map-js "^1.0.2" + "nanoid" "^3.2.0" + "picocolors" "^1.0.0" + "source-map-js" "^1.0.2" -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +"prepend-http@^2.0.0": + "integrity" "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + "resolved" "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" + "version" "2.0.0" -pretty-error@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" - integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== +"pretty-error@^4.0.0": + "integrity" "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==" + "resolved" "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz" + "version" "4.0.0" dependencies: - lodash "^4.17.20" - renderkid "^3.0.0" + "lodash" "^4.17.20" + "renderkid" "^3.0.0" -pretty-time@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" - integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== +"pretty-time@^1.1.0": + "integrity" "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" + "resolved" "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz" + "version" "1.1.0" -prism-react-renderer@^1.2.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.1.tgz#88fc9d0df6bed06ca2b9097421349f8c2f24e30d" - integrity sha512-xUeDMEz074d0zc5y6rxiMp/dlC7C+5IDDlaEUlcBOFE2wddz7hz5PNupb087mPwTt7T9BrFmewObfCBuf/LKwQ== +"prism-react-renderer@^1.2.1": + "integrity" "sha512-xUeDMEz074d0zc5y6rxiMp/dlC7C+5IDDlaEUlcBOFE2wddz7hz5PNupb087mPwTt7T9BrFmewObfCBuf/LKwQ==" + "resolved" "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.1.tgz" + "version" "1.3.1" -prismjs@^1.23.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057" - integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA== +"prismjs@^1.23.0": + "integrity" "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==" + "resolved" "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz" + "version" "1.27.0" -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +"process-nextick-args@~2.0.0": + "integrity" "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "resolved" "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + "version" "2.0.1" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== +"promise@^7.1.1": + "integrity" "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==" + "resolved" "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz" + "version" "7.3.1" dependencies: - asap "~2.0.3" + "asap" "~2.0.3" -prompts@^2.4.1, prompts@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" - integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== +"prompts@^2.4.1", "prompts@^2.4.2": + "integrity" "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==" + "resolved" "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" + "version" "2.4.2" dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" + "kleur" "^3.0.3" + "sisteransi" "^1.0.5" -prop-types@^15.6.2, prop-types@^15.7.2: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== +"prop-types@^15.0.0", "prop-types@^15.6.2", "prop-types@^15.7.2": + "integrity" "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==" + "resolved" "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" + "version" "15.8.1" dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" + "loose-envify" "^1.4.0" + "object-assign" "^4.1.1" + "react-is" "^16.13.1" -property-information@^5.0.0, property-information@^5.3.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" - integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== +"property-information@^5.0.0", "property-information@^5.3.0": + "integrity" "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==" + "resolved" "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz" + "version" "5.6.0" dependencies: - xtend "^4.0.0" + "xtend" "^4.0.0" -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== +"proxy-addr@~2.0.7": + "integrity" "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==" + "resolved" "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" + "version" "2.0.7" dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" + "forwarded" "0.2.0" + "ipaddr.js" "1.9.1" -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== +"pump@^3.0.0": + "integrity" "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==" + "resolved" "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" + "version" "3.0.0" dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" + "end-of-stream" "^1.1.0" + "once" "^1.3.1" -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= +"punycode@^1.3.2": + "integrity" "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "resolved" "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + "version" "1.4.1" -punycode@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= +"punycode@^2.1.0": + "integrity" "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "resolved" "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" + "version" "2.1.1" -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +"punycode@1.3.2": + "integrity" "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + "resolved" "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + "version" "1.3.2" -pupa@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" - integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== +"pupa@^2.1.1": + "integrity" "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==" + "resolved" "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz" + "version" "2.1.1" dependencies: - escape-goat "^2.0.0" + "escape-goat" "^2.0.0" -pure-color@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e" - integrity sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4= +"pure-color@^1.2.0": + "integrity" "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" + "resolved" "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz" + "version" "1.3.0" -qs@6.9.7: - version "6.9.7" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" - integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== +"qs@6.9.7": + "integrity" "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" + "resolved" "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz" + "version" "6.9.7" -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +"querystring@0.2.0": + "integrity" "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "resolved" "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" + "version" "0.2.0" -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +"queue-microtask@^1.2.2": + "integrity" "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + "resolved" "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + "version" "1.2.3" -queue@6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" - integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== +"queue@6.0.2": + "integrity" "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==" + "resolved" "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz" + "version" "6.0.2" dependencies: - inherits "~2.0.3" + "inherits" "~2.0.3" -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== +"randombytes@^2.1.0": + "integrity" "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==" + "resolved" "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + "version" "2.1.0" dependencies: - safe-buffer "^5.1.0" + "safe-buffer" "^5.1.0" -range-parser@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= +"range-parser@^1.2.1", "range-parser@~1.2.1": + "integrity" "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + "resolved" "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + "version" "1.2.1" -range-parser@^1.2.1, range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== +"range-parser@1.2.0": + "integrity" "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + "resolved" "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz" + "version" "1.2.0" -raw-body@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" - integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== +"raw-body@2.4.3": + "integrity" "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==" + "resolved" "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz" + "version" "2.4.3" dependencies: - bytes "3.1.2" - http-errors "1.8.1" - iconv-lite "0.4.24" - unpipe "1.0.0" + "bytes" "3.1.2" + "http-errors" "1.8.1" + "iconv-lite" "0.4.24" + "unpipe" "1.0.0" -rc@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== +"rc@^1.2.8": + "integrity" "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==" + "resolved" "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" + "version" "1.2.8" dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" + "deep-extend" "^0.6.0" + "ini" "~1.3.0" + "minimist" "^1.2.0" + "strip-json-comments" "~2.0.1" -react-base16-styling@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c" - integrity sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw= +"react-base16-styling@^0.6.0": + "integrity" "sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw=" + "resolved" "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz" + "version" "0.6.0" dependencies: - base16 "^1.0.0" - lodash.curry "^4.0.1" - lodash.flow "^3.3.0" - pure-color "^1.2.0" + "base16" "^1.0.0" + "lodash.curry" "^4.0.1" + "lodash.flow" "^3.3.0" + "pure-color" "^1.2.0" -react-dev-utils@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.0.tgz#4eab12cdb95692a077616770b5988f0adf806526" - integrity sha512-xBQkitdxozPxt1YZ9O1097EJiVpwHr9FoAuEVURCKV0Av8NBERovJauzP7bo1ThvuhZ4shsQ1AJiu4vQpoT1AQ== +"react-dev-utils@^12.0.0": + "integrity" "sha512-xBQkitdxozPxt1YZ9O1097EJiVpwHr9FoAuEVURCKV0Av8NBERovJauzP7bo1ThvuhZ4shsQ1AJiu4vQpoT1AQ==" + "resolved" "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz" + "version" "12.0.0" dependencies: "@babel/code-frame" "^7.16.0" - address "^1.1.2" - browserslist "^4.18.1" - chalk "^4.1.2" - cross-spawn "^7.0.3" - detect-port-alt "^1.1.6" - escape-string-regexp "^4.0.0" - filesize "^8.0.6" - find-up "^5.0.0" - fork-ts-checker-webpack-plugin "^6.5.0" - global-modules "^2.0.0" - globby "^11.0.4" - gzip-size "^6.0.0" - immer "^9.0.7" - is-root "^2.1.0" - loader-utils "^3.2.0" - open "^8.4.0" - pkg-up "^3.1.0" - prompts "^2.4.2" - react-error-overlay "^6.0.10" - recursive-readdir "^2.2.2" - shell-quote "^1.7.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" + "address" "^1.1.2" + "browserslist" "^4.18.1" + "chalk" "^4.1.2" + "cross-spawn" "^7.0.3" + "detect-port-alt" "^1.1.6" + "escape-string-regexp" "^4.0.0" + "filesize" "^8.0.6" + "find-up" "^5.0.0" + "fork-ts-checker-webpack-plugin" "^6.5.0" + "global-modules" "^2.0.0" + "globby" "^11.0.4" + "gzip-size" "^6.0.0" + "immer" "^9.0.7" + "is-root" "^2.1.0" + "loader-utils" "^3.2.0" + "open" "^8.4.0" + "pkg-up" "^3.1.0" + "prompts" "^2.4.2" + "react-error-overlay" "^6.0.10" + "recursive-readdir" "^2.2.2" + "shell-quote" "^1.7.3" + "strip-ansi" "^6.0.1" + "text-table" "^0.2.0" -react-dom@^16.10.2: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" - integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== +"react-dom@*", "react-dom@^16.10.2", "react-dom@^16.8.4 || ^17.0.0", "react-dom@^17.0.0 || ^16.3.0 || ^15.5.4", "react-dom@>= 16.8.0 < 18.0.0": + "integrity" "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==" + "resolved" "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz" + "version" "16.14.0" dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.19.1" + "loose-envify" "^1.1.0" + "object-assign" "^4.1.1" + "prop-types" "^15.6.2" + "scheduler" "^0.19.1" -react-error-overlay@^6.0.10: - version "6.0.10" - resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.10.tgz#0fe26db4fa85d9dbb8624729580e90e7159a59a6" - integrity sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA== +"react-error-overlay@^6.0.10": + "integrity" "sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA==" + "resolved" "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz" + "version" "6.0.10" -react-fast-compare@^3.1.1: - version "3.2.0" - resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" - integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== +"react-fast-compare@^3.1.1": + "integrity" "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + "resolved" "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz" + "version" "3.2.0" -react-helmet@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" - integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== +"react-helmet@^6.1.0": + "integrity" "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==" + "resolved" "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz" + "version" "6.1.0" dependencies: - object-assign "^4.1.1" - prop-types "^15.7.2" - react-fast-compare "^3.1.1" - react-side-effect "^2.1.0" + "object-assign" "^4.1.1" + "prop-types" "^15.7.2" + "react-fast-compare" "^3.1.1" + "react-side-effect" "^2.1.0" -react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +"react-is@^16.13.1", "react-is@^16.6.0", "react-is@^16.7.0": + "integrity" "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "resolved" "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" + "version" "16.13.1" -react-json-view@^1.21.3: - version "1.21.3" - resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475" - integrity sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw== +"react-json-view@^1.21.3": + "integrity" "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==" + "resolved" "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz" + "version" "1.21.3" dependencies: - flux "^4.0.1" - react-base16-styling "^0.6.0" - react-lifecycles-compat "^3.0.4" - react-textarea-autosize "^8.3.2" + "flux" "^4.0.1" + "react-base16-styling" "^0.6.0" + "react-lifecycles-compat" "^3.0.4" + "react-textarea-autosize" "^8.3.2" -react-lifecycles-compat@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" - integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +"react-lifecycles-compat@^3.0.4": + "integrity" "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + "resolved" "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz" + "version" "3.0.4" -react-loadable-ssr-addon-v5-slorber@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz#2cdc91e8a744ffdf9e3556caabeb6e4278689883" - integrity sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A== +"react-loadable-ssr-addon-v5-slorber@^1.0.1": + "integrity" "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==" + "resolved" "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz" + "version" "1.0.1" dependencies: "@babel/runtime" "^7.10.3" -react-popupbox@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/react-popupbox/-/react-popupbox-2.0.8.tgz#9e0c96dcf4ddbbea8d03c28ee6c0634f0d51b791" - integrity sha512-5DT0SxLMIchKgnUkdPwTzvFhtTL5SOQd6n5dzUnnELiimjFE8eaQwL1n58NZUxs9oJsHXF3qQNvcgwEfn8VHrw== +"react-loadable@*", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": + "integrity" "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz" + "version" "5.5.2" dependencies: - deepmerge "^1.3.2" - react "^16.3.1" + "@types/react" "*" + "prop-types" "^15.6.2" -react-router-config@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988" - integrity sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg== +"react-popupbox@^2.0.8": + "integrity" "sha512-5DT0SxLMIchKgnUkdPwTzvFhtTL5SOQd6n5dzUnnELiimjFE8eaQwL1n58NZUxs9oJsHXF3qQNvcgwEfn8VHrw==" + "resolved" "https://registry.npmjs.org/react-popupbox/-/react-popupbox-2.0.8.tgz" + "version" "2.0.8" + dependencies: + "deepmerge" "^1.3.2" + "react" "^16.3.1" + +"react-router-config@^5.1.1": + "integrity" "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==" + "resolved" "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz" + "version" "5.1.1" dependencies: "@babel/runtime" "^7.1.2" -react-router-dom@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.0.tgz#da1bfb535a0e89a712a93b97dd76f47ad1f32363" - integrity sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ== +"react-router-dom@^5.2.0": + "integrity" "sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==" + "resolved" "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.0.tgz" + "version" "5.3.0" dependencies: "@babel/runtime" "^7.12.13" - history "^4.9.0" - loose-envify "^1.3.1" - prop-types "^15.6.2" - react-router "5.2.1" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" + "history" "^4.9.0" + "loose-envify" "^1.3.1" + "prop-types" "^15.6.2" + "react-router" "5.2.1" + "tiny-invariant" "^1.0.2" + "tiny-warning" "^1.0.0" -react-router@5.2.1, react-router@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.1.tgz#4d2e4e9d5ae9425091845b8dbc6d9d276239774d" - integrity sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ== +"react-router@^5.2.0", "react-router@>=5", "react-router@5.2.1": + "integrity" "sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==" + "resolved" "https://registry.npmjs.org/react-router/-/react-router-5.2.1.tgz" + "version" "5.2.1" dependencies: "@babel/runtime" "^7.12.13" - history "^4.9.0" - hoist-non-react-statics "^3.1.0" - loose-envify "^1.3.1" - mini-create-react-context "^0.4.0" - path-to-regexp "^1.7.0" - prop-types "^15.6.2" - react-is "^16.6.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" + "history" "^4.9.0" + "hoist-non-react-statics" "^3.1.0" + "loose-envify" "^1.3.1" + "mini-create-react-context" "^0.4.0" + "path-to-regexp" "^1.7.0" + "prop-types" "^15.6.2" + "react-is" "^16.6.0" + "tiny-invariant" "^1.0.2" + "tiny-warning" "^1.0.0" -react-side-effect@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3" - integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ== +"react-side-effect@^2.1.0": + "integrity" "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==" + "resolved" "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz" + "version" "2.1.1" -react-textarea-autosize@^8.3.2: - version "8.3.3" - resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz#f70913945369da453fd554c168f6baacd1fa04d8" - integrity sha512-2XlHXK2TDxS6vbQaoPbMOfQ8GK7+irc2fVK6QFIcC8GOnH3zI/v481n+j1L0WaPVvKxwesnY93fEfH++sus2rQ== +"react-textarea-autosize@^8.3.2": + "integrity" "sha512-2XlHXK2TDxS6vbQaoPbMOfQ8GK7+irc2fVK6QFIcC8GOnH3zI/v481n+j1L0WaPVvKxwesnY93fEfH++sus2rQ==" + "resolved" "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz" + "version" "8.3.3" dependencies: "@babel/runtime" "^7.10.2" - use-composed-ref "^1.0.0" - use-latest "^1.0.0" + "use-composed-ref" "^1.0.0" + "use-latest" "^1.0.0" -react@^16.10.2, react@^16.3.1: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" - integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== +"react@*", "react@^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "react@^15.0.2 || ^16.0.0 || ^17.0.0", "react@^16.10.2", "react@^16.13.1", "react@^16.13.1 || ^17.0.0", "react@^16.14.0", "react@^16.3.0 || ^17.0.0", "react@^16.3.1", "react@^16.8.0 || ^17.0.0", "react@^16.8.4 || ^17.0.0", "react@^17.0.0 || ^16.3.0 || ^15.5.4", "react@>= 16.8.0 < 18.0.0", "react@>=0.14.9", "react@>=15", "react@>=16.3.0": + "integrity" "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==" + "resolved" "https://registry.npmjs.org/react/-/react-16.14.0.tgz" + "version" "16.14.0" dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" + "loose-envify" "^1.1.0" + "object-assign" "^4.1.1" + "prop-types" "^15.6.2" -readable-stream@^2.0.1: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== +"readable-stream@^2.0.1": + "integrity" "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==" + "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" + "version" "2.3.7" dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" + "core-util-is" "~1.0.0" + "inherits" "~2.0.3" + "isarray" "~1.0.0" + "process-nextick-args" "~2.0.0" + "safe-buffer" "~5.1.1" + "string_decoder" "~1.1.1" + "util-deprecate" "~1.0.1" -readable-stream@^3.0.6, readable-stream@^3.1.1: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== +"readable-stream@^3.0.6", "readable-stream@^3.1.1": + "integrity" "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==" + "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" + "version" "3.6.0" dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" + "inherits" "^2.0.3" + "string_decoder" "^1.1.1" + "util-deprecate" "^1.0.1" -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== +"readdirp@~3.6.0": + "integrity" "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==" + "resolved" "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + "version" "3.6.0" dependencies: - picomatch "^2.2.1" + "picomatch" "^2.2.1" -reading-time@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb" - integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg== +"reading-time@^1.5.0": + "integrity" "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" + "resolved" "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz" + "version" "1.5.0" -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= +"rechoir@^0.6.2": + "integrity" "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=" + "resolved" "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz" + "version" "0.6.2" dependencies: - resolve "^1.1.6" + "resolve" "^1.1.6" -recursive-readdir@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" - integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== +"recursive-readdir@^2.2.2": + "integrity" "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==" + "resolved" "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz" + "version" "2.2.2" dependencies: - minimatch "3.0.4" + "minimatch" "3.0.4" -regenerate-unicode-properties@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" - integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== +"regenerate-unicode-properties@^10.0.1": + "integrity" "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==" + "resolved" "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz" + "version" "10.0.1" dependencies: - regenerate "^1.4.2" + "regenerate" "^1.4.2" -regenerate-unicode-properties@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" - integrity sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA== +"regenerate-unicode-properties@^9.0.0": + "integrity" "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==" + "resolved" "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz" + "version" "9.0.0" dependencies: - regenerate "^1.4.2" + "regenerate" "^1.4.2" -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== +"regenerate@^1.4.2": + "integrity" "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + "resolved" "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" + "version" "1.4.2" -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== +"regenerator-runtime@^0.13.4": + "integrity" "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "resolved" "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz" + "version" "0.13.9" -regenerator-transform@^0.14.2: - version "0.14.5" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" - integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== +"regenerator-transform@^0.14.2": + "integrity" "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==" + "resolved" "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz" + "version" "0.14.5" dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" - integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== +"regexp.prototype.flags@^1.2.0": + "integrity" "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==" + "resolved" "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz" + "version" "1.4.1" dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" -regexpu-core@^4.5.4: - version "4.8.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" - integrity sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg== +"regexpu-core@^4.5.4": + "integrity" "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==" + "resolved" "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz" + "version" "4.8.0" dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^9.0.0" - regjsgen "^0.5.2" - regjsparser "^0.7.0" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" + "regenerate" "^1.4.2" + "regenerate-unicode-properties" "^9.0.0" + "regjsgen" "^0.5.2" + "regjsparser" "^0.7.0" + "unicode-match-property-ecmascript" "^2.0.0" + "unicode-match-property-value-ecmascript" "^2.0.0" -regexpu-core@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" - integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== +"regexpu-core@^5.0.1": + "integrity" "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==" + "resolved" "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz" + "version" "5.0.1" dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^10.0.1" - regjsgen "^0.6.0" - regjsparser "^0.8.2" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" + "regenerate" "^1.4.2" + "regenerate-unicode-properties" "^10.0.1" + "regjsgen" "^0.6.0" + "regjsparser" "^0.8.2" + "unicode-match-property-ecmascript" "^2.0.0" + "unicode-match-property-value-ecmascript" "^2.0.0" -registry-auth-token@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" - integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== +"registry-auth-token@^4.0.0": + "integrity" "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==" + "resolved" "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz" + "version" "4.2.1" dependencies: - rc "^1.2.8" + "rc" "^1.2.8" -registry-url@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" - integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== +"registry-url@^5.0.0": + "integrity" "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==" + "resolved" "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz" + "version" "5.1.0" dependencies: - rc "^1.2.8" + "rc" "^1.2.8" -regjsgen@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" - integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== +"regjsgen@^0.5.2": + "integrity" "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" + "resolved" "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz" + "version" "0.5.2" -regjsgen@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" - integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== +"regjsgen@^0.6.0": + "integrity" "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" + "resolved" "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz" + "version" "0.6.0" -regjsparser@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968" - integrity sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ== +"regjsparser@^0.7.0": + "integrity" "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==" + "resolved" "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz" + "version" "0.7.0" dependencies: - jsesc "~0.5.0" + "jsesc" "~0.5.0" -regjsparser@^0.8.2: - version "0.8.4" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" - integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== +"regjsparser@^0.8.2": + "integrity" "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==" + "resolved" "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz" + "version" "0.8.4" dependencies: - jsesc "~0.5.0" + "jsesc" "~0.5.0" -rehype-parse@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-6.0.2.tgz#aeb3fdd68085f9f796f1d3137ae2b85a98406964" - integrity sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug== +"rehype-parse@^6.0.2": + "integrity" "sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug==" + "resolved" "https://registry.npmjs.org/rehype-parse/-/rehype-parse-6.0.2.tgz" + "version" "6.0.2" dependencies: - hast-util-from-parse5 "^5.0.0" - parse5 "^5.0.0" - xtend "^4.0.0" + "hast-util-from-parse5" "^5.0.0" + "parse5" "^5.0.0" + "xtend" "^4.0.0" -relateurl@^0.2.7: - version "0.2.7" - resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= +"relateurl@^0.2.7": + "integrity" "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" + "resolved" "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz" + "version" "0.2.7" -remark-admonitions@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/remark-admonitions/-/remark-admonitions-1.2.1.tgz#87caa1a442aa7b4c0cafa04798ed58a342307870" - integrity sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow== +"remark-admonitions@^1.2.1": + "integrity" "sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow==" + "resolved" "https://registry.npmjs.org/remark-admonitions/-/remark-admonitions-1.2.1.tgz" + "version" "1.2.1" dependencies: - rehype-parse "^6.0.2" - unified "^8.4.2" - unist-util-visit "^2.0.1" + "rehype-parse" "^6.0.2" + "unified" "^8.4.2" + "unist-util-visit" "^2.0.1" -remark-emoji@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/remark-emoji/-/remark-emoji-2.2.0.tgz#1c702090a1525da5b80e15a8f963ef2c8236cac7" - integrity sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w== +"remark-emoji@^2.1.0": + "integrity" "sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w==" + "resolved" "https://registry.npmjs.org/remark-emoji/-/remark-emoji-2.2.0.tgz" + "version" "2.2.0" dependencies: - emoticon "^3.2.0" - node-emoji "^1.10.0" - unist-util-visit "^2.0.3" + "emoticon" "^3.2.0" + "node-emoji" "^1.10.0" + "unist-util-visit" "^2.0.3" -remark-footnotes@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-2.0.0.tgz#9001c4c2ffebba55695d2dd80ffb8b82f7e6303f" - integrity sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ== +"remark-footnotes@2.0.0": + "integrity" "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==" + "resolved" "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz" + "version" "2.0.0" -remark-mdx-remove-exports@^1.6.22: - version "1.6.22" - resolved "https://registry.yarnpkg.com/remark-mdx-remove-exports/-/remark-mdx-remove-exports-1.6.22.tgz#9e34f3d02c9c54b02ca0a1fde946449338d06ecb" - integrity sha512-7g2uiTmTGfz5QyVb+toeX25frbk1Y6yd03RXGPtqx0+DVh86Gb7MkNYbk7H2X27zdZ3CQv1W/JqlFO0Oo8IxVA== +"remark-mdx-remove-exports@^1.6.22": + "integrity" "sha512-7g2uiTmTGfz5QyVb+toeX25frbk1Y6yd03RXGPtqx0+DVh86Gb7MkNYbk7H2X27zdZ3CQv1W/JqlFO0Oo8IxVA==" + "resolved" "https://registry.npmjs.org/remark-mdx-remove-exports/-/remark-mdx-remove-exports-1.6.22.tgz" + "version" "1.6.22" dependencies: - unist-util-remove "2.0.0" + "unist-util-remove" "2.0.0" -remark-mdx-remove-imports@^1.6.22: - version "1.6.22" - resolved "https://registry.yarnpkg.com/remark-mdx-remove-imports/-/remark-mdx-remove-imports-1.6.22.tgz#79f711c95359cff437a120d1fbdc1326ec455826" - integrity sha512-lmjAXD8Ltw0TsvBzb45S+Dxx7LTJAtDaMneMAv8LAUIPEyYoKkmGbmVsiF0/pY6mhM1Q16swCmu1TN+ie/vn/A== +"remark-mdx-remove-imports@^1.6.22": + "integrity" "sha512-lmjAXD8Ltw0TsvBzb45S+Dxx7LTJAtDaMneMAv8LAUIPEyYoKkmGbmVsiF0/pY6mhM1Q16swCmu1TN+ie/vn/A==" + "resolved" "https://registry.npmjs.org/remark-mdx-remove-imports/-/remark-mdx-remove-imports-1.6.22.tgz" + "version" "1.6.22" dependencies: - unist-util-remove "2.0.0" + "unist-util-remove" "2.0.0" -remark-mdx@1.6.22: - version "1.6.22" - resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.6.22.tgz#06a8dab07dcfdd57f3373af7f86bd0e992108bbd" - integrity sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ== +"remark-mdx@1.6.22": + "integrity" "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==" + "resolved" "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz" + "version" "1.6.22" dependencies: "@babel/core" "7.12.9" "@babel/helper-plugin-utils" "7.10.4" "@babel/plugin-proposal-object-rest-spread" "7.12.1" "@babel/plugin-syntax-jsx" "7.12.1" "@mdx-js/util" "1.6.22" - is-alphabetical "1.0.4" - remark-parse "8.0.3" - unified "9.2.0" + "is-alphabetical" "1.0.4" + "remark-parse" "8.0.3" + "unified" "9.2.0" -remark-parse@8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-8.0.3.tgz#9c62aa3b35b79a486454c690472906075f40c7e1" - integrity sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q== +"remark-parse@8.0.3": + "integrity" "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==" + "resolved" "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz" + "version" "8.0.3" dependencies: - ccount "^1.0.0" - collapse-white-space "^1.0.2" - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - is-word-character "^1.0.0" - markdown-escapes "^1.0.0" - parse-entities "^2.0.0" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - trim "0.0.1" - trim-trailing-lines "^1.0.0" - unherit "^1.0.4" - unist-util-remove-position "^2.0.0" - vfile-location "^3.0.0" - xtend "^4.0.1" + "ccount" "^1.0.0" + "collapse-white-space" "^1.0.2" + "is-alphabetical" "^1.0.0" + "is-decimal" "^1.0.0" + "is-whitespace-character" "^1.0.0" + "is-word-character" "^1.0.0" + "markdown-escapes" "^1.0.0" + "parse-entities" "^2.0.0" + "repeat-string" "^1.5.4" + "state-toggle" "^1.0.0" + "trim" "0.0.1" + "trim-trailing-lines" "^1.0.0" + "unherit" "^1.0.4" + "unist-util-remove-position" "^2.0.0" + "vfile-location" "^3.0.0" + "xtend" "^4.0.1" -remark-squeeze-paragraphs@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz#76eb0e085295131c84748c8e43810159c5653ead" - integrity sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw== +"remark-squeeze-paragraphs@4.0.0": + "integrity" "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==" + "resolved" "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz" + "version" "4.0.0" dependencies: - mdast-squeeze-paragraphs "^4.0.0" + "mdast-squeeze-paragraphs" "^4.0.0" -remarkable-admonitions@^0.2.1: - version "0.2.2" - resolved "https://registry.yarnpkg.com/remarkable-admonitions/-/remarkable-admonitions-0.2.2.tgz#8765f9ec66be4f4c651a4e1cfb559dd7f920819c" - integrity sha512-CcMTEcLYmJLXX3IVMk4LyW4oFD2NQxh5FeLzn4k89TAPpyWIeVix/B/g/gDbZAUpCNY9l6heovR5NNIktf8X5A== +"remarkable-admonitions@^0.2.1": + "integrity" "sha512-CcMTEcLYmJLXX3IVMk4LyW4oFD2NQxh5FeLzn4k89TAPpyWIeVix/B/g/gDbZAUpCNY9l6heovR5NNIktf8X5A==" + "resolved" "https://registry.npmjs.org/remarkable-admonitions/-/remarkable-admonitions-0.2.2.tgz" + "version" "0.2.2" -renderkid@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" - integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== +"renderkid@^3.0.0": + "integrity" "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==" + "resolved" "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz" + "version" "3.0.0" dependencies: - css-select "^4.1.3" - dom-converter "^0.2.0" - htmlparser2 "^6.1.0" - lodash "^4.17.21" - strip-ansi "^6.0.1" + "css-select" "^4.1.3" + "dom-converter" "^0.2.0" + "htmlparser2" "^6.1.0" + "lodash" "^4.17.21" + "strip-ansi" "^6.0.1" -repeat-string@^1.5.4: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= +"repeat-string@^1.5.4": + "integrity" "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "resolved" "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + "version" "1.6.1" -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +"require-from-string@^2.0.2": + "integrity" "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + "resolved" "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + "version" "2.0.2" "require-like@>= 0.1.1": - version "0.1.2" - resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" - integrity sha1-rW8wwTvs15cBDEaK+ndcDAprR/o= + "integrity" "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=" + "resolved" "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" + "version" "0.1.2" -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +"requires-port@^1.0.0": + "integrity" "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + "resolved" "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + "version" "1.0.0" -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +"resolve-from@^4.0.0": + "integrity" "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + "resolved" "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + "version" "4.0.0" -resolve-pathname@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" - integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== +"resolve-pathname@^3.0.0": + "integrity" "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + "resolved" "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz" + "version" "3.0.0" -resolve@^1.1.6, resolve@^1.14.2, resolve@^1.3.2: - version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== +"resolve@^1.1.6", "resolve@^1.14.2", "resolve@^1.3.2": + "integrity" "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==" + "resolved" "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz" + "version" "1.22.0" dependencies: - is-core-module "^2.8.1" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" + "is-core-module" "^2.8.1" + "path-parse" "^1.0.7" + "supports-preserve-symlinks-flag" "^1.0.0" -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= +"responselike@^1.0.2": + "integrity" "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=" + "resolved" "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" + "version" "1.0.2" dependencies: - lowercase-keys "^1.0.0" + "lowercase-keys" "^1.0.0" -retry@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== +"retry@^0.13.1": + "integrity" "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + "resolved" "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" + "version" "0.13.1" -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +"reusify@^1.0.4": + "integrity" "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + "resolved" "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + "version" "1.0.4" -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== +"rimraf@^3.0.2": + "integrity" "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==" + "resolved" "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + "version" "3.0.2" dependencies: - glob "^7.1.3" + "glob" "^7.1.3" -rtl-detect@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.0.4.tgz#40ae0ea7302a150b96bc75af7d749607392ecac6" - integrity sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ== +"rtl-detect@^1.0.4": + "integrity" "sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==" + "resolved" "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.4.tgz" + "version" "1.0.4" -rtlcss@^3.3.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/rtlcss/-/rtlcss-3.5.0.tgz#c9eb91269827a102bac7ae3115dd5d049de636c3" - integrity sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A== +"rtlcss@^3.3.0": + "integrity" "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==" + "resolved" "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz" + "version" "3.5.0" dependencies: - find-up "^5.0.0" - picocolors "^1.0.0" - postcss "^8.3.11" - strip-json-comments "^3.1.1" + "find-up" "^5.0.0" + "picocolors" "^1.0.0" + "postcss" "^8.3.11" + "strip-json-comments" "^3.1.1" -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== +"run-parallel@^1.1.9": + "integrity" "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==" + "resolved" "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + "version" "1.2.0" dependencies: - queue-microtask "^1.2.2" + "queue-microtask" "^1.2.2" -rxjs@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.4.tgz#3d6bd407e6b7ce9a123e76b1e770dc5761aa368d" - integrity sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ== +"rxjs@^7.5.4": + "integrity" "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==" + "resolved" "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz" + "version" "7.5.4" dependencies: - tslib "^2.1.0" + "tslib" "^2.1.0" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +"safe-buffer@^5.0.1", "safe-buffer@^5.1.0", "safe-buffer@>=5.1.0", "safe-buffer@~5.2.0", "safe-buffer@5.2.1": + "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + "version" "5.2.1" -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +"safe-buffer@~5.1.0", "safe-buffer@~5.1.1": + "integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + "version" "5.1.2" + +"safe-buffer@5.1.2": + "integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + "version" "5.1.2" "safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + "integrity" "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "resolved" "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + "version" "2.1.2" -sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +"sax@^1.2.4": + "integrity" "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "resolved" "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" + "version" "1.2.4" -scheduler@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" - integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== +"scheduler@^0.19.1": + "integrity" "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==" + "resolved" "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz" + "version" "0.19.1" dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" + "loose-envify" "^1.1.0" + "object-assign" "^4.1.1" -schema-utils@2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== - dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" - -schema-utils@^2.6.5: - version "2.7.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" - integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== +"schema-utils@^2.6.5": + "integrity" "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz" + "version" "2.7.1" dependencies: "@types/json-schema" "^7.0.5" - ajv "^6.12.4" - ajv-keywords "^3.5.2" + "ajv" "^6.12.4" + "ajv-keywords" "^3.5.2" -schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== +"schema-utils@^3.0.0", "schema-utils@^3.1.0", "schema-utils@^3.1.1": + "integrity" "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz" + "version" "3.1.1" dependencies: "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" + "ajv" "^6.12.5" + "ajv-keywords" "^3.5.2" -schema-utils@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" - integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== +"schema-utils@^4.0.0": + "integrity" "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz" + "version" "4.0.0" dependencies: "@types/json-schema" "^7.0.9" - ajv "^8.8.0" - ajv-formats "^2.1.1" - ajv-keywords "^5.0.0" + "ajv" "^8.8.0" + "ajv-formats" "^2.1.1" + "ajv-keywords" "^5.0.0" -section-matter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" - integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== +"schema-utils@2.7.0": + "integrity" "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" + "version" "2.7.0" dependencies: - extend-shallow "^2.0.1" - kind-of "^6.0.0" + "@types/json-schema" "^7.0.4" + "ajv" "^6.12.2" + "ajv-keywords" "^3.4.1" -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= - -selfsigned@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.0.0.tgz#e927cd5377cbb0a1075302cff8df1042cc2bce5b" - integrity sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ== +"section-matter@^1.0.0": + "integrity" "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==" + "resolved" "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz" + "version" "1.0.0" dependencies: - node-forge "^1.2.0" + "extend-shallow" "^2.0.1" + "kind-of" "^6.0.0" -semver-diff@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" - integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== +"select-hose@^2.0.0": + "integrity" "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + "resolved" "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" + "version" "2.0.0" + +"selfsigned@^2.0.0": + "integrity" "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==" + "resolved" "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz" + "version" "2.0.0" dependencies: - semver "^6.3.0" + "node-forge" "^1.2.0" -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== +"semver-diff@^3.1.1": + "integrity" "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==" + "resolved" "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz" + "version" "3.1.1" dependencies: - lru-cache "^6.0.0" + "semver" "^6.3.0" -send@0.17.2: - version "0.17.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" - integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== +"semver@^5.4.1": + "integrity" "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "resolved" "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" + "version" "5.7.1" + +"semver@^6.0.0", "semver@^6.1.1", "semver@^6.1.2", "semver@^6.2.0", "semver@^6.3.0": + "integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + "version" "6.3.0" + +"semver@^7.3.2": + "integrity" "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" + "version" "7.3.5" dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "1.8.1" - mime "1.6.0" - ms "2.1.3" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" + "lru-cache" "^6.0.0" -serialize-javascript@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== +"semver@^7.3.4": + "integrity" "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" + "version" "7.3.5" dependencies: - randombytes "^2.1.0" + "lru-cache" "^6.0.0" -serve-handler@^6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.3.tgz#1bf8c5ae138712af55c758477533b9117f6435e8" - integrity sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w== +"semver@^7.3.5": + "integrity" "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" + "version" "7.3.5" dependencies: - bytes "3.0.0" - content-disposition "0.5.2" - fast-url-parser "1.1.3" - mime-types "2.1.18" - minimatch "3.0.4" - path-is-inside "1.0.2" - path-to-regexp "2.2.1" - range-parser "1.2.0" + "lru-cache" "^6.0.0" -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= +"semver@7.0.0": + "integrity" "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz" + "version" "7.0.0" + +"send@0.17.2": + "integrity" "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==" + "resolved" "https://registry.npmjs.org/send/-/send-0.17.2.tgz" + "version" "0.17.2" dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" + "debug" "2.6.9" + "depd" "~1.1.2" + "destroy" "~1.0.4" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "etag" "~1.8.1" + "fresh" "0.5.2" + "http-errors" "1.8.1" + "mime" "1.6.0" + "ms" "2.1.3" + "on-finished" "~2.3.0" + "range-parser" "~1.2.1" + "statuses" "~1.5.0" -serve-static@1.14.2: - version "1.14.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" - integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== +"serialize-javascript@^6.0.0": + "integrity" "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==" + "resolved" "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" + "version" "6.0.0" dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.2" + "randombytes" "^2.1.0" -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== +"serve-handler@^6.1.3": + "integrity" "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==" + "resolved" "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz" + "version" "6.1.3" dependencies: - kind-of "^6.0.2" + "bytes" "3.0.0" + "content-disposition" "0.5.2" + "fast-url-parser" "1.1.3" + "mime-types" "2.1.18" + "minimatch" "3.0.4" + "path-is-inside" "1.0.2" + "path-to-regexp" "2.2.1" + "range-parser" "1.2.0" -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== +"serve-index@^1.9.1": + "integrity" "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=" + "resolved" "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" + "version" "1.9.1" dependencies: - shebang-regex "^3.0.0" + "accepts" "~1.3.4" + "batch" "0.6.1" + "debug" "2.6.9" + "escape-html" "~1.0.3" + "http-errors" "~1.6.2" + "mime-types" "~2.1.17" + "parseurl" "~1.3.2" -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shell-quote@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" - integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== - -shelljs@^0.8.4: - version "0.8.5" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" - integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== +"serve-static@1.14.2": + "integrity" "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==" + "resolved" "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz" + "version" "1.14.2" dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "parseurl" "~1.3.3" + "send" "0.17.2" -signal-exit@^3.0.2, signal-exit@^3.0.3: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +"setimmediate@^1.0.5": + "integrity" "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + "resolved" "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" + "version" "1.0.5" -sirv@^1.0.7: - version "1.0.19" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" - integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== +"setprototypeof@1.1.0": + "integrity" "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + "resolved" "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" + "version" "1.1.0" + +"setprototypeof@1.2.0": + "integrity" "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "resolved" "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + "version" "1.2.0" + +"shallow-clone@^3.0.0": + "integrity" "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==" + "resolved" "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "kind-of" "^6.0.2" + +"shebang-command@^2.0.0": + "integrity" "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==" + "resolved" "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "shebang-regex" "^3.0.0" + +"shebang-regex@^3.0.0": + "integrity" "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + "resolved" "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + "version" "3.0.0" + +"shell-quote@^1.7.3": + "integrity" "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + "resolved" "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz" + "version" "1.7.3" + +"shelljs@^0.8.4": + "integrity" "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==" + "resolved" "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz" + "version" "0.8.5" + dependencies: + "glob" "^7.0.0" + "interpret" "^1.0.0" + "rechoir" "^0.6.2" + +"signal-exit@^3.0.2", "signal-exit@^3.0.3": + "integrity" "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "resolved" "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + "version" "3.0.7" + +"sirv@^1.0.7": + "integrity" "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==" + "resolved" "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz" + "version" "1.0.19" dependencies: "@polka/url" "^1.0.0-next.20" - mrmime "^1.0.0" - totalist "^1.0.0" + "mrmime" "^1.0.0" + "totalist" "^1.0.0" -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +"sisteransi@^1.0.5": + "integrity" "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + "resolved" "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" + "version" "1.0.5" -sitemap@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-7.1.1.tgz#eeed9ad6d95499161a3eadc60f8c6dce4bea2bef" - integrity sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg== +"sitemap@^7.0.0": + "integrity" "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==" + "resolved" "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz" + "version" "7.1.1" dependencies: "@types/node" "^17.0.5" "@types/sax" "^1.2.1" - arg "^5.0.0" - sax "^1.2.4" + "arg" "^5.0.0" + "sax" "^1.2.4" -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +"slash@^3.0.0": + "integrity" "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + "resolved" "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + "version" "3.0.0" -slash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" - integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +"slash@^4.0.0": + "integrity" "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + "resolved" "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" + "version" "4.0.0" -sockjs@^0.3.21: - version "0.3.24" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" - integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== +"sockjs@^0.3.21": + "integrity" "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==" + "resolved" "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" + "version" "0.3.24" dependencies: - faye-websocket "^0.11.3" - uuid "^8.3.2" - websocket-driver "^0.7.4" + "faye-websocket" "^0.11.3" + "uuid" "^8.3.2" + "websocket-driver" "^0.7.4" -sort-css-media-queries@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.0.4.tgz#b2badfa519cb4a938acbc6d3aaa913d4949dc908" - integrity sha512-PAIsEK/XupCQwitjv7XxoMvYhT7EAfyzI3hsy/MyDgTvc+Ft55ctdkctJLOy6cQejaIC+zjpUL4djFVm2ivOOw== +"sort-css-media-queries@2.0.4": + "integrity" "sha512-PAIsEK/XupCQwitjv7XxoMvYhT7EAfyzI3hsy/MyDgTvc+Ft55ctdkctJLOy6cQejaIC+zjpUL4djFVm2ivOOw==" + "resolved" "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.0.4.tgz" + "version" "2.0.4" -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +"source-list-map@^2.0.0": + "integrity" "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + "resolved" "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz" + "version" "2.0.1" -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +"source-map-js@^1.0.2": + "integrity" "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + "resolved" "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" + "version" "1.0.2" -source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== +"source-map-support@~0.5.20": + "integrity" "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==" + "resolved" "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + "version" "0.5.21" dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" + "buffer-from" "^1.0.0" + "source-map" "^0.6.0" -source-map@^0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= +"source-map@^0.5.0": + "integrity" "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + "version" "0.5.7" -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +"source-map@^0.6.0", "source-map@^0.6.1", "source-map@~0.6.0", "source-map@~0.6.1": + "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + "version" "0.6.1" -sourcemap-codec@^1.4.4: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +"sourcemap-codec@^1.4.4": + "integrity" "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "resolved" "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" + "version" "1.4.8" -space-separated-tokens@^1.0.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" - integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== +"space-separated-tokens@^1.0.0": + "integrity" "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" + "resolved" "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz" + "version" "1.1.5" -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== +"spdy-transport@^3.0.0": + "integrity" "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==" + "resolved" "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz" + "version" "3.0.0" dependencies: - debug "^4.1.0" - detect-node "^2.0.4" - hpack.js "^2.1.6" - obuf "^1.1.2" - readable-stream "^3.0.6" - wbuf "^1.7.3" + "debug" "^4.1.0" + "detect-node" "^2.0.4" + "hpack.js" "^2.1.6" + "obuf" "^1.1.2" + "readable-stream" "^3.0.6" + "wbuf" "^1.7.3" -spdy@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" - integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== +"spdy@^4.0.2": + "integrity" "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==" + "resolved" "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz" + "version" "4.0.2" dependencies: - debug "^4.1.0" - handle-thing "^2.0.0" - http-deceiver "^1.2.7" - select-hose "^2.0.0" - spdy-transport "^3.0.0" + "debug" "^4.1.0" + "handle-thing" "^2.0.0" + "http-deceiver" "^1.2.7" + "select-hose" "^2.0.0" + "spdy-transport" "^3.0.0" -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +"sprintf-js@~1.0.2": + "integrity" "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "resolved" "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + "version" "1.0.3" -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +"stable@^0.1.8": + "integrity" "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + "resolved" "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" + "version" "0.1.8" -state-toggle@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" - integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== +"state-toggle@^1.0.0": + "integrity" "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==" + "resolved" "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz" + "version" "1.0.3" -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", "statuses@~1.5.0": + "integrity" "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + "resolved" "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + "version" "1.5.0" -std-env@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.0.1.tgz#bc4cbc0e438610197e34c2d79c3df30b491f5182" - integrity sha512-mC1Ps9l77/97qeOZc+HrOL7TIaOboHqMZ24dGVQrlxFcpPpfCHpH+qfUT7Dz+6mlG8+JPA1KfBQo19iC/+Ngcw== +"std-env@^3.0.1": + "integrity" "sha512-mC1Ps9l77/97qeOZc+HrOL7TIaOboHqMZ24dGVQrlxFcpPpfCHpH+qfUT7Dz+6mlG8+JPA1KfBQo19iC/+Ngcw==" + "resolved" "https://registry.npmjs.org/std-env/-/std-env-3.0.1.tgz" + "version" "3.0.1" -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== +"string_decoder@^1.1.1": + "integrity" "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==" + "resolved" "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + "version" "1.3.0" dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" + "safe-buffer" "~5.2.0" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== +"string_decoder@~1.1.1": + "integrity" "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==" + "resolved" "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + "version" "1.1.1" dependencies: - safe-buffer "~5.2.0" + "safe-buffer" "~5.1.0" -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== +"string-width@^4.0.0", "string-width@^4.1.0", "string-width@^4.2.2": + "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" + "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + "version" "4.2.3" dependencies: - safe-buffer "~5.1.0" + "emoji-regex" "^8.0.0" + "is-fullwidth-code-point" "^3.0.0" + "strip-ansi" "^6.0.1" -stringify-object@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" - integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== +"stringify-object@^3.3.0": + "integrity" "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==" + "resolved" "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz" + "version" "3.3.0" dependencies: - get-own-enumerable-property-symbols "^3.0.0" - is-obj "^1.0.1" - is-regexp "^1.0.0" + "get-own-enumerable-property-symbols" "^3.0.0" + "is-obj" "^1.0.1" + "is-regexp" "^1.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== +"strip-ansi@^6.0.0", "strip-ansi@^6.0.1": + "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + "version" "6.0.1" dependencies: - ansi-regex "^5.0.1" + "ansi-regex" "^5.0.1" -strip-ansi@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" - integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== +"strip-ansi@^7.0.0": + "integrity" "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz" + "version" "7.0.1" dependencies: - ansi-regex "^6.0.1" + "ansi-regex" "^6.0.1" -strip-bom-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" - integrity sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI= +"strip-bom-string@^1.0.0": + "integrity" "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=" + "resolved" "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz" + "version" "1.0.0" -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +"strip-final-newline@^2.0.0": + "integrity" "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + "resolved" "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + "version" "2.0.0" -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +"strip-json-comments@^3.1.1": + "integrity" "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + "version" "3.1.1" -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +"strip-json-comments@~2.0.1": + "integrity" "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + "version" "2.0.1" -style-to-object@0.3.0, style-to-object@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" - integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== +"style-to-object@^0.3.0", "style-to-object@0.3.0": + "integrity" "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==" + "resolved" "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz" + "version" "0.3.0" dependencies: - inline-style-parser "0.1.1" + "inline-style-parser" "0.1.1" -stylehacks@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.0.3.tgz#2ef3de567bfa2be716d29a93bf3d208c133e8d04" - integrity sha512-ENcUdpf4yO0E1rubu8rkxI+JGQk4CgjchynZ4bDBJDfqdy+uhTRSWb8/F3Jtu+Bw5MW45Po3/aQGeIyyxgQtxg== +"stylehacks@^5.0.3": + "integrity" "sha512-ENcUdpf4yO0E1rubu8rkxI+JGQk4CgjchynZ4bDBJDfqdy+uhTRSWb8/F3Jtu+Bw5MW45Po3/aQGeIyyxgQtxg==" + "resolved" "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.3.tgz" + "version" "5.0.3" dependencies: - browserslist "^4.16.6" - postcss-selector-parser "^6.0.4" + "browserslist" "^4.16.6" + "postcss-selector-parser" "^6.0.4" -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== +"supports-color@^5.3.0": + "integrity" "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + "version" "5.5.0" dependencies: - has-flag "^3.0.0" + "has-flag" "^3.0.0" -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== +"supports-color@^7.1.0": + "integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + "version" "7.2.0" dependencies: - has-flag "^4.0.0" + "has-flag" "^4.0.0" -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== +"supports-color@^8.0.0": + "integrity" "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + "version" "8.1.1" dependencies: - has-flag "^4.0.0" + "has-flag" "^4.0.0" -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +"supports-preserve-symlinks-flag@^1.0.0": + "integrity" "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "resolved" "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + "version" "1.0.0" -svg-parser@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" - integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== +"svg-parser@^2.0.2": + "integrity" "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + "resolved" "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz" + "version" "2.0.4" -svgo@^2.5.0, svgo@^2.7.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" - integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== +"svgo@^2.5.0", "svgo@^2.7.0": + "integrity" "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==" + "resolved" "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz" + "version" "2.8.0" dependencies: "@trysound/sax" "0.2.0" - commander "^7.2.0" - css-select "^4.1.3" - css-tree "^1.1.3" - csso "^4.2.0" - picocolors "^1.0.0" - stable "^0.1.8" + "commander" "^7.2.0" + "css-select" "^4.1.3" + "css-tree" "^1.1.3" + "csso" "^4.2.0" + "picocolors" "^1.0.0" + "stable" "^0.1.8" -tapable@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +"tapable@^1.0.0": + "integrity" "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + "resolved" "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz" + "version" "1.1.3" -tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== +"tapable@^2.0.0", "tapable@^2.1.1", "tapable@^2.2.0": + "integrity" "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + "resolved" "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + "version" "2.2.1" -terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.2.4: - version "5.3.1" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz#0320dcc270ad5372c1e8993fabbd927929773e54" - integrity sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g== +"terser-webpack-plugin@^5.1.3", "terser-webpack-plugin@^5.2.4": + "integrity" "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==" + "resolved" "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz" + "version" "5.3.1" dependencies: - jest-worker "^27.4.5" - schema-utils "^3.1.1" - serialize-javascript "^6.0.0" - source-map "^0.6.1" - terser "^5.7.2" + "jest-worker" "^27.4.5" + "schema-utils" "^3.1.1" + "serialize-javascript" "^6.0.0" + "source-map" "^0.6.1" + "terser" "^5.7.2" -terser@^5.10.0, terser@^5.7.2: - version "5.14.2" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10" - integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA== +"terser@^5.10.0", "terser@^5.7.2": + "integrity" "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==" + "resolved" "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz" + "version" "5.14.2" dependencies: "@jridgewell/source-map" "^0.3.2" - acorn "^8.5.0" - commander "^2.20.0" - source-map-support "~0.5.20" + "acorn" "^8.5.0" + "commander" "^2.20.0" + "source-map-support" "~0.5.20" -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +"text-table@^0.2.0": + "integrity" "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + "resolved" "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + "version" "0.2.0" -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +"thunky@^1.0.2": + "integrity" "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + "resolved" "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" + "version" "1.1.0" -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" - integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= +"timsort@^0.3.0": + "integrity" "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + "resolved" "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz" + "version" "0.3.0" -tiny-invariant@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" - integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== +"tiny-invariant@^1.0.2": + "integrity" "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" + "resolved" "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz" + "version" "1.2.0" -tiny-warning@^1.0.0, tiny-warning@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" - integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +"tiny-warning@^1.0.0", "tiny-warning@^1.0.3": + "integrity" "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + "resolved" "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz" + "version" "1.0.3" -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= +"to-fast-properties@^2.0.0": + "integrity" "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "resolved" "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + "version" "2.0.0" -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== +"to-readable-stream@^1.0.0": + "integrity" "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + "resolved" "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" + "version" "1.0.0" -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== +"to-regex-range@^5.0.1": + "integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==" + "resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + "version" "5.0.1" dependencies: - is-number "^7.0.0" + "is-number" "^7.0.0" -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +"toidentifier@1.0.1": + "integrity" "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + "resolved" "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + "version" "1.0.1" -totalist@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" - integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +"totalist@^1.0.0": + "integrity" "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==" + "resolved" "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz" + "version" "1.1.0" -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +"tr46@~0.0.3": + "integrity" "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "resolved" "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + "version" "0.0.3" -trim-trailing-lines@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" - integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== +"trim-trailing-lines@^1.0.0": + "integrity" "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==" + "resolved" "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz" + "version" "1.1.4" -trim@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" - integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= +"trim@0.0.1": + "integrity" "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" + "resolved" "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz" + "version" "0.0.1" -trough@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" - integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== +"trough@^1.0.0": + "integrity" "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" + "resolved" "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz" + "version" "1.0.5" -tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +"tslib@^2.0.3", "tslib@^2.1.0", "tslib@^2.2.0", "tslib@^2.3.1": + "integrity" "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" + "version" "2.3.1" -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +"type-fest@^0.20.2": + "integrity" "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + "version" "0.20.2" -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== +"type-is@~1.6.18": + "integrity" "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==" + "resolved" "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" + "version" "1.6.18" dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" + "media-typer" "0.3.0" + "mime-types" "~2.1.24" -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== +"typedarray-to-buffer@^3.1.5": + "integrity" "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==" + "resolved" "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" + "version" "3.1.5" dependencies: - is-typedarray "^1.0.0" + "is-typedarray" "^1.0.0" -ua-parser-js@^0.7.30: - version "0.7.33" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" - integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== +"typescript@>= 2.7": + "integrity" "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" + "resolved" "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + "version" "4.9.5" -unherit@^1.0.4: - version "1.1.3" - resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" - integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== +"ua-parser-js@^0.7.30": + "integrity" "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==" + "resolved" "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz" + "version" "0.7.33" + +"unherit@^1.0.4": + "integrity" "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==" + "resolved" "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz" + "version" "1.1.3" dependencies: - inherits "^2.0.0" - xtend "^4.0.0" + "inherits" "^2.0.0" + "xtend" "^4.0.0" -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== +"unicode-canonical-property-names-ecmascript@^2.0.0": + "integrity" "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + "resolved" "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz" + "version" "2.0.0" -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== +"unicode-match-property-ecmascript@^2.0.0": + "integrity" "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==" + "resolved" "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz" + "version" "2.0.0" dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" + "unicode-canonical-property-names-ecmascript" "^2.0.0" + "unicode-property-aliases-ecmascript" "^2.0.0" -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== +"unicode-match-property-value-ecmascript@^2.0.0": + "integrity" "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" + "resolved" "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz" + "version" "2.0.0" -unicode-property-aliases-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" - integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== +"unicode-property-aliases-ecmascript@^2.0.0": + "integrity" "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" + "resolved" "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz" + "version" "2.0.0" -unified@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8" - integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== +"unified@^8.4.2": + "integrity" "sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA==" + "resolved" "https://registry.npmjs.org/unified/-/unified-8.4.2.tgz" + "version" "8.4.2" dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-buffer "^2.0.0" - is-plain-obj "^2.0.0" - trough "^1.0.0" - vfile "^4.0.0" + "bail" "^1.0.0" + "extend" "^3.0.0" + "is-plain-obj" "^2.0.0" + "trough" "^1.0.0" + "vfile" "^4.0.0" -unified@^8.4.2: - version "8.4.2" - resolved "https://registry.yarnpkg.com/unified/-/unified-8.4.2.tgz#13ad58b4a437faa2751a4a4c6a16f680c500fff1" - integrity sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA== +"unified@9.2.0": + "integrity" "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==" + "resolved" "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz" + "version" "9.2.0" dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-plain-obj "^2.0.0" - trough "^1.0.0" - vfile "^4.0.0" + "bail" "^1.0.0" + "extend" "^3.0.0" + "is-buffer" "^2.0.0" + "is-plain-obj" "^2.0.0" + "trough" "^1.0.0" + "vfile" "^4.0.0" -unique-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== +"unique-string@^2.0.0": + "integrity" "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==" + "resolved" "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz" + "version" "2.0.0" dependencies: - crypto-random-string "^2.0.0" + "crypto-random-string" "^2.0.0" -unist-builder@2.0.3, unist-builder@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" - integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== +"unist-builder@^2.0.0", "unist-builder@2.0.3": + "integrity" "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==" + "resolved" "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz" + "version" "2.0.3" -unist-util-generated@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" - integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== +"unist-util-generated@^1.0.0": + "integrity" "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==" + "resolved" "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz" + "version" "1.1.6" -unist-util-is@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" - integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== +"unist-util-is@^4.0.0": + "integrity" "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==" + "resolved" "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz" + "version" "4.1.0" -unist-util-position@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" - integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== +"unist-util-position@^3.0.0": + "integrity" "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==" + "resolved" "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz" + "version" "3.1.0" -unist-util-remove-position@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" - integrity sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA== +"unist-util-remove-position@^2.0.0": + "integrity" "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==" + "resolved" "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz" + "version" "2.0.1" dependencies: - unist-util-visit "^2.0.0" + "unist-util-visit" "^2.0.0" -unist-util-remove@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.0.0.tgz#32c2ad5578802f2ca62ab808173d505b2c898488" - integrity sha512-HwwWyNHKkeg/eXRnE11IpzY8JT55JNM1YCwwU9YNCnfzk6s8GhPXrVBBZWiwLeATJbI7euvoGSzcy9M29UeW3g== +"unist-util-remove@^2.0.0": + "integrity" "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==" + "resolved" "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.1.0.tgz" + "version" "2.1.0" dependencies: - unist-util-is "^4.0.0" + "unist-util-is" "^4.0.0" -unist-util-remove@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.1.0.tgz#b0b4738aa7ee445c402fda9328d604a02d010588" - integrity sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q== +"unist-util-remove@2.0.0": + "integrity" "sha512-HwwWyNHKkeg/eXRnE11IpzY8JT55JNM1YCwwU9YNCnfzk6s8GhPXrVBBZWiwLeATJbI7euvoGSzcy9M29UeW3g==" + "resolved" "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.0.0.tgz" + "version" "2.0.0" dependencies: - unist-util-is "^4.0.0" + "unist-util-is" "^4.0.0" -unist-util-stringify-position@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" - integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== +"unist-util-stringify-position@^2.0.0": + "integrity" "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==" + "resolved" "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz" + "version" "2.0.3" dependencies: "@types/unist" "^2.0.2" -unist-util-visit-parents@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" - integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== +"unist-util-visit-parents@^3.0.0": + "integrity" "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==" + "resolved" "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz" + "version" "3.1.1" dependencies: "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" + "unist-util-is" "^4.0.0" -unist-util-visit@2.0.3, unist-util-visit@^2.0.0, unist-util-visit@^2.0.1, unist-util-visit@^2.0.2, unist-util-visit@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" - integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== +"unist-util-visit@^2.0.0", "unist-util-visit@^2.0.1", "unist-util-visit@^2.0.2", "unist-util-visit@^2.0.3", "unist-util-visit@2.0.3": + "integrity" "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==" + "resolved" "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz" + "version" "2.0.3" dependencies: "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" - unist-util-visit-parents "^3.0.0" + "unist-util-is" "^4.0.0" + "unist-util-visit-parents" "^3.0.0" -universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +"universalify@^2.0.0": + "integrity" "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + "resolved" "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" + "version" "2.0.0" -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= +"unpipe@~1.0.0", "unpipe@1.0.0": + "integrity" "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "resolved" "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + "version" "1.0.0" -update-notifier@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" - integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== +"update-browserslist-db@^1.0.10": + "integrity" "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==" + "resolved" "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz" + "version" "1.0.10" dependencies: - boxen "^5.0.0" - chalk "^4.1.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.4.0" - is-npm "^5.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.1.0" - pupa "^2.1.1" - semver "^7.3.4" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" + "escalade" "^3.1.1" + "picocolors" "^1.0.0" -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== +"update-notifier@^5.1.0": + "integrity" "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==" + "resolved" "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz" + "version" "5.1.0" dependencies: - punycode "^2.1.0" + "boxen" "^5.0.0" + "chalk" "^4.1.0" + "configstore" "^5.0.1" + "has-yarn" "^2.1.0" + "import-lazy" "^2.1.0" + "is-ci" "^2.0.0" + "is-installed-globally" "^0.4.0" + "is-npm" "^5.0.0" + "is-yarn-global" "^0.3.0" + "latest-version" "^5.1.0" + "pupa" "^2.1.1" + "semver" "^7.3.4" + "semver-diff" "^3.1.1" + "xdg-basedir" "^4.0.0" -url-loader@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" - integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== +"uri-js@^4.2.2": + "integrity" "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==" + "resolved" "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + "version" "4.4.1" dependencies: - loader-utils "^2.0.0" - mime-types "^2.1.27" - schema-utils "^3.0.0" + "punycode" "^2.1.0" -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= +"url-loader@^4.1.1": + "integrity" "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==" + "resolved" "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz" + "version" "4.1.1" dependencies: - prepend-http "^2.0.0" + "loader-utils" "^2.0.0" + "mime-types" "^2.1.27" + "schema-utils" "^3.0.0" -url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= +"url-parse-lax@^3.0.0": + "integrity" "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=" + "resolved" "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" + "version" "3.0.0" dependencies: - punycode "1.3.2" - querystring "0.2.0" + "prepend-http" "^2.0.0" -use-composed-ref@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.2.1.tgz#9bdcb5ccd894289105da2325e1210079f56bf849" - integrity sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw== - -use-isomorphic-layout-effect@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz#7bb6589170cd2987a152042f9084f9effb75c225" - integrity sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ== - -use-latest@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.2.0.tgz#a44f6572b8288e0972ec411bdd0840ada366f232" - integrity sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw== +"url@^0.11.0": + "integrity" "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=" + "resolved" "https://registry.npmjs.org/url/-/url-0.11.0.tgz" + "version" "0.11.0" dependencies: - use-isomorphic-layout-effect "^1.0.0" + "punycode" "1.3.2" + "querystring" "0.2.0" -util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +"use-composed-ref@^1.0.0": + "integrity" "sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw==" + "resolved" "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.2.1.tgz" + "version" "1.2.1" -utila@~0.4: - version "0.4.0" - resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" - integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= +"use-isomorphic-layout-effect@^1.0.0": + "integrity" "sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ==" + "resolved" "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz" + "version" "1.1.1" -utility-types@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" - integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== +"use-latest@^1.0.0": + "integrity" "sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw==" + "resolved" "https://registry.npmjs.org/use-latest/-/use-latest-1.2.0.tgz" + "version" "1.2.0" + dependencies: + "use-isomorphic-layout-effect" "^1.0.0" -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= +"util-deprecate@^1.0.1", "util-deprecate@^1.0.2", "util-deprecate@~1.0.1": + "integrity" "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "resolved" "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "version" "1.0.2" -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +"utila@~0.4": + "integrity" "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" + "resolved" "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz" + "version" "0.4.0" -value-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" - integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== +"utility-types@^3.10.0": + "integrity" "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==" + "resolved" "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz" + "version" "3.10.0" -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +"utils-merge@1.0.1": + "integrity" "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "resolved" "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" + "version" "1.0.1" -vfile-location@^3.0.0, vfile-location@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" - integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== +"uuid@^8.3.2": + "integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "resolved" "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + "version" "8.3.2" -vfile-message@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" - integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== +"value-equal@^1.0.1": + "integrity" "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + "resolved" "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz" + "version" "1.0.1" + +"vary@~1.1.2": + "integrity" "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "resolved" "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + "version" "1.1.2" + +"vfile-location@^3.0.0", "vfile-location@^3.2.0": + "integrity" "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==" + "resolved" "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz" + "version" "3.2.0" + +"vfile-message@^2.0.0": + "integrity" "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==" + "resolved" "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz" + "version" "2.0.4" dependencies: "@types/unist" "^2.0.0" - unist-util-stringify-position "^2.0.0" + "unist-util-stringify-position" "^2.0.0" -vfile@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" - integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== +"vfile@^4.0.0": + "integrity" "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==" + "resolved" "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz" + "version" "4.2.1" dependencies: "@types/unist" "^2.0.0" - is-buffer "^2.0.0" - unist-util-stringify-position "^2.0.0" - vfile-message "^2.0.0" + "is-buffer" "^2.0.0" + "unist-util-stringify-position" "^2.0.0" + "vfile-message" "^2.0.0" -wait-on@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.1.tgz#16bbc4d1e4ebdd41c5b4e63a2e16dbd1f4e5601e" - integrity sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw== +"wait-on@^6.0.0": + "integrity" "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==" + "resolved" "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz" + "version" "6.0.1" dependencies: - axios "^0.25.0" - joi "^17.6.0" - lodash "^4.17.21" - minimist "^1.2.5" - rxjs "^7.5.4" + "axios" "^0.25.0" + "joi" "^17.6.0" + "lodash" "^4.17.21" + "minimist" "^1.2.5" + "rxjs" "^7.5.4" -watchpack@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.1.tgz#4200d9447b401156eeca7767ee610f8809bc9d25" - integrity sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA== +"watchpack@^2.3.1": + "integrity" "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==" + "resolved" "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz" + "version" "2.3.1" dependencies: - glob-to-regexp "^0.4.1" - graceful-fs "^4.1.2" + "glob-to-regexp" "^0.4.1" + "graceful-fs" "^4.1.2" -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== +"wbuf@^1.1.0", "wbuf@^1.7.3": + "integrity" "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==" + "resolved" "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz" + "version" "1.7.3" dependencies: - minimalistic-assert "^1.0.0" + "minimalistic-assert" "^1.0.0" -web-namespaces@^1.0.0, web-namespaces@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" - integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== +"web-namespaces@^1.0.0", "web-namespaces@^1.1.2": + "integrity" "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==" + "resolved" "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz" + "version" "1.1.4" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= +"webidl-conversions@^3.0.0": + "integrity" "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + "version" "3.0.1" -webpack-bundle-analyzer@^4.4.2: - version "4.5.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5" - integrity sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ== +"webpack-bundle-analyzer@^4.4.2": + "integrity" "sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==" + "resolved" "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz" + "version" "4.5.0" dependencies: - acorn "^8.0.4" - acorn-walk "^8.0.0" - chalk "^4.1.0" - commander "^7.2.0" - gzip-size "^6.0.0" - lodash "^4.17.20" - opener "^1.5.2" - sirv "^1.0.7" - ws "^7.3.1" + "acorn" "^8.0.4" + "acorn-walk" "^8.0.0" + "chalk" "^4.1.0" + "commander" "^7.2.0" + "gzip-size" "^6.0.0" + "lodash" "^4.17.20" + "opener" "^1.5.2" + "sirv" "^1.0.7" + "ws" "^7.3.1" -webpack-dev-middleware@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz#aa079a8dedd7e58bfeab358a9af7dab304cee57f" - integrity sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg== +"webpack-dev-middleware@^5.3.1": + "integrity" "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==" + "resolved" "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz" + "version" "5.3.1" dependencies: - colorette "^2.0.10" - memfs "^3.4.1" - mime-types "^2.1.31" - range-parser "^1.2.1" - schema-utils "^4.0.0" + "colorette" "^2.0.10" + "memfs" "^3.4.1" + "mime-types" "^2.1.31" + "range-parser" "^1.2.1" + "schema-utils" "^4.0.0" -webpack-dev-server@^4.7.1: - version "4.7.4" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz#d0ef7da78224578384e795ac228d8efb63d5f945" - integrity sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A== +"webpack-dev-server@^4.7.1": + "integrity" "sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A==" + "resolved" "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz" + "version" "4.7.4" dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" @@ -7519,212 +7582,217 @@ webpack-dev-server@^4.7.1: "@types/serve-index" "^1.9.1" "@types/sockjs" "^0.3.33" "@types/ws" "^8.2.2" - ansi-html-community "^0.0.8" - bonjour "^3.5.0" - chokidar "^3.5.3" - colorette "^2.0.10" - compression "^1.7.4" - connect-history-api-fallback "^1.6.0" - default-gateway "^6.0.3" - del "^6.0.0" - express "^4.17.1" - graceful-fs "^4.2.6" - html-entities "^2.3.2" - http-proxy-middleware "^2.0.0" - ipaddr.js "^2.0.1" - open "^8.0.9" - p-retry "^4.5.0" - portfinder "^1.0.28" - schema-utils "^4.0.0" - selfsigned "^2.0.0" - serve-index "^1.9.1" - sockjs "^0.3.21" - spdy "^4.0.2" - strip-ansi "^7.0.0" - webpack-dev-middleware "^5.3.1" - ws "^8.4.2" + "ansi-html-community" "^0.0.8" + "bonjour" "^3.5.0" + "chokidar" "^3.5.3" + "colorette" "^2.0.10" + "compression" "^1.7.4" + "connect-history-api-fallback" "^1.6.0" + "default-gateway" "^6.0.3" + "del" "^6.0.0" + "express" "^4.17.1" + "graceful-fs" "^4.2.6" + "html-entities" "^2.3.2" + "http-proxy-middleware" "^2.0.0" + "ipaddr.js" "^2.0.1" + "open" "^8.0.9" + "p-retry" "^4.5.0" + "portfinder" "^1.0.28" + "schema-utils" "^4.0.0" + "selfsigned" "^2.0.0" + "serve-index" "^1.9.1" + "sockjs" "^0.3.21" + "spdy" "^4.0.2" + "strip-ansi" "^7.0.0" + "webpack-dev-middleware" "^5.3.1" + "ws" "^8.4.2" -webpack-merge@^5.8.0: - version "5.8.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" - integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== +"webpack-merge@^5.8.0": + "integrity" "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==" + "resolved" "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz" + "version" "5.8.0" dependencies: - clone-deep "^4.0.1" - wildcard "^2.0.0" + "clone-deep" "^4.0.1" + "wildcard" "^2.0.0" -webpack-sources@^1.1.0, webpack-sources@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== +"webpack-sources@^1.1.0", "webpack-sources@^1.4.3": + "integrity" "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==" + "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz" + "version" "1.4.3" dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" + "source-list-map" "^2.0.0" + "source-map" "~0.6.1" -webpack-sources@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" - integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== +"webpack-sources@^3.2.3": + "integrity" "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" + "version" "3.2.3" -webpack@^5.61.0: - version "5.69.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.69.1.tgz#8cfd92c192c6a52c99ab00529b5a0d33aa848dc5" - integrity sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A== +"webpack@^4.0.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", "webpack@^4.4.0 || ^5.0.0", "webpack@^5.0.0", "webpack@^5.1.0", "webpack@^5.20.0", "webpack@^5.61.0", "webpack@>= 4", "webpack@>=2", "webpack@>=4.41.1 || 5.x", "webpack@3 || 4 || 5", "webpack@5.x": + "integrity" "sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==" + "resolved" "https://registry.npmjs.org/webpack/-/webpack-5.69.1.tgz" + "version" "5.69.1" dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/wasm-edit" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" - acorn "^8.4.1" - acorn-import-assertions "^1.7.6" - browserslist "^4.14.5" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.8.3" - es-module-lexer "^0.9.0" - eslint-scope "5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" - json-parse-better-errors "^1.0.2" - loader-runner "^4.2.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - schema-utils "^3.1.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.1.3" - watchpack "^2.3.1" - webpack-sources "^3.2.3" + "acorn" "^8.4.1" + "acorn-import-assertions" "^1.7.6" + "browserslist" "^4.14.5" + "chrome-trace-event" "^1.0.2" + "enhanced-resolve" "^5.8.3" + "es-module-lexer" "^0.9.0" + "eslint-scope" "5.1.1" + "events" "^3.2.0" + "glob-to-regexp" "^0.4.1" + "graceful-fs" "^4.2.9" + "json-parse-better-errors" "^1.0.2" + "loader-runner" "^4.2.0" + "mime-types" "^2.1.27" + "neo-async" "^2.6.2" + "schema-utils" "^3.1.0" + "tapable" "^2.1.1" + "terser-webpack-plugin" "^5.1.3" + "watchpack" "^2.3.1" + "webpack-sources" "^3.2.3" -webpackbar@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-5.0.2.tgz#d3dd466211c73852741dfc842b7556dcbc2b0570" - integrity sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ== +"webpackbar@^5.0.2": + "integrity" "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==" + "resolved" "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz" + "version" "5.0.2" dependencies: - chalk "^4.1.0" - consola "^2.15.3" - pretty-time "^1.1.0" - std-env "^3.0.1" + "chalk" "^4.1.0" + "consola" "^2.15.3" + "pretty-time" "^1.1.0" + "std-env" "^3.0.1" -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== +"websocket-driver@^0.7.4", "websocket-driver@>=0.5.1": + "integrity" "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==" + "resolved" "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" + "version" "0.7.4" dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" + "http-parser-js" ">=0.5.1" + "safe-buffer" ">=5.1.0" + "websocket-extensions" ">=0.1.1" -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== +"websocket-extensions@>=0.1.1": + "integrity" "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + "resolved" "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" + "version" "0.1.4" -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= +"whatwg-url@^5.0.0": + "integrity" "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=" + "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + "version" "5.0.0" dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" + "tr46" "~0.0.3" + "webidl-conversions" "^3.0.0" -which@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== +"which@^1.3.1": + "integrity" "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==" + "resolved" "https://registry.npmjs.org/which/-/which-1.3.1.tgz" + "version" "1.3.1" dependencies: - isexe "^2.0.0" + "isexe" "^2.0.0" -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== +"which@^2.0.1": + "integrity" "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==" + "resolved" "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + "version" "2.0.2" dependencies: - isexe "^2.0.0" + "isexe" "^2.0.0" -widest-line@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" - integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== +"widest-line@^3.1.0": + "integrity" "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==" + "resolved" "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz" + "version" "3.1.0" dependencies: - string-width "^4.0.0" + "string-width" "^4.0.0" -wildcard@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" - integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== +"wildcard@^2.0.0": + "integrity" "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" + "resolved" "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz" + "version" "2.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +"wrap-ansi@^7.0.0": + "integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==" + "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + "version" "7.0.0" dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" + "ansi-styles" "^4.0.0" + "string-width" "^4.1.0" + "strip-ansi" "^6.0.0" -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +"wrappy@1": + "integrity" "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "version" "1.0.2" -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== +"write-file-atomic@^3.0.0": + "integrity" "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==" + "resolved" "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" + "version" "3.0.3" dependencies: - imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" + "imurmurhash" "^0.1.4" + "is-typedarray" "^1.0.0" + "signal-exit" "^3.0.2" + "typedarray-to-buffer" "^3.1.5" -ws@^7.3.1: - version "7.5.7" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" - integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== +"ws@^7.3.1": + "integrity" "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==" + "resolved" "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz" + "version" "7.5.7" -ws@^8.4.2: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== +"ws@^8.4.2": + "integrity" "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==" + "resolved" "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz" + "version" "8.5.0" -xdg-basedir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" - integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== +"xdg-basedir@^4.0.0": + "integrity" "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + "resolved" "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz" + "version" "4.0.0" -xml-js@^1.6.11: - version "1.6.11" - resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" - integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== +"xml-js@^1.6.11": + "integrity" "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==" + "resolved" "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz" + "version" "1.6.11" dependencies: - sax "^1.2.4" + "sax" "^1.2.4" -xtend@^4.0.0, xtend@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +"xtend@^4.0.0", "xtend@^4.0.1": + "integrity" "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + "resolved" "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" + "version" "4.0.2" -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +"yallist@^3.0.2": + "integrity" "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "resolved" "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + "version" "3.1.1" -yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +"yallist@^4.0.0": + "integrity" "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "resolved" "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + "version" "4.0.0" -yarn@^1.17.3: - version "1.22.17" - resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.17.tgz#bf910747d22497b573131f7341c0e1d15c74036c" - integrity sha512-H0p241BXaH0UN9IeH//RT82tl5PfNraVpSpEoW+ET7lmopNC61eZ+A+IDvU8FM6Go5vx162SncDL8J1ZjRBriQ== +"yaml@^1.10.0", "yaml@^1.10.2", "yaml@^1.7.2": + "integrity" "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + "resolved" "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + "version" "1.10.2" -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +"yarn@^1.17.3": + "integrity" "sha512-H0p241BXaH0UN9IeH//RT82tl5PfNraVpSpEoW+ET7lmopNC61eZ+A+IDvU8FM6Go5vx162SncDL8J1ZjRBriQ==" + "resolved" "https://registry.npmjs.org/yarn/-/yarn-1.22.17.tgz" + "version" "1.22.17" -zwitch@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" - integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== +"yocto-queue@^0.1.0": + "integrity" "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + "resolved" "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + "version" "0.1.0" + +"zwitch@^1.0.0": + "integrity" "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==" + "resolved" "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz" + "version" "1.0.5" From dad56a3ab8794b01efd06947881c7b7c9782f718 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 14 Mar 2023 15:45:26 +0000 Subject: [PATCH 1269/1271] Update artist_hosts_maya_arnold.md --- website/docs/artist_hosts_maya_arnold.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/artist_hosts_maya_arnold.md b/website/docs/artist_hosts_maya_arnold.md index b3c02a0894..da16ba66c0 100644 --- a/website/docs/artist_hosts_maya_arnold.md +++ b/website/docs/artist_hosts_maya_arnold.md @@ -6,7 +6,7 @@ sidebar_label: Arnold ## Arnold Scene Source (.ass) Arnold Scene Source can be published as a single file or a sequence of files, determined by the frame range. -When creating the instance, two objectsets are created; `content` and `proxy`. Meshes in the `proxy` objectset will be the viewport representation when loading as `standin`. Proxy representations are stored as `resources` of the subset. +When creating the instance, two objectsets are created; `content` and `proxy`. Meshes in the `proxy` objectset will be the viewport representation when loading as `standin`. ### Arnold Scene Source Proxy Workflow In order to utilize operators and proxies, the content and proxy nodes need to share the same names (including the shape names). This is done by parenting the content and proxy nodes into separate groups. For example: From 87655075a9d5e4cc47b5a760fa5d31056444b634 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 15 Mar 2023 03:26:53 +0000 Subject: [PATCH 1270/1271] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 6ab03c2121..39a7dc9344 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.15.2" +__version__ = "3.15.3-nightly.1" From 0cb3585d91e63352dff24a861c0a19fc3a96425b Mon Sep 17 00:00:00 2001 From: mre7a <68907585+mre7a@users.noreply.github.com> Date: Wed, 15 Mar 2023 12:55:50 +0100 Subject: [PATCH 1271/1271] Resolve missing OPENPYPE_MONGO in deadline global job preload (#4484) * replace SpawnProcess with subprocess * clean up * replace spawn process with run process --------- Co-authored-by: Seyedmohammadreza Hashemizadeh --- .../repository/custom/plugins/GlobalJobPreLoad.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 20a58c9131..15226bb773 100644 --- a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -362,11 +362,11 @@ def inject_openpype_environment(deadlinePlugin): args_str = subprocess.list2cmdline(args) print(">>> Executing: {} {}".format(exe, args_str)) - process = ProcessUtils.SpawnProcess( - exe, args_str, os.path.dirname(exe) + process_exitcode = deadlinePlugin.RunProcess( + exe, args_str, os.path.dirname(exe), -1 ) - ProcessUtils.WaitForExit(process, -1) - if process.ExitCode != 0: + + if process_exitcode != 0: raise RuntimeError( "Failed to run OpenPype process to extract environments." )