diff --git a/openpype/hosts/tvpaint/api/plugin.py b/openpype/hosts/tvpaint/api/plugin.py index e148e44a27..e65c25b8d1 100644 --- a/openpype/hosts/tvpaint/api/plugin.py +++ b/openpype/hosts/tvpaint/api/plugin.py @@ -1,8 +1,21 @@ +import re +import uuid + +import avalon.api + from openpype.api import PypeCreatorMixin -from avalon.tvpaint import pipeline +from openpype.hosts.tvpaint.api import ( + pipeline, + lib +) -class Creator(PypeCreatorMixin, pipeline.Creator): +class Creator(PypeCreatorMixin, avalon.api.Creator): + def __init__(self, *args, **kwargs): + super(Creator, self).__init__(*args, **kwargs) + # Add unified identifier created with `uuid` module + self.data["uuid"] = str(uuid.uuid4()) + @classmethod def get_dynamic_data(cls, *args, **kwargs): dynamic_data = super(Creator, cls).get_dynamic_data(*args, **kwargs) @@ -17,3 +30,95 @@ class Creator(PypeCreatorMixin, pipeline.Creator): if "task" not in dynamic_data and task_name: dynamic_data["task"] = task_name return dynamic_data + + @staticmethod + def are_instances_same(instance_1, instance_2): + """Compare instances but skip keys with unique values. + + During compare are skiped keys that will be 100% sure + different on new instance, like "id". + + Returns: + bool: True if instances are same. + """ + if ( + not isinstance(instance_1, dict) + or not isinstance(instance_2, dict) + ): + return instance_1 == instance_2 + + checked_keys = set() + checked_keys.add("id") + for key, value in instance_1.items(): + if key not in checked_keys: + if key not in instance_2: + return False + if value != instance_2[key]: + return False + checked_keys.add(key) + + for key in instance_2.keys(): + if key not in checked_keys: + return False + return True + + def write_instances(self, data): + self.log.debug( + "Storing instance data to workfile. {}".format(str(data)) + ) + return pipeline.write_instances(data) + + def process(self): + data = pipeline.list_instances() + data.append(self.data) + self.write_instances(data) + + +class Loader(avalon.api.Loader): + hosts = ["tvpaint"] + + @staticmethod + def get_members_from_container(container): + if "members" not in container and "objectName" in container: + # Backwards compatibility + layer_ids_str = container.get("objectName") + return [ + int(layer_id) for layer_id in layer_ids_str.split("|") + ] + return container["members"] + + def get_unique_layer_name(self, asset_name, name): + """Layer name with counter as suffix. + + Find higher 3 digit suffix from all layer names in scene matching regex + `{asset_name}_{name}_{suffix}`. Higher 3 digit suffix is used + as base for next number if scene does not contain layer matching regex + `0` is used ase base. + + Args: + asset_name (str): Name of subset's parent asset document. + name (str): Name of loaded subset. + + Returns: + (str): `{asset_name}_{name}_{higher suffix + 1}` + """ + layer_name_base = "{}_{}".format(asset_name, name) + + counter_regex = re.compile(r"_(\d{3})$") + + higher_counter = 0 + for layer in lib.get_layers_data(): + layer_name = layer["name"] + if not layer_name.startswith(layer_name_base): + continue + number_subpart = layer_name[len(layer_name_base):] + groups = counter_regex.findall(number_subpart) + if len(groups) != 1: + continue + + counter = int(groups[0]) + if counter > higher_counter: + higher_counter = counter + continue + + return "{}_{:0>3d}".format(layer_name_base, higher_counter + 1)