From 0e61a2d51e13497314a365636cbcf67aa1cd1d12 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 4 Dec 2019 14:58:08 +0100 Subject: [PATCH 01/31] feat(nks): colecting timecodes with otio --- .../nukestudio/publish/collect_timecodes.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 pype/plugins/nukestudio/publish/collect_timecodes.py diff --git a/pype/plugins/nukestudio/publish/collect_timecodes.py b/pype/plugins/nukestudio/publish/collect_timecodes.py new file mode 100644 index 0000000000..b3d4a5e8c5 --- /dev/null +++ b/pype/plugins/nukestudio/publish/collect_timecodes.py @@ -0,0 +1,88 @@ +import pyblish.api +import opentimelineio.opentime as otio_ot + + +class CollectClipTimecodes(pyblish.api.InstancePlugin): + """Collect time with OpenTimelineIO: source_h(In,Out)[timecode, sec], timeline(In,Out)[timecode, sec]""" + + order = pyblish.api.CollectorOrder + 0.101 + label = "Collect Timecodes" + hosts = ["nukestudio"] + + def process(self, instance): + + data = dict() + self.log.debug("__ instance.data: {}".format(instance.data)) + # Timeline data. + handle_start = instance.data["handleStart"] + handle_end = instance.data["handleEnd"] + + source_in_h = instance.data("sourceInH", + instance.data("sourceIn") - handle_start) + source_out_h = instance.data("sourceOutH", + instance.data("sourceOut") + handle_end) + + timeline_in = instance.data["clipIn"] + timeline_out = instance.data["clipOut"] + + # set frame start with tag or take it from timeline + frame_start = instance.data.get("startingFrame") + + if not frame_start: + frame_start = timeline_in + + source = instance.data.get("source") + + otio_data = dict() + self.log.debug("__ source: `{}`".format(source)) + + rate_fps = instance.context.data["fps"] + + otio_in_h_ratio = otio_ot.RationalTime( + value=(source.timecodeStart() + ( + source_in_h + (source_out_h - source_in_h))), + rate=rate_fps) + + otio_out_h_ratio = otio_ot.RationalTime( + value=(source.timecodeStart() + source_in_h), + rate=rate_fps) + + otio_timeline_in_ratio = otio_ot.RationalTime( + value=int( + instance.data.get("timelineTimecodeStart", 0)) + timeline_in, + rate=rate_fps) + + otio_timeline_out_ratio = otio_ot.RationalTime( + value=int( + instance.data.get("timelineTimecodeStart", 0)) + timeline_out, + rate=rate_fps) + + otio_data.update({ + + "otioClipInHTimecode": otio_ot.to_timecode(otio_in_h_ratio), + + "otioClipOutHTimecode": otio_ot.to_timecode(otio_out_h_ratio), + + "otioClipInHSec": otio_ot.to_seconds(otio_in_h_ratio), + + "otioClipOutHSec": otio_ot.to_seconds(otio_out_h_ratio), + + "otioTimelineInTimecode": otio_ot.to_timecode( + otio_timeline_in_ratio), + + "otioTimelineOutTimecode": otio_ot.to_timecode( + otio_timeline_out_ratio), + + "otioTimelineInSec": otio_ot.to_seconds(otio_timeline_in_ratio), + + "otioTimelineOutSec": otio_ot.to_seconds(otio_timeline_out_ratio) + }) + + data.update({ + "otioData": otio_data, + "sourceTimecodeIn": otio_ot.to_timecode(otio_in_h_ratio), + "sourceTimecodeOut": otio_ot.to_timecode(otio_out_h_ratio), + } + ) + instance.data.update(data) + self.log.debug("data: {}".format(instance.data)) From 89e5e72059c7e27b091ee36ab147aa369505787c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 4 Dec 2019 15:05:51 +0100 Subject: [PATCH 02/31] feat(nks): adding media/clip duration to collect frame ranges --- pype/plugins/nukestudio/publish/collect_frame_ranges.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pype/plugins/nukestudio/publish/collect_frame_ranges.py b/pype/plugins/nukestudio/publish/collect_frame_ranges.py index 38224f683d..e0a53d7501 100644 --- a/pype/plugins/nukestudio/publish/collect_frame_ranges.py +++ b/pype/plugins/nukestudio/publish/collect_frame_ranges.py @@ -34,15 +34,22 @@ class CollectClipFrameRanges(pyblish.api.InstancePlugin): frame_start = timeline_in frame_end = frame_start + (timeline_out - timeline_in) + source = instance.data.get("source") data.update( { + data.update({ + "sourceFirst": source_in_h, "sourceInH": source_in_h, "sourceOutH": source_out_h, "frameStart": frame_start, "frameEnd": frame_end, "clipInH": timeline_in_h, - "clipOutH": timeline_out_h + "clipOutH": timeline_out_h, + "mediaDurationH": instance.data.get( + "mediaDuration") + handle_start + handle_end, + "clipDurationH": instance.data.get( + "clipDuration") + handle_start + handle_end } ) self.log.debug("__ data: {}".format(data)) From cf01f447265170fbd009d88e24cf2686018de793 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 4 Dec 2019 15:09:16 +0100 Subject: [PATCH 03/31] feat(nks): collect review identify if mediaSource needs to be trimed --- pype/plugins/nukestudio/publish/collect_reviews.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pype/plugins/nukestudio/publish/collect_reviews.py b/pype/plugins/nukestudio/publish/collect_reviews.py index f9032b2ca4..c127b977e6 100644 --- a/pype/plugins/nukestudio/publish/collect_reviews.py +++ b/pype/plugins/nukestudio/publish/collect_reviews.py @@ -100,6 +100,19 @@ class CollectReviews(api.InstancePlugin): "name": "preview", "ext": ext } + + # if int(rev_inst.data.get("sourceIn")) > + mediaDuration = instance.data.get("mediaDuration") + clipDuration = instance.data.get("clipDuration") + + if mediaDuration > clipDuration: + self.log.debug("Media duration higher: {}".format( + (mediaDuration - clipDuration))) + # representation.update({ + # "frameStart": instance.data.get("sourceInH"), + # "frameEnd": instance.data.get("sourceOutH") + # }) + instance.data["representations"].append(representation) self.log.debug("Added representation: {}".format(representation)) From 128719aeb12a314ac0122dc612000ba33a758dbd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 4 Dec 2019 15:09:50 +0100 Subject: [PATCH 04/31] feat(nks): improving calculation of fps --- pype/plugins/nukestudio/publish/collect_framerate.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pype/plugins/nukestudio/publish/collect_framerate.py b/pype/plugins/nukestudio/publish/collect_framerate.py index a0fd4df599..694052f802 100644 --- a/pype/plugins/nukestudio/publish/collect_framerate.py +++ b/pype/plugins/nukestudio/publish/collect_framerate.py @@ -1,5 +1,6 @@ from pyblish import api + class CollectFramerate(api.ContextPlugin): """Collect framerate from selected sequence.""" @@ -9,4 +10,13 @@ class CollectFramerate(api.ContextPlugin): def process(self, context): sequence = context.data["activeSequence"] - context.data["fps"] = sequence.framerate().toFloat() + context.data["fps"] = self.get_rate(sequence) + + def get_rate(self, sequence): + num, den = sequence.framerate().toRational() + rate = float(num) / float(den) + + if rate.is_integer(): + return rate + + return round(rate, 3) From bda292a64fce755de40774b897e3364c3ccc6087 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 20 Mar 2020 14:46:59 +0100 Subject: [PATCH 05/31] feat(global): review workflow accepting nks host --- pype/plugins/global/publish/extract_burnin.py | 4 ++-- pype/plugins/global/publish/extract_review.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pype/plugins/global/publish/extract_burnin.py b/pype/plugins/global/publish/extract_burnin.py index 086a1fdfb2..f136a2fdac 100644 --- a/pype/plugins/global/publish/extract_burnin.py +++ b/pype/plugins/global/publish/extract_burnin.py @@ -18,7 +18,7 @@ class ExtractBurnin(pype.api.Extractor): label = "Extract burnins" order = pyblish.api.ExtractorOrder + 0.03 families = ["review", "burnin"] - hosts = ["nuke", "maya", "shell"] + hosts = ["nuke", "maya", "shell", "nukestudio"] optional = True def process(self, instance): @@ -185,7 +185,7 @@ class ExtractBurnin(pype.api.Extractor): self.log.debug("Output: {}".format(output)) repre_update = { - "anatomy_template": "render", + "anatomy_template": repre.get("anatomy_template", "render"), "files": movieFileBurnin, "name": repre["name"], "tags": [x for x in repre["tags"] if x != "delete"] diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index c8a8510fb2..7c5f90b135 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -20,15 +20,15 @@ class ExtractReview(pyblish.api.InstancePlugin): label = "Extract Review" order = pyblish.api.ExtractorOrder + 0.02 families = ["review"] - hosts = ["nuke", "maya", "shell"] + hosts = ["nuke", "maya", "shell", "nukestudio"] outputs = {} ext_filter = [] to_width = 1920 to_height = 1080 - def process(self, instance): + def process(self, instance): output_profiles = self.outputs or {} inst_data = instance.data @@ -170,7 +170,8 @@ class ExtractReview(pyblish.api.InstancePlugin): frame_start_handle = frame_start - handle_start frame_end_handle = frame_end + handle_end if isinstance(repre["files"], list): - if frame_start_handle != repre.get("detectedStart", frame_start_handle): + if frame_start_handle != repre.get( + "detectedStart", frame_start_handle): frame_start_handle = repre.get("detectedStart") # exclude handle if no handles defined From b149388be34b1c1a9722c2d685a15a0510873876 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 20 Mar 2020 14:47:18 +0100 Subject: [PATCH 06/31] feat(nks): adding plugin for cutting video parts --- .../publish/extract_review_cutup_video.py | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 pype/plugins/nukestudio/publish/extract_review_cutup_video.py diff --git a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py new file mode 100644 index 0000000000..445a26a184 --- /dev/null +++ b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py @@ -0,0 +1,107 @@ +import os +from pyblish import api +import pype + + +class ExtractReviewCutUpVideo(pype.api.Extractor): + """Cut up clips from long video file""" + + order = api.ExtractorOrder + # order = api.CollectorOrder + 0.1023 + label = "Extract Review CutUp Video" + hosts = ["nukestudio"] + families = ["review"] + + # presets + tags_addition = [] + + def process(self, instance): + inst_data = instance.data + asset = inst_data['asset'] + + # get representation and loop them + representations = inst_data["representations"] + + ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg") + + # filter out mov and img sequences + representations_new = representations[:] + for repre in representations: + input_args = list() + output_args = list() + + tags = repre.get("tags", []) + + if "cut-up" not in tags: + continue + + self.log.debug("__ repre: {}".format(repre)) + + file = repre.get("files") + staging_dir = repre.get("stagingDir") + frame_start = repre.get("frameStart") + frame_end = repre.get("frameEnd") + fps = repre.get("fps") + ext = repre.get("ext") + + new_file_name = "{}_{}".format(asset, file) + + full_input_path = os.path.join( + staging_dir, file) + + full_output_path = os.path.join( + staging_dir, new_file_name) + + self.log.debug("__ full_input_path: {}".format(full_input_path)) + self.log.debug("__ full_output_path: {}".format(full_output_path)) + + input_args.append("-y") + input_args.append("-i {}".format(full_input_path)) + + start_sec = float(frame_start) / fps + input_args.append("-ss {:0.2f}".format(start_sec)) + + output_args.append("-c copy") + duration_sec = float(frame_end - frame_start + 1) / fps + output_args.append("-t {:0.2f}".format(duration_sec)) + + # output filename + output_args.append(full_output_path) + + mov_args = [ + ffmpeg_path, + " ".join(input_args), + " ".join(output_args) + ] + subprcs_cmd = " ".join(mov_args) + + # run subprocess + self.log.debug("Executing: {}".format(subprcs_cmd)) + output = pype.api.subprocess(subprcs_cmd) + self.log.debug("Output: {}".format(output)) + + repre_new = { + "files": new_file_name, + "stagingDir": staging_dir, + "frameStart": frame_start, + "frameEnd": frame_end, + "frameStartFtrack": frame_start, + "frameEndFtrack": frame_end, + "step": 1, + "fps": fps, + "name": "cut_up_preview", + "tags": ["cut-up", "review", "delete"] + self.tags_addition, + "ext": ext, + "anatomy_template": "publish" + } + + representations_new.append(repre_new) + + for repre in representations_new: + if ("delete" in repre.get("tags", [])) and ( + "cut_up_preview" not in repre["name"]): + representations_new.remove(repre) + + self.log.debug( + "new representations: {}".format(representations_new)) + instance.data["representations"] = representations_new From 55b3b41fd944f4c41c400a79e60f948aa4652b9b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 20 Mar 2020 14:47:54 +0100 Subject: [PATCH 07/31] feat(nks): adjusting collect review for cutting --- .../nukestudio/publish/collect_reviews.py | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_reviews.py b/pype/plugins/nukestudio/publish/collect_reviews.py index ed9b7a3636..72e0c03009 100644 --- a/pype/plugins/nukestudio/publish/collect_reviews.py +++ b/pype/plugins/nukestudio/publish/collect_reviews.py @@ -78,8 +78,6 @@ class CollectReviews(api.InstancePlugin): file_dir = os.path.dirname(file_path) file = os.path.basename(file_path) ext = os.path.splitext(file)[-1][1:] - handleStart = rev_inst.data.get("handleStart") - handleEnd = rev_inst.data.get("handleEnd") # change label instance.data["label"] = "{0} - {1} - ({2}) - review".format( @@ -94,27 +92,26 @@ class CollectReviews(api.InstancePlugin): "stagingDir": file_dir, "frameStart": rev_inst.data.get("sourceIn"), "frameEnd": rev_inst.data.get("sourceOut"), - "frameStartFtrack": rev_inst.data.get("sourceIn") - handleStart, - "frameEndFtrack": rev_inst.data.get("sourceOut") + handleEnd, + "frameStartFtrack": rev_inst.data.get("sourceInH"), + "frameEndFtrack": rev_inst.data.get("sourceOutH"), "step": 1, "fps": rev_inst.data.get("fps"), - "preview": True, - "thumbnail": False, "name": "preview", + "tags": ["preview"], "ext": ext } - # if int(rev_inst.data.get("sourceIn")) > - mediaDuration = instance.data.get("mediaDuration") - clipDuration = instance.data.get("clipDuration") + media_duration = instance.data.get("mediaDuration") + clip_duration_h = instance.data.get("clipDurationH") - if mediaDuration > clipDuration: + if media_duration > clip_duration_h: self.log.debug("Media duration higher: {}".format( - (mediaDuration - clipDuration))) - # representation.update({ - # "frameStart": instance.data.get("sourceInH"), - # "frameEnd": instance.data.get("sourceOutH") - # }) + (media_duration - clip_duration_h))) + representation.update({ + "frameStart": instance.data.get("sourceInH"), + "frameEnd": instance.data.get("sourceOutH"), + "tags": ["cut-up", "delete"] + }) instance.data["representations"].append(representation) From 18f6acc794ef999067585380bd9583242d2bd6d1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 20 Mar 2020 14:48:20 +0100 Subject: [PATCH 08/31] clean(nks): old way of representation data --- pype/plugins/nukestudio/publish/collect_plates.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_plates.py b/pype/plugins/nukestudio/publish/collect_plates.py index 4ed281f0ee..56d6db0166 100644 --- a/pype/plugins/nukestudio/publish/collect_plates.py +++ b/pype/plugins/nukestudio/publish/collect_plates.py @@ -147,7 +147,6 @@ class CollectPlatesData(api.InstancePlugin): "version": version }) - try: basename, ext = os.path.splitext(source_file) head, padding = os.path.splitext(basename) @@ -192,8 +191,7 @@ class CollectPlatesData(api.InstancePlugin): "frameEnd": instance.data["sourceOut"] - instance.data["sourceIn"] + 1, 'step': 1, 'fps': instance.context.data["fps"], - 'preview': True, - 'thumbnail': False, + 'tags': ["preview"], 'name': "preview", 'ext': "mov", } From ac9a20cf08dded766a453eb2115fd8e069a1bc28 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 20 Mar 2020 14:48:48 +0100 Subject: [PATCH 09/31] fix(nks): improving data --- .../publish/collect_frame_ranges.py | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_frame_ranges.py b/pype/plugins/nukestudio/publish/collect_frame_ranges.py index e0a53d7501..24b23fae01 100644 --- a/pype/plugins/nukestudio/publish/collect_frame_ranges.py +++ b/pype/plugins/nukestudio/publish/collect_frame_ranges.py @@ -36,20 +36,16 @@ class CollectClipFrameRanges(pyblish.api.InstancePlugin): frame_end = frame_start + (timeline_out - timeline_in) source = instance.data.get("source") - data.update( - { data.update({ - "sourceFirst": source_in_h, - "sourceInH": source_in_h, - "sourceOutH": source_out_h, - "frameStart": frame_start, - "frameEnd": frame_end, - "clipInH": timeline_in_h, - "clipOutH": timeline_out_h, - "mediaDurationH": instance.data.get( - "mediaDuration") + handle_start + handle_end, - "clipDurationH": instance.data.get( - "clipDuration") + handle_start + handle_end + "sourceFirst": source_in_h, + "sourceInH": source_in_h, + "sourceOutH": source_out_h, + "frameStart": frame_start, + "frameEnd": frame_end, + "clipInH": timeline_in_h, + "clipOutH": timeline_out_h, + "clipDurationH": instance.data.get( + "clipDuration") + handle_start + handle_end } ) self.log.debug("__ data: {}".format(data)) From 31a7c4405b835eff4f6d82fb3b4acd0d063be02e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 20 Mar 2020 14:49:14 +0100 Subject: [PATCH 10/31] fix(nks): duration method was wrong --- pype/plugins/nukestudio/publish/collect_clips.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_clips.py b/pype/plugins/nukestudio/publish/collect_clips.py index 6a1dad9a6d..78920fdbb9 100644 --- a/pype/plugins/nukestudio/publish/collect_clips.py +++ b/pype/plugins/nukestudio/publish/collect_clips.py @@ -97,8 +97,7 @@ class CollectClips(api.ContextPlugin): "effects": effects, "sourceIn": int(item.sourceIn()), "sourceOut": int(item.sourceOut()), - "mediaDuration": (int(item.sourceOut()) - - int(item.sourceIn())) + 1, + "mediaDuration": source.duration(), "clipIn": int(item.timelineIn()), "clipOut": int(item.timelineOut()), "clipDuration": (int(item.timelineOut()) - From b503870d3f93164495048c0b0b0a246bd0720788 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 20 Mar 2020 14:49:51 +0100 Subject: [PATCH 11/31] fix(global): original repre tags are included to preset tags --- pype/plugins/global/publish/extract_review.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 7c5f90b135..ea05360250 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -77,6 +77,12 @@ class ExtractReview(pyblish.api.InstancePlugin): repre_new = repre.copy() ext = profile.get("ext", None) p_tags = profile.get('tags', []) + + # append repre tags into profile tags + for t in tags: + if t not in p_tags: + p_tags.append(t) + self.log.info("p_tags: `{}`".format(p_tags)) # adding control for presets to be sequence From eba14f8a8df89b6f7c0b73715b927d16a8f75ae5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 May 2020 17:37:19 +0200 Subject: [PATCH 12/31] clean(nks): Hound recomendations --- .../nukestudio/publish/collect_frame_ranges.py | 1 - .../nukestudio/publish/collect_timecodes.py | 14 ++++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_frame_ranges.py b/pype/plugins/nukestudio/publish/collect_frame_ranges.py index 24b23fae01..6993fa5e67 100644 --- a/pype/plugins/nukestudio/publish/collect_frame_ranges.py +++ b/pype/plugins/nukestudio/publish/collect_frame_ranges.py @@ -34,7 +34,6 @@ class CollectClipFrameRanges(pyblish.api.InstancePlugin): frame_start = timeline_in frame_end = frame_start + (timeline_out - timeline_in) - source = instance.data.get("source") data.update({ "sourceFirst": source_in_h, diff --git a/pype/plugins/nukestudio/publish/collect_timecodes.py b/pype/plugins/nukestudio/publish/collect_timecodes.py index b3d4a5e8c5..5ac07314a4 100644 --- a/pype/plugins/nukestudio/publish/collect_timecodes.py +++ b/pype/plugins/nukestudio/publish/collect_timecodes.py @@ -3,7 +3,10 @@ import opentimelineio.opentime as otio_ot class CollectClipTimecodes(pyblish.api.InstancePlugin): - """Collect time with OpenTimelineIO: source_h(In,Out)[timecode, sec], timeline(In,Out)[timecode, sec]""" + """Collect time with OpenTimelineIO: + source_h(In,Out)[timecode, sec] + timeline(In,Out)[timecode, sec] + """ order = pyblish.api.CollectorOrder + 0.101 label = "Collect Timecodes" @@ -79,10 +82,9 @@ class CollectClipTimecodes(pyblish.api.InstancePlugin): }) data.update({ - "otioData": otio_data, - "sourceTimecodeIn": otio_ot.to_timecode(otio_in_h_ratio), - "sourceTimecodeOut": otio_ot.to_timecode(otio_out_h_ratio), - } - ) + "otioData": otio_data, + "sourceTimecodeIn": otio_ot.to_timecode(otio_in_h_ratio), + "sourceTimecodeOut": otio_ot.to_timecode(otio_out_h_ratio) + }) instance.data.update(data) self.log.debug("data: {}".format(instance.data)) From fdbb6a306efb537f03d58ed3cd78a7ce58d0b269 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 May 2020 19:47:28 +0200 Subject: [PATCH 13/31] fix(nks): duplicity in hierarchy tag and not matching names clips --- .../nukestudio/publish/collect_clips.py | 77 ++++++++++++------- .../publish/collect_hierarchy_context.py | 37 +++++++-- 2 files changed, 81 insertions(+), 33 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_clips.py b/pype/plugins/nukestudio/publish/collect_clips.py index 746df67485..0e92193b14 100644 --- a/pype/plugins/nukestudio/publish/collect_clips.py +++ b/pype/plugins/nukestudio/publish/collect_clips.py @@ -47,11 +47,32 @@ class CollectClips(api.ContextPlugin): track = item.parent() source = item.source().mediaSource() source_path = source.firstpath() + clip_in = int(item.timelineIn()) + clip_out = int(item.timelineOut()) file_head = source.filenameHead() file_info = next((f for f in source.fileinfos()), None) source_first_frame = file_info.startFrame() is_sequence = False + self.log.debug( + "__ assets_shared: {}".format(context.data["assetsShared"])) + match_range = next( + (k for k, v in context.data["assetsShared"].items() + if (v.get("_clipIn", 0) == clip_in) + and (v.get("_clipOut", 0) == clip_out) + ), False) + + if asset in str(match_range): + match_range = False + + assert (not match_range), ( + "matching clip: {asset}" + " timeline range ({clip_in}:{clip_out})" + " conflicting with {match_range}" + " >> rename any of clips to be the same as the other <<" + ).format( + **locals()) + if not source.singleFile(): self.log.info("Single file") is_sequence = True @@ -89,32 +110,31 @@ class CollectClips(api.ContextPlugin): ) data.update({ - "name": "{0}_{1}".format(track.name(), item.name()), - "item": item, - "source": source, - "timecodeStart": str(source.timecodeStart()), - "timelineTimecodeStart": str(sequence.timecodeStart()), - "sourcePath": source_path, - "sourceFileHead": file_head, - "isSequence": is_sequence, - "track": track.name(), - "trackIndex": track_index, - "sourceFirst": source_first_frame, - "effects": effects, - "sourceIn": int(item.sourceIn()), - "sourceOut": int(item.sourceOut()), - "mediaDuration": (int(item.sourceOut()) - - int(item.sourceIn())) + 1, - "clipIn": int(item.timelineIn()), - "clipOut": int(item.timelineOut()), - "clipDuration": ( - int(item.timelineOut()) - int( - item.timelineIn())) + 1, - "asset": asset, - "family": "clip", - "families": [], - "handleStart": projectdata.get("handleStart", 0), - "handleEnd": projectdata.get("handleEnd", 0)}) + "name": "{0}_{1}".format(track.name(), item.name()), + "item": item, + "source": source, + "timecodeStart": str(source.timecodeStart()), + "timelineTimecodeStart": str(sequence.timecodeStart()), + "sourcePath": source_path, + "sourceFileHead": file_head, + "isSequence": is_sequence, + "track": track.name(), + "trackIndex": track_index, + "sourceFirst": source_first_frame, + "effects": effects, + "sourceIn": int(item.sourceIn()), + "sourceOut": int(item.sourceOut()), + "mediaDuration": source.duration(), + "clipIn": clip_in, + "clipOut": clip_out, + "clipDuration": ( + int(item.timelineOut()) - int( + item.timelineIn())) + 1, + "asset": asset, + "family": "clip", + "families": [], + "handleStart": projectdata.get("handleStart", 0), + "handleEnd": projectdata.get("handleEnd", 0)}) instance = context.create_instance(**data) @@ -122,7 +142,10 @@ class CollectClips(api.ContextPlugin): self.log.info("Created instance.data: {}".format(instance.data)) self.log.debug(">> effects: {}".format(instance.data["effects"])) - context.data["assetsShared"][asset] = dict() + context.data["assetsShared"][asset] = { + "_clipIn": clip_in, + "_clipOut": clip_out + } # from now we are collecting only subtrackitems on # track with no video items diff --git a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py index 5bc9bea7dd..a46baabe1b 100644 --- a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py +++ b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py @@ -37,11 +37,13 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): assets_shared = context.data.get("assetsShared") tags = instance.data.get("tags", None) clip = instance.data["item"] - asset = instance.data.get("asset") + asset = instance.data["asset"] sequence = context.data['activeSequence'] width = int(sequence.format().width()) height = int(sequence.format().height()) pixel_aspect = sequence.format().pixelAspect() + clip_in = instance.data["clipIn"] + clip_out = instance.data["clipOut"] fps = context.data["fps"] # build data for inner nukestudio project property @@ -72,6 +74,24 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): # and finding only hierarchical tag if "hierarchy" in t_type.lower(): + match = next( + (k for k, v in assets_shared.items() + if (v["_clipIn"] == clip_in) + and (v["_clipOut"] == clip_out) + ), False) + self.log.warning("Clip matching name: {}".format(match)) + self.log.debug( + "__ assets_shared[match]: {}".format( + assets_shared[match])) + # check if hierarchy key is in match + if not assets_shared[match].get("hierarchy"): + match = False + assert not match, ( + "Two clips above each other with" + " hierarchy tag are not allowed" + " >> keep hierarchy tag only in one of them <<" + ) + d_metadata = dict() parents = list() @@ -82,7 +102,8 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): if "shot" in template.lower(): instance.data["asset"] = [ t for t in template.split('/')][-1] - template = "/".join([t for t in template.split('/')][0:-1]) + template = "/".join( + [t for t in template.split('/')][0:-1]) # take template from Tag.note and break it into parts template_split = template.split("/") @@ -149,8 +170,12 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): instance.data["hierarchy"] = hierarchy instance.data["parents"] = parents + self.log.info( + "clip: {asset}[{clip_in}:{clip_out}]".format( + **locals())) # adding to asset shared dict - self.log.debug("__ assets_shared: {}".format(assets_shared)) + self.log.debug( + "__ assets_shared: {}".format(assets_shared)) if assets_shared.get(asset): self.log.debug("Adding to shared assets: `{}`".format( asset)) @@ -166,7 +191,7 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): "resolutionHeight": height, "pixelAspect": pixel_aspect, "fps": fps, - "tasks": instance.data["tasks"] + "tasks": instance.data["tasks"] }) # adding frame start if any on instance @@ -175,8 +200,8 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): asset_shared.update({ "startingFrame": start_frame }) - - + self.log.debug( + "assets_shared: {assets_shared}".format(**locals())) class CollectHierarchyContext(pyblish.api.ContextPlugin): '''Collecting Hierarchy from instaces and building From d4899b69dc90f52901720e66562ac07f4621d43e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 11 May 2020 19:27:58 +0200 Subject: [PATCH 14/31] feat(nks): adding empty black frames at handle start if missing --- .../publish/collect_clip_resolution.py | 21 +++++ .../nukestudio/publish/collect_clips.py | 2 +- .../publish/collect_hierarchy_context.py | 10 +-- .../nukestudio/publish/collect_plates.py | 10 +-- .../nukestudio/publish/collect_reviews.py | 12 ++- .../publish/extract_review_cutup_video.py | 87 ++++++++++++++++--- 6 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 pype/plugins/nukestudio/publish/collect_clip_resolution.py diff --git a/pype/plugins/nukestudio/publish/collect_clip_resolution.py b/pype/plugins/nukestudio/publish/collect_clip_resolution.py new file mode 100644 index 0000000000..b70f8f2f95 --- /dev/null +++ b/pype/plugins/nukestudio/publish/collect_clip_resolution.py @@ -0,0 +1,21 @@ +import pyblish.api + + +class CollectClipResolution(pyblish.api.InstancePlugin): + """Collect clip geometry resolution""" + + order = pyblish.api.CollectorOrder + 0.101 + label = "Collect Clip Resoluton" + hosts = ["nukestudio"] + + def process(self, instance): + sequence = instance.context.data['activeSequence'] + resolution_width = int(sequence.format().width()) + resolution_height = int(sequence.format().height()) + pixel_aspect = sequence.format().pixelAspect() + + instance.data.update({ + "resolutionWidth": resolution_width, + "resolutionHeight": resolution_height, + "pixelAspect": pixel_aspect + }) diff --git a/pype/plugins/nukestudio/publish/collect_clips.py b/pype/plugins/nukestudio/publish/collect_clips.py index 0e92193b14..81ab9b40dd 100644 --- a/pype/plugins/nukestudio/publish/collect_clips.py +++ b/pype/plugins/nukestudio/publish/collect_clips.py @@ -124,7 +124,7 @@ class CollectClips(api.ContextPlugin): "effects": effects, "sourceIn": int(item.sourceIn()), "sourceOut": int(item.sourceOut()), - "mediaDuration": source.duration(), + "mediaDuration": int(source.duration()), "clipIn": clip_in, "clipOut": clip_out, "clipDuration": ( diff --git a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py index a46baabe1b..edf08dec6e 100644 --- a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py +++ b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py @@ -39,9 +39,9 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): clip = instance.data["item"] asset = instance.data["asset"] sequence = context.data['activeSequence'] - width = int(sequence.format().width()) - height = int(sequence.format().height()) - pixel_aspect = sequence.format().pixelAspect() + resolution_width = instance.data["resolutionWidth"] + resolution_height = instance.data["resolutionHeight"] + pixel_aspect = instance.data["pixelAspect"] clip_in = instance.data["clipIn"] clip_out = instance.data["clipOut"] fps = context.data["fps"] @@ -187,8 +187,8 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): "asset": asset, "hierarchy": hierarchy, "parents": parents, - "resolutionWidth": width, - "resolutionHeight": height, + "resolutionWidth": resolution_width, + "resolutionHeight": resolution_height, "pixelAspect": pixel_aspect, "fps": fps, "tasks": instance.data["tasks"] diff --git a/pype/plugins/nukestudio/publish/collect_plates.py b/pype/plugins/nukestudio/publish/collect_plates.py index 4f21b02e3a..5e8c0ecedc 100644 --- a/pype/plugins/nukestudio/publish/collect_plates.py +++ b/pype/plugins/nukestudio/publish/collect_plates.py @@ -64,15 +64,15 @@ class CollectPlates(api.InstancePlugin): # adding SourceResolution if Tag was present if instance.data.get("sourceResolution") and instance.data.get("main"): item = instance.data["item"] - width = int(item.source().mediaSource().width()) - height = int(item.source().mediaSource().height()) + resolution_width = int(item.source().mediaSource().width()) + resolution_height = int(item.source().mediaSource().height()) pixel_aspect = int(item.source().mediaSource().pixelAspect()) self.log.info("Source Width and Height are: `{0} x {1} : {2}`".format( - width, height, pixel_aspect)) + resolution_width, resolution_height, pixel_aspect)) data.update({ - "width": width, - "height": height, + "resolutionWidth": resolution_width, + "resolutionHeight": resolution_height, "pixelAspect": pixel_aspect }) diff --git a/pype/plugins/nukestudio/publish/collect_reviews.py b/pype/plugins/nukestudio/publish/collect_reviews.py index 6919950b77..63b8b7d397 100644 --- a/pype/plugins/nukestudio/publish/collect_reviews.py +++ b/pype/plugins/nukestudio/publish/collect_reviews.py @@ -110,7 +110,15 @@ class CollectReviews(api.InstancePlugin): representation.update({ "frameStart": instance.data.get("sourceInH"), "frameEnd": instance.data.get("sourceOutH"), - "tags": ["cut-up", "delete"] + "tags": ["_cut-bigger", "delete"] + }) + elif media_duration < clip_duration_h: + self.log.debug("Media duration higher: {}".format( + (media_duration - clip_duration_h))) + representation.update({ + "frameStart": instance.data.get("sourceInH"), + "frameEnd": instance.data.get("sourceOutH"), + "tags": ["_cut-smaller", "delete"] }) instance.data["representations"].append(representation) @@ -133,7 +141,7 @@ class CollectReviews(api.InstancePlugin): self.log.debug("__ thumb_path: {}".format(thumb_path)) thumb_frame = instance.data["sourceIn"] + ((instance.data["sourceOut"] - instance.data["sourceIn"])/2) - + self.log.debug("__ thumb_frame: {}".format(thumb_frame)) thumbnail = item.thumbnail(thumb_frame).save( thumb_path, format='png' diff --git a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py index 445a26a184..1db6fbd13c 100644 --- a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py +++ b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py @@ -6,8 +6,8 @@ import pype class ExtractReviewCutUpVideo(pype.api.Extractor): """Cut up clips from long video file""" - order = api.ExtractorOrder - # order = api.CollectorOrder + 0.1023 + # order = api.ExtractorOrder + order = api.CollectorOrder + 0.1023 label = "Extract Review CutUp Video" hosts = ["nukestudio"] families = ["review"] @@ -22,7 +22,16 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): # get representation and loop them representations = inst_data["representations"] + # resolution data + resolution_width = inst_data["resolutionWidth"] + resolution_height = inst_data["resolutionHeight"] + pixel_aspect = inst_data["pixelAspect"] + + # frame range data + media_duration = inst_data["mediaDuration"] + ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg") + ffprobe_path = pype.lib.get_ffmpeg_tool_path("ffprobe") # filter out mov and img sequences representations_new = representations[:] @@ -32,7 +41,9 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): tags = repre.get("tags", []) - if "cut-up" not in tags: + if not next( + (t for t in tags + if t in ["_cut-bigger", "_cut-smaller"]), None): continue self.log.debug("__ repre: {}".format(repre)) @@ -49,21 +60,77 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): full_input_path = os.path.join( staging_dir, file) + full_output_dir = os.path.join( + staging_dir, "cuts") + + os.path.isdir(full_output_dir) or os.makedirs(full_output_dir) + full_output_path = os.path.join( - staging_dir, new_file_name) + full_output_dir, new_file_name) self.log.debug("__ full_input_path: {}".format(full_input_path)) self.log.debug("__ full_output_path: {}".format(full_output_path)) + # check if audio stream is in input video file + ffprob_cmd = ( + "{ffprobe_path} -i {full_input_path} -show_streams " + "-select_streams a -loglevel error" + ).format(**locals()) + self.log.debug("ffprob_cmd: {}".format(ffprob_cmd)) + audio_check_output = pype.api.subprocess(ffprob_cmd) + self.log.debug("audio_check_output: {}".format(audio_check_output)) + + # translate frame to sec + start_sec = float(frame_start) / fps + duration_sec = float(frame_end - frame_start + 1) / fps + input_args.append("-y") + + if start_sec < 0: + audio_empty = "" + audio_output = "" + audio_layer = "" + v_inp_idx = 0 + black_duration = abs(start_sec) + start_sec = 0 + duration_sec = float(frame_end - ( + frame_start + (black_duration * fps)) + 1) / fps + + if audio_check_output: + # adding input for empty audio + input_args.append("-f lavfi -i anullsrc") + audio_empty = ( + "[0]atrim=duration={black_duration}[ga0];" + ).format(**locals()) + audio_output = ":a=1" + audio_layer = "[ga0]" + v_inp_idx = 1 + + # adding input for video black frame + input_args.append(( + "-f lavfi -i \"color=c=black:" + "s={resolution_width}x{resolution_height}:r={fps}\"" + ).format(**locals())) + + # concutting black frame togather + output_args.append(( + "-filter_complex \"" + "{audio_empty}" + "[{v_inp_idx}]trim=duration={black_duration}[gv0];" + "[gv0]{audio_layer}[1:v]" + "concat=n=2:v=1{audio_output}\"" + ).format(**locals())) + + input_args.append("-ss {:0.2f}".format(start_sec)) + input_args.append("-t {:0.2f}".format(duration_sec)) input_args.append("-i {}".format(full_input_path)) - start_sec = float(frame_start) / fps - input_args.append("-ss {:0.2f}".format(start_sec)) + # check if not missing frames at the end + self.log.debug("media_duration: {}".format(media_duration)) + self.log.debug("frame_end: {}".format(frame_end)) - output_args.append("-c copy") - duration_sec = float(frame_end - frame_start + 1) / fps - output_args.append("-t {:0.2f}".format(duration_sec)) + # make sure it is having no frame to frame comprassion + output_args.append("-intra") # output filename output_args.append(full_output_path) @@ -90,7 +157,7 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): "step": 1, "fps": fps, "name": "cut_up_preview", - "tags": ["cut-up", "review", "delete"] + self.tags_addition, + "tags": ["review", "delete"] + self.tags_addition, "ext": ext, "anatomy_template": "publish" } From a270c55a31ec1fb80e2c4a12574d3a6d851c474b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 12 May 2020 16:59:49 +0200 Subject: [PATCH 15/31] fix(nks): collect timecodes not used --- pype/plugins/nukestudio/{publish => _unused}/collect_timecodes.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pype/plugins/nukestudio/{publish => _unused}/collect_timecodes.py (100%) diff --git a/pype/plugins/nukestudio/publish/collect_timecodes.py b/pype/plugins/nukestudio/_unused/collect_timecodes.py similarity index 100% rename from pype/plugins/nukestudio/publish/collect_timecodes.py rename to pype/plugins/nukestudio/_unused/collect_timecodes.py From 7d689cfe258b8915bae62a1c7b690d9ea0afe590 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 12 May 2020 17:00:35 +0200 Subject: [PATCH 16/31] feat(nks): reediting shorter or longer clips with empty frames --- .../publish/extract_review_cutup_video.py | 107 ++++++++++++++---- 1 file changed, 83 insertions(+), 24 deletions(-) diff --git a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py index 1db6fbd13c..4a546910ad 100644 --- a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py +++ b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py @@ -22,10 +22,9 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): # get representation and loop them representations = inst_data["representations"] - # resolution data + # get resolution default resolution_width = inst_data["resolutionWidth"] resolution_height = inst_data["resolutionHeight"] - pixel_aspect = inst_data["pixelAspect"] # frame range data media_duration = inst_data["mediaDuration"] @@ -86,24 +85,39 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): input_args.append("-y") - if start_sec < 0: - audio_empty = "" - audio_output = "" - audio_layer = "" + # check if not missing frames at start + if (start_sec < 0) or (media_duration < frame_end): + # init empty variables + video_empty_start = video_layer_start = "" + audio_empty_start = audio_layer_start = "" + video_empty_end = video_layer_end = "" + audio_empty_end = audio_layer_end = "" + audio_input = audio_output = "" v_inp_idx = 0 - black_duration = abs(start_sec) - start_sec = 0 - duration_sec = float(frame_end - ( - frame_start + (black_duration * fps)) + 1) / fps + concat_n = 1 + + # try to get video native resolution data + try: + resolution_output = pype.api.subprocess(( + "{ffprobe_path} -i {full_input_path} -v error " + "-select_streams v:0 -show_entries " + "stream=width,height -of csv=s=x:p=0" + ).format(**locals())) + + x, y = resolution_output.split("x") + resolution_width = int(x) + resolution_height = int(y) + except Exception as E: + self.log.warning( + "Video native resolution is untracable: {}".format(E)) if audio_check_output: # adding input for empty audio input_args.append("-f lavfi -i anullsrc") - audio_empty = ( - "[0]atrim=duration={black_duration}[ga0];" - ).format(**locals()) + + # define audio empty concat variables + audio_input = "[1:a]" audio_output = ":a=1" - audio_layer = "[ga0]" v_inp_idx = 1 # adding input for video black frame @@ -112,23 +126,68 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): "s={resolution_width}x{resolution_height}:r={fps}\"" ).format(**locals())) - # concutting black frame togather + if (start_sec < 0): + # recalculate input video timing + empty_start_dur = abs(start_sec) + start_sec = 0 + duration_sec = float(frame_end - ( + frame_start + (empty_start_dur * fps)) + 1) / fps + + # define starting empty video concat variables + video_empty_start = ( + "[{v_inp_idx}]trim=duration={empty_start_dur}[gv0];" + ).format(**locals()) + video_layer_start = "[gv0]" + + if audio_check_output: + # define starting empty audio concat variables + audio_empty_start = ( + "[0]atrim=duration={empty_start_dur}[ga0];" + ).format(**locals()) + audio_layer_start = "[ga0]" + + # alter concat number of clips + concat_n += 1 + + # check if not missing frames at the end + if (media_duration < frame_end): + # recalculate timing + empty_end_dur = float(frame_end - media_duration + 1) / fps + duration_sec = float(media_duration - frame_start) / fps + + # define ending empty video concat variables + video_empty_end = ( + "[{v_inp_idx}]trim=duration={empty_end_dur}[gv1];" + ).format(**locals()) + video_layer_end = "[gv1]" + + if audio_check_output: + # define ending empty audio concat variables + audio_empty_end = ( + "[0]atrim=duration={empty_end_dur}[ga1];" + ).format(**locals()) + audio_layer_end = "[ga0]" + + # alter concat number of clips + concat_n += 1 + + # concatting black frame togather output_args.append(( "-filter_complex \"" - "{audio_empty}" - "[{v_inp_idx}]trim=duration={black_duration}[gv0];" - "[gv0]{audio_layer}[1:v]" - "concat=n=2:v=1{audio_output}\"" + "{audio_empty_start}" + "{video_empty_start}" + "{audio_empty_end}" + "{video_empty_end}" + "{video_layer_start}{audio_layer_start}[1:v]{audio_input}" + "{video_layer_end}{audio_layer_end}" + "concat=n={concat_n}:v=1{audio_output}\"" ).format(**locals())) + # append ffmpeg input video clip input_args.append("-ss {:0.2f}".format(start_sec)) input_args.append("-t {:0.2f}".format(duration_sec)) input_args.append("-i {}".format(full_input_path)) - # check if not missing frames at the end - self.log.debug("media_duration: {}".format(media_duration)) - self.log.debug("frame_end: {}".format(frame_end)) - # make sure it is having no frame to frame comprassion output_args.append("-intra") @@ -170,5 +229,5 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): representations_new.remove(repre) self.log.debug( - "new representations: {}".format(representations_new)) + "Representations: {}".format(representations_new)) instance.data["representations"] = representations_new From 8f3f3a4d921622e1e53bb6cc6f6b110fc028bb58 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 12 May 2020 17:22:05 +0200 Subject: [PATCH 17/31] feat(nks): PR comments --- .../nukestudio/publish/collect_clips.py | 3 ++- .../nukestudio/publish/collect_reviews.py | 27 +++++++++++-------- .../publish/extract_review_cutup_video.py | 10 ++++--- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_clips.py b/pype/plugins/nukestudio/publish/collect_clips.py index 81ab9b40dd..94ec72a2ed 100644 --- a/pype/plugins/nukestudio/publish/collect_clips.py +++ b/pype/plugins/nukestudio/publish/collect_clips.py @@ -55,7 +55,8 @@ class CollectClips(api.ContextPlugin): is_sequence = False self.log.debug( - "__ assets_shared: {}".format(context.data["assetsShared"])) + "__ assets_shared: {}".format( + context.data["assetsShared"])) match_range = next( (k for k, v in context.data["assetsShared"].items() if (v.get("_clipIn", 0) == clip_in) diff --git a/pype/plugins/nukestudio/publish/collect_reviews.py b/pype/plugins/nukestudio/publish/collect_reviews.py index 63b8b7d397..aa8c60767c 100644 --- a/pype/plugins/nukestudio/publish/collect_reviews.py +++ b/pype/plugins/nukestudio/publish/collect_reviews.py @@ -36,9 +36,10 @@ class CollectReviews(api.InstancePlugin): return if not track: - self.log.debug( - "Skipping \"{}\" because tag is not having `track` in metadata".format(instance) - ) + self.log.debug(( + "Skipping \"{}\" because tag is not having" + "`track` in metadata" + ).format(instance)) return # add to representations @@ -68,10 +69,11 @@ class CollectReviews(api.InstancePlugin): rev_inst.data["name"])) if rev_inst is None: - raise RuntimeError( - "TrackItem from track name `{}` has to be also selected".format( - track) - ) + raise RuntimeError(( + "TrackItem from track name `{}` has to" + "be also selected" + ).format(track)) + instance.data["families"].append("review") file_path = rev_inst.data.get("sourcePath") @@ -140,15 +142,18 @@ class CollectReviews(api.InstancePlugin): thumb_path = os.path.join(staging_dir, thumb_file) self.log.debug("__ thumb_path: {}".format(thumb_path)) - thumb_frame = instance.data["sourceIn"] + ((instance.data["sourceOut"] - instance.data["sourceIn"])/2) + thumb_frame = instance.data["sourceIn"] + ( + (instance.data["sourceOut"] - instance.data["sourceIn"]) / 2) self.log.debug("__ thumb_frame: {}".format(thumb_frame)) thumbnail = item.thumbnail(thumb_frame).save( thumb_path, format='png' ) - self.log.debug("__ sourceIn: `{}`".format(instance.data["sourceIn"])) - self.log.debug("__ thumbnail: `{}`, frame: `{}`".format(thumbnail, thumb_frame)) + self.log.debug( + "__ sourceIn: `{}`".format(instance.data["sourceIn"])) + self.log.debug( + "__ thumbnail: `{}`, frame: `{}`".format(thumbnail, thumb_frame)) self.log.debug("__ thumbnail: {}".format(thumbnail)) @@ -177,7 +182,7 @@ class CollectReviews(api.InstancePlugin): version_data.update({k: instance.data[k] for k in transfer_data}) if 'version' in instance.data: - version_data["version"] = instance.data[version] + version_data["version"] = instance.data["version"] # add to data of representation version_data.update({ diff --git a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py index 4a546910ad..13fc8dbcf7 100644 --- a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py +++ b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py @@ -40,9 +40,13 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): tags = repre.get("tags", []) - if not next( - (t for t in tags - if t in ["_cut-bigger", "_cut-smaller"]), None): + # check if supported tags are in representation for activation + filter_tag = False + for tag in ["_cut-bigger", "_cut-smaller"]: + if tag in tags: + filter_tag = True + break + if not filter_tag: continue self.log.debug("__ repre: {}".format(repre)) From 4342db71ce57f2930d09b6966aa72d07e29e2b81 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 12 May 2020 17:46:20 +0200 Subject: [PATCH 18/31] feat(nks): adding comment making more clear the process --- .../nukestudio/publish/collect_hierarchy_context.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py index edf08dec6e..0cb7e7f56a 100644 --- a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py +++ b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py @@ -74,11 +74,14 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): # and finding only hierarchical tag if "hierarchy" in t_type.lower(): - match = next( - (k for k, v in assets_shared.items() - if (v["_clipIn"] == clip_in) - and (v["_clipOut"] == clip_out) - ), False) + # check if any clip with the same clip range + # is alerady in asset shared so + match = next(( + k for k, v in assets_shared.items() + if (v["_clipIn"] == clip_in) + and (v["_clipOut"] == clip_out) + ), False) + self.log.warning("Clip matching name: {}".format(match)) self.log.debug( "__ assets_shared[match]: {}".format( From 0d7ed78843412a0d474c8a558d448f3f0926f6bf Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 12 May 2020 17:49:56 +0200 Subject: [PATCH 19/31] feat(nks): resolving PR comment --- pype/plugins/nukestudio/publish/extract_review_cutup_video.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py index 13fc8dbcf7..30225ae7f2 100644 --- a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py +++ b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py @@ -87,8 +87,6 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): start_sec = float(frame_start) / fps duration_sec = float(frame_end - frame_start + 1) / fps - input_args.append("-y") - # check if not missing frames at start if (start_sec < 0) or (media_duration < frame_end): # init empty variables @@ -196,6 +194,7 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): output_args.append("-intra") # output filename + output_args.append("-y") output_args.append(full_output_path) mov_args = [ From 77d3611b9fd39df2866432305d55b67085bfb989 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 May 2020 11:14:06 +0200 Subject: [PATCH 20/31] feat(nks): processing PR comments - better readability --- .../nukestudio/publish/collect_clips.py | 19 ++++++++++++++----- .../publish/collect_hierarchy_context.py | 12 ++++++++---- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_clips.py b/pype/plugins/nukestudio/publish/collect_clips.py index 94ec72a2ed..5625a471dc 100644 --- a/pype/plugins/nukestudio/publish/collect_clips.py +++ b/pype/plugins/nukestudio/publish/collect_clips.py @@ -57,19 +57,28 @@ class CollectClips(api.ContextPlugin): self.log.debug( "__ assets_shared: {}".format( context.data["assetsShared"])) - match_range = next( + + # Check for clips with the same range + # this is for testing if any vertically neighbouring + # clips has been already processed + clip_matching_with_range = next( (k for k, v in context.data["assetsShared"].items() if (v.get("_clipIn", 0) == clip_in) and (v.get("_clipOut", 0) == clip_out) ), False) - if asset in str(match_range): - match_range = False + # check if clip name is the same in matched + # vertically neighbouring clip + # if it is then it is correct and resent variable to False + # not to be rised wrong name exception + if asset in str(clip_matching_with_range): + clip_matching_with_range = False - assert (not match_range), ( + # rise wrong name exception if found one + assert (not clip_matching_with_range), ( "matching clip: {asset}" " timeline range ({clip_in}:{clip_out})" - " conflicting with {match_range}" + " conflicting with {clip_matching_with_range}" " >> rename any of clips to be the same as the other <<" ).format( **locals()) diff --git a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py index 0cb7e7f56a..38040f8c51 100644 --- a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py +++ b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py @@ -74,21 +74,25 @@ class CollectHierarchyInstance(pyblish.api.ContextPlugin): # and finding only hierarchical tag if "hierarchy" in t_type.lower(): - # check if any clip with the same clip range - # is alerady in asset shared so + # Check for clips with the same range + # this is for testing if any vertically neighbouring + # clips has been already processed match = next(( k for k, v in assets_shared.items() if (v["_clipIn"] == clip_in) and (v["_clipOut"] == clip_out) ), False) - self.log.warning("Clip matching name: {}".format(match)) self.log.debug( "__ assets_shared[match]: {}".format( assets_shared[match])) - # check if hierarchy key is in match + + # check if hierarchy key is present in matched + # vertically neighbouring clip if not assets_shared[match].get("hierarchy"): match = False + + # rise exception if multiple hierarchy tag found assert not match, ( "Two clips above each other with" " hierarchy tag are not allowed" From aa0ae99daa95c7a5b320914f4eb20e648db7123d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 May 2020 11:33:03 +0200 Subject: [PATCH 21/31] fix(nks): plugin should be in extract order.. previous was for testing --- pype/plugins/nukestudio/publish/extract_review_cutup_video.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py index 30225ae7f2..dd4fc0cfff 100644 --- a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py +++ b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py @@ -6,8 +6,8 @@ import pype class ExtractReviewCutUpVideo(pype.api.Extractor): """Cut up clips from long video file""" - # order = api.ExtractorOrder - order = api.CollectorOrder + 0.1023 + order = api.ExtractorOrder + # order = api.CollectorOrder + 0.1023 label = "Extract Review CutUp Video" hosts = ["nukestudio"] families = ["review"] From cb12a83ff94a477880a7b869f6a5dc872b1483fd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 May 2020 11:43:06 +0200 Subject: [PATCH 22/31] feat(nks): only copy codec if source should be shortened --- pype/plugins/nukestudio/publish/extract_review_cutup_video.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py index dd4fc0cfff..8647f0817d 100644 --- a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py +++ b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py @@ -190,6 +190,10 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): input_args.append("-t {:0.2f}".format(duration_sec)) input_args.append("-i {}".format(full_input_path)) + # add copy audio video codec if only shortening clip + if "_cut-bigger" in tags: + output_args.append("-c:v copy") + # make sure it is having no frame to frame comprassion output_args.append("-intra") From 15815360e4da47e31e731aa6b1081109ba78f2d7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 May 2020 15:00:04 +0200 Subject: [PATCH 23/31] fix(global): integrate didn't understand new Anatomy --- pype/plugins/global/publish/integrate_new.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index f7ea4eebf9..6e237e46e9 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -301,6 +301,8 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): sequence_repre = isinstance(files, list) repre_context = None if sequence_repre: + self.log.debug( + "files: {}".format(files)) src_collections, remainder = clique.assemble(files) self.log.debug( "src_tail_collections: {}".format(str(src_collections))) @@ -342,7 +344,10 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): if repre.get("frameStart"): frame_start_padding = ( - anatomy.templates["render"]["padding"] + anatomy.templates["render"].get( + "padding", + anatomy.templates["frame_padding"] + ) ) index_frame_start = int(repre.get("frameStart")) From f1dd4fcf4c44f34c1fe492b4bef06bd2558f4e33 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 May 2020 15:05:36 +0200 Subject: [PATCH 24/31] fix(global): integrate swap order of padding request from anatomy --- pype/plugins/global/publish/integrate_new.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index 6e237e46e9..b054d8c309 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -343,12 +343,12 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): index_frame_start = None if repre.get("frameStart"): - frame_start_padding = ( - anatomy.templates["render"].get( - "padding", - anatomy.templates["frame_padding"] - ) + # TODO: bck compatibility `templates["render"]["padding"]` + frame_start_padding = anatomy.templates.get( + "frame_padding", + anatomy.templates["render"].get("padding") ) + index_frame_start = int(repre.get("frameStart")) # exception for slate workflow From 59c5f363905e89a707352b05f4c72575f6061f72 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 May 2020 15:06:14 +0200 Subject: [PATCH 25/31] fix(nks): frame start was not correctly calculated --- pype/plugins/nukestudio/publish/collect_clips.py | 2 +- pype/plugins/nukestudio/publish/collect_frame_ranges.py | 1 - pype/plugins/nukestudio/publish/collect_plates.py | 4 +--- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_clips.py b/pype/plugins/nukestudio/publish/collect_clips.py index 5625a471dc..d39e25bfc6 100644 --- a/pype/plugins/nukestudio/publish/collect_clips.py +++ b/pype/plugins/nukestudio/publish/collect_clips.py @@ -51,7 +51,7 @@ class CollectClips(api.ContextPlugin): clip_out = int(item.timelineOut()) file_head = source.filenameHead() file_info = next((f for f in source.fileinfos()), None) - source_first_frame = file_info.startFrame() + source_first_frame = int(file_info.startFrame()) is_sequence = False self.log.debug( diff --git a/pype/plugins/nukestudio/publish/collect_frame_ranges.py b/pype/plugins/nukestudio/publish/collect_frame_ranges.py index 6993fa5e67..1cb5e5dd1e 100644 --- a/pype/plugins/nukestudio/publish/collect_frame_ranges.py +++ b/pype/plugins/nukestudio/publish/collect_frame_ranges.py @@ -36,7 +36,6 @@ class CollectClipFrameRanges(pyblish.api.InstancePlugin): frame_end = frame_start + (timeline_out - timeline_in) data.update({ - "sourceFirst": source_in_h, "sourceInH": source_in_h, "sourceOutH": source_out_h, "frameStart": frame_start, diff --git a/pype/plugins/nukestudio/publish/collect_plates.py b/pype/plugins/nukestudio/publish/collect_plates.py index 5e8c0ecedc..770cef7e3f 100644 --- a/pype/plugins/nukestudio/publish/collect_plates.py +++ b/pype/plugins/nukestudio/publish/collect_plates.py @@ -102,9 +102,6 @@ class CollectPlatesData(api.InstancePlugin): instance.data["representations"] = list() version_data = dict() - context = instance.context - anatomy = context.data.get("anatomy", None) - padding = int(anatomy.templates['render']['padding']) name = instance.data["subset"] source_path = instance.data["sourcePath"] @@ -149,6 +146,7 @@ class CollectPlatesData(api.InstancePlugin): source_first_frame = instance.data.get("sourceFirst") source_file_head = instance.data.get("sourceFileHead") + self.log.debug("source_first_frame: `{}`".format(source_first_frame)) if instance.data.get("isSequence", False): self.log.info("Is sequence of files") From cb42c78a8c9f3fba54268cf73cfedd8c39dd39c7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 May 2020 15:06:43 +0200 Subject: [PATCH 26/31] fix(nks): cutting videos added to own folder `cuts` --- pype/plugins/nukestudio/publish/extract_review_cutup_video.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py index 8647f0817d..b5c0e8387d 100644 --- a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py +++ b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py @@ -215,7 +215,7 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): repre_new = { "files": new_file_name, - "stagingDir": staging_dir, + "stagingDir": full_output_dir, "frameStart": frame_start, "frameEnd": frame_end, "frameStartFtrack": frame_start, From 5bfafb28b5bf96902c57b658a5d1d78c97b9651a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 15 May 2020 11:52:07 +0200 Subject: [PATCH 27/31] fix(nks): don't add copy codec if -filter_complex added into cmd --- .../nukestudio/publish/extract_review_cutup_video.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py index b5c0e8387d..a4fbf90bed 100644 --- a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py +++ b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py @@ -87,8 +87,13 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): start_sec = float(frame_start) / fps duration_sec = float(frame_end - frame_start + 1) / fps + empty_add = None + # check if not missing frames at start if (start_sec < 0) or (media_duration < frame_end): + # for later swithing off `-c:v copy` output arg + empty_add = True + # init empty variables video_empty_start = video_layer_start = "" audio_empty_start = audio_layer_start = "" @@ -191,7 +196,7 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): input_args.append("-i {}".format(full_input_path)) # add copy audio video codec if only shortening clip - if "_cut-bigger" in tags: + if ("_cut-bigger" in tags) and (not empty_add): output_args.append("-c:v copy") # make sure it is having no frame to frame comprassion From a37c944d01def5fc3dac692f6617ba54cc941bf2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 19 May 2020 11:28:15 +0200 Subject: [PATCH 28/31] ftrack session created during publishing has set auto connect of event hub to True --- pype/plugins/ftrack/publish/collect_ftrack_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/ftrack/publish/collect_ftrack_api.py b/pype/plugins/ftrack/publish/collect_ftrack_api.py index 0aad3b2433..151b8882a3 100644 --- a/pype/plugins/ftrack/publish/collect_ftrack_api.py +++ b/pype/plugins/ftrack/publish/collect_ftrack_api.py @@ -22,7 +22,7 @@ class CollectFtrackApi(pyblish.api.ContextPlugin): ftrack_log.setLevel(logging.WARNING) # Collect session - session = ftrack_api.Session() + session = ftrack_api.Session(auto_connect_event_hub=True) self.log.debug("Ftrack user: \"{0}\"".format(session.api_user)) context.data["ftrackSession"] = session From 5d045b776a0c1b703699fd70bb72c1b4ad07cc91 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 19 May 2020 15:22:22 +0200 Subject: [PATCH 29/31] use copy.deepcopy(...) instead of calling .copy() on dictionaries --- pype/ftrack/lib/avalon_sync.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pype/ftrack/lib/avalon_sync.py b/pype/ftrack/lib/avalon_sync.py index 920f3431a7..f354cfbc20 100644 --- a/pype/ftrack/lib/avalon_sync.py +++ b/pype/ftrack/lib/avalon_sync.py @@ -2,6 +2,7 @@ import os import re import queue import collections +import copy from pype.ftrack.lib.io_nonsingleton import DbConnector @@ -756,19 +757,19 @@ class SyncEntitiesFactory: prepared_avalon_attr_ca_id = avalon_attrs_ca_id.get(attr_key) if prepared_attrs: self.entities_dict[entity_id]["custom_attributes"] = ( - prepared_attrs.copy() + copy.deepcopy(prepared_attrs) ) if prepared_attrs_ca_id: self.entities_dict[entity_id]["custom_attributes_id"] = ( - prepared_attrs_ca_id.copy() + copy.deepcopy(prepared_attrs_ca_id) ) if prepared_avalon_attr: self.entities_dict[entity_id]["avalon_attrs"] = ( - prepared_avalon_attr.copy() + copy.deepcopy(prepared_avalon_attr) ) if prepared_avalon_attr_ca_id: self.entities_dict[entity_id]["avalon_attrs_id"] = ( - prepared_avalon_attr_ca_id.copy() + copy.deepcopy(prepared_avalon_attr_ca_id) ) # TODO query custom attributes by entity_id @@ -852,7 +853,7 @@ class SyncEntitiesFactory: # Skip project because has stored defaults at the moment if entity_dict["entity_type"] == "project": continue - entity_dict["hier_attrs"] = prepare_dict.copy() + entity_dict["hier_attrs"] = copy.deepcopy(prepare_dict) for key, val in prepare_dict_avalon.items(): entity_dict["avalon_attrs"][key] = val @@ -909,7 +910,7 @@ class SyncEntitiesFactory: while not hier_down_queue.empty(): hier_values, parent_id = hier_down_queue.get() for child_id in self.entities_dict[parent_id]["children"]: - _hier_values = hier_values.copy() + _hier_values = copy.deepcopy(hier_values) for key in attributes_by_key.keys(): if key.startswith("avalon_"): store_key = "avalon_attrs" @@ -1891,7 +1892,7 @@ class SyncEntitiesFactory: parents_queue.put((self.ft_project_id, [], False)) while not parents_queue.empty(): ftrack_id, parent_parents, changed = parents_queue.get() - _parents = parent_parents.copy() + _parents = copy.deepcopy(parent_parents) if ftrack_id not in hierarchy_changing_ids and not changed: if ftrack_id != self.ft_project_id: _parents.append(self.entities_dict[ftrack_id]["name"]) From 4363af53d385d3a5cd3689e01517dba48cc40bb2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 19 May 2020 15:22:33 +0200 Subject: [PATCH 30/31] fix issue of hierachical enumerators with multiselection --- pype/ftrack/lib/avalon_sync.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pype/ftrack/lib/avalon_sync.py b/pype/ftrack/lib/avalon_sync.py index f354cfbc20..6bf0cd9754 100644 --- a/pype/ftrack/lib/avalon_sync.py +++ b/pype/ftrack/lib/avalon_sync.py @@ -879,7 +879,13 @@ class SyncEntitiesFactory: for item in values["data"]: value = item["value"] - if value is None: + # WARNING It is not possible to propage enumerate hierachical + # attributes with multiselection 100% right. Unseting all values + # will cause inheritance from parent. + if ( + value is None + or (isinstance(value, (tuple, list)) and not value) + ): continue entity_id = item["entity_id"] key = attribute_key_by_id[item["configuration_id"]] From e6ef67e46c85db0289bf4789d82f533bb61d47a3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 20 May 2020 13:55:32 +0200 Subject: [PATCH 31/31] fix(nuke): fixing family `render2d` consistency --- pype/plugins/nuke/publish/collect_writes.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pype/plugins/nuke/publish/collect_writes.py b/pype/plugins/nuke/publish/collect_writes.py index 1850df2d00..c70953d23f 100644 --- a/pype/plugins/nuke/publish/collect_writes.py +++ b/pype/plugins/nuke/publish/collect_writes.py @@ -115,7 +115,7 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): # Add version data to instance version_data = { - "colorspace": node["colorspace"].value(), + "colorspace": node["colorspace"].value(), } instance.data["family"] = "write" @@ -150,6 +150,11 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): "deadlinePriority": deadlinePriority }) + if "render" in families: + instance.data["family"] = "render2d" + if "render" not in families: + instance.data["families"].insert(0, "render") + if "prerender" in families: instance.data.update({ "family": "prerender",