diff --git a/openpype/api.py b/openpype/api.py index b692b36065..9ce745b653 100644 --- a/openpype/api.py +++ b/openpype/api.py @@ -3,7 +3,6 @@ from .settings import ( get_project_settings, get_current_project_settings, get_anatomy_settings, - get_environments, SystemSettings, ProjectSettings @@ -23,7 +22,6 @@ from .lib import ( get_app_environments_for_context, source_hash, get_latest_version, - get_global_environments, get_local_site_id, change_openpype_mongo_url, create_project_folders, @@ -69,10 +67,10 @@ __all__ = [ "get_project_settings", "get_current_project_settings", "get_anatomy_settings", - "get_environments", "get_project_basic_paths", "SystemSettings", + "ProjectSettings", "PypeLogger", "Logger", @@ -102,8 +100,9 @@ __all__ = [ # get contextual data "version_up", - "get_hierarchy", "get_asset", + "get_hierarchy", + "get_workdir_data", "get_version_from_path", "get_last_version_from_path", "get_app_environments_for_context", @@ -111,7 +110,6 @@ __all__ = [ "run_subprocess", "get_latest_version", - "get_global_environments", "get_local_site_id", "change_openpype_mongo_url", diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index c87445fdd3..11108ba49f 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -873,6 +873,5 @@ class OpenClipSolver(flib.MediaInfoFile): if feed_clr_obj is not None: feed_clr_obj = ET.Element( "colourSpace", {"type": "string"}) + feed_clr_obj.text = profile_name feed_storage_obj.append(feed_clr_obj) - - feed_clr_obj.text = profile_name diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index 95c2002bd9..5174f9db48 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -26,12 +26,10 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): add_tasks = [] def process(self, context): - project = context.data["flameProject"] selected_segments = context.data["flameSelectedSegments"] self.log.debug("__ selected_segments: {}".format(selected_segments)) self.otio_timeline = context.data["otioTimeline"] - self.clips_in_reels = opfapi.get_clips_in_reels(project) self.fps = context.data["fps"] # process all sellected @@ -63,9 +61,6 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): # get file path file_path = clip_data["fpath"] - # get source clip - source_clip = self._get_reel_clip(file_path) - first_frame = opfapi.get_frame_from_filename(file_path) or 0 head, tail = self._get_head_tail(clip_data, first_frame) @@ -103,7 +98,6 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): "families": families, "publish": marker_data["publish"], "fps": self.fps, - "flameSourceClip": source_clip, "sourceFirstFrame": int(first_frame), "path": file_path, "flameAddTasks": self.add_tasks, @@ -258,14 +252,6 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): ) return head, tail - def _get_reel_clip(self, path): - match_reel_clip = [ - clip for clip in self.clips_in_reels - if clip["fpath"] == path - ] - if match_reel_clip: - return match_reel_clip.pop() - def _get_resolution_to_data(self, data, context): assert data.get("otioClip"), "Missing `otioClip` data" diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index a780f8c9e5..fd0ece2590 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -1,4 +1,5 @@ import os +import re from pprint import pformat from copy import deepcopy @@ -6,6 +7,8 @@ import pyblish.api import openpype.api from openpype.hosts.flame import api as opfapi +import flame + class ExtractSubsetResources(openpype.api.Extractor): """ @@ -20,27 +23,31 @@ class ExtractSubsetResources(openpype.api.Extractor): # plugin defaults default_presets = { "thumbnail": { + "active": True, "ext": "jpg", "xml_preset_file": "Jpeg (8-bit).xml", "xml_preset_dir": "", "export_type": "File Sequence", - "ignore_comment_attrs": True, + "parsed_comment_attrs": False, "colorspace_out": "Output - sRGB", "representation_add_range": False, - "representation_tags": ["thumbnail"] + "representation_tags": ["thumbnail"], + "path_regex": ".*" }, "ftrackpreview": { + "active": True, "ext": "mov", "xml_preset_file": "Apple iPad (1920x1080).xml", "xml_preset_dir": "", "export_type": "Movie", - "ignore_comment_attrs": True, + "parsed_comment_attrs": False, "colorspace_out": "Output - Rec.709", "representation_add_range": True, "representation_tags": [ "review", "delete" - ] + ], + "path_regex": ".*" } } keep_original_representation = False @@ -61,13 +68,10 @@ class ExtractSubsetResources(openpype.api.Extractor): # flame objects segment = instance.data["item"] + asset_name = instance.data["asset"] segment_name = segment.name.get_value() + clip_path = instance.data["path"] sequence_clip = instance.context.data["flameSequence"] - clip_data = instance.data["flameSourceClip"] - - reel_clip = None - if clip_data: - reel_clip = clip_data["PyClip"] # segment's parent track name s_track_name = segment.parent.name.get_value() @@ -104,14 +108,44 @@ class ExtractSubsetResources(openpype.api.Extractor): for unique_name, preset_config in export_presets.items(): modify_xml_data = {} + # get activating attributes + activated_preset = preset_config["active"] + filter_path_regex = preset_config.get("filter_path_regex") + + self.log.info( + "Preset `{}` is active `{}` with filter `{}`".format( + unique_name, activated_preset, filter_path_regex + ) + ) + self.log.debug( + "__ clip_path: `{}`".format(clip_path)) + + # skip if not activated presete + if not activated_preset: + continue + + # exclude by regex filter if any + if ( + filter_path_regex + and not re.search(filter_path_regex, clip_path) + ): + continue + # get all presets attributes + extension = preset_config["ext"] preset_file = preset_config["xml_preset_file"] preset_dir = preset_config["xml_preset_dir"] export_type = preset_config["export_type"] repre_tags = preset_config["representation_tags"] - ignore_comment_attrs = preset_config["ignore_comment_attrs"] + parsed_comment_attrs = preset_config["parsed_comment_attrs"] color_out = preset_config["colorspace_out"] + self.log.info( + "Processing `{}` as `{}` to `{}` type...".format( + preset_file, export_type, extension + ) + ) + # get attribures related loading in integrate_batch_group load_to_batch_group = preset_config.get( "load_to_batch_group") @@ -131,161 +165,157 @@ class ExtractSubsetResources(openpype.api.Extractor): in_mark = (source_start_handles - source_first_frame) + 1 out_mark = in_mark + source_duration_handles - # make test for type of preset and available reel_clip - if ( - not reel_clip - and export_type != "Sequence Publish" - ): - self.log.warning(( - "Skipping preset {}. Not available " - "reel clip for {}").format( - preset_file, segment_name - )) - continue - - # by default export source clips - exporting_clip = reel_clip - + exporting_clip = None + name_patern_xml = "_{}.".format( + unique_name) if export_type == "Sequence Publish": # change export clip to sequence - exporting_clip = sequence_clip + exporting_clip = flame.duplicate(sequence_clip) - # change in/out marks to timeline in/out - in_mark = clip_in - out_mark = clip_out + # only keep visible layer where instance segment is child + self.hide_others( + exporting_clip, segment_name, s_track_name) - # add xml tags modifications - modify_xml_data.update({ - "exportHandles": True, - "nbHandles": handles, - "startFrame": frame_start - }) + # change name patern + name_patern_xml = ( + "__{}.").format( + unique_name) + else: + exporting_clip = self.import_clip(clip_path) + exporting_clip.name.set_value("{}_{}".format( + asset_name, segment_name)) - if not ignore_comment_attrs: - # add any xml overrides collected form segment.comment - modify_xml_data.update(instance.data["xml_overrides"]) + # change in/out marks to timeline in/out + in_mark = clip_in + out_mark = clip_out + + # add xml tags modifications + modify_xml_data.update({ + "exportHandles": True, + "nbHandles": handles, + "startFrame": frame_start, + "namePattern": name_patern_xml + }) + + if parsed_comment_attrs: + # add any xml overrides collected form segment.comment + modify_xml_data.update(instance.data["xml_overrides"]) self.log.debug("__ modify_xml_data: {}".format(pformat( modify_xml_data ))) - # with maintained duplication loop all presets - with opfapi.maintained_object_duplication( - exporting_clip) as duplclip: - kwargs = {} + export_kwargs = {} + # validate xml preset file is filled + if preset_file == "": + raise ValueError( + ("Check Settings for {} preset: " + "`XML preset file` is not filled").format( + unique_name) + ) - if export_type == "Sequence Publish": - # only keep visible layer where instance segment is child - self.hide_others(duplclip, segment_name, s_track_name) + # resolve xml preset dir if not filled + if preset_dir == "": + preset_dir = opfapi.get_preset_path_by_xml_name( + preset_file) - # validate xml preset file is filled - if preset_file == "": + if not preset_dir: raise ValueError( ("Check Settings for {} preset: " - "`XML preset file` is not filled").format( - unique_name) + "`XML preset file` {} is not found").format( + unique_name, preset_file) ) - # resolve xml preset dir if not filled - if preset_dir == "": - preset_dir = opfapi.get_preset_path_by_xml_name( - preset_file) + # create preset path + preset_orig_xml_path = str(os.path.join( + preset_dir, preset_file + )) - if not preset_dir: - raise ValueError( - ("Check Settings for {} preset: " - "`XML preset file` {} is not found").format( - unique_name, preset_file) - ) + preset_path = opfapi.modify_preset_file( + preset_orig_xml_path, staging_dir, modify_xml_data) - # create preset path - preset_orig_xml_path = str(os.path.join( - preset_dir, preset_file - )) + # define kwargs based on preset type + if "thumbnail" in unique_name: + export_kwargs["thumb_frame_number"] = int(in_mark + ( + source_duration_handles / 2)) + else: + export_kwargs.update({ + "in_mark": in_mark, + "out_mark": out_mark + }) - preset_path = opfapi.modify_preset_file( - preset_orig_xml_path, staging_dir, modify_xml_data) + # get and make export dir paths + export_dir_path = str(os.path.join( + staging_dir, unique_name + )) + os.makedirs(export_dir_path) - # define kwargs based on preset type - if "thumbnail" in unique_name: - kwargs["thumb_frame_number"] = in_mark + ( - source_duration_handles / 2) - else: - kwargs.update({ - "in_mark": in_mark, - "out_mark": out_mark - }) + # export + opfapi.export_clip( + export_dir_path, exporting_clip, preset_path, **export_kwargs) - # get and make export dir paths - export_dir_path = str(os.path.join( - staging_dir, unique_name - )) - os.makedirs(export_dir_path) + # create representation data + representation_data = { + "name": unique_name, + "outputName": unique_name, + "ext": extension, + "stagingDir": export_dir_path, + "tags": repre_tags, + "data": { + "colorspace": color_out + }, + "load_to_batch_group": load_to_batch_group, + "batch_group_loader_name": batch_group_loader_name + } - # export - opfapi.export_clip( - export_dir_path, duplclip, preset_path, **kwargs) + # collect all available content of export dir + files = os.listdir(export_dir_path) - extension = preset_config["ext"] + # make sure no nested folders inside + n_stage_dir, n_files = self._unfolds_nested_folders( + export_dir_path, files, extension) - # create representation data - representation_data = { - "name": unique_name, - "outputName": unique_name, - "ext": extension, - "stagingDir": export_dir_path, - "tags": repre_tags, - "data": { - "colorspace": color_out - }, - "load_to_batch_group": load_to_batch_group, - "batch_group_loader_name": batch_group_loader_name - } + # fix representation in case of nested folders + if n_stage_dir: + representation_data["stagingDir"] = n_stage_dir + files = n_files - # collect all available content of export dir - files = os.listdir(export_dir_path) + # add files to represetation but add + # imagesequence as list + if ( + # first check if path in files is not mov extension + [ + f for f in files + if os.path.splitext(f)[-1] == ".mov" + ] + # then try if thumbnail is not in unique name + or unique_name == "thumbnail" + ): + representation_data["files"] = files.pop() + else: + representation_data["files"] = files - # make sure no nested folders inside - n_stage_dir, n_files = self._unfolds_nested_folders( - export_dir_path, files, extension) + # add frame range + if preset_config["representation_add_range"]: + representation_data.update({ + "frameStart": frame_start_handle, + "frameEnd": ( + frame_start_handle + source_duration_handles), + "fps": instance.data["fps"] + }) - # fix representation in case of nested folders - if n_stage_dir: - representation_data["stagingDir"] = n_stage_dir - files = n_files + instance.data["representations"].append(representation_data) - # add files to represetation but add - # imagesequence as list - if ( - # first check if path in files is not mov extension - [ - f for f in files - if os.path.splitext(f)[-1] == ".mov" - ] - # then try if thumbnail is not in unique name - or unique_name == "thumbnail" - ): - representation_data["files"] = files.pop() - else: - representation_data["files"] = files + # add review family if found in tags + if "review" in repre_tags: + instance.data["families"].append("review") - # add frame range - if preset_config["representation_add_range"]: - representation_data.update({ - "frameStart": frame_start_handle, - "frameEnd": ( - frame_start_handle + source_duration_handles), - "fps": instance.data["fps"] - }) + self.log.info("Added representation: {}".format( + representation_data)) - instance.data["representations"].append(representation_data) - - # add review family if found in tags - if "review" in repre_tags: - instance.data["families"].append("review") - - self.log.info("Added representation: {}".format( - representation_data)) + if export_type == "Sequence Publish": + # at the end remove the duplicated clip + flame.delete(exporting_clip) self.log.debug("All representations: {}".format( pformat(instance.data["representations"]))) @@ -373,3 +403,18 @@ class ExtractSubsetResources(openpype.api.Extractor): for segment in track.segments: if segment.name.get_value() != segment_name: segment.hidden = True + + def import_clip(self, path): + """ + Import clip from path + """ + clips = flame.import_clips(path) + self.log.info("Clips [{}] imported from `{}`".format(clips, path)) + if not clips: + self.log.warning("Path `{}` is not having any clips".format(path)) + return None + elif len(clips) > 1: + self.log.warning( + "Path `{}` is containing more that one clip".format(path) + ) + return clips[0] diff --git a/openpype/hosts/flame/plugins/publish/validate_source_clip.py b/openpype/hosts/flame/plugins/publish/validate_source_clip.py deleted file mode 100644 index 345c00e05a..0000000000 --- a/openpype/hosts/flame/plugins/publish/validate_source_clip.py +++ /dev/null @@ -1,26 +0,0 @@ -import pyblish - - -@pyblish.api.log -class ValidateSourceClip(pyblish.api.InstancePlugin): - """Validate instance is not having empty `flameSourceClip`""" - - order = pyblish.api.ValidatorOrder - label = "Validate Source Clip" - hosts = ["flame"] - families = ["clip"] - optional = True - active = False - - def process(self, instance): - flame_source_clip = instance.data["flameSourceClip"] - - self.log.debug("_ flame_source_clip: {}".format(flame_source_clip)) - - if flame_source_clip is None: - raise AttributeError(( - "Timeline segment `{}` is not having " - "relative clip in reels. Please make sure " - "you push `Save Sources` button in Conform Tab").format( - instance.data["asset"] - )) diff --git a/openpype/hosts/hiero/api/tags.py b/openpype/hosts/hiero/api/tags.py index e15e3119a6..8877b92b9d 100644 --- a/openpype/hosts/hiero/api/tags.py +++ b/openpype/hosts/hiero/api/tags.py @@ -10,16 +10,6 @@ log = Logger.get_logger(__name__) def tag_data(): return { - # "Retiming": { - # "editable": "1", - # "note": "Clip has retime or TimeWarp effects (or multiple effects stacked on the clip)", # noqa - # "icon": "retiming.png", - # "metadata": { - # "family": "retiming", - # "marginIn": 1, - # "marginOut": 1 - # } - # }, "[Lenses]": { "Set lense here": { "editable": "1", @@ -48,6 +38,16 @@ def tag_data(): "family": "comment", "subset": "main" } + }, + "FrameMain": { + "editable": "1", + "note": "Publishing a frame subset.", + "icon": "z_layer_main.png", + "metadata": { + "family": "frame", + "subset": "main", + "format": "png" + } } } diff --git a/openpype/hosts/hiero/plugins/publish/collect_frame_tag_instances.py b/openpype/hosts/hiero/plugins/publish/collect_frame_tag_instances.py new file mode 100644 index 0000000000..982a34efd6 --- /dev/null +++ b/openpype/hosts/hiero/plugins/publish/collect_frame_tag_instances.py @@ -0,0 +1,142 @@ +from pprint import pformat +import re +import ast +import json + +import pyblish.api + + +class CollectFrameTagInstances(pyblish.api.ContextPlugin): + """Collect frames from tags. + + Tag is expected to have metadata: + { + "family": "frame" + "subset": "main" + } + """ + + order = pyblish.api.CollectorOrder + label = "Collect Frames" + hosts = ["hiero"] + + def process(self, context): + self._context = context + + # collect all sequence tags + subset_data = self._create_frame_subset_data_sequence(context) + + self.log.debug("__ subset_data: {}".format( + pformat(subset_data) + )) + + # create instances + self._create_instances(subset_data) + + def _get_tag_data(self, tag): + data = {} + + # get tag metadata attribute + tag_data = tag.metadata() + + # convert tag metadata to normal keys names and values to correct types + for k, v in dict(tag_data).items(): + key = k.replace("tag.", "") + + try: + # capture exceptions which are related to strings only + if re.match(r"^[\d]+$", v): + value = int(v) + elif re.match(r"^True$", v): + value = True + elif re.match(r"^False$", v): + value = False + elif re.match(r"^None$", v): + value = None + elif re.match(r"^[\w\d_]+$", v): + value = v + else: + value = ast.literal_eval(v) + except (ValueError, SyntaxError): + value = v + + data[key] = value + + return data + + def _create_frame_subset_data_sequence(self, context): + + sequence_tags = [] + sequence = context.data["activeTimeline"] + + # get all publishable sequence frames + publish_frames = range(int(sequence.duration() + 1)) + + self.log.debug("__ publish_frames: {}".format( + pformat(publish_frames) + )) + + # get all sequence tags + for tag in sequence.tags(): + tag_data = self._get_tag_data(tag) + self.log.debug("__ tag_data: {}".format( + pformat(tag_data) + )) + if not tag_data: + continue + + if "family" not in tag_data: + continue + + if tag_data["family"] != "frame": + continue + + sequence_tags.append(tag_data) + + self.log.debug("__ sequence_tags: {}".format( + pformat(sequence_tags) + )) + + # first collect all available subset tag frames + subset_data = {} + for tag_data in sequence_tags: + frame = int(tag_data["start"]) + + if frame not in publish_frames: + continue + + subset = tag_data["subset"] + + if subset in subset_data: + # update existing subset key + subset_data[subset]["frames"].append(frame) + else: + # create new subset key + subset_data[subset] = { + "frames": [frame], + "format": tag_data["format"], + "asset": context.data["assetEntity"]["name"] + } + return subset_data + + def _create_instances(self, subset_data): + # create instance per subset + for subset_name, subset_data in subset_data.items(): + name = "frame" + subset_name.title() + data = { + "name": name, + "label": "{} {}".format(name, subset_data["frames"]), + "family": "image", + "families": ["frame"], + "asset": subset_data["asset"], + "subset": name, + "format": subset_data["format"], + "frames": subset_data["frames"] + } + self._context.create_instance(**data) + + self.log.info( + "Created instance: {}".format( + json.dumps(data, sort_keys=True, indent=4) + ) + ) diff --git a/openpype/hosts/hiero/plugins/publish/extract_frames.py b/openpype/hosts/hiero/plugins/publish/extract_frames.py new file mode 100644 index 0000000000..aa3eda2e9f --- /dev/null +++ b/openpype/hosts/hiero/plugins/publish/extract_frames.py @@ -0,0 +1,82 @@ +import os +import pyblish.api +import openpype + + +class ExtractFrames(openpype.api.Extractor): + """Extracts frames""" + + order = pyblish.api.ExtractorOrder + label = "Extract Frames" + hosts = ["hiero"] + families = ["frame"] + movie_extensions = ["mov", "mp4"] + + def process(self, instance): + oiio_tool_path = openpype.lib.get_oiio_tools_path() + staging_dir = self.staging_dir(instance) + output_template = os.path.join(staging_dir, instance.data["name"]) + sequence = instance.context.data["activeTimeline"] + + files = [] + for frame in instance.data["frames"]: + track_item = sequence.trackItemAt(frame) + media_source = track_item.source().mediaSource() + input_path = media_source.fileinfos()[0].filename() + input_frame = ( + track_item.mapTimelineToSource(frame) + + track_item.source().mediaSource().startTime() + ) + output_ext = instance.data["format"] + output_path = output_template + output_path += ".{:04d}.{}".format(int(frame), output_ext) + + args = [oiio_tool_path] + + ext = os.path.splitext(input_path)[1][1:] + if ext in self.movie_extensions: + args.extend(["--subimage", str(int(input_frame))]) + else: + args.extend(["--frames", str(int(input_frame))]) + + if ext == "exr": + args.extend(["--powc", "0.45,0.45,0.45,1.0"]) + + args.extend([input_path, "-o", output_path]) + output = openpype.api.run_subprocess(args) + + failed_output = "oiiotool produced no output." + if failed_output in output: + raise ValueError( + "oiiotool processing failed. Args: {}".format(args) + ) + + files.append(output_path) + + # Feedback to user because "oiiotool" can make the publishing + # appear unresponsive. + self.log.info( + "Processed {} of {} frames".format( + instance.data["frames"].index(frame) + 1, + len(instance.data["frames"]) + ) + ) + + if len(files) == 1: + instance.data["representations"] = [ + { + "name": output_ext, + "ext": output_ext, + "files": os.path.basename(files[0]), + "stagingDir": staging_dir + } + ] + else: + instance.data["representations"] = [ + { + "name": output_ext, + "ext": output_ext, + "files": [os.path.basename(x) for x in files], + "stagingDir": staging_dir + } + ] diff --git a/openpype/hosts/hiero/plugins/publish/precollect_workfile.py b/openpype/hosts/hiero/plugins/publish/precollect_workfile.py index 29c0397f79..b9f58c15f6 100644 --- a/openpype/hosts/hiero/plugins/publish/precollect_workfile.py +++ b/openpype/hosts/hiero/plugins/publish/precollect_workfile.py @@ -68,6 +68,7 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): "subset": "{}{}".format(asset, subset.capitalize()), "item": project, "family": "workfile", + "families": [], "representations": [workfile_representation, thumb_representation] } @@ -77,6 +78,7 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin): # update context with main project attributes context_data = { "activeProject": project, + "activeTimeline": active_timeline, "otioTimeline": otio_timeline, "currentFile": curent_file, "colorspace": self.get_colorspace(project), diff --git a/openpype/hosts/hiero/plugins/publish_old_workflow/collect_clip_resolution.py b/openpype/hosts/hiero/plugins/publish_old_workflow/collect_clip_resolution.py deleted file mode 100644 index 1d0727d0af..0000000000 --- a/openpype/hosts/hiero/plugins/publish_old_workflow/collect_clip_resolution.py +++ /dev/null @@ -1,38 +0,0 @@ -import pyblish.api - - -class CollectClipResolution(pyblish.api.InstancePlugin): - """Collect clip geometry resolution""" - - order = pyblish.api.CollectorOrder - 0.1 - label = "Collect Clip Resolution" - hosts = ["hiero"] - families = ["clip"] - - def process(self, instance): - sequence = instance.context.data['activeSequence'] - item = instance.data["item"] - source_resolution = instance.data.get("sourceResolution", None) - - resolution_width = int(sequence.format().width()) - resolution_height = int(sequence.format().height()) - pixel_aspect = sequence.format().pixelAspect() - - # source exception - if source_resolution: - resolution_width = int(item.source().mediaSource().width()) - resolution_height = int(item.source().mediaSource().height()) - pixel_aspect = item.source().mediaSource().pixelAspect() - - resolution_data = { - "resolutionWidth": resolution_width, - "resolutionHeight": resolution_height, - "pixelAspect": pixel_aspect - } - # add to instacne data - instance.data.update(resolution_data) - - self.log.info("Resolution of instance '{}' is: {}".format( - instance, - resolution_data - )) diff --git a/openpype/hosts/hiero/plugins/publish_old_workflow/collect_host_version.py b/openpype/hosts/hiero/plugins/publish_old_workflow/collect_host_version.py deleted file mode 100644 index 76e5bd11d5..0000000000 --- a/openpype/hosts/hiero/plugins/publish_old_workflow/collect_host_version.py +++ /dev/null @@ -1,15 +0,0 @@ -import pyblish.api - - -class CollectHostVersion(pyblish.api.ContextPlugin): - """Inject the hosts version into context""" - - label = "Collect Host and HostVersion" - order = pyblish.api.CollectorOrder - 0.5 - - def process(self, context): - import nuke - import pyblish.api - - context.set_data("host", pyblish.api.current_host()) - context.set_data('hostVersion', value=nuke.NUKE_VERSION_STRING) diff --git a/openpype/hosts/hiero/plugins/publish_old_workflow/collect_tag_retime.py b/openpype/hosts/hiero/plugins/publish_old_workflow/collect_tag_retime.py deleted file mode 100644 index 0634130976..0000000000 --- a/openpype/hosts/hiero/plugins/publish_old_workflow/collect_tag_retime.py +++ /dev/null @@ -1,32 +0,0 @@ -from pyblish import api - - -class CollectTagRetime(api.InstancePlugin): - """Collect Retiming from Tags of selected track items.""" - - order = api.CollectorOrder + 0.014 - label = "Collect Retiming Tag" - hosts = ["hiero"] - families = ['clip'] - - def process(self, instance): - # gets tags - tags = instance.data["tags"] - - for t in tags: - t_metadata = dict(t["metadata"]) - t_family = t_metadata.get("tag.family", "") - - # gets only task family tags and collect labels - if "retiming" in t_family: - margin_in = t_metadata.get("tag.marginIn", "") - margin_out = t_metadata.get("tag.marginOut", "") - - instance.data["retimeMarginIn"] = int(margin_in) - instance.data["retimeMarginOut"] = int(margin_out) - instance.data["retime"] = True - - self.log.info("retimeMarginIn: `{}`".format(margin_in)) - self.log.info("retimeMarginOut: `{}`".format(margin_out)) - - instance.data["families"] += ["retime"] diff --git a/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_instances.py b/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_instances.py deleted file mode 100644 index f9cc158e79..0000000000 --- a/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_instances.py +++ /dev/null @@ -1,223 +0,0 @@ -from compiler.ast import flatten -from pyblish import api -from openpype.hosts.hiero import api as phiero -import hiero -# from openpype.hosts.hiero.api import lib -# reload(lib) -# reload(phiero) - - -class PreCollectInstances(api.ContextPlugin): - """Collect all Track items selection.""" - - order = api.CollectorOrder - 0.509 - label = "Pre-collect Instances" - hosts = ["hiero"] - - def process(self, context): - track_items = phiero.get_track_items( - selected=True, check_tagged=True, check_enabled=True) - # only return enabled track items - if not track_items: - track_items = phiero.get_track_items( - check_enabled=True, check_tagged=True) - # get sequence and video tracks - sequence = context.data["activeSequence"] - tracks = sequence.videoTracks() - - # add collection to context - tracks_effect_items = self.collect_sub_track_items(tracks) - - context.data["tracksEffectItems"] = tracks_effect_items - - self.log.info( - "Processing enabled track items: {}".format(len(track_items))) - - for _ti in track_items: - data = {} - clip = _ti.source() - - # get clips subtracks and anotations - annotations = self.clip_annotations(clip) - subtracks = self.clip_subtrack(_ti) - self.log.debug("Annotations: {}".format(annotations)) - self.log.debug(">> Subtracks: {}".format(subtracks)) - - # get pype tag data - tag_parsed_data = phiero.get_track_item_pype_data(_ti) - # self.log.debug(pformat(tag_parsed_data)) - - if not tag_parsed_data: - continue - - if tag_parsed_data.get("id") != "pyblish.avalon.instance": - continue - # add tag data to instance data - data.update({ - k: v for k, v in tag_parsed_data.items() - if k not in ("id", "applieswhole", "label") - }) - - asset = tag_parsed_data["asset"] - subset = tag_parsed_data["subset"] - review_track = tag_parsed_data.get("reviewTrack") - hiero_track = tag_parsed_data.get("heroTrack") - audio = tag_parsed_data.get("audio") - - # remove audio attribute from data - data.pop("audio") - - # insert family into families - family = tag_parsed_data["family"] - families = [str(f) for f in tag_parsed_data["families"]] - families.insert(0, str(family)) - - track = _ti.parent() - media_source = _ti.source().mediaSource() - source_path = media_source.firstpath() - file_head = media_source.filenameHead() - file_info = media_source.fileinfos().pop() - source_first_frame = int(file_info.startFrame()) - - # apply only for review and master track instance - if review_track and hiero_track: - families += ["review", "ftrack"] - - data.update({ - "name": "{} {} {}".format(asset, subset, families), - "asset": asset, - "item": _ti, - "families": families, - - # tags - "tags": _ti.tags(), - - # track item attributes - "track": track.name(), - "trackItem": track, - "reviewTrack": review_track, - - # version data - "versionData": { - "colorspace": _ti.sourceMediaColourTransform() - }, - - # source attribute - "source": source_path, - "sourceMedia": media_source, - "sourcePath": source_path, - "sourceFileHead": file_head, - "sourceFirst": source_first_frame, - - # clip's effect - "clipEffectItems": subtracks - }) - - instance = context.create_instance(**data) - - self.log.info("Creating instance.data: {}".format(instance.data)) - - if audio: - a_data = dict() - - # add tag data to instance data - a_data.update({ - k: v for k, v in tag_parsed_data.items() - if k not in ("id", "applieswhole", "label") - }) - - # create main attributes - subset = "audioMain" - family = "audio" - families = ["clip", "ftrack"] - families.insert(0, str(family)) - - name = "{} {} {}".format(asset, subset, families) - - a_data.update({ - "name": name, - "subset": subset, - "asset": asset, - "family": family, - "families": families, - "item": _ti, - - # tags - "tags": _ti.tags(), - }) - - a_instance = context.create_instance(**a_data) - self.log.info("Creating audio instance: {}".format(a_instance)) - - @staticmethod - def clip_annotations(clip): - """ - Returns list of Clip's hiero.core.Annotation - """ - annotations = [] - subTrackItems = flatten(clip.subTrackItems()) - annotations += [item for item in subTrackItems if isinstance( - item, hiero.core.Annotation)] - return annotations - - @staticmethod - def clip_subtrack(clip): - """ - Returns list of Clip's hiero.core.SubTrackItem - """ - subtracks = [] - subTrackItems = flatten(clip.parent().subTrackItems()) - for item in subTrackItems: - # avoid all anotation - if isinstance(item, hiero.core.Annotation): - continue - # # avoid all not anaibled - if not item.isEnabled(): - continue - subtracks.append(item) - return subtracks - - @staticmethod - def collect_sub_track_items(tracks): - """ - Returns dictionary with track index as key and list of subtracks - """ - # collect all subtrack items - sub_track_items = dict() - for track in tracks: - items = track.items() - - # skip if no clips on track > need track with effect only - if items: - continue - - # skip all disabled tracks - if not track.isEnabled(): - continue - - track_index = track.trackIndex() - _sub_track_items = flatten(track.subTrackItems()) - - # continue only if any subtrack items are collected - if len(_sub_track_items) < 1: - continue - - enabled_sti = list() - # loop all found subtrack items and check if they are enabled - for _sti in _sub_track_items: - # checking if not enabled - if not _sti.isEnabled(): - continue - if isinstance(_sti, hiero.core.Annotation): - continue - # collect the subtrack item - enabled_sti.append(_sti) - - # continue only if any subtrack items are collected - if len(enabled_sti) < 1: - continue - - # add collection of subtrackitems to dict - sub_track_items[track_index] = enabled_sti - - return sub_track_items diff --git a/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_workfile.py b/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_workfile.py deleted file mode 100644 index 693e151f6f..0000000000 --- a/openpype/hosts/hiero/plugins/publish_old_workflow/precollect_workfile.py +++ /dev/null @@ -1,74 +0,0 @@ -import os -import pyblish.api -from openpype.hosts.hiero import api as phiero -from openpype.pipeline import legacy_io - - -class PreCollectWorkfile(pyblish.api.ContextPlugin): - """Inject the current working file into context""" - - label = "Pre-collect Workfile" - order = pyblish.api.CollectorOrder - 0.51 - - def process(self, context): - asset = legacy_io.Session["AVALON_ASSET"] - subset = "workfile" - - project = phiero.get_current_project() - active_sequence = phiero.get_current_sequence() - video_tracks = active_sequence.videoTracks() - audio_tracks = active_sequence.audioTracks() - current_file = project.path() - staging_dir = os.path.dirname(current_file) - base_name = os.path.basename(current_file) - - # get workfile's colorspace properties - _clrs = {} - _clrs["useOCIOEnvironmentOverride"] = project.useOCIOEnvironmentOverride() # noqa - _clrs["lutSetting16Bit"] = project.lutSetting16Bit() - _clrs["lutSetting8Bit"] = project.lutSetting8Bit() - _clrs["lutSettingFloat"] = project.lutSettingFloat() - _clrs["lutSettingLog"] = project.lutSettingLog() - _clrs["lutSettingViewer"] = project.lutSettingViewer() - _clrs["lutSettingWorkingSpace"] = project.lutSettingWorkingSpace() - _clrs["lutUseOCIOForExport"] = project.lutUseOCIOForExport() - _clrs["ocioConfigName"] = project.ocioConfigName() - _clrs["ocioConfigPath"] = project.ocioConfigPath() - - # set main project attributes to context - context.data["activeProject"] = project - context.data["activeSequence"] = active_sequence - context.data["videoTracks"] = video_tracks - context.data["audioTracks"] = audio_tracks - context.data["currentFile"] = current_file - context.data["colorspace"] = _clrs - - self.log.info("currentFile: {}".format(current_file)) - - # creating workfile representation - representation = { - 'name': 'hrox', - 'ext': 'hrox', - 'files': base_name, - "stagingDir": staging_dir, - } - - instance_data = { - "name": "{}_{}".format(asset, subset), - "asset": asset, - "subset": "{}{}".format(asset, subset.capitalize()), - "item": project, - "family": "workfile", - - # version data - "versionData": { - "colorspace": _clrs - }, - - # source attribute - "sourcePath": current_file, - "representations": [representation] - } - - instance = context.create_instance(**instance_data) - self.log.info("Creating instance: {}".format(instance)) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 4e38f811c9..3223feaec7 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -400,7 +400,7 @@ def add_write_node(name, **kwarg): return w -def read(node): +def read_avalon_data(node): """Return user-defined knobs from given `node` Args: @@ -415,8 +415,6 @@ def read(node): return knob_name[len("avalon:"):] elif knob_name.startswith("ak:"): return knob_name[len("ak:"):] - else: - return knob_name data = dict() @@ -445,7 +443,8 @@ def read(node): (knob_type == 26 and value) ): key = compat_prefixed(knob_name) - data[key] = value + if key is not None: + data[key] = value if knob_name == first_user_knob: break @@ -507,20 +506,74 @@ def get_created_node_imageio_setting(**kwarg): log.debug(kwarg) nodeclass = kwarg.get("nodeclass", None) creator = kwarg.get("creator", None) + subset = kwarg.get("subset", None) assert any([creator, nodeclass]), nuke.message( "`{}`: Missing mandatory kwargs `host`, `cls`".format(__file__)) - imageio_nodes = get_nuke_imageio_settings()["nodes"]["requiredNodes"] + imageio_nodes = get_nuke_imageio_settings()["nodes"] + required_nodes = imageio_nodes["requiredNodes"] + override_nodes = imageio_nodes["overrideNodes"] imageio_node = None - for node in imageio_nodes: + for node in required_nodes: log.info(node) - if (nodeclass in node["nukeNodeClass"]) and ( - creator in node["plugins"]): + if ( + nodeclass in node["nukeNodeClass"] + and creator in node["plugins"] + ): imageio_node = node break + log.debug("__ imageio_node: {}".format(imageio_node)) + + # find matching override node + override_imageio_node = None + for onode in override_nodes: + log.info(onode) + if nodeclass not in node["nukeNodeClass"]: + continue + + if creator not in node["plugins"]: + continue + + if ( + onode["subsets"] + and not any(re.search(s, subset) for s in onode["subsets"]) + ): + continue + + override_imageio_node = onode + break + + log.debug("__ override_imageio_node: {}".format(override_imageio_node)) + # add overrides to imageio_node + if override_imageio_node: + # get all knob names in imageio_node + knob_names = [k["name"] for k in imageio_node["knobs"]] + + for oknob in override_imageio_node["knobs"]: + for knob in imageio_node["knobs"]: + # override matching knob name + if oknob["name"] == knob["name"]: + log.debug( + "_ overriding knob: `{}` > `{}`".format( + knob, oknob + )) + if not oknob["value"]: + # remove original knob if no value found in oknob + imageio_node["knobs"].remove(knob) + else: + # override knob value with oknob's + knob["value"] = oknob["value"] + + # add missing knobs into imageio_node + if oknob["name"] not in knob_names: + log.debug( + "_ adding knob: `{}`".format(oknob)) + imageio_node["knobs"].append(oknob) + knob_names.append(oknob["name"]) + log.info("ImageIO node: {}".format(imageio_node)) return imageio_node @@ -542,7 +595,7 @@ def get_imageio_input_colorspace(filename): def on_script_load(): ''' Callback for ffmpeg support ''' - if nuke.env['LINUX']: + if nuke.env["LINUX"]: nuke.tcl('load ffmpegReader') nuke.tcl('load ffmpegWriter') else: @@ -567,7 +620,7 @@ def check_inventory_versions(): if container: node = nuke.toNode(container["objectName"]) - avalon_knob_data = read(node) + avalon_knob_data = read_avalon_data(node) # get representation from io representation = legacy_io.find_one({ @@ -593,7 +646,7 @@ def check_inventory_versions(): versions = legacy_io.find({ "type": "version", "parent": version["parent"] - }).distinct('name') + }).distinct("name") max_version = max(versions) @@ -623,20 +676,20 @@ def writes_version_sync(): if _NODE_TAB_NAME not in each.knobs(): continue - avalon_knob_data = read(each) + avalon_knob_data = read_avalon_data(each) try: - if avalon_knob_data['families'] not in ["render"]: - log.debug(avalon_knob_data['families']) + if avalon_knob_data["families"] not in ["render"]: + log.debug(avalon_knob_data["families"]) continue - node_file = each['file'].value() + node_file = each["file"].value() node_version = "v" + get_version_from_path(node_file) log.debug("node_version: {}".format(node_version)) node_new_file = node_file.replace(node_version, new_version) - each['file'].setValue(node_new_file) + each["file"].setValue(node_new_file) if not os.path.isdir(os.path.dirname(node_new_file)): log.warning("Path does not exist! I am creating it.") os.makedirs(os.path.dirname(node_new_file)) @@ -665,18 +718,19 @@ def check_subsetname_exists(nodes, subset_name): bool: True of False """ return next((True for n in nodes - if subset_name in read(n).get("subset", "")), + if subset_name in read_avalon_data(n).get("subset", "")), False) def get_render_path(node): ''' Generate Render path from presets regarding avalon knob data ''' - data = {'avalon': read(node)} + data = {'avalon': read_avalon_data(node)} data_preset = { - "nodeclass": data['avalon']['family'], - "families": [data['avalon']['families']], - "creator": data['avalon']['creator'] + "nodeclass": data["avalon"]["family"], + "families": [data["avalon"]["families"]], + "creator": data["avalon"]["creator"], + "subset": data["avalon"]["subset"] } nuke_imageio_writes = get_created_node_imageio_setting(**data_preset) @@ -749,7 +803,7 @@ def format_anatomy(data): def script_name(): ''' Returns nuke script path ''' - return nuke.root().knob('name').value() + return nuke.root().knob("name").value() def add_button_write_to_read(node): @@ -844,7 +898,7 @@ def create_write_node(name, data, input=None, prenodes=None, # adding dataflow template log.debug("imageio_writes: `{}`".format(imageio_writes)) for knob in imageio_writes["knobs"]: - _data.update({knob["name"]: knob["value"]}) + _data[knob["name"]] = knob["value"] _data = fix_data_for_node_create(_data) @@ -1193,15 +1247,19 @@ class WorkfileSettings(object): erased_viewers = [] for v in nuke.allNodes(filter="Viewer"): - v['viewerProcess'].setValue(str(viewer_dict["viewerProcess"])) + # set viewProcess to preset from settings + v["viewerProcess"].setValue( + str(viewer_dict["viewerProcess"]) + ) + if str(viewer_dict["viewerProcess"]) \ - not in v['viewerProcess'].value(): + not in v["viewerProcess"].value(): copy_inputs = v.dependencies() copy_knobs = {k: v[k].value() for k in v.knobs() if k not in filter_knobs} # delete viewer with wrong settings - erased_viewers.append(v['name'].value()) + erased_viewers.append(v["name"].value()) nuke.delete(v) # create new viewer @@ -1217,7 +1275,7 @@ class WorkfileSettings(object): nv[k].setValue(v) # set viewerProcess - nv['viewerProcess'].setValue(str(viewer_dict["viewerProcess"])) + nv["viewerProcess"].setValue(str(viewer_dict["viewerProcess"])) if erased_viewers: log.warning( @@ -1293,12 +1351,12 @@ class WorkfileSettings(object): for node in nuke.allNodes(filter="Group"): # get data from avalon knob - avalon_knob_data = read(node) + avalon_knob_data = read_avalon_data(node) - if not avalon_knob_data: + if avalon_knob_data.get("id") != "pyblish.avalon.instance": continue - if avalon_knob_data["id"] != "pyblish.avalon.instance": + if "creator" not in avalon_knob_data: continue # establish families @@ -1309,7 +1367,8 @@ class WorkfileSettings(object): data_preset = { "nodeclass": avalon_knob_data["family"], "families": families, - "creator": avalon_knob_data['creator'] + "creator": avalon_knob_data["creator"], + "subset": avalon_knob_data["subset"] } nuke_imageio_writes = get_created_node_imageio_setting( @@ -1342,7 +1401,6 @@ class WorkfileSettings(object): write_node[knob["name"]].setValue(value) - def set_reads_colorspace(self, read_clrs_inputs): """ Setting colorspace to Read nodes @@ -1368,17 +1426,16 @@ class WorkfileSettings(object): current = n["colorspace"].value() future = str(preset_clrsp) if current != future: - changes.update({ - n.name(): { - "from": current, - "to": future - } - }) + changes[n.name()] = { + "from": current, + "to": future + } + log.debug(changes) if changes: msg = "Read nodes are not set to correct colospace:\n\n" for nname, knobs in changes.items(): - msg += str( + msg += ( " - node: '{0}' is now '{1}' but should be '{2}'\n" ).format(nname, knobs["from"], knobs["to"]) @@ -1610,17 +1667,17 @@ def get_hierarchical_attr(entity, attr, default=None): if not value: break - if value or entity['type'].lower() == 'project': + if value or entity["type"].lower() == "project": return value - parent_id = entity['parent'] + parent_id = entity["parent"] if ( - entity['type'].lower() == 'asset' - and entity.get('data', {}).get('visualParent') + entity["type"].lower() == "asset" + and entity.get("data", {}).get("visualParent") ): - parent_id = entity['data']['visualParent'] + parent_id = entity["data"]["visualParent"] - parent = legacy_io.find_one({'_id': parent_id}) + parent = legacy_io.find_one({"_id": parent_id}) return get_hierarchical_attr(parent, attr) @@ -1630,12 +1687,13 @@ def get_write_node_template_attr(node): ''' # get avalon data from node - data = dict() - data['avalon'] = read(node) + data = {"avalon": read_avalon_data(node)} + data_preset = { - "nodeclass": data['avalon']['family'], - "families": [data['avalon']['families']], - "creator": data['avalon']['creator'] + "nodeclass": data["avalon"]["family"], + "families": [data["avalon"]["families"]], + "creator": data["avalon"]["creator"], + "subset": data["avalon"]["subset"] } # get template data @@ -1646,10 +1704,11 @@ def get_write_node_template_attr(node): "file": get_render_path(node) }) - # adding imageio template - {correct_data.update({k: v}) - for k, v in nuke_imageio_writes.items() - if k not in ["_id", "_previous"]} + # adding imageio knob presets + for k, v in nuke_imageio_writes.items(): + if k in ["_id", "_previous"]: + continue + correct_data[k] = v # fix badly encoded data return fix_data_for_node_create(correct_data) @@ -1765,8 +1824,8 @@ def maintained_selection(): Example: >>> with maintained_selection(): - ... node['selected'].setValue(True) - >>> print(node['selected'].value()) + ... node["selected"].setValue(True) + >>> print(node["selected"].value()) False """ previous_selection = nuke.selectedNodes() @@ -1774,11 +1833,11 @@ def maintained_selection(): yield finally: # unselect all selection in case there is some - current_seletion = nuke.selectedNodes() - [n['selected'].setValue(False) for n in current_seletion] + reset_selection() + # and select all previously selected nodes if previous_selection: - [n['selected'].setValue(True) for n in previous_selection] + select_nodes(previous_selection) def reset_selection(): diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 0194acd196..2785eb65cd 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -32,7 +32,7 @@ from .lib import ( launch_workfiles_app, check_inventory_versions, set_avalon_knob_data, - read, + read_avalon_data, Context ) @@ -359,7 +359,7 @@ def parse_container(node): dict: The container schema data for this container node. """ - data = read(node) + data = read_avalon_data(node) # (TODO) Remove key validation when `ls` has re-implemented. # diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index eaf0ab6911..fdb5930cb2 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -260,8 +260,6 @@ class ExporterReview(object): return nuke_imageio["viewer"]["viewerProcess"] - - class ExporterReviewLut(ExporterReview): """ Generator object for review lut from Nuke @@ -673,7 +671,8 @@ class AbstractWriteRender(OpenPypeCreator): write_data = { "nodeclass": self.n_class, "families": [self.family], - "avalon": self.data + "avalon": self.data, + "subset": self.data["subset"] } # add creator data diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index 2e8843d2e0..2a79d600ba 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -52,7 +52,7 @@ class ExtractReviewDataMov(openpype.api.Extractor): for o_name, o_data in self.outputs.items(): f_families = o_data["filter"]["families"] f_task_types = o_data["filter"]["task_types"] - f_subsets = o_data["filter"]["sebsets"] + f_subsets = o_data["filter"]["subsets"] self.log.debug( "f_families `{}` > families: {}".format( diff --git a/openpype/hosts/tvpaint/lib.py b/openpype/hosts/tvpaint/lib.py index 715ebb4a9d..c67ab1e4fb 100644 --- a/openpype/hosts/tvpaint/lib.py +++ b/openpype/hosts/tvpaint/lib.py @@ -573,7 +573,7 @@ def composite_rendered_layers( layer_ids_by_position[layer_position] = layer["layer_id"] # Sort layer positions - sorted_positions = tuple(sorted(layer_ids_by_position.keys())) + sorted_positions = tuple(reversed(sorted(layer_ids_by_position.keys()))) # Prepare variable where filepaths without any rendered content # - transparent will be created transparent_filepaths = set() diff --git a/openpype/hosts/tvpaint/plugins/create/create_render_layer.py b/openpype/hosts/tvpaint/plugins/create/create_render_layer.py index c1af9632b1..3b5bd47189 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render_layer.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render_layer.py @@ -24,7 +24,9 @@ class CreateRenderlayer(plugin.Creator): " {clip_id} {group_id} {r} {g} {b} \"{name}\"" ) - dynamic_subset_keys = ["render_pass", "render_layer", "group"] + dynamic_subset_keys = [ + "renderpass", "renderlayer", "render_pass", "render_layer", "group" + ] @classmethod def get_dynamic_data( @@ -34,12 +36,17 @@ class CreateRenderlayer(plugin.Creator): variant, task_name, asset_id, project_name, host_name ) # Use render pass name from creator's plugin - dynamic_data["render_pass"] = cls.render_pass + dynamic_data["renderpass"] = cls.render_pass # Add variant to render layer - dynamic_data["render_layer"] = variant + 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 diff --git a/openpype/hosts/tvpaint/plugins/create/create_render_pass.py b/openpype/hosts/tvpaint/plugins/create/create_render_pass.py index a7f717ccec..26fa8ac51a 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render_pass.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render_pass.py @@ -20,7 +20,9 @@ class CreateRenderPass(plugin.Creator): icon = "cube" defaults = ["Main"] - dynamic_subset_keys = ["render_pass", "render_layer"] + dynamic_subset_keys = [ + "renderpass", "renderlayer", "render_pass", "render_layer" + ] @classmethod def get_dynamic_data( @@ -29,9 +31,13 @@ class CreateRenderPass(plugin.Creator): dynamic_data = super(CreateRenderPass, cls).get_dynamic_data( variant, task_name, asset_id, project_name, host_name ) - dynamic_data["render_pass"] = variant + 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 @@ -115,6 +121,7 @@ class CreateRenderPass(plugin.Creator): 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 @@ -129,7 +136,7 @@ class CreateRenderPass(plugin.Creator): self.data["group_id"] = group_id self.data["pass"] = variant - self.data["render_layer"] = render_layer + self.data["renderlayer"] = render_layer # Collect selected layer ids to be stored into instance layer_names = [layer["name"] for layer in selected_layers] diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_instances.py b/openpype/hosts/tvpaint/plugins/publish/collect_instances.py index 188aa8c41a..782907b65d 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_instances.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_instances.py @@ -45,6 +45,21 @@ class CollectInstances(pyblish.api.ContextPlugin): 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 @@ -191,7 +206,7 @@ class CollectInstances(pyblish.api.ContextPlugin): "Creating render pass instance. \"{}\"".format(pass_name) ) # Change label - render_layer = instance_data["render_layer"] + render_layer = instance_data["renderlayer"] # Backwards compatibility # - subset names were not stored as final subset names during creation diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_scene_render.py b/openpype/hosts/tvpaint/plugins/publish/collect_scene_render.py index 1c042a62fb..2b8dbdc5b4 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_scene_render.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_scene_render.py @@ -69,9 +69,13 @@ class CollectRenderScene(pyblish.api.ContextPlugin): # Variant is using render pass name variant = self.render_layer dynamic_data = { - "render_layer": self.render_layer, - "render_pass": self.render_pass + "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_with_asset_doc( @@ -100,7 +104,9 @@ class CollectRenderScene(pyblish.api.ContextPlugin): "representations": [], "layers": copy.deepcopy(context.data["layersData"]), "asset": asset_name, - "task": task_name + "task": task_name, + # Add render layer to instance data + "renderlayer": self.render_layer } instance = context.create_instance(**instance_data) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 9c59c6f313..3c1d71ecd5 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -48,7 +48,6 @@ from .attribute_definitions import ( from .env_tools import ( env_value_to_bool, get_paths_from_environ, - get_global_environments ) from .terminal import Terminal @@ -249,7 +248,6 @@ __all__ = [ "env_value_to_bool", "get_paths_from_environ", - "get_global_environments", "get_vendor_bin_path", "get_oiio_tools_path", diff --git a/openpype/lib/env_tools.py b/openpype/lib/env_tools.py index 6521d20f1e..25bcbf7c1b 100644 --- a/openpype/lib/env_tools.py +++ b/openpype/lib/env_tools.py @@ -69,57 +69,3 @@ def get_paths_from_environ(env_key=None, env_value=None, return_first=False): return None # Return all existing paths from environment variable return existing_paths - - -def get_global_environments(env=None): - """Load global environments from Pype. - - Return prepared and parsed global environments by pype's settings. Use - combination of "global" environments set in pype's settings and enabled - modules. - - Args: - env (dict, optional): Initial environments. Empty dictionary is used - when not entered. - - Returns; - dict of str: Loaded and processed environments. - - """ - import acre - from openpype.modules import ModulesManager - from openpype.settings import get_environments - - if env is None: - env = {} - - # Get global environments from settings - all_settings_env = get_environments() - parsed_global_env = acre.parse(all_settings_env["global"]) - - # Merge with entered environments - merged_env = acre.append(env, parsed_global_env) - - # Get environments from Pype modules - modules_manager = ModulesManager() - - module_envs = modules_manager.collect_global_environments() - publish_plugin_dirs = modules_manager.collect_plugin_paths()["publish"] - - # Set pyblish plugins paths if any module want to register them - if publish_plugin_dirs: - publish_paths_str = os.environ.get("PYBLISHPLUGINPATH") or "" - publish_paths = publish_paths_str.split(os.pathsep) - _publish_paths = { - os.path.normpath(path) for path in publish_paths if path - } - for path in publish_plugin_dirs: - _publish_paths.add(os.path.normpath(path)) - module_envs["PYBLISHPLUGINPATH"] = os.pathsep.join(_publish_paths) - - # Merge environments with current environments and update values - if module_envs: - parsed_envs = acre.parse(module_envs) - merged_env = acre.merge(parsed_envs, merged_env) - - return acre.compute(merged_env, cleanup=True) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index 23c908299f..5ad1fc71c4 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -306,11 +306,11 @@ def _load_modules(): basename, ext = os.path.splitext(filename) if os.path.isdir(fullpath): - # Check existence of init fil + # Check existence of init file init_path = os.path.join(fullpath, "__init__.py") if not os.path.exists(init_path): log.debug(( - "Module directory does not contan __init__.py file {}" + "Module directory does not contain __init__.py file {}" ).format(fullpath)) continue @@ -353,11 +353,11 @@ def _load_modules(): basename, ext = os.path.splitext(filename) if os.path.isdir(fullpath): - # Check existence of init fil + # Check existence of init file init_path = os.path.join(fullpath, "__init__.py") if not os.path.exists(init_path): log.debug(( - "Module directory does not contan __init__.py file {}" + "Module directory does not contain __init__.py file {}" ).format(fullpath)) continue diff --git a/openpype/plugins/publish/collect_hierarchy.py b/openpype/plugins/publish/collect_hierarchy.py index 4e94acce4a..a96d444be6 100644 --- a/openpype/plugins/publish/collect_hierarchy.py +++ b/openpype/plugins/publish/collect_hierarchy.py @@ -30,14 +30,15 @@ class CollectHierarchy(pyblish.api.ContextPlugin): # shot data dict shot_data = {} - family = instance.data.get("family") + family = instance.data["family"] + families = instance.data["families"] # filter out all unepropriate instances if not instance.data["publish"]: continue # exclude other families then self.families with intersection - if not set(self.families).intersection([family]): + if not set(self.families).intersection(set(families + [family])): continue # exclude if not masterLayer True diff --git a/openpype/settings/__init__.py b/openpype/settings/__init__.py index 14e4678050..ca7157812d 100644 --- a/openpype/settings/__init__.py +++ b/openpype/settings/__init__.py @@ -22,7 +22,6 @@ from .lib import ( get_project_settings, get_current_project_settings, get_anatomy_settings, - get_environments, get_local_settings ) from .entities import ( @@ -54,7 +53,6 @@ __all__ = ( "get_project_settings", "get_current_project_settings", "get_anatomy_settings", - "get_environments", "get_local_settings", "SystemSettings", diff --git a/openpype/settings/constants.py b/openpype/settings/constants.py index 19ff953eb4..cd84d4db1c 100644 --- a/openpype/settings/constants.py +++ b/openpype/settings/constants.py @@ -3,14 +3,11 @@ import re # Metadata keys for work with studio and project overrides M_OVERRIDDEN_KEY = "__overriden_keys__" -# Metadata key for storing information about environments -M_ENVIRONMENT_KEY = "__environment_keys__" # Metadata key for storing dynamic created labels M_DYNAMIC_KEY_LABEL = "__dynamic_keys_labels__" METADATA_KEYS = frozenset([ M_OVERRIDDEN_KEY, - M_ENVIRONMENT_KEY, M_DYNAMIC_KEY_LABEL ]) @@ -35,7 +32,6 @@ KEY_REGEX = re.compile(r"^[{}]+$".format(KEY_ALLOWED_SYMBOLS)) __all__ = ( "M_OVERRIDDEN_KEY", - "M_ENVIRONMENT_KEY", "M_DYNAMIC_KEY_LABEL", "METADATA_KEYS", diff --git a/openpype/settings/defaults/project_anatomy/imageio.json b/openpype/settings/defaults/project_anatomy/imageio.json index 7a3f49452e..fedae994bf 100644 --- a/openpype/settings/defaults/project_anatomy/imageio.json +++ b/openpype/settings/defaults/project_anatomy/imageio.json @@ -165,7 +165,7 @@ ] } ], - "customNodes": [] + "overrideNodes": [] }, "regexInputs": { "inputs": [ diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index ef7a2a4467..dd8c05d460 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -55,18 +55,23 @@ "keep_original_representation": false, "export_presets_mapping": { "exr16fpdwaa": { + "active": true, + "export_type": "File Sequence", "ext": "exr", "xml_preset_file": "OpenEXR (16-bit fp DWAA).xml", - "xml_preset_dir": "", - "export_type": "File Sequence", - "ignore_comment_attrs": false, "colorspace_out": "ACES - ACEScg", + "xml_preset_dir": "", + "parsed_comment_attrs": true, "representation_add_range": true, "representation_tags": [], "load_to_batch_group": true, - "batch_group_loader_name": "LoadClip" + "batch_group_loader_name": "LoadClipBatch", + "filter_path_regex": ".*" } } + }, + "IntegrateBatchGroup": { + "enabled": false } }, "load": { @@ -87,7 +92,8 @@ "png", "h264", "mov", - "mp4" + "mp4", + "exr16fpdwaa" ], "reel_group_name": "OpenPype_Reels", "reel_name": "Loaded", @@ -110,7 +116,8 @@ "png", "h264", "mov", - "mp4" + "mp4", + "exr16fpdwaa" ], "reel_name": "OP_LoadedReel", "clip_name_template": "{asset}_{subset}_{output}" diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 7317a3da1c..7b223798f1 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -307,7 +307,7 @@ ], "task_types": [], "tasks": [], - "template": "{family}{Task}_{Render_layer}_{Render_pass}" + "template": "{family}{Task}_{Renderlayer}_{Renderpass}" }, { "families": [ diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index ab015271ff..0b03a00187 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -120,7 +120,7 @@ "filter": { "task_types": [], "families": [], - "sebsets": [] + "subsets": [] }, "read_raw": false, "viewer_process_override": "", @@ -220,11 +220,12 @@ "repre_names": [ "exr", "dpx", - "mov" + "mov", + "mp4", + "h264" ], "loaders": [ - "LoadSequence", - "LoadMov" + "LoadClip" ] } ], diff --git a/openpype/settings/entities/base_entity.py b/openpype/settings/entities/base_entity.py index 21ee44ae77..741f13c49b 100644 --- a/openpype/settings/entities/base_entity.py +++ b/openpype/settings/entities/base_entity.py @@ -127,12 +127,6 @@ class BaseItemEntity(BaseEntity): # Entity is in hierarchy of dynamically created entity self.is_in_dynamic_item = False - # Entity will save metadata about environments - # - this is current possible only for RawJsonEnity - self.is_env_group = False - # Key of environment group key must be unique across system settings - self.env_group_key = None - # Roles of an entity self.roles = None @@ -286,16 +280,6 @@ class BaseItemEntity(BaseEntity): ).format(self.group_item.path) raise EntitySchemaError(self, reason) - # Validate that env group entities will be stored into file. - # - env group entities must store metadata which is not possible if - # metadata would be outside of file - if self.file_item is None and self.is_env_group: - reason = ( - "Environment item is not inside file" - " item so can't store metadata for defaults." - ) - raise EntitySchemaError(self, reason) - # Dynamic items must not have defined labels. (UI specific) if self.label and self.is_dynamic_item: raise EntitySchemaError( @@ -862,11 +846,6 @@ class ItemEntity(BaseItemEntity): if self.is_dynamic_item: self.require_key = False - # If value should be stored to environments and uder which group key - # - the key may be dynamically changed by it's parent on save - self.env_group_key = self.schema_data.get("env_group_key") - self.is_env_group = bool(self.env_group_key is not None) - # Root item reference self.root_item = self.parent.root_item diff --git a/openpype/settings/entities/dict_mutable_keys_entity.py b/openpype/settings/entities/dict_mutable_keys_entity.py index a0c93b97a7..e6d332b9ad 100644 --- a/openpype/settings/entities/dict_mutable_keys_entity.py +++ b/openpype/settings/entities/dict_mutable_keys_entity.py @@ -15,7 +15,6 @@ from .exceptions import ( from openpype.settings.constants import ( METADATA_KEYS, M_DYNAMIC_KEY_LABEL, - M_ENVIRONMENT_KEY, KEY_REGEX, KEY_ALLOWED_SYMBOLS ) @@ -148,11 +147,7 @@ class DictMutableKeysEntity(EndpointEntity): ): raise InvalidKeySymbols(self.path, key) - if self.value_is_env_group: - item_schema = copy.deepcopy(self.item_schema) - item_schema["env_group_key"] = key - else: - item_schema = self.item_schema + item_schema = self.item_schema new_child = self.create_schema_object(item_schema, self, True) self.children_by_key[key] = new_child @@ -216,9 +211,7 @@ class DictMutableKeysEntity(EndpointEntity): self.children_label_by_id = {} self.store_as_list = self.schema_data.get("store_as_list") or False - self.value_is_env_group = ( - self.schema_data.get("value_is_env_group") or False - ) + self.required_keys = self.schema_data.get("required_keys") or [] self.collapsible_key = self.schema_data.get("collapsible_key") or False # GUI attributes @@ -241,9 +234,6 @@ class DictMutableKeysEntity(EndpointEntity): object_type.update(input_modifiers) self.item_schema = object_type - if self.value_is_env_group: - self.item_schema["env_group_key"] = "" - if self.group_item is None: self.is_group = True @@ -259,10 +249,6 @@ class DictMutableKeysEntity(EndpointEntity): if used_temp_label: self.label = None - if self.value_is_env_group and self.store_as_list: - reason = "Item can't store environments metadata to list output." - raise EntitySchemaError(self, reason) - if not self.schema_data.get("object_type"): reason = ( "Modifiable dictionary must have specified `object_type`." @@ -579,18 +565,10 @@ class DictMutableKeysEntity(EndpointEntity): output.append([key, child_value]) return output - output = {} - for key, child_entity in self.children_by_key.items(): - child_value = child_entity.settings_value() - # TODO child should have setter of env group key se child can - # know what env group represents. - if self.value_is_env_group: - if key not in child_value[M_ENVIRONMENT_KEY]: - _metadata = child_value[M_ENVIRONMENT_KEY] - _m_keykey = tuple(_metadata.keys())[0] - env_keys = child_value[M_ENVIRONMENT_KEY].pop(_m_keykey) - child_value[M_ENVIRONMENT_KEY][key] = env_keys - output[key] = child_value + output = { + key: child_entity.settings_value() + for key, child_entity in self.children_by_key.items() + } output.update(self.metadata) return output diff --git a/openpype/settings/entities/input_entities.py b/openpype/settings/entities/input_entities.py index 3dcd238672..89f12afd9b 100644 --- a/openpype/settings/entities/input_entities.py +++ b/openpype/settings/entities/input_entities.py @@ -15,10 +15,7 @@ from .exceptions import ( EntitySchemaError ) -from openpype.settings.constants import ( - METADATA_KEYS, - M_ENVIRONMENT_KEY -) +from openpype.settings.constants import METADATA_KEYS class EndpointEntity(ItemEntity): @@ -534,13 +531,7 @@ class RawJsonEntity(InputEntity): @property def metadata(self): - output = {} - if isinstance(self._current_value, dict) and self.is_env_group: - output[M_ENVIRONMENT_KEY] = { - self.env_group_key: list(self._current_value.keys()) - } - - return output + return {} @property def has_unsaved_changes(self): @@ -549,15 +540,6 @@ class RawJsonEntity(InputEntity): result = self.metadata != self._metadata_for_current_state() return result - def schema_validations(self): - if self.store_as_string and self.is_env_group: - reason = ( - "RawJson entity can't store environment group metadata" - " as string." - ) - raise EntitySchemaError(self, reason) - super(RawJsonEntity, self).schema_validations() - def _convert_to_valid_type(self, value): if isinstance(value, STRING_TYPE): try: @@ -583,9 +565,6 @@ class RawJsonEntity(InputEntity): def _settings_value(self): value = super(RawJsonEntity, self)._settings_value() - if self.is_env_group and isinstance(value, dict): - value.update(self.metadata) - if self.store_as_string: return json.dumps(value) return value diff --git a/openpype/settings/entities/root_entities.py b/openpype/settings/entities/root_entities.py index edb4407679..ff76fa5180 100644 --- a/openpype/settings/entities/root_entities.py +++ b/openpype/settings/entities/root_entities.py @@ -52,7 +52,6 @@ from openpype.settings.lib import ( get_available_studio_project_settings_overrides_versions, get_available_studio_project_anatomy_overrides_versions, - find_environments, apply_overrides ) @@ -422,11 +421,6 @@ class RootEntity(BaseItemEntity): """ pass - @abstractmethod - def _validate_defaults_to_save(self, value): - """Validate default values before save.""" - pass - def _save_default_values(self): """Save default values. @@ -435,7 +429,6 @@ class RootEntity(BaseItemEntity): DEFAULTS. """ settings_value = self.settings_value() - self._validate_defaults_to_save(settings_value) defaults_dir = self.defaults_dir() for file_path, value in settings_value.items(): @@ -604,8 +597,6 @@ class SystemSettings(RootEntity): def _save_studio_values(self): settings_value = self.settings_value() - self._validate_duplicated_env_group(settings_value) - self.log.debug("Saving system settings: {}".format( json.dumps(settings_value, indent=4) )) @@ -613,29 +604,6 @@ class SystemSettings(RootEntity): # Reset source version after restart self._source_version = None - def _validate_defaults_to_save(self, value): - """Valiations of default values before save.""" - self._validate_duplicated_env_group(value) - - def _validate_duplicated_env_group(self, value, override_state=None): - """ Validate duplicated environment groups. - - Raises: - DuplicatedEnvGroups: When value contain duplicated env groups. - """ - value = copy.deepcopy(value) - if override_state is None: - override_state = self._override_state - - if override_state is OverrideState.STUDIO: - default_values = get_default_settings()[SYSTEM_SETTINGS_KEY] - final_value = apply_overrides(default_values, value) - else: - final_value = value - - # Check if final_value contain duplicated environment groups - find_environments(final_value) - def _save_project_values(self): """System settings can't have project overrides. @@ -911,10 +879,6 @@ class ProjectSettings(RootEntity): if warnings: raise SaveWarningExc(warnings) - def _validate_defaults_to_save(self, value): - """Valiations of default values before save.""" - pass - def _validate_values_to_save(self, value): pass diff --git a/openpype/settings/entities/schemas/README.md b/openpype/settings/entities/schemas/README.md index b4bfef2972..b4c878fe0f 100644 --- a/openpype/settings/entities/schemas/README.md +++ b/openpype/settings/entities/schemas/README.md @@ -46,8 +46,7 @@ }, { "type": "raw-json", "label": "{host_label} Environments", - "key": "{host_name}_environments", - "env_group_key": "{host_name}" + "key": "{host_name}_environments" }, { "type": "path", "key": "{host_name}_executables", 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 fe11d63ac2..ace404b47a 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -238,25 +238,19 @@ "type": "dict", "children": [ { - "key": "ext", - "label": "Output extension", - "type": "text" + "type": "boolean", + "key": "active", + "label": "Is active", + "default": true }, { - "key": "xml_preset_file", - "label": "XML preset file (with ext)", - "type": "text" - }, - { - "key": "xml_preset_dir", - "label": "XML preset folder (optional)", - "type": "text" + "type": "separator" }, { "key": "export_type", "label": "Eport clip type", "type": "enum", - "default": "File Sequence", + "default": "Sequence Publish", "enum_items": [ { "Movie": "Movie" @@ -268,59 +262,125 @@ "Sequence Publish": "Sequence Publish" } ] - }, { - "type": "separator" + "key": "ext", + "label": "Output extension", + "type": "text", + "default": "exr" }, { - "type": "boolean", - "key": "ignore_comment_attrs", - "label": "Ignore attributes parsed from a segment comments" - }, - { - "type": "separator" + "key": "xml_preset_file", + "label": "XML preset file (with ext)", + "type": "text" }, { "key": "colorspace_out", "label": "Output color (imageio)", - "type": "text" - }, - { - "type": "separator" - }, - { - "type": "boolean", - "key": "representation_add_range", - "label": "Add frame range to representation" - }, - { - "type": "list", - "key": "representation_tags", - "label": "Add representation tags", - "object_type": { - "type": "text", - "multiline": false - } - }, - { - "type": "separator" - }, - { - "type": "boolean", - "key": "load_to_batch_group", - "label": "Load to batch group reel", - "default": false - }, - { "type": "text", - "key": "batch_group_loader_name", - "label": "Use loader name" + "default": "linear" + }, + { + "type": "collapsible-wrap", + "label": "Other parameters", + "collapsible": true, + "collapsed": true, + "children": [ + { + "key": "xml_preset_dir", + "label": "XML preset folder (optional)", + "type": "text" + }, + { + "type": "separator" + }, + { + "type": "boolean", + "key": "parsed_comment_attrs", + "label": "Include parsed attributes from comments", + "default": false + + }, + { + "type": "separator" + }, + { + "type": "collapsible-wrap", + "label": "Representation", + "collapsible": true, + "collapsed": true, + "children": [ + { + "type": "boolean", + "key": "representation_add_range", + "label": "Add frame range to representation" + }, + { + "type": "list", + "key": "representation_tags", + "label": "Add representation tags", + "object_type": { + "type": "text", + "multiline": false + } + } + ] + }, + { + "type": "collapsible-wrap", + "label": "Loading during publish", + "collapsible": true, + "collapsed": true, + "children": [ + { + "type": "boolean", + "key": "load_to_batch_group", + "label": "Load to batch group reel", + "default": false + }, + { + "type": "text", + "key": "batch_group_loader_name", + "label": "Use loader name" + } + ] + } + + ] + }, + { + "type": "collapsible-wrap", + "label": "Filtering", + "collapsible": true, + "collapsed": true, + "children": [ + { + "key": "filter_path_regex", + "label": "Regex in clip path", + "type": "text", + "default": ".*" + } + ] } ] } } ] + }, + { + "type": "dict", + "collapsible": true, + "key": "IntegrateBatchGroup", + "label": "IntegrateBatchGroup", + "is_group": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] } ] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json index 9f142bad09..819f7121c4 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json @@ -253,7 +253,7 @@ { "key": "requiredNodes", "type": "list", - "label": "Required Nodes", + "label": "Plugin required", "object_type": { "type": "dict", "children": [ @@ -272,35 +272,43 @@ "label": "Nuke Node Class" }, { - "type": "splitter" - }, - { - "key": "knobs", + "type": "collapsible-wrap", "label": "Knobs", - "type": "list", - "object_type": { - "type": "dict", - "children": [ - { - "type": "text", - "key": "name", - "label": "Name" - }, - { - "type": "text", - "key": "value", - "label": "Value" + "collapsible": true, + "collapsed": true, + "children": [ + { + "key": "knobs", + "type": "list", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "text", + "key": "value", + "label": "Value" + } + ] } - ] - } + } + ] } + ] } }, + { + "type": "splitter" + }, { "type": "list", - "key": "customNodes", - "label": "Custom Nodes", + "key": "overrideNodes", + "label": "Plugin's node overrides", "object_type": { "type": "dict", "children": [ @@ -319,27 +327,37 @@ "label": "Nuke Node Class" }, { - "type": "splitter" + "key": "subsets", + "label": "Subsets", + "type": "list", + "object_type": "text" }, { - "key": "knobs", - "label": "Knobs", - "type": "list", - "object_type": { - "type": "dict", - "children": [ - { - "type": "text", - "key": "name", - "label": "Name" - }, - { - "type": "text", - "key": "value", - "label": "Value" + "type": "collapsible-wrap", + "label": "Knobs overrides", + "collapsible": true, + "collapsed": true, + "children": [ + { + "key": "knobs", + "type": "list", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "text", + "key": "value", + "label": "Value" + } + ] } - ] - } + } + ] } ] } @@ -446,7 +464,7 @@ { "key": "flame", "type": "dict", - "label": "Flame/Flair", + "label": "Flame & Flare", "children": [ { "key": "project", 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 4a796f1933..d67fb309bd 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 @@ -212,7 +212,7 @@ "object_type": "text" }, { - "key": "sebsets", + "key": "subsets", "label": "Subsets", "type": "list", "object_type": "text" diff --git a/openpype/settings/entities/schemas/system_schema/example_schema.json b/openpype/settings/entities/schemas/system_schema/example_schema.json index 6a86dae259..b9747b5f4f 100644 --- a/openpype/settings/entities/schemas/system_schema/example_schema.json +++ b/openpype/settings/entities/schemas/system_schema/example_schema.json @@ -117,19 +117,6 @@ } ] }, - { - "key": "env_group_test", - "label": "EnvGroup Test", - "type": "dict", - "children": [ - { - "key": "key_to_store_in_system_settings", - "label": "Testing environment group", - "type": "raw-json", - "env_group_key": "test_group" - } - ] - }, { "key": "dict_wrapper", "type": "dict", diff --git a/openpype/settings/entities/schemas/system_schema/example_template.json b/openpype/settings/entities/schemas/system_schema/example_template.json index ff78c78e8f..9955cf5651 100644 --- a/openpype/settings/entities/schemas/system_schema/example_template.json +++ b/openpype/settings/entities/schemas/system_schema/example_template.json @@ -7,8 +7,7 @@ { "type": "raw-json", "label": "{host_label} Environments", - "key": "{host_name}_environments", - "env_group_key": "{host_name}" + "key": "{host_name}_environments" }, { "type": "path", diff --git a/openpype/settings/entities/schemas/system_schema/schema_general.json b/openpype/settings/entities/schemas/system_schema/schema_general.json index 0090c54386..5b6d8d5d62 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_general.json +++ b/openpype/settings/entities/schemas/system_schema/schema_general.json @@ -34,7 +34,6 @@ "key": "environment", "label": "Environment", "type": "raw-json", - "env_group_key": "global", "require_restart": true }, { diff --git a/openpype/settings/lib.py b/openpype/settings/lib.py index 937329b417..f921b9c318 100644 --- a/openpype/settings/lib.py +++ b/openpype/settings/lib.py @@ -9,7 +9,6 @@ from .exceptions import ( ) from .constants import ( M_OVERRIDDEN_KEY, - M_ENVIRONMENT_KEY, METADATA_KEYS, @@ -457,24 +456,6 @@ def get_local_settings(): return _LOCAL_SETTINGS_HANDLER.get_local_settings() -class DuplicatedEnvGroups(Exception): - def __init__(self, duplicated): - self.origin_duplicated = duplicated - self.duplicated = {} - for key, items in duplicated.items(): - self.duplicated[key] = [] - for item in items: - self.duplicated[key].append("/".join(item["parents"])) - - msg = "Duplicated environment group keys. {}".format( - ", ".join([ - "\"{}\"".format(env_key) for env_key in self.duplicated.keys() - ]) - ) - - super(DuplicatedEnvGroups, self).__init__(msg) - - def load_openpype_default_settings(): """Load openpype default settings.""" return load_jsons_from_dir(DEFAULTS_DIR) @@ -624,69 +605,6 @@ def load_jsons_from_dir(path, *args, **kwargs): return output -def find_environments(data, with_items=False, parents=None): - """ Find environemnt values from system settings by it's metadata. - - Args: - data(dict): System settings data or dictionary which may contain - environments metadata. - - Returns: - dict: Key as Environment key and value for `acre` module. - """ - if not data or not isinstance(data, dict): - return {} - - output = {} - if parents is None: - parents = [] - - if M_ENVIRONMENT_KEY in data: - metadata = data.get(M_ENVIRONMENT_KEY) - for env_group_key, env_keys in metadata.items(): - if env_group_key not in output: - output[env_group_key] = [] - - _env_values = {} - for key in env_keys: - _env_values[key] = data[key] - - item = { - "env": _env_values, - "parents": parents[:-1] - } - output[env_group_key].append(item) - - for key, value in data.items(): - _parents = copy.deepcopy(parents) - _parents.append(key) - result = find_environments(value, True, _parents) - if not result: - continue - - for env_group_key, env_values in result.items(): - if env_group_key not in output: - output[env_group_key] = [] - - for env_values_item in env_values: - output[env_group_key].append(env_values_item) - - if with_items: - return output - - duplicated_env_groups = {} - final_output = {} - for key, value_in_list in output.items(): - if len(value_in_list) > 1: - duplicated_env_groups[key] = value_in_list - else: - final_output[key] = value_in_list[0]["env"] - - if duplicated_env_groups: - raise DuplicatedEnvGroups(duplicated_env_groups) - return final_output - - def subkey_merge(_dict, value, keys): key = keys.pop(0) if not keys: @@ -1082,19 +1000,6 @@ def get_current_project_settings(): return get_project_settings(project_name) -def get_environments(): - """Calculated environment based on defaults and system settings. - - Any default environment also found in the system settings will be fully - overridden by the one from the system settings. - - Returns: - dict: Output should be ready for `acre` module. - """ - - return find_environments(get_system_settings(False)) - - def get_general_environments(): """Get general environments. diff --git a/openpype/tools/settings/settings/README.md b/openpype/tools/settings/settings/README.md index 1c916ddff2..c29664a907 100644 --- a/openpype/tools/settings/settings/README.md +++ b/openpype/tools/settings/settings/README.md @@ -44,8 +44,7 @@ }, { "type": "raw-json", "label": "{host_label} Environments", - "key": "{host_name}_environments", - "env_group_key": "{host_name}" + "key": "{host_name}_environments" }, { "type": "path-widget", "key": "{host_name}_executables", diff --git a/openpype/tools/settings/settings/dict_mutable_widget.py b/openpype/tools/settings/settings/dict_mutable_widget.py index 6489266131..1c704b3cd5 100644 --- a/openpype/tools/settings/settings/dict_mutable_widget.py +++ b/openpype/tools/settings/settings/dict_mutable_widget.py @@ -465,10 +465,6 @@ class ModifiableDictItem(QtWidgets.QWidget): self.entity_widget.change_key(key, self) self.update_style() - @property - def value_is_env_group(self): - return self.entity_widget.value_is_env_group - def update_key_label(self): if not self.collapsible_key: return diff --git a/start.py b/start.py index 38eb9e9bf4..4d4801c1e5 100644 --- a/start.py +++ b/start.py @@ -266,18 +266,9 @@ def set_openpype_global_environments() -> None: """Set global OpenPype's environments.""" import acre - try: - from openpype.settings import get_general_environments + from openpype.settings import get_general_environments - general_env = get_general_environments() - - except Exception: - # Backwards compatibility for OpenPype versions where - # `get_general_environments` does not exists yet - from openpype.settings import get_environments - - all_env = get_environments() - general_env = all_env["global"] + general_env = get_general_environments() merged_env = acre.merge( acre.parse(general_env),