Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publishing: Low-level dynamic runtime instance creator #691

Draft
wants to merge 5 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def create_instance(
"productType": in_data["productType"],
"family": in_data["productType"],
"families": instance_families,
"representations": [],
"representations": in_data.get("representations", []),
"thumbnailSource": thumbnail_path
})
for key, value in in_data.items():
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import os

from ayon_core.pipeline.create import (
Creator,
CreatedInstance,
get_product_name
)
from ayon_api import get_folder_by_path, get_task_by_name


def create_representation_data(files):
"""Create representation data needed for `instance.data['representations']"""
first_file = files[0]
folder, filename = os.path.split(first_file)

# Files should be filename only in representation
files = [os.path.basename(filepath) for filepath in files]

ext = os.path.splitext(filename)[-1].strip(".")
return {
"name": ext,
"ext": ext,
"files": files if len(files) > 1 else first_file,
"stagingDir": folder,
}


class CreateRuntimeInstance(Creator):
"""Create in-memory instances for dynamic PDG publishing of files.

These instances do not persist and are meant for headless automated
publishing. The created instances are transient and will be gone on
resetting the `CreateContext` since they will not be recollected.

TODO: The goal is for this runtime instance to be so generic that it can
run anywhere, globally - and needs no knowledge about its host. It's
the simplest 'entry point' to ingesting something from anywhere.
So it should have no Houdini or host-specific logic

"""
# TODO: This should be a global HIDDEN creator instead!
identifier = "io.openpype.creators.runtime_instance"
label = "Ingest"
product_type = "dynamic" # not actually used
icon = "gears"

def create(self, product_name, instance_data, pre_create_data):

# Unfortunately the Create Context will provide the product name
# even before the `create` call without listening to pre create data
# or the instance data - so instead we ignore the product name here
# and redefine it ourselves based on the `variant` in instance data
product_type = pre_create_data.get("product_type") or instance_data["product_type"]
project_name = self.create_context.project_name
folder_entity = get_folder_by_path(project_name,
instance_data["folderPath"])
task_entity = get_task_by_name(project_name,
folder_id=folder_entity["id"],
task_name=instance_data["task"])
product_name = self._get_product_name_dynamic(
self.create_context.project_name,
folder_entity=folder_entity,
task_entity=task_entity,
variant=instance_data["variant"],
product_type=product_type
)

custom_instance_data = pre_create_data.get("instance_data")
if custom_instance_data:
instance_data.update(custom_instance_data)

# TODO: Add support for multiple representations
files = pre_create_data["files"]
representations = [create_representation_data(files)]
instance_data["representations"] = representations
instance_data["families"] = ["dynamic"]

# We ingest it as a different product type then the creator's generic
# ingest product type. For example, we specify `pointcache`
instance = CreatedInstance(
product_type=product_type,
product_name=product_name,
data=instance_data,
creator=self
)
self._add_instance_to_context(instance)

return instance

# Instances are all dynamic at run-time and cannot be persisted or
# re-collected
def collect_instances(self):
pass

def update_instances(self, update_list):
pass

def remove_instances(self, instances):
for instance in instances:
self._remove_instance_from_context(instance)

def _get_product_name_dynamic(
self,
project_name,
folder_entity,
task_entity,
variant,
product_type,
host_name=None,
instance=None
):
"""Implementation similar to `self.get_product_name` but taking
`productType` as argument instead of using the 'generic' product type
on the Creator itself."""
if host_name is None:
host_name = self.create_context.host_name

task_name = task_type = None
if task_entity:
task_name = task_entity["name"]
task_type = task_entity["taskType"]

dynamic_data = self.get_dynamic_data(
project_name,
folder_entity,
task_entity,
variant,
host_name,
instance
)

return get_product_name(
project_name,
task_name,
task_type,
host_name,
product_type,
variant,
dynamic_data=dynamic_data,
project_settings=self.project_settings
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pyblish.api


class CollectNoProductTypeFamilyDynamic(pyblish.api.InstancePlugin):
"""Collect data for caching to Deadline."""

order = pyblish.api.CollectorOrder - 0.49
families = ["dynamic"]
hosts = ["houdini"]
targets = ["local", "remote"]
label = "Override Dynamic Instance families"

def process(self, instance):
# Do not allow `productType` to creep into the pyblish families
# so that e.g. any regular plug-ins for `pointcache` or alike do
# not trigger.
instance.data["family"] = "dynamic"
instance.data["families"] = ["dynamic"]