mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-26 13:52:15 +01:00
OP-3446 - implemented render_mov_batch in TrayPublisher
This commit is contained in:
parent
3b7a0a5bee
commit
74e57f9f49
5 changed files with 371 additions and 1 deletions
61
openpype/hosts/traypublisher/api/batch_lib.py
Normal file
61
openpype/hosts/traypublisher/api/batch_lib.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Helper functions to find matching asset for (multiple) processed source files
|
||||
import os
|
||||
import collections
|
||||
|
||||
from openpype.client import get_assets
|
||||
|
||||
|
||||
def get_children_assets_by_name(project_name, top_asset_doc):
|
||||
""" Get all children for 'top_asset_doc' by theirs name
|
||||
|
||||
Args:
|
||||
project_name (str)
|
||||
top_asset_doc (asset doc) (eg dict)
|
||||
Returns:
|
||||
(dict) {"shot1": shot1_asset_doc}
|
||||
"""
|
||||
assets_by_parent_id = get_asset_docs_by_parent_id(project_name)
|
||||
_children_docs = get_children_docs(
|
||||
assets_by_parent_id, top_asset_doc
|
||||
)
|
||||
children_docs = {
|
||||
children_doc["name"].lower(): children_doc
|
||||
for children_doc in _children_docs
|
||||
}
|
||||
return children_docs
|
||||
|
||||
|
||||
def get_asset_docs_by_parent_id(project_name):
|
||||
""" Query all assets for project and store them by parent's id to list
|
||||
|
||||
Args:
|
||||
project_name (str)
|
||||
Returns:
|
||||
(dict) { _id of parent :[asset_doc1, asset_doc2]}
|
||||
"""
|
||||
asset_docs_by_parent_id = collections.defaultdict(list)
|
||||
for asset_doc in get_assets(project_name):
|
||||
parent_id = asset_doc["data"]["visualParent"]
|
||||
asset_docs_by_parent_id[parent_id].append(asset_doc)
|
||||
return asset_docs_by_parent_id
|
||||
|
||||
|
||||
def get_children_docs(documents_by_parent_id, parent_doc):
|
||||
""" Recursively find all children in reverse order
|
||||
|
||||
Last children first.
|
||||
Args:
|
||||
documents_by_parent_id (dict)
|
||||
parent_doc (asset doc, eg dict)
|
||||
Returns
|
||||
(list) of asset docs
|
||||
"""
|
||||
output = []
|
||||
children = documents_by_parent_id.get(parent_doc["_id"]) or tuple()
|
||||
for child in children:
|
||||
output.extend(
|
||||
get_children_docs(documents_by_parent_id, child)
|
||||
)
|
||||
output.append(parent_doc)
|
||||
return output
|
||||
|
||||
191
openpype/hosts/traypublisher/plugins/create/create_mov_batch.py
Normal file
191
openpype/hosts/traypublisher/plugins/create/create_mov_batch.py
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
import copy
|
||||
import os
|
||||
import re
|
||||
|
||||
from openpype.client import get_assets
|
||||
from openpype.hosts.traypublisher.api import pipeline
|
||||
from openpype.lib import FileDef, TextDef, get_subset_name_with_asset_doc
|
||||
from openpype.pipeline import (
|
||||
CreatedInstance
|
||||
)
|
||||
|
||||
from openpype.hosts.traypublisher.api.plugin import TrayPublishCreator
|
||||
|
||||
|
||||
class BatchMovCreator(TrayPublishCreator):
|
||||
"""Creates instances from .mov file(s)."""
|
||||
identifier = "render_mov_batch"
|
||||
label = "Batch Mov"
|
||||
family = "render"
|
||||
description = "Publish batch of movs"
|
||||
host_name = "traypublisher"
|
||||
|
||||
create_allow_context_change = False
|
||||
version_regex = re.compile(r"^(.+)_v([0-9]+)$")
|
||||
|
||||
default_tasks = ["Compositing"]
|
||||
|
||||
extensions = [".mov"]
|
||||
|
||||
def __init__(self, project_settings, *args, **kwargs):
|
||||
super(BatchMovCreator, self).__init__(project_settings,
|
||||
*args, **kwargs)
|
||||
self._default_variants = (project_settings["traypublisher"]
|
||||
["BatchMovCreator"]
|
||||
["default_variants"])
|
||||
|
||||
def get_icon(self):
|
||||
return "fa.file"
|
||||
|
||||
def create(self, subset_name, data, pre_create_data):
|
||||
file_paths = pre_create_data.get("filepath")
|
||||
if not file_paths:
|
||||
return
|
||||
|
||||
for file_info in file_paths:
|
||||
instance_data = copy.deepcopy(data)
|
||||
file_name = file_info["filenames"][0]
|
||||
filepath = os.path.join(file_info["directory"], file_name)
|
||||
instance_data["creator_attributes"] = {"filepath": filepath}
|
||||
|
||||
asset_doc, version = self.get_asset_doc_from_file_name(
|
||||
file_name, self.project_name)
|
||||
|
||||
subset_name, task_name = self._get_subset_and_task(
|
||||
asset_doc, data["variant"], self.project_name)
|
||||
|
||||
instance_data["task"] = task_name
|
||||
instance_data["asset"] = asset_doc["name"]
|
||||
|
||||
# Create new instance
|
||||
new_instance = CreatedInstance(self.family, subset_name,
|
||||
instance_data, self)
|
||||
# Host implementation of storing metadata about instance
|
||||
pipeline.HostContext.add_instance(new_instance.data_to_store())
|
||||
# Add instance to current context
|
||||
self._add_instance_to_context(new_instance)
|
||||
|
||||
def get_asset_doc_from_file_name(self, source_filename, project_name):
|
||||
"""Try to parse out asset name from file name provided.
|
||||
|
||||
Artists might provide various file name formats.
|
||||
Currently handled:
|
||||
- chair.mov
|
||||
- chair_v001.mov
|
||||
- my_chair_to_upload.mov
|
||||
"""
|
||||
version = None
|
||||
asset_name = os.path.splitext(source_filename)[0]
|
||||
# Always first check if source filename is in assets
|
||||
matching_asset_doc = self._get_asset_by_name_case_not_sensitive(
|
||||
project_name, asset_name)
|
||||
|
||||
if matching_asset_doc is None:
|
||||
matching_asset_doc, version = (
|
||||
self._parse_with_version(project_name, asset_name))
|
||||
|
||||
if matching_asset_doc is None:
|
||||
matching_asset_doc = self._parse_containing(project_name,
|
||||
asset_name)
|
||||
|
||||
if matching_asset_doc is None:
|
||||
raise ValueError(
|
||||
"Cannot guess asset name from {}".format(source_filename))
|
||||
|
||||
return matching_asset_doc, version
|
||||
|
||||
def _parse_with_version(self, project_name, asset_name):
|
||||
"""Try to parse asset name from a file name containing version too
|
||||
|
||||
Eg. 'chair_v001.mov' >> 'chair', 1
|
||||
"""
|
||||
self.log.debug((
|
||||
"Asset doc by \"{}\" was not found, trying version regex."
|
||||
).format(asset_name))
|
||||
|
||||
matching_asset_doc = version_number = None
|
||||
|
||||
regex_result = self.version_regex.findall(asset_name)
|
||||
if regex_result:
|
||||
_asset_name, _version_number = regex_result[0]
|
||||
matching_asset_doc = self._get_asset_by_name_case_not_sensitive(
|
||||
project_name, _asset_name)
|
||||
if matching_asset_doc:
|
||||
version_number = int(_version_number)
|
||||
|
||||
return matching_asset_doc, version_number
|
||||
|
||||
def _parse_containing(self, project_name, asset_name):
|
||||
"""Look if file name contains any existing asset name"""
|
||||
for asset_doc in get_assets(project_name, fields=["name"]):
|
||||
if asset_doc["name"].lower() in asset_name.lower():
|
||||
return get_assets(project_name,
|
||||
asset_names=[asset_doc["name"]])
|
||||
|
||||
def _get_subset_and_task(self, asset_doc, variant, project_name):
|
||||
"""Create subset name according to standard template process"""
|
||||
task_name = self._get_task_name(asset_doc)
|
||||
|
||||
subset_name = get_subset_name_with_asset_doc(
|
||||
self.family,
|
||||
variant,
|
||||
task_name,
|
||||
asset_doc,
|
||||
project_name
|
||||
)
|
||||
|
||||
return subset_name, task_name
|
||||
|
||||
def _get_task_name(self, asset_doc):
|
||||
"""Get applicable task from 'asset_doc' """
|
||||
available_task_names = {}
|
||||
asset_tasks = asset_doc.get("data", {}).get("tasks") or {}
|
||||
for task_name in asset_tasks.keys():
|
||||
available_task_names[task_name.lower()] = task_name
|
||||
|
||||
task_name = None
|
||||
for _task_name in self.default_tasks:
|
||||
_task_name_low = _task_name.lower()
|
||||
if _task_name_low in available_task_names:
|
||||
task_name = available_task_names[_task_name_low]
|
||||
break
|
||||
|
||||
return task_name
|
||||
|
||||
def get_default_variants(self):
|
||||
return self._default_variants
|
||||
|
||||
def get_instance_attr_defs(self):
|
||||
return []
|
||||
|
||||
def get_pre_create_attr_defs(self):
|
||||
# Use same attributes as for instance attributes
|
||||
return [
|
||||
FileDef(
|
||||
"filepath",
|
||||
folders=False,
|
||||
single_item=False,
|
||||
extensions=self.extensions,
|
||||
label="Filepath"
|
||||
)
|
||||
]
|
||||
|
||||
def get_detail_description(self):
|
||||
return """# Publish batch of .mov to multiple assets.
|
||||
|
||||
File names must then contain only asset name, or asset name + version.
|
||||
(eg. 'chair.mov', 'chair_v001.mov', not really safe `my_chair_v001.mov`
|
||||
"""
|
||||
|
||||
def _get_asset_by_name_case_not_sensitive(self, project_name, asset_name):
|
||||
"""Handle more cases in file names"""
|
||||
asset_name = re.compile(asset_name, re.IGNORECASE)
|
||||
|
||||
assets = list(get_assets(project_name, asset_names=[asset_name]))
|
||||
if assets:
|
||||
if len(assets) > 1:
|
||||
self.log.warning("Too many records found for {}".format(
|
||||
asset_name))
|
||||
return
|
||||
|
||||
return assets.pop()
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import os
|
||||
|
||||
import pyblish.api
|
||||
from openpype.pipeline import OpenPypePyblishPluginMixin
|
||||
|
||||
|
||||
class CollectMovBatch(
|
||||
pyblish.api.InstancePlugin, OpenPypePyblishPluginMixin
|
||||
):
|
||||
"""Collect file url for batch mov and create representation."""
|
||||
|
||||
label = "Collect Mov Batch Files"
|
||||
order = pyblish.api.CollectorOrder
|
||||
|
||||
hosts = ["traypublisher"]
|
||||
|
||||
def process(self, instance):
|
||||
if not instance.data.get("creator_identifier") == "render_mov_batch":
|
||||
return
|
||||
|
||||
file_url = instance.data["creator_attributes"]["filepath"]
|
||||
file_name = os.path.basename(file_url)
|
||||
_, ext = os.path.splitext(file_name)
|
||||
|
||||
repre = {
|
||||
"name": ext[1:],
|
||||
"ext": ext[1:],
|
||||
"files": file_name,
|
||||
"stagingDir": os.path.dirname(file_url)
|
||||
}
|
||||
|
||||
instance.data["representations"].append(repre)
|
||||
|
||||
self.log.debug("instance.data {}".format(instance.data))
|
||||
|
|
@ -31,5 +31,18 @@
|
|||
".aep"
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"BatchMovCreator": {
|
||||
"family": "render_mov_batch",
|
||||
"identifier": "",
|
||||
"label": "Batch Mov",
|
||||
"icon": "fa.file",
|
||||
"default_variants": [],
|
||||
"description": "",
|
||||
"detailed_description": "",
|
||||
"default_tasks": "Compositing",
|
||||
"extensions": [
|
||||
".mov"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -78,6 +78,77 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "BatchMovCreator",
|
||||
"label": "Batch Mov Creator",
|
||||
"use_label_wrap": true,
|
||||
"collapsible_key": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"key": "family",
|
||||
"label": "Family"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "identifier",
|
||||
"label": "Identifier",
|
||||
"placeholder": "< Use 'Family' >",
|
||||
"tooltip": "All creators must have unique identifier.\nBy default is used 'family' but if you need to have more creators with same families\nyou have to set identifier too."
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "label",
|
||||
"label": "Label"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "icon",
|
||||
"label": "Icon"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "default_variants",
|
||||
"label": "Default variants",
|
||||
"object_type": {
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "description",
|
||||
"label": "Description"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "detailed_description",
|
||||
"label": "Detailed Description",
|
||||
"multiline": true
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "default_tasks",
|
||||
"label": "Default task"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "extensions",
|
||||
"label": "Extensions",
|
||||
"use_label_wrap": true,
|
||||
"collapsible_key": true,
|
||||
"collapsed": false,
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue