diff --git a/.gitignore b/.gitignore index 68bc17f..2dc53ca 100644 --- a/.gitignore +++ b/.gitignore @@ -157,4 +157,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ diff --git a/quad_pyblish_module/plugins/hiero/__init__.py b/quad_pyblish_module/plugins/hiero/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/quad_pyblish_module/plugins/hiero/publish/__init__.py b/quad_pyblish_module/plugins/hiero/publish/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/quad_pyblish_module/plugins/hiero/publish/integrate_version_to_task.py b/quad_pyblish_module/plugins/hiero/publish/integrate_version_to_task.py new file mode 100644 index 0000000..3486359 --- /dev/null +++ b/quad_pyblish_module/plugins/hiero/publish/integrate_version_to_task.py @@ -0,0 +1,79 @@ +from pyblish import api + + +class IntegrateVersionToTask(api.ContextPlugin): + """Integrate Version To Task""" + + order = api.IntegratorOrder + 10.1 + hosts = ["hiero"] + families = ["clip"] + label = "Integrate Version To Task" + optional = False + + def get_all_task_types(self, project): + """Get all task types from the project schema. + + :param project: The Ftrack project Entity. + :type project: ftrack Project Object + :return: A dictionary containing all task types. + :rtype: dict + + """ + tasks = {} + proj_template = project['project_schema'] + temp_task_types = proj_template['_task_type_schema']['types'] + + for type in temp_task_types: + if type['name'] not in tasks: + tasks[type['name']] = type + + return tasks + + def process(self, context): + """Process the Pyblish context. + + :param context: The Pyblish context. + :type context: pyblish.api.Context + """ + # Get Data we need + task_type = context.data.get('task').lower() + ftrack_session = context.data.get('ftrackSession') + ftrack_project = context.data.get('ftrackProject') + ftrack_task_types = self.get_all_task_types(ftrack_project) + + if ftrack_session is None: + self.log.info('Ftrack session is not created.') + return + + # Build Plugin dict list to analyze + to_analyze_lst = [] + for result_dict in context.data.get('results'): + if 'Collect OTIO Review' in result_dict['plugin'].label: + to_analyze_lst.append(result_dict) + + for to_analyze in to_analyze_lst: + ftrack_shot_version = to_analyze['instance'].data['ftrackEntity'] + ftrack_asset_versions = to_analyze['instance'].data['ftrackIntegratedAssetVersions'] + + # Check if confo task already exists in ftrack + is_task_exist = ftrack_session.query( + 'Task where name is "{0}" and parent.id is {1}'.format(task_type, ftrack_shot_version['id'])).first() + if not is_task_exist: + new_task = ftrack_session.create( + 'Task', + { + 'name': task_type, + 'parent': ftrack_shot_version, + 'type': ftrack_task_types[task_type] + } + ) + else: + new_task = is_task_exist + + ftrack_session.commit() + + # Link Publish version to Task + for asset_version in ftrack_asset_versions: + asset_version['task'] = new_task + + ftrack_session.commit() diff --git a/quad_pyblish_module/plugins/kitsu/publish/__init__.py b/quad_pyblish_module/plugins/kitsu/publish/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/quad_pyblish_module/plugins/kitsu/publish/integrate_kitsu_sequence.py b/quad_pyblish_module/plugins/kitsu/publish/integrate_kitsu_sequence.py new file mode 100644 index 0000000..e7a4528 --- /dev/null +++ b/quad_pyblish_module/plugins/kitsu/publish/integrate_kitsu_sequence.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +import re +import gazu +import pyblish.api + + +class IntegrateKitsuSequence(pyblish.api.InstancePlugin): + """Integrate Kitsu Sequence""" + + order = pyblish.api.IntegratorOrder + 0.02 + label = "Kitsu Sequence" + families = ["render", "kitsu"] + optional = True + + def process(self, instance): + # Check comment has been created + comment_id = instance.data.get("kitsu_comment", {}).get("id") + if not comment_id: + self.log.debug( + "Comment not created, review not pushed to preview." + ) + return + + # Add review representations as preview of comment + task_id = instance.data["kitsu_task"]["id"] + for representation in instance.data.get("representations", []): + # Skip if Publish Sequence option hasn't been enabled from creator subset. + if not instance.data["creator_attributes"].get("publish_sequence"): + continue + + # Skip if Extract Sequence has interpreted image generation + # as review (before video concatenation) instead of a simple sequence. + if "sequence" not in representation.get("tags", []): + continue + + filesnames = representation.get("files") + if not filesnames: + self.log.warning("No files found following sequence extract.") + raise IndexError + + extension = representation.get("ext") + if not extension: + self.log.warning("No extension found in representation.") + raise IndexError + + published_path = representation.get("published_path") + if not published_path: + self.log.warning("No publish path found in representation.") + raise IndexError + + for filename in filesnames: + image_filepath = _generate_files_paths(published_path, extension, filename) + gazu.task.add_preview( + task_id, comment_id, image_filepath, normalize_movie=True + ) + + self.log.info("{} images has been uploaded to Kitsu.".format(len(filesnames))) + + +def _generate_files_paths(published_path, extension, filename): + return re.sub("\d{4}."+extension, filename, published_path) diff --git a/quad_pyblish_module/plugins/tvpaint/create/create_playblast.py b/quad_pyblish_module/plugins/tvpaint/create/create_playblast.py new file mode 100644 index 0000000..3ef1e08 --- /dev/null +++ b/quad_pyblish_module/plugins/tvpaint/create/create_playblast.py @@ -0,0 +1,129 @@ +from openpype.client import get_asset_by_name +from openpype.lib import ( + prepare_template_data, + EnumDef, + BoolDef, +) +from openpype.pipeline.create import CreatedInstance +from openpype.hosts.tvpaint.api.plugin import TVPaintAutoCreator + + +class TVPaintPlayblastCreator(TVPaintAutoCreator): + family = "render" + subset_template_family_filter = "playblast" + identifier = "render.playblast" + label = "Playblast" + icon = "fa.file-image-o" + + # Settings + mark_for_review = True + active_on_create = True + + def apply_settings(self, project_settings, system_settings): + plugin_settings = ( + project_settings["fix_custom_settings"]["tvpaint"]["create"]["create_playblast"] + ) + self.mark_for_review = plugin_settings["mark_for_review"] + self.active_on_create = plugin_settings["active_on_create"] + self.default_variant = plugin_settings["default_variant"] + self.default_variants = plugin_settings["default_variants"] + self.exports_types = ['camera', 'scene'] + self.export_type = self.exports_types[0] + + def _create_new_instance(self): + create_context = self.create_context + host_name = create_context.host_name + project_name = create_context.get_current_project_name() + asset_name = create_context.get_current_asset_name() + task_name = create_context.get_current_task_name() + + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + self.default_variant, + task_name, + asset_doc, + project_name, + host_name + ) + data = { + "asset": asset_name, + "task": task_name, + "variant": self.default_variant, + "creator_attributes": { + "mark_for_review": self.mark_for_review, + "export_type": self.export_type + }, + "label": self._get_label(subset_name), + "active": self.active_on_create + } + + new_instance = CreatedInstance( + self.family, subset_name, data, self + ) + instances_data = self.host.list_instances() + instances_data.append(new_instance.data_to_store()) + self.host.write_instances(instances_data) + self._add_instance_to_context(new_instance) + return new_instance + + def create(self): + existing_instance = None + for instance in self.create_context.instances: + if instance.creator_identifier == self.identifier: + existing_instance = instance + break + + if existing_instance is None: + return self._create_new_instance() + + create_context = self.create_context + host_name = create_context.host_name + project_name = create_context.get_current_project_name() + asset_name = create_context.get_current_asset_name() + task_name = create_context.get_current_task_name() + + if ( + existing_instance["asset"] != asset_name + or existing_instance["task"] != task_name + ): + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + existing_instance["variant"], + task_name, + asset_doc, + project_name, + host_name, + existing_instance + ) + existing_instance["asset"] = asset_name + existing_instance["task"] = task_name + existing_instance["subset"] = subset_name + + existing_instance["label"] = self._get_label(existing_instance["subset"]) + + def _get_label(self, subset_name): + try: + subset_name = subset_name.format(**prepare_template_data({})) + except (KeyError, ValueError): + pass + + return subset_name + + def get_dynamic_data(self, variant, *args, **kwargs): + dynamic_data = super().get_dynamic_data(variant, *args, **kwargs) + return dynamic_data + + def get_instance_attr_defs(self): + return [ + BoolDef( + "mark_for_review", + label="Review by default", + default=self.mark_for_review + ), + EnumDef( + "export_type", + self.exports_types, + label="Export type", + default=self.export_type + ) + ] diff --git a/quad_pyblish_module/plugins/tvpaint/create/create_publish_layout.py b/quad_pyblish_module/plugins/tvpaint/create/create_publish_layout.py new file mode 100644 index 0000000..d625168 --- /dev/null +++ b/quad_pyblish_module/plugins/tvpaint/create/create_publish_layout.py @@ -0,0 +1,129 @@ +from openpype.client import get_asset_by_name +from openpype.lib import ( + prepare_template_data, + EnumDef, + BoolDef, +) +from openpype.pipeline.create import ( + CreatedInstance, + CreatorError, +) +from openpype.hosts.tvpaint.api.plugin import TVPaintAutoCreator + + +class TVPaintPublishLayoutCreator(TVPaintAutoCreator): + family = "render" + subset_template_family_filter = "publish.sequence" + identifier = "publish.sequence" + label = "Publish Layout" + icon = "fa.file-image-o" + + # Settings + publish_sequence = True + keep_layers_transparency = False + + def apply_settings(self, project_settings, system_settings): + plugin_settings = ( + project_settings["fix_custom_settings"]["tvpaint"]["create"]["create_publish_layout"] + ) + self.active_on_create = plugin_settings["active_on_create"] + self.keep_layers_transparency = plugin_settings["keep_layers_transparency"] + self.default_variant = plugin_settings["default_variant"] + self.default_variants = plugin_settings["default_variants"] + + def _create_new_instance(self): + create_context = self.create_context + host_name = create_context.host_name + project_name = create_context.get_current_project_name() + asset_name = create_context.get_current_asset_name() + task_name = create_context.get_current_task_name() + + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + self.default_variant, + task_name, + asset_doc, + project_name, + host_name + ) + data = { + "asset": asset_name, + "task": task_name, + "variant": self.default_variant, + "creator_attributes": { + "publish_sequence": self.publish_sequence, + "keep_layers_transparency": self.keep_layers_transparency + }, + "label": self._get_label(subset_name), + "active": self.active_on_create + } + + new_instance = CreatedInstance( + self.family, subset_name, data, self + ) + instances_data = self.host.list_instances() + instances_data.append(new_instance.data_to_store()) + self.host.write_instances(instances_data) + self._add_instance_to_context(new_instance) + return new_instance + + def create(self): + existing_instance = None + for instance in self.create_context.instances: + if instance.creator_identifier == self.identifier: + existing_instance = instance + break + + if existing_instance is None: + return self._create_new_instance() + + create_context = self.create_context + host_name = create_context.host_name + project_name = create_context.get_current_project_name() + asset_name = create_context.get_current_asset_name() + task_name = create_context.get_current_task_name() + + if ( + existing_instance["asset"] != asset_name + or existing_instance["task"] != task_name + ): + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + existing_instance["variant"], + task_name, + asset_doc, + project_name, + host_name, + existing_instance + ) + existing_instance["asset"] = asset_name + existing_instance["task"] = task_name + existing_instance["subset"] = subset_name + + existing_instance["label"] = self._get_label(existing_instance["subset"]) + + def _get_label(self, subset_name): + try: + subset_name = subset_name.format(**prepare_template_data({})) + except (KeyError, ValueError): + pass + + return subset_name + + def get_dynamic_data(self, variant, *args, **kwargs): + dynamic_data = super().get_dynamic_data(variant, *args, **kwargs) + return dynamic_data + + def get_instance_attr_defs(self): + return [ + BoolDef( + "keep_layers_transparency", + label="Keep Layers Transparency", + default=self.keep_layers_transparency + ), + BoolDef( + "publish_sequence", + label="Review", + default=self.publish_sequence + ) + ] diff --git a/quad_pyblish_module/settings/defaults/project_settings.json b/quad_pyblish_module/settings/defaults/project_settings.json index cf459fc..190daa1 100644 --- a/quad_pyblish_module/settings/defaults/project_settings.json +++ b/quad_pyblish_module/settings/defaults/project_settings.json @@ -7,6 +7,9 @@ }, "ExtractJson": { "enabled": false + }, + "IntegrateKitsuSequence": { + "enabled": false } }, "create": { @@ -14,6 +17,20 @@ "enabled": true, "default_variant": "Main", "default_variants": [] + }, + "create_playblast": { + "enabled": true, + "active_on_create": true, + "mark_for_review": true, + "default_variant": "Main", + "default_variants": [] + }, + "create_publish_layout": { + "enabled": true, + "active_on_create": true, + "keep_layers_transparency": true, + "default_variant": "Main", + "default_variants": [] } } } diff --git a/quad_pyblish_module/settings/schemas/project_schemas/tvpaint.json b/quad_pyblish_module/settings/schemas/project_schemas/tvpaint.json index 5785638..e8c9a8d 100644 --- a/quad_pyblish_module/settings/schemas/project_schemas/tvpaint.json +++ b/quad_pyblish_module/settings/schemas/project_schemas/tvpaint.json @@ -38,6 +38,20 @@ "label": "Enabled" } ] + }, + { + "type": "dict", + "key": "IntegrateKitsuSequence", + "label": "Integrate Kitsu Sequence", + "is_group": false, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] } ] }, @@ -73,6 +87,80 @@ } } ] + }, + { + "type": "dict", + "collapsible": true, + "key": "create_playblast", + "label": "Create Playblast", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "active_on_create", + "label": "Active by default" + }, + { + "type": "boolean", + "key": "mark_for_review", + "label": "Review by default" + }, + { + "type": "text", + "key": "default_variant", + "label": "Default variant" + }, + { + "type": "list", + "key": "default_variants", + "label": "Default variants", + "object_type": { + "type": "text" + } + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "create_publish_layout", + "label": "Create Publish Layout", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "active_on_create", + "label": "Active on create" + }, + { + "type": "boolean", + "key": "keep_layers_transparency", + "label": "Keep Layers Transparency" + }, + { + "type": "text", + "key": "default_variant", + "label": "Default variant" + }, + { + "type": "list", + "key": "default_variants", + "label": "Default variants", + "object_type": { + "type": "text" + } + } + ] } ] }