Skip to content

Commit 618539c

Browse files
authored
Merge branch 'develop' into enhancement/project_root_folders_support_list_of_str_for_leaf_folders
2 parents 8008ab0 + 7c4fb2d commit 618539c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+115
-8707
lines changed

client/ayon_core/cli.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,6 @@ def _set_global_environments() -> None:
252252
os.environ.update(env)
253253

254254
# Hardcoded default values
255-
os.environ["PYBLISH_GUI"] = "pyblish_pype"
256255
# Change scale factor only if is not set
257256
if "QT_AUTO_SCREEN_SCALE_FACTOR" not in os.environ:
258257
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
@@ -290,8 +289,6 @@ def main(*args, **kwargs):
290289
split_paths = python_path.split(os.pathsep)
291290

292291
additional_paths = [
293-
# add AYON tools for 'pyblish_pype'
294-
os.path.join(AYON_CORE_ROOT, "tools"),
295292
# add common AYON vendor
296293
# (common for multiple Python interpreter versions)
297294
os.path.join(AYON_CORE_ROOT, "vendor", "python")

client/ayon_core/lib/python_2_comp.py

Lines changed: 0 additions & 17 deletions
This file was deleted.

client/ayon_core/pipeline/farm/pyblish_functions.py

Lines changed: 106 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from __future__ import annotations
12
import copy
23
import os
34
import re
@@ -247,7 +248,8 @@ def create_skeleton_instance(
247248
"useSequenceForReview": data.get("useSequenceForReview", True),
248249
# map inputVersions `ObjectId` -> `str` so json supports it
249250
"inputVersions": list(map(str, data.get("inputVersions", []))),
250-
"colorspace": data.get("colorspace")
251+
"colorspace": data.get("colorspace"),
252+
"hasExplicitFrames": data.get("hasExplicitFrames")
251253
}
252254

253255
if data.get("renderlayer"):
@@ -324,8 +326,8 @@ def prepare_representations(
324326
skip_integration_repre_list (list): exclude specific extensions,
325327
do_not_add_review (bool): explicitly skip review
326328
color_managed_plugin (publish.ColormanagedPyblishPluginMixin)
327-
frames_to_render (str): implicit or explicit range of frames to render
328-
this value is sent to Deadline in JobInfo.Frames
329+
frames_to_render (str | None): implicit or explicit range of frames
330+
to render this value is sent to Deadline in JobInfo.Frames
329331
Returns:
330332
list of representations
331333
@@ -337,7 +339,7 @@ def prepare_representations(
337339
log = Logger.get_logger("farm_publishing")
338340

339341
if frames_to_render is not None:
340-
frames_to_render = _get_real_frames_to_render(frames_to_render)
342+
frames_to_render = convert_frames_str_to_list(frames_to_render)
341343
else:
342344
# Backwards compatibility for older logic
343345
frame_start = int(skeleton_data.get("frameStartHandle"))
@@ -386,17 +388,21 @@ def prepare_representations(
386388
frame_start -= 1
387389
frames_to_render.insert(0, frame_start)
388390

389-
files = _get_real_files_to_render(collection, frames_to_render)
391+
filenames = [
392+
os.path.basename(filepath)
393+
for filepath in _get_real_files_to_render(
394+
collection, frames_to_render
395+
)
396+
]
390397
# explicitly disable review by user
391398
preview = preview and not do_not_add_review
392399
rep = {
393400
"name": ext,
394401
"ext": ext,
395-
"files": files,
402+
"files": filenames,
403+
"stagingDir": staging,
396404
"frameStart": frame_start,
397405
"frameEnd": frame_end,
398-
# If expectedFile are absolute, we need only filenames
399-
"stagingDir": staging,
400406
"fps": skeleton_data.get("fps"),
401407
"tags": ["review"] if preview else [],
402408
}
@@ -475,21 +481,45 @@ def prepare_representations(
475481
return representations
476482

477483

478-
def _get_real_frames_to_render(frames):
479-
"""Returns list of frames that should be rendered.
484+
def convert_frames_str_to_list(frames: str) -> list[int]:
485+
"""Convert frames definition string to frames.
486+
487+
Handles formats as:
488+
>>> convert_frames_str_to_list('1001')
489+
[1001]
490+
>>> convert_frames_str_to_list('1002,1004')
491+
[1002, 1004]
492+
>>> convert_frames_str_to_list('1003-1005')
493+
[1003, 1004, 1005]
494+
>>> convert_frames_str_to_list('1001-1021x5')
495+
[1001, 1006, 1011, 1016, 1021]
496+
497+
Args:
498+
frames (str): String with frames definition.
499+
500+
Returns:
501+
list[int]: List of frames.
480502
481-
Artists could want to selectively render only particular frames
482503
"""
483-
frames_to_render = []
504+
step_pattern = re.compile(r"(?:step|by|every|x|:)(\d+)$")
505+
506+
output = []
507+
step = 1
484508
for frame in frames.split(","):
485509
if "-" in frame:
486-
splitted = frame.split("-")
487-
frames_to_render.extend(
488-
range(int(splitted[0]), int(splitted[1])+1))
510+
frame_start, frame_end = frame.split("-")
511+
match = step_pattern.findall(frame_end)
512+
if match:
513+
step = int(match[0])
514+
frame_end = re.sub(step_pattern, "", frame_end)
515+
516+
output.extend(
517+
range(int(frame_start), int(frame_end) + 1, step)
518+
)
489519
else:
490-
frames_to_render.append(int(frame))
491-
frames_to_render.sort()
492-
return frames_to_render
520+
output.append(int(frame))
521+
output.sort()
522+
return output
493523

494524

495525
def _get_real_files_to_render(collection, frames_to_render):
@@ -502,22 +532,23 @@ def _get_real_files_to_render(collection, frames_to_render):
502532
This range would override and filter previously prepared expected files
503533
from DCC.
504534
535+
Example:
536+
>>> expected_files = clique.parse([
537+
>>> "foo_v01.0001.exr",
538+
>>> "foo_v01.0002.exr",
539+
>>> ])
540+
>>> frames_to_render = [1]
541+
>>> _get_real_files_to_render(expected_files, frames_to_render)
542+
["foo_v01.0001.exr"]
543+
505544
Args:
506545
collection (clique.Collection): absolute paths
507546
frames_to_render (list[int]): of int 1001
547+
508548
Returns:
509-
(list[str])
549+
list[str]: absolute paths of files to be rendered
550+
510551
511-
Example:
512-
--------
513-
514-
expectedFiles = [
515-
"foo_v01.0001.exr",
516-
"foo_v01.0002.exr",
517-
]
518-
frames_to_render = 1
519-
>>
520-
["foo_v01.0001.exr"] - only explicitly requested frame returned
521552
"""
522553
included_frames = set(collection.indexes).intersection(frames_to_render)
523554
real_collection = clique.Collection(
@@ -526,13 +557,17 @@ def _get_real_files_to_render(collection, frames_to_render):
526557
collection.padding,
527558
indexes=included_frames
528559
)
529-
real_full_paths = list(real_collection)
530-
return [os.path.basename(file_url) for file_url in real_full_paths]
560+
return list(real_collection)
531561

532562

533-
def create_instances_for_aov(instance, skeleton, aov_filter,
534-
skip_integration_repre_list,
535-
do_not_add_review):
563+
def create_instances_for_aov(
564+
instance,
565+
skeleton,
566+
aov_filter,
567+
skip_integration_repre_list,
568+
do_not_add_review,
569+
frames_to_render=None
570+
):
536571
"""Create instances from AOVs.
537572
538573
This will create new pyblish.api.Instances by going over expected
@@ -544,6 +579,7 @@ def create_instances_for_aov(instance, skeleton, aov_filter,
544579
aov_filter (dict): AOV filter.
545580
skip_integration_repre_list (list): skip
546581
do_not_add_review (bool): Explicitly disable reviews
582+
frames_to_render (str | None): Frames to render.
547583
548584
Returns:
549585
list of pyblish.api.Instance: Instances created from
@@ -590,7 +626,8 @@ def create_instances_for_aov(instance, skeleton, aov_filter,
590626
aov_filter,
591627
additional_color_data,
592628
skip_integration_repre_list,
593-
do_not_add_review
629+
do_not_add_review,
630+
frames_to_render
594631
)
595632

596633

@@ -719,8 +756,15 @@ def get_product_name_and_group_from_template(
719756
return resulting_product_name, resulting_group_name
720757

721758

722-
def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data,
723-
skip_integration_repre_list, do_not_add_review):
759+
def _create_instances_for_aov(
760+
instance,
761+
skeleton,
762+
aov_filter,
763+
additional_data,
764+
skip_integration_repre_list,
765+
do_not_add_review,
766+
frames_to_render
767+
):
724768
"""Create instance for each AOV found.
725769
726770
This will create new instance for every AOV it can detect in expected
@@ -734,7 +778,8 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data,
734778
skip_integration_repre_list (list): list of extensions that shouldn't
735779
be published
736780
do_not_add_review (bool): explicitly disable review
737-
781+
frames_to_render (str | None): implicit or explicit range of
782+
frames to render this value is sent to Deadline in JobInfo.Frames
738783
739784
Returns:
740785
list of instances
@@ -754,10 +799,23 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data,
754799
# go through AOVs in expected files
755800
for aov, files in expected_files[0].items():
756801
collected_files = _collect_expected_files_for_aov(files)
757-
758-
expected_filepath = collected_files
759-
if isinstance(collected_files, (list, tuple)):
760-
expected_filepath = collected_files[0]
802+
first_filepath = collected_files
803+
if isinstance(first_filepath, (list, tuple)):
804+
first_filepath = first_filepath[0]
805+
staging_dir = os.path.dirname(first_filepath)
806+
807+
if (
808+
frames_to_render is not None
809+
and isinstance(collected_files, (list, tuple)) # not single file
810+
):
811+
aov_frames_to_render = convert_frames_str_to_list(frames_to_render)
812+
collections, _ = clique.assemble(collected_files)
813+
collected_files = _get_real_files_to_render(
814+
collections[0], aov_frames_to_render)
815+
else:
816+
frame_start = int(skeleton.get("frameStartHandle"))
817+
frame_end = int(skeleton.get("frameEndHandle"))
818+
aov_frames_to_render = list(range(frame_start, frame_end + 1))
761819

762820
dynamic_data = {
763821
"aov": aov,
@@ -768,7 +826,7 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data,
768826
# TODO: this must be changed to be more robust. Any coincidence
769827
# of camera name in the file path will be considered as
770828
# camera name. This is not correct.
771-
camera = [cam for cam in cameras if cam in expected_filepath]
829+
camera = [cam for cam in cameras if cam in first_filepath]
772830

773831
# Is there just one camera matching?
774832
# TODO: this is not true, we can have multiple cameras in the scene
@@ -813,18 +871,16 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data,
813871
dynamic_data=dynamic_data
814872
)
815873

816-
staging = os.path.dirname(expected_filepath)
817-
818874
try:
819-
staging = remap_source(staging, anatomy)
875+
staging_dir = remap_source(staging_dir, anatomy)
820876
except ValueError as e:
821877
log.warning(e)
822878

823879
log.info("Creating data for: {}".format(product_name))
824880

825881
app = os.environ.get("AYON_HOST_NAME", "")
826882

827-
render_file_name = os.path.basename(expected_filepath)
883+
render_file_name = os.path.basename(first_filepath)
828884

829885
aov_patterns = aov_filter
830886

@@ -881,10 +937,10 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data,
881937
"name": ext,
882938
"ext": ext,
883939
"files": collected_files,
884-
"frameStart": int(skeleton["frameStartHandle"]),
885-
"frameEnd": int(skeleton["frameEndHandle"]),
940+
"frameStart": aov_frames_to_render[0],
941+
"frameEnd": aov_frames_to_render[-1],
886942
# If expectedFile are absolute, we need only filenames
887-
"stagingDir": staging,
943+
"stagingDir": staging_dir,
888944
"fps": new_instance.get("fps"),
889945
"tags": ["review"] if preview else [],
890946
"colorspaceData": {

client/ayon_core/pipeline/load/plugins.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,7 @@
1313

1414

1515
class LoaderPlugin(list):
16-
"""Load representation into host application
17-
18-
Arguments:
19-
context (dict): avalon-core:context-1.0
20-
21-
.. versionadded:: 4.0
22-
This class was introduced
23-
24-
"""
16+
"""Load representation into host application"""
2517

2618
product_types = set()
2719
representations = set()

client/ayon_core/plugins/publish/extract_color_transcode.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,9 @@ def _translate_to_sequence(self, files_to_convert):
280280

281281
collection = collections[0]
282282
frames = list(collection.indexes)
283+
if collection.holes():
284+
return files_to_convert
285+
283286
frame_str = "{}-{}#".format(frames[0], frames[-1])
284287
file_name = "{}{}{}".format(collection.head, frame_str,
285288
collection.tail)

client/ayon_core/plugins/publish/integrate.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,8 @@ def prepare_representation(
706706
# In case source are published in place we need to
707707
# skip renumbering
708708
repre_frame_start = repre.get("frameStart")
709-
if repre_frame_start is not None:
709+
explicit_frames = instance.data.get("hasExplicitFrames", False)
710+
if not explicit_frames and repre_frame_start is not None:
710711
index_frame_start = int(repre_frame_start)
711712
# Shift destination sequence to the start frame
712713
destination_indexes = [

client/ayon_core/tools/pyblish_pype/__init__.py

Lines changed: 0 additions & 13 deletions
This file was deleted.

client/ayon_core/tools/pyblish_pype/__main__.py

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)