diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index e6e6294a81..8b6cfc52f1 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -441,6 +441,15 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): in_frame += time_scalar frame_range.append(in_frame) + # Different editorial DCC might have different TimeWarp logic. + # The following logic assumes that the "lookup" list values are + # frame offsets relative to the current source frame number. + # + # media_source_range |______1_____|______2______|______3______| + # + # media_retimed_range |______2_____|______2______|______3______| + # + # TimeWarp lookup +1 0 0 for tw_idx, tw in enumerate(time_warp_nodes): for idx, frame_number in enumerate(frame_range): # First timewarp, apply on media range @@ -467,9 +476,32 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): ) # adjust range if needed + media_in_trimmed_before_tw = media_in_trimmed media_in_trimmed = max(min(frame_range), media_in) media_out_trimmed = min(max(frame_range), media_out) + # If TimeWarp changes the first frame of the soure range, + # we need to offset the first TimeWarp values accordingly. + # + # expected_range |______2_____|______2______|______3______| + # + # EDITORIAL + # media_source_range |______1_____|______2______|______3______| + # + # TimeWarp lookup +1 0 0 + # + # EXTRACTED PLATE + # plate_range |______2_____|______3______|_ _ _ _ _ _ _| + # + # expected TimeWarp 0 -1 -1 + if media_in_trimmed != media_in_trimmed_before_tw: + offset = media_in_trimmed_before_tw - media_in_trimmed + offset *= 1.0 / time_scalar + time_warp_nodes[0]["lookup"] = [ + value + offset + for value in time_warp_nodes[0]["lookup"] + ] + # adjust available handles if needed if (media_in_trimmed - media_in) < handle_start: handle_start = max(0, media_in_trimmed - media_in) diff --git a/client/ayon_core/plugins/publish/collect_otio_review.py b/client/ayon_core/plugins/publish/collect_otio_review.py index 4708b0a97c..064d4e3f3b 100644 --- a/client/ayon_core/plugins/publish/collect_otio_review.py +++ b/client/ayon_core/plugins/publish/collect_otio_review.py @@ -36,6 +36,16 @@ class CollectOtioReview(pyblish.api.InstancePlugin): # optionally get `reviewTrack` review_track_name = instance.data.get("reviewTrack") + # [clip_media] setting: + # Extract current clip source range as reviewable. + # Flag review content from otio_clip. + if not review_track_name and "review" in instance.data["families"]: + otio_review_clips = [otio_clip] + + # skip if no review track available + elif not review_track_name: + return + # generate range in parent otio_tl_range = otio_clip.range_in_parent() @@ -43,12 +53,14 @@ class CollectOtioReview(pyblish.api.InstancePlugin): clip_frame_end = int( otio_tl_range.start_time.value + otio_tl_range.duration.value) - # skip if no review track available - if not review_track_name: - return - # loop all tracks and match with name in `reviewTrack` for track in otio_timeline.tracks: + + # No review track defined, skip the loop + if review_track_name is None: + break + + # Not current review track, skip it. if review_track_name != track.name: continue diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index deb51f62a5..d07c956856 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -195,13 +195,6 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, file=filename) - if ( - not instance.data.get("otioReviewClips") - and "review" in instance.data["families"] - ): - review_repre = self._create_representation( - frame_start, frame_end, collection=collection, - delete=True, review=True) else: _trim = False @@ -217,13 +210,6 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, file=filename, trim=_trim) - if ( - not instance.data.get("otioReviewClips") - and "review" in instance.data["families"] - ): - review_repre = self._create_representation( - frame_start, frame_end, - file=filename, delete=True, review=True) instance.data["originalDirname"] = self.staging_dir @@ -236,9 +222,6 @@ class CollectOtioSubsetResources( instance.data["representations"].append(repre) - # add review representation to instance data - if review_repre: - instance.data["representations"].append(review_repre) self.log.debug(instance.data) diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index 275c3e7c58..c2788af77c 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -320,6 +320,9 @@ class ExtractOTIOReview( end = max(collection.indexes) files = [f for f in collection] + # single frame sequence + if len(files) == 1: + files = files[0] ext = collection.format("{tail}") representation_data.update({ "name": ext[1:], diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index fbab60623f..112d00b3e4 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -231,17 +231,17 @@ def test_movie_timewarp(): 'Class': 'TimeWarp', 'length': 4.0, 'lookup': [ - 2.0, - 1.8959999809265136, - 1.767999971389771, - 1.59199997138977, - 1.3439999809265135, - 1.0, - 0.5440000181198119, - -0.007999974250793684, - -0.6319999756813051, - -1.3039999847412114, - -2.0 + 0.0, + -0.10400001907348644, + -0.23200002861022906, + -0.4080000286102301, + -0.6560000190734865, + -1.0, + -1.455999981880188, + -2.0079999742507937, + -2.631999975681305, + -3.3039999847412114, + -4.0 ], 'name': 'TimeWarp2' } @@ -581,17 +581,17 @@ def test_img_sequence_timewarp_beyond_range(): 'Class': 'TimeWarp', 'length': 1.0, 'lookup': [ - -5.0, - -3.9440000305175777, - -2.852000034332275, - -1.6880000228881844, - -0.4160000076293944, - 1.0, - 2.5839999923706056, - 4.311999977111817, - 6.147999965667726, - 8.055999969482421, - 10.0 + 0.0, + 1.0559999694824223, + 2.147999965667725, + 3.3119999771118156, + 4.583999992370606, + 6.0, + 7.583999992370606, + 9.311999977111817, + 11.147999965667726, + 13.055999969482421, + 15.0 ], 'name': 'TimeWarp3' } @@ -632,17 +632,17 @@ def test_img_sequence_2X_speed_timewarp(): 'Class': 'TimeWarp', 'length': 4.0, 'lookup': [ - 2.0, - 1.7039999923706055, - 1.431999991416931, - 1.2079999942779531, - 1.055999998092652, - 1.0, - 1.056000007629395, - 1.208000022888184, - 1.432000034332276, - 1.7040000305175766, - 2.0 + 0.0, + -0.2960000076293945, + -0.568000008583069, + -0.7920000057220469, + -0.944000001907348, + -1.0, + -0.9439999923706051, + -0.791999977111816, + -0.5679999656677239, + -0.29599996948242335, + 0.0 ], 'name': 'TimeWarp6' } @@ -682,17 +682,17 @@ def test_img_sequence_multiple_timewarps(): 'Class': 'TimeWarp', 'length': 1.0, 'lookup': [ - -5.0, - -3.9440000305175777, - -2.852000034332275, - -1.6880000228881844, - -0.4160000076293944, - 1.0, - 2.5839999923706056, - 4.311999977111817, - 6.147999965667726, - 8.055999969482421, - 10.0 + 0.0, + 1.0559999694824223, + 2.147999965667725, + 3.3119999771118156, + 4.583999992370606, + 6.0, + 7.583999992370606, + 9.311999977111817, + 11.147999965667726, + 13.055999969482421, + 15.0 ], 'name': 'TimeWarp3' },