mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into enhancement/AY-5270_Nuke-Enhance-Viewer-process-override-with-colorspace-display-view
This commit is contained in:
commit
48aecf846d
12 changed files with 306 additions and 58 deletions
|
|
@ -1,6 +1,80 @@
|
|||
import ayon_api
|
||||
import json
|
||||
import collections
|
||||
|
||||
from ayon_core.lib import CacheItem
|
||||
import ayon_api
|
||||
from ayon_api.graphql import FIELD_VALUE, GraphQlQuery, fields_to_dict
|
||||
|
||||
from ayon_core.lib import NestedCacheItem
|
||||
|
||||
|
||||
# --- Implementation that should be in ayon-python-api ---
|
||||
# The implementation is not available in all versions of ayon-python-api.
|
||||
def users_graphql_query(fields):
|
||||
query = GraphQlQuery("Users")
|
||||
names_var = query.add_variable("userNames", "[String!]")
|
||||
project_name_var = query.add_variable("projectName", "String!")
|
||||
|
||||
users_field = query.add_field_with_edges("users")
|
||||
users_field.set_filter("names", names_var)
|
||||
users_field.set_filter("projectName", project_name_var)
|
||||
|
||||
nested_fields = fields_to_dict(set(fields))
|
||||
|
||||
query_queue = collections.deque()
|
||||
for key, value in nested_fields.items():
|
||||
query_queue.append((key, value, users_field))
|
||||
|
||||
while query_queue:
|
||||
item = query_queue.popleft()
|
||||
key, value, parent = item
|
||||
field = parent.add_field(key)
|
||||
if value is FIELD_VALUE:
|
||||
continue
|
||||
|
||||
for k, v in value.items():
|
||||
query_queue.append((k, v, field))
|
||||
return query
|
||||
|
||||
|
||||
def get_users(project_name=None, usernames=None, fields=None):
|
||||
"""Get Users.
|
||||
|
||||
Only administrators and managers can fetch all users. For other users
|
||||
it is required to pass in 'project_name' filter.
|
||||
|
||||
Args:
|
||||
project_name (Optional[str]): Project name.
|
||||
usernames (Optional[Iterable[str]]): Filter by usernames.
|
||||
fields (Optional[Iterable[str]]): Fields to be queried
|
||||
for users.
|
||||
|
||||
Returns:
|
||||
Generator[dict[str, Any]]: Queried users.
|
||||
|
||||
"""
|
||||
filters = {}
|
||||
if usernames is not None:
|
||||
usernames = set(usernames)
|
||||
if not usernames:
|
||||
return
|
||||
filters["userNames"] = list(usernames)
|
||||
|
||||
if project_name is not None:
|
||||
filters["projectName"] = project_name
|
||||
|
||||
con = ayon_api.get_server_api_connection()
|
||||
if not fields:
|
||||
fields = con.get_default_fields_for_type("user")
|
||||
|
||||
query = users_graphql_query(set(fields))
|
||||
for attr, filter_value in filters.items():
|
||||
query.set_variable_value(attr, filter_value)
|
||||
|
||||
for parsed_data in query.continuous_query(con):
|
||||
for user in parsed_data["users"]:
|
||||
user["accessGroups"] = json.loads(user["accessGroups"])
|
||||
yield user
|
||||
# --- END of ayon-python-api implementation ---
|
||||
|
||||
|
||||
class UserItem:
|
||||
|
|
@ -32,19 +106,19 @@ class UserItem:
|
|||
class UsersModel:
|
||||
def __init__(self, controller):
|
||||
self._controller = controller
|
||||
self._users_cache = CacheItem(default_factory=list)
|
||||
self._users_cache = NestedCacheItem(default_factory=list)
|
||||
|
||||
def get_user_items(self):
|
||||
def get_user_items(self, project_name):
|
||||
"""Get user items.
|
||||
|
||||
Returns:
|
||||
List[UserItem]: List of user items.
|
||||
|
||||
"""
|
||||
self._invalidate_cache()
|
||||
return self._users_cache.get_data()
|
||||
self._invalidate_cache(project_name)
|
||||
return self._users_cache[project_name].get_data()
|
||||
|
||||
def get_user_items_by_name(self):
|
||||
def get_user_items_by_name(self, project_name):
|
||||
"""Get user items by name.
|
||||
|
||||
Implemented as most of cases using this model will need to find
|
||||
|
|
@ -56,10 +130,10 @@ class UsersModel:
|
|||
"""
|
||||
return {
|
||||
user_item.username: user_item
|
||||
for user_item in self.get_user_items()
|
||||
for user_item in self.get_user_items(project_name)
|
||||
}
|
||||
|
||||
def get_user_item_by_username(self, username):
|
||||
def get_user_item_by_username(self, project_name, username):
|
||||
"""Get user item by username.
|
||||
|
||||
Args:
|
||||
|
|
@ -69,16 +143,22 @@ class UsersModel:
|
|||
Union[UserItem, None]: User item or None if not found.
|
||||
|
||||
"""
|
||||
self._invalidate_cache()
|
||||
for user_item in self.get_user_items():
|
||||
self._invalidate_cache(project_name)
|
||||
for user_item in self.get_user_items(project_name):
|
||||
if user_item.username == username:
|
||||
return user_item
|
||||
return None
|
||||
|
||||
def _invalidate_cache(self):
|
||||
if self._users_cache.is_valid:
|
||||
def _invalidate_cache(self, project_name):
|
||||
cache = self._users_cache[project_name]
|
||||
if cache.is_valid:
|
||||
return
|
||||
self._users_cache.update_data([
|
||||
|
||||
if project_name is None:
|
||||
cache.update_data([])
|
||||
return
|
||||
|
||||
self._users_cache[project_name].update_data([
|
||||
UserItem.from_entity_data(user)
|
||||
for user in ayon_api.get_users()
|
||||
for user in get_users(project_name)
|
||||
])
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
from .window import ContextDialog, main
|
||||
from .window import ContextDialog, main, ask_for_context
|
||||
|
||||
|
||||
__all__ = (
|
||||
"ContextDialog",
|
||||
"main",
|
||||
"ask_for_context"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -791,3 +791,12 @@ def main(
|
|||
window.show()
|
||||
app.exec_()
|
||||
controller.store_output()
|
||||
|
||||
|
||||
def ask_for_context(strict=True):
|
||||
controller = ContextDialogController()
|
||||
controller.set_strict(strict)
|
||||
window = ContextDialog(controller=controller)
|
||||
window.exec_()
|
||||
|
||||
return controller.get_selected_context()
|
||||
|
|
|
|||
|
|
@ -834,12 +834,13 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_workarea_file_items(self, folder_id, task_id):
|
||||
def get_workarea_file_items(self, folder_id, task_name, sender=None):
|
||||
"""Get workarea file items.
|
||||
|
||||
Args:
|
||||
folder_id (str): Folder id.
|
||||
task_id (str): Task id.
|
||||
task_name (str): Task name.
|
||||
sender (Optional[str]): Who requested workarea file items.
|
||||
|
||||
Returns:
|
||||
list[FileItem]: List of workarea file items.
|
||||
|
|
@ -905,12 +906,12 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_workfile_info(self, folder_id, task_id, filepath):
|
||||
def get_workfile_info(self, folder_id, task_name, filepath):
|
||||
"""Workfile info from database.
|
||||
|
||||
Args:
|
||||
folder_id (str): Folder id.
|
||||
task_id (str): Task id.
|
||||
task_name (str): Task id.
|
||||
filepath (str): Workfile path.
|
||||
|
||||
Returns:
|
||||
|
|
@ -921,7 +922,7 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
def save_workfile_info(self, folder_id, task_id, filepath, note):
|
||||
def save_workfile_info(self, folder_id, task_name, filepath, note):
|
||||
"""Save workfile info to database.
|
||||
|
||||
At this moment the only information which can be saved about
|
||||
|
|
@ -932,7 +933,7 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon):
|
|||
|
||||
Args:
|
||||
folder_id (str): Folder id.
|
||||
task_id (str): Task id.
|
||||
task_name (str): Task id.
|
||||
filepath (str): Workfile path.
|
||||
note (Union[str, None]): Note.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -278,7 +278,8 @@ class BaseWorkfileController(
|
|||
)
|
||||
|
||||
def get_user_items_by_name(self):
|
||||
return self._users_model.get_user_items_by_name()
|
||||
project_name = self.get_current_project_name()
|
||||
return self._users_model.get_user_items_by_name(project_name)
|
||||
|
||||
# Host information
|
||||
def get_workfile_extensions(self):
|
||||
|
|
@ -410,9 +411,11 @@ class BaseWorkfileController(
|
|||
return self._workfiles_model.get_workarea_dir_by_context(
|
||||
folder_id, task_id)
|
||||
|
||||
def get_workarea_file_items(self, folder_id, task_id):
|
||||
def get_workarea_file_items(self, folder_id, task_name, sender=None):
|
||||
task_id = self._get_task_id(folder_id, task_name)
|
||||
return self._workfiles_model.get_workarea_file_items(
|
||||
folder_id, task_id)
|
||||
folder_id, task_id, task_name
|
||||
)
|
||||
|
||||
def get_workarea_save_as_data(self, folder_id, task_id):
|
||||
return self._workfiles_model.get_workarea_save_as_data(
|
||||
|
|
@ -447,12 +450,14 @@ class BaseWorkfileController(
|
|||
return self._workfiles_model.get_published_file_items(
|
||||
folder_id, task_name)
|
||||
|
||||
def get_workfile_info(self, folder_id, task_id, filepath):
|
||||
def get_workfile_info(self, folder_id, task_name, filepath):
|
||||
task_id = self._get_task_id(folder_id, task_name)
|
||||
return self._workfiles_model.get_workfile_info(
|
||||
folder_id, task_id, filepath
|
||||
)
|
||||
|
||||
def save_workfile_info(self, folder_id, task_id, filepath, note):
|
||||
def save_workfile_info(self, folder_id, task_name, filepath, note):
|
||||
task_id = self._get_task_id(folder_id, task_name)
|
||||
self._workfiles_model.save_workfile_info(
|
||||
folder_id, task_id, filepath, note
|
||||
)
|
||||
|
|
@ -627,6 +632,17 @@ class BaseWorkfileController(
|
|||
def _emit_event(self, topic, data=None):
|
||||
self.emit_event(topic, data, "controller")
|
||||
|
||||
def _get_task_id(self, folder_id, task_name, sender=None):
|
||||
task_item = self._hierarchy_model.get_task_item_by_name(
|
||||
self.get_current_project_name(),
|
||||
folder_id,
|
||||
task_name,
|
||||
sender
|
||||
)
|
||||
if not task_item:
|
||||
return None
|
||||
return task_item.id
|
||||
|
||||
# Expected selection
|
||||
# - expected selection is used to restore selection after refresh
|
||||
# or when current context should be used
|
||||
|
|
@ -722,7 +738,7 @@ class BaseWorkfileController(
|
|||
self._host_save_workfile(dst_filepath)
|
||||
|
||||
# Make sure workfile info exists
|
||||
self.save_workfile_info(folder_id, task_id, dst_filepath, None)
|
||||
self.save_workfile_info(folder_id, task_name, dst_filepath, None)
|
||||
|
||||
# Create extra folders
|
||||
create_workdir_extra_folders(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
import re
|
||||
import copy
|
||||
import uuid
|
||||
|
||||
import arrow
|
||||
import ayon_api
|
||||
|
|
@ -173,7 +174,7 @@ class WorkareaModel:
|
|||
folder_mapping[task_id] = workdir
|
||||
return workdir
|
||||
|
||||
def get_file_items(self, folder_id, task_id):
|
||||
def get_file_items(self, folder_id, task_id, task_name):
|
||||
items = []
|
||||
if not folder_id or not task_id:
|
||||
return items
|
||||
|
|
@ -192,7 +193,7 @@ class WorkareaModel:
|
|||
continue
|
||||
|
||||
workfile_info = self._controller.get_workfile_info(
|
||||
folder_id, task_id, filepath
|
||||
folder_id, task_name, filepath
|
||||
)
|
||||
modified = os.path.getmtime(filepath)
|
||||
items.append(FileItem(
|
||||
|
|
@ -587,6 +588,7 @@ class WorkfileEntitiesModel:
|
|||
|
||||
username = self._get_current_username()
|
||||
workfile_info = {
|
||||
"id": uuid.uuid4().hex,
|
||||
"path": rootless_path,
|
||||
"taskId": task_id,
|
||||
"attrib": {
|
||||
|
|
@ -770,19 +772,21 @@ class WorkfilesModel:
|
|||
return self._workarea_model.get_workarea_dir_by_context(
|
||||
folder_id, task_id)
|
||||
|
||||
def get_workarea_file_items(self, folder_id, task_id):
|
||||
def get_workarea_file_items(self, folder_id, task_id, task_name):
|
||||
"""Workfile items for passed context from workarea.
|
||||
|
||||
Args:
|
||||
folder_id (Union[str, None]): Folder id.
|
||||
task_id (Union[str, None]): Task id.
|
||||
task_name (Union[str, None]): Task name.
|
||||
|
||||
Returns:
|
||||
list[FileItem]: List of file items matching workarea of passed
|
||||
context.
|
||||
"""
|
||||
|
||||
return self._workarea_model.get_file_items(folder_id, task_id)
|
||||
return self._workarea_model.get_file_items(
|
||||
folder_id, task_id, task_name
|
||||
)
|
||||
|
||||
def get_workarea_save_as_data(self, folder_id, task_id):
|
||||
return self._workarea_model.get_workarea_save_as_data(
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class WorkAreaFilesModel(QtGui.QStandardItemModel):
|
|||
self._empty_item_used = False
|
||||
self._published_mode = False
|
||||
self._selected_folder_id = None
|
||||
self._selected_task_id = None
|
||||
self._selected_task_name = None
|
||||
|
||||
self._add_missing_context_item()
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ class WorkAreaFilesModel(QtGui.QStandardItemModel):
|
|||
|
||||
def _on_task_changed(self, event):
|
||||
self._selected_folder_id = event["folder_id"]
|
||||
self._selected_task_id = event["task_id"]
|
||||
self._selected_task_name = event["task_name"]
|
||||
if not self._published_mode:
|
||||
self._fill_items()
|
||||
|
||||
|
|
@ -179,13 +179,13 @@ class WorkAreaFilesModel(QtGui.QStandardItemModel):
|
|||
|
||||
def _fill_items_impl(self):
|
||||
folder_id = self._selected_folder_id
|
||||
task_id = self._selected_task_id
|
||||
if not folder_id or not task_id:
|
||||
task_name = self._selected_task_name
|
||||
if not folder_id or not task_name:
|
||||
self._add_missing_context_item()
|
||||
return
|
||||
|
||||
file_items = self._controller.get_workarea_file_items(
|
||||
folder_id, task_id
|
||||
folder_id, task_name
|
||||
)
|
||||
root_item = self.invisibleRootItem()
|
||||
if not file_items:
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class SidePanelWidget(QtWidgets.QWidget):
|
|||
self._btn_note_save = btn_note_save
|
||||
|
||||
self._folder_id = None
|
||||
self._task_id = None
|
||||
self._task_name = None
|
||||
self._filepath = None
|
||||
self._orig_note = ""
|
||||
self._controller = controller
|
||||
|
|
@ -93,10 +93,10 @@ class SidePanelWidget(QtWidgets.QWidget):
|
|||
|
||||
def _on_selection_change(self, event):
|
||||
folder_id = event["folder_id"]
|
||||
task_id = event["task_id"]
|
||||
task_name = event["task_name"]
|
||||
filepath = event["path"]
|
||||
|
||||
self._set_context(folder_id, task_id, filepath)
|
||||
self._set_context(folder_id, task_name, filepath)
|
||||
|
||||
def _on_note_change(self):
|
||||
text = self._note_input.toPlainText()
|
||||
|
|
@ -106,19 +106,19 @@ class SidePanelWidget(QtWidgets.QWidget):
|
|||
note = self._note_input.toPlainText()
|
||||
self._controller.save_workfile_info(
|
||||
self._folder_id,
|
||||
self._task_id,
|
||||
self._task_name,
|
||||
self._filepath,
|
||||
note
|
||||
)
|
||||
self._orig_note = note
|
||||
self._btn_note_save.setEnabled(False)
|
||||
|
||||
def _set_context(self, folder_id, task_id, filepath):
|
||||
def _set_context(self, folder_id, task_name, filepath):
|
||||
workfile_info = None
|
||||
# Check if folder, task and file are selected
|
||||
if bool(folder_id) and bool(task_id) and bool(filepath):
|
||||
if bool(folder_id) and bool(task_name) and bool(filepath):
|
||||
workfile_info = self._controller.get_workfile_info(
|
||||
folder_id, task_id, filepath
|
||||
folder_id, task_name, filepath
|
||||
)
|
||||
enabled = workfile_info is not None
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ class SidePanelWidget(QtWidgets.QWidget):
|
|||
self._btn_note_save.setEnabled(enabled)
|
||||
|
||||
self._folder_id = folder_id
|
||||
self._task_id = task_id
|
||||
self._task_name = task_name
|
||||
self._filepath = filepath
|
||||
|
||||
# Disable inputs and remove texts if any required arguments are
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
"""Creator plugin for creating publishable Houdini Digital Assets."""
|
||||
import ayon_api
|
||||
|
||||
from ayon_core.pipeline import CreatorError
|
||||
from ayon_core.pipeline import (
|
||||
CreatorError,
|
||||
get_current_project_name
|
||||
)
|
||||
from ayon_houdini.api import plugin
|
||||
import hou
|
||||
|
||||
|
|
@ -56,8 +59,18 @@ class CreateHDA(plugin.HoudiniCreator):
|
|||
raise CreatorError(
|
||||
"cannot create hda from node {}".format(to_hda))
|
||||
|
||||
# Pick a unique type name for HDA product per folder path per project.
|
||||
type_name = (
|
||||
"{project_name}{folder_path}_{node_name}".format(
|
||||
project_name=get_current_project_name(),
|
||||
folder_path=folder_path.replace("/","_"),
|
||||
node_name=node_name
|
||||
)
|
||||
)
|
||||
|
||||
hda_node = to_hda.createDigitalAsset(
|
||||
name=node_name,
|
||||
name=type_name,
|
||||
description=node_name,
|
||||
hda_file_name="$HIP/{}.hda".format(node_name)
|
||||
)
|
||||
hda_node.layoutChildren()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from ayon_core.pipeline import get_representation_path
|
||||
from ayon_core.pipeline.load import LoadError
|
||||
from ayon_houdini.api import (
|
||||
pipeline,
|
||||
plugin
|
||||
|
|
@ -28,14 +29,18 @@ class HdaLoader(plugin.HoudiniLoader):
|
|||
# Get the root node
|
||||
obj = hou.node("/obj")
|
||||
|
||||
# Create a unique name
|
||||
counter = 1
|
||||
namespace = namespace or context["folder"]["name"]
|
||||
formatted = "{}_{}".format(namespace, name) if namespace else name
|
||||
node_name = "{0}_{1:03d}".format(formatted, counter)
|
||||
node_name = "{}_{}".format(namespace, name) if namespace else name
|
||||
|
||||
hou.hda.installFile(file_path)
|
||||
hda_node = obj.createNode(name, node_name)
|
||||
|
||||
# Get the type name from the HDA definition.
|
||||
hda_defs = hou.hda.definitionsInFile(file_path)
|
||||
if not hda_defs:
|
||||
raise LoadError(f"No HDA definitions found in file: {file_path}")
|
||||
|
||||
type_name = hda_defs[0].nodeTypeName()
|
||||
hda_node = obj.createNode(type_name, node_name)
|
||||
|
||||
self[:] = [hda_node]
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,6 @@ from .lib import (
|
|||
INSTANCE_DATA_KNOB,
|
||||
get_main_window,
|
||||
WorkfileSettings,
|
||||
# TODO: remove this once workfile builder will be removed
|
||||
process_workfile_builder,
|
||||
start_workfile_template_builder,
|
||||
launch_workfiles_app,
|
||||
check_inventory_versions,
|
||||
|
|
@ -67,6 +65,7 @@ from .workio import (
|
|||
current_file
|
||||
)
|
||||
from .constants import ASSIST
|
||||
from . import push_to_project
|
||||
|
||||
log = Logger.get_logger(__name__)
|
||||
|
||||
|
|
@ -159,9 +158,6 @@ def add_nuke_callbacks():
|
|||
# template builder callbacks
|
||||
nuke.addOnCreate(start_workfile_template_builder, nodeClass="Root")
|
||||
|
||||
# TODO: remove this callback once workfile builder will be removed
|
||||
nuke.addOnCreate(process_workfile_builder, nodeClass="Root")
|
||||
|
||||
# fix ffmpeg settings on script
|
||||
nuke.addOnScriptLoad(on_script_load)
|
||||
|
||||
|
|
@ -332,6 +328,11 @@ def _install_menu():
|
|||
lambda: update_placeholder()
|
||||
)
|
||||
|
||||
menu.addCommand(
|
||||
"Push to Project",
|
||||
lambda: push_to_project.main()
|
||||
)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Experimental tools...",
|
||||
|
|
|
|||
118
server_addon/nuke/client/ayon_nuke/api/push_to_project.py
Normal file
118
server_addon/nuke/client/ayon_nuke/api/push_to_project.py
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
from collections import defaultdict
|
||||
import shutil
|
||||
import os
|
||||
|
||||
from ayon_api import get_project, get_folder_by_id, get_task_by_id
|
||||
from ayon_core.settings import get_project_settings
|
||||
from ayon_core.pipeline import Anatomy, registered_host
|
||||
from ayon_core.pipeline.template_data import get_template_data
|
||||
from ayon_core.pipeline.workfile import get_workdir_with_workdir_data
|
||||
from ayon_core.tools import context_dialog
|
||||
|
||||
from .utils import bake_gizmos_recursively
|
||||
from .lib import MENU_LABEL
|
||||
|
||||
import nuke
|
||||
|
||||
|
||||
def bake_container(container):
|
||||
"""Bake containers to read nodes."""
|
||||
|
||||
node = container["node"]
|
||||
|
||||
# Fetch knobs to remove in order.
|
||||
knobs_to_remove = []
|
||||
remove = False
|
||||
for count in range(0, node.numKnobs()):
|
||||
knob = node.knob(count)
|
||||
|
||||
# All knobs from "AYON" tab knob onwards.
|
||||
if knob.name() == MENU_LABEL:
|
||||
remove = True
|
||||
|
||||
if remove:
|
||||
knobs_to_remove.append(knob)
|
||||
|
||||
# Dont remove knobs from "containerId" onwards.
|
||||
if knob.name() == "containerId":
|
||||
remove = False
|
||||
|
||||
# Knobs needs to be remove in reverse order, because child knobs needs to
|
||||
# be remove first.
|
||||
for knob in reversed(knobs_to_remove):
|
||||
node.removeKnob(knob)
|
||||
|
||||
node["tile_color"].setValue(0)
|
||||
|
||||
|
||||
def main():
|
||||
context = context_dialog.ask_for_context()
|
||||
|
||||
if context is None:
|
||||
return
|
||||
|
||||
# Get workfile path to save to.
|
||||
project_name = context["project_name"]
|
||||
project = get_project(project_name)
|
||||
folder = get_folder_by_id(project_name, context["folder_id"])
|
||||
task = get_task_by_id(project_name, context["task_id"])
|
||||
host = registered_host()
|
||||
project_settings = get_project_settings(project_name)
|
||||
anatomy = Anatomy(project_name)
|
||||
|
||||
workdir_data = get_template_data(
|
||||
project, folder, task, host.name, project_settings
|
||||
)
|
||||
|
||||
workdir = get_workdir_with_workdir_data(
|
||||
workdir_data,
|
||||
project_name,
|
||||
anatomy,
|
||||
project_settings=project_settings
|
||||
)
|
||||
# Save current workfile.
|
||||
current_file = host.current_file()
|
||||
host.save_file(current_file)
|
||||
|
||||
for container in host.ls():
|
||||
bake_container(container)
|
||||
|
||||
# Bake gizmos.
|
||||
bake_gizmos_recursively()
|
||||
|
||||
# Copy all read node files to "resources" folder next to workfile and
|
||||
# change file path.
|
||||
first_frame = int(nuke.root()["first_frame"].value())
|
||||
last_frame = int(nuke.root()["last_frame"].value())
|
||||
files_by_node_name = defaultdict(set)
|
||||
nodes_by_name = {}
|
||||
for count in range(first_frame, last_frame + 1):
|
||||
nuke.frame(count)
|
||||
for node in nuke.allNodes(filter="Read"):
|
||||
files_by_node_name[node.name()].add(
|
||||
nuke.filename(node, nuke.REPLACE)
|
||||
)
|
||||
nodes_by_name[node.name()] = node
|
||||
|
||||
resources_dir = os.path.join(workdir, "resources")
|
||||
for name, files in files_by_node_name.items():
|
||||
dir = os.path.join(resources_dir, name)
|
||||
if not os.path.exists(dir):
|
||||
os.makedirs(dir)
|
||||
|
||||
for f in files:
|
||||
shutil.copy(f, os.path.join(dir, os.path.basename(f)))
|
||||
|
||||
node = nodes_by_name[name]
|
||||
path = node["file"].value().replace(os.path.dirname(f), dir)
|
||||
node["file"].setValue(path.replace("\\", "/"))
|
||||
|
||||
# Save current workfile to new context.
|
||||
pushed_workfile = os.path.join(
|
||||
workdir, os.path.basename(current_file))
|
||||
host.save_file(pushed_workfile)
|
||||
|
||||
# Open current context workfile.
|
||||
host.open_file(current_file)
|
||||
|
||||
nuke.message(f"Pushed to project: \n{pushed_workfile}")
|
||||
Loading…
Add table
Add a link
Reference in a new issue