Skip to content

Commit

Permalink
Implement workfile instance serializer.
Browse files Browse the repository at this point in the history
  • Loading branch information
robin-ynput committed Nov 5, 2024
1 parent e420699 commit c76051e
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 18 deletions.
4 changes: 3 additions & 1 deletion client/ayon_flame/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
Creator,
PublishableClip,
ClipLoader,
OpenClipSolver
OpenClipSolver,
FlameCreator,
HiddenFlameCreator,
)
from .workio import (
open_file,
Expand Down
47 changes: 46 additions & 1 deletion client/ayon_flame/api/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from ayon_core import style
from ayon_core.lib import Logger, StringTemplate
from ayon_core.pipeline import LegacyCreator, LoaderPlugin
from ayon_core.pipeline import LegacyCreator, LoaderPlugin, HiddenCreator, Creator
from ayon_core.pipeline.colorspace import get_remapped_colorspace_to_native
from ayon_core.settings import get_current_project_settings

Expand Down Expand Up @@ -325,6 +325,51 @@ def create_widget(self, *args, **kwargs):
return widget.get_results_back()


class HiddenFlameCreator(HiddenCreator):
"""HiddenCreator class wrapper
"""
settings_category = "flame"

def collect_instances(self):
pass

def update_instances(self, update_list):
pass

def remove_instances(self, instances):
pass


class FlameCreator(Creator):
"""Creator class wrapper
"""
settings_category = "flame"

def __init__(self, *args, **kwargs):
super(Creator, self).__init__(*args, **kwargs)
self.presets = get_current_project_settings()[
"flame"]["create"].get(self.__class__.__name__, {})

def create(self, product_name, instance_data, pre_create_data):
"""Prepare data for new instance creation.
Args:
product_name(str): Product name of created instance.
instance_data(dict): Base data for instance.
pre_create_data(dict): Data based on pre creation attributes.
Those may affect how creator works.
"""
# adding basic current context resolve objects
self.project = flib.get_current_project()
self.sequence = flib.get_current_sequence(flib.CTX.selection)

selected = pre_create_data.get("use_selection", False)
self.selected = flib.get_sequence_segments(
self.sequence,
selected=selected
)


class PublishableClip:
"""
Convert a segment to publishable instance
Expand Down
120 changes: 104 additions & 16 deletions client/ayon_flame/plugins/create/create_workfile.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating workfiles."""
import ayon_api
import json
from xml.etree import ElementTree as ET

from ayon_core.pipeline import (
AutoCreator,
CreatedInstance,
)

from adsk.libwiretapPythonClientAPI import (
WireTapClient,
WireTapServerHandle,
WireTapNodeHandle,
WireTapStr
)

import ayon_flame.api as flapi


class CreateWorkfile(AutoCreator):
"""Workfile auto-creator."""
Expand All @@ -14,22 +25,77 @@ class CreateWorkfile(AutoCreator):
identifier = "io.ayon.creators.flame.workfile"
label = "Workfile"
product_type = "workfile"

icon = "fa5.file"
default_variant = "Main"

def collect_instances(self):
# https://forums.autodesk.com/t5/flame-forum/store-persistent-variable-with-flame-project/td-p/9437717
_METADATA_KEY = "Nickname"

@classmethod
def _get_project_metadata_handle(cls):
""" Initialize project metadata setup.
Returns:
object. Flame wiretap handle for current project
"""
current_project = flapi.get_current_project()

wiretap_client = WireTapClient()
wiretap_client.init()

server = WireTapServerHandle("localhost:IFFFS")
project_node_handle = WireTapNodeHandle(server, "/projects/{current_project.name}")
return project_node_handle

@classmethod
def _get_project_metadata(cls):
""" Returns the metadata stored at current project.
Returns:
xml.etree.ElementTree. The project metadata data.
"""
project_node_handle = cls._get_project_metadata_handle()
metadata = WireTapStr()
project_node_handle.getMetaData("XML", "", 1, metadata)
return ET.fromstring(metadata.c_str())

@classmethod
def _dump_instance_data(cls, data):
""" Dump instance data into AyonData project tag.
Args:
data (dict): The data to push to the project tag.
"""
metadata = cls._get_project_metadata()
nickname_entry, = metadata.findall(cls._METADATA_KEY)
nickname_entry.text = json.dumps(data)

@classmethod
def _load_instance_data(cls):
""" Returns the data stored in AyonData project tag if any.
Returns:
dict. The metadata instance data.
"""
metadata = cls._get_project_metadata()
nickname_entry, = metadata.findall(cls._METADATA_KEY)
return json.loads(nickname_entry.text) if nickname_entry.text else {}

def _create_new_instance(self):
"""Create a new workfile instance.
Returns:
dict. The data of the instance to be created.
"""
variant = self.default_variant
project_name = self.create_context.get_current_project_name()
folder_path = self.create_context.get_current_folder_path()
task_name = self.create_context.get_current_task_name()
host_name = self.create_context.host_name

folder_entity = ayon_api.get_folder_by_path(
project_name, folder_path)
task_entity = ayon_api.get_task_by_name(
project_name, folder_entity["id"], task_name
)
folder_entity = self.create_context.get_current_folder_entity()
task_entity = self.create_context.get_current_task_entity()

product_name = self.get_product_name(
project_name,
folder_entity,
Expand Down Expand Up @@ -58,13 +124,35 @@ def collect_instances(self):
self._add_instance_to_context(current_instance)

def create(self, options=None):
# no need to create if it is created
# in `collect_instances`
pass
"""Auto-create an instance by default."""
instance_data = self._load_instance_data()
if instance_data:
return

self.log.info("Auto-creating workfile instance...")
data = self._create_new_instance()
current_instance = CreatedInstance(
self.product_type, data["productName"], data, self)
self._add_instance_to_context(current_instance)

def collect_instances(self):
"""Collect from timeline marker or create a new one."""
data = self._load_instance_data()
if not data:
return

instance = CreatedInstance(
self.product_type, data["productName"], data, self
)
self._add_instance_to_context(instance)

def update_instances(self, update_list):
# TODO: Implement
# This needs to be implemented to allow persisting any instance
# data on resets. We'll need to decide where to store workfile
# instance data reliably. Likely metadata on the *current project*?
pass
"""Store changes in project metadata so they can be recollected.
Args:
update_list(List[UpdateData]): Gets list of tuples. Each item
contain changed instance and its changes.
"""
for created_inst, _ in update_list:
data = created_inst.data_to_store()
self._dump_instance_data(data)

0 comments on commit c76051e

Please sign in to comment.