Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Dirmap in Nuke #2198

Merged
merged 10 commits into from
Nov 10, 2021
117 changes: 12 additions & 105 deletions openpype/hosts/maya/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from openpype.lib import any_outdated
import openpype.hosts.maya
from openpype.hosts.maya.lib import copy_workspace_mel
from openpype.lib.path_tools import HostDirmap
from . import menu, lib

log = logging.getLogger("openpype.hosts.maya")
Expand All @@ -30,7 +31,8 @@ def install():

project_settings = get_project_settings(os.getenv("AVALON_PROJECT"))
# process path mapping
process_dirmap(project_settings)
dirmap_processor = MayaDirmap("maya", project_settings)
dirmap_processor.process_dirmap()

pyblish.register_plugin_path(PUBLISH_PATH)
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
Expand Down Expand Up @@ -60,110 +62,6 @@ def install():
avalon.data["familiesStateToggled"] = ["imagesequence"]


def process_dirmap(project_settings):
# type: (dict) -> None
"""Go through all paths in Settings and set them using `dirmap`.

If artists has Site Sync enabled, take dirmap mapping directly from
Local Settings when artist is syncing workfile locally.

Args:
project_settings (dict): Settings for current project.

"""
local_mapping = _get_local_sync_dirmap(project_settings)
if not project_settings["maya"].get("maya-dirmap") and not local_mapping:
return

mapping = local_mapping or \
project_settings["maya"]["maya-dirmap"]["paths"] \
or {}
mapping_enabled = project_settings["maya"]["maya-dirmap"]["enabled"] \
or bool(local_mapping)

if not mapping or not mapping_enabled:
return
if mapping.get("source-path") and mapping_enabled is True:
log.info("Processing directory mapping ...")
cmds.dirmap(en=True)
for k, sp in enumerate(mapping["source-path"]):
try:
print("{} -> {}".format(sp, mapping["destination-path"][k]))
cmds.dirmap(m=(sp, mapping["destination-path"][k]))
cmds.dirmap(m=(mapping["destination-path"][k], sp))
except IndexError:
# missing corresponding destination path
log.error(("invalid dirmap mapping, missing corresponding"
" destination directory."))
break
except RuntimeError:
log.error("invalid path {} -> {}, mapping not registered".format(
sp, mapping["destination-path"][k]
))
continue


def _get_local_sync_dirmap(project_settings):
"""
Returns dirmap if synch to local project is enabled.

Only valid mapping is from roots of remote site to local site set in
Local Settings.

Args:
project_settings (dict)
Returns:
dict : { "source-path": [XXX], "destination-path": [YYYY]}
"""
import json
mapping = {}

if not project_settings["global"]["sync_server"]["enabled"]:
log.debug("Site Sync not enabled")
return mapping

from openpype.settings.lib import get_site_local_overrides
from openpype.modules import ModulesManager

manager = ModulesManager()
sync_module = manager.modules_by_name["sync_server"]

project_name = os.getenv("AVALON_PROJECT")
sync_settings = sync_module.get_sync_project_setting(
os.getenv("AVALON_PROJECT"), exclude_locals=False, cached=False)
log.debug(json.dumps(sync_settings, indent=4))

active_site = sync_module.get_local_normalized_site(
sync_module.get_active_site(project_name))
remote_site = sync_module.get_local_normalized_site(
sync_module.get_remote_site(project_name))
log.debug("active {} - remote {}".format(active_site, remote_site))

if active_site == "local" \
and project_name in sync_module.get_enabled_projects()\
and active_site != remote_site:
overrides = get_site_local_overrides(os.getenv("AVALON_PROJECT"),
active_site)
for root_name, value in overrides.items():
if os.path.isdir(value):
try:
mapping["destination-path"] = [value]
mapping["source-path"] = [sync_settings["sites"]\
[remote_site]\
["root"]\
[root_name]]
except IndexError:
# missing corresponding destination path
log.debug("overrides".format(overrides))
log.error(
("invalid dirmap mapping, missing corresponding"
" destination directory."))
break

log.debug("local sync mapping:: {}".format(mapping))
return mapping


def uninstall():
pyblish.deregister_plugin_path(PUBLISH_PATH)
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
Expand Down Expand Up @@ -326,3 +224,12 @@ def before_workfile_save(workfile_path):

workdir = os.path.dirname(workfile_path)
copy_workspace_mel(workdir)


class MayaDirmap(HostDirmap):
def on_enable_dirmap(self):
cmds.dirmap(en=True)

def dirmap_routine(self, source_path, destination_path):
cmds.dirmap(m=(source_path, destination_path))
cmds.dirmap(m=(destination_path, source_path))
42 changes: 42 additions & 0 deletions openpype/hosts/nuke/startup/menu.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
import os
from openpype.hosts.nuke.api.lib import (
on_script_load,
check_inventory_versions,
WorkfileSettings
)
from openpype.lib.path_tools import HostDirmap
from openpype.settings import get_project_settings
from openpype.modules import ModulesManager

import nuke
from openpype.api import Logger

log = Logger().get_logger(__name__)


class NukeDirmap(HostDirmap):
kalisp marked this conversation as resolved.
Show resolved Hide resolved
def __init__(self, host_name, project_settings, sync_module, file_name):
"""
Args:
host_name (str): Nuke
project_settings (dict): settings of current project
sync_module (SyncServerModule): to limit reinitialization
file_name (str): full path of referenced file from workfiles
"""
self.host_name = host_name
self.project_settings = project_settings
self.file_name = file_name
self.sync_module = sync_module

def on_enable_dirmap(self):
pass

def dirmap_routine(self, source_path, destination_path):
log.debug("{}: {}->{}".format(self.file_name,
source_path, destination_path))
self.file_name = self.file_name.replace(source_path, destination_path)


# fix ffmpeg settings on script
nuke.addOnScriptLoad(on_script_load)

Expand All @@ -20,4 +47,19 @@
# # set apply all workfile settings on script load and save
nuke.addOnScriptLoad(WorkfileSettings().set_context_settings)


project_settings = get_project_settings(os.getenv("AVALON_PROJECT"))
manager = ModulesManager()
sync_module = manager.modules_by_name["sync_server"]


def myFilenameFilter(file_name):
dirmap_processor = NukeDirmap("nuke", project_settings, sync_module,
file_name)
dirmap_processor.process_dirmap()
return dirmap_processor.file_name


nuke.addFilenameFilter(myFilenameFilter)

log.info('Automatic syncing of write file knob to script version')
146 changes: 146 additions & 0 deletions openpype/lib/path_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import logging
import os
import re
import abc
import six


from .anatomy import Anatomy
Expand Down Expand Up @@ -196,3 +198,147 @@ def get_project_basic_paths(project_name):
if isinstance(folder_structure, str):
folder_structure = json.loads(folder_structure)
return _list_path_items(folder_structure)


@six.add_metaclass(abc.ABCMeta)
class HostDirmap:
"""
Abstract class for running dirmap on a workfile in a host.

Dirmap is used to translate paths inside of host workfile from one
OS to another. (Eg. arstist created workfile on Win, different artists
opens same file on Linux.)

Expects methods to be implemented inside of host:
on_dirmap_enabled: run host code for enabling dirmap
do_dirmap: run host code to do actual remapping
"""
def __init__(self, host_name, project_settings, sync_module=None):
self.host_name = host_name
self.project_settings = project_settings
self.sync_module = sync_module # to limit reinit of Modules

@abc.abstractmethod
def on_enable_dirmap(self):
"""
Run host dependent operation for enabling dirmap if necessary.
"""

@abc.abstractmethod
def dirmap_routine(self, source_path, destination_path):
"""
Run host dependent remapping from source_path to destination_path
"""

def process_dirmap(self):
# type: (dict) -> None
"""Go through all paths in Settings and set them using `dirmap`.

If artists has Site Sync enabled, take dirmap mapping directly from
Local Settings when artist is syncing workfile locally.

Args:
project_settings (dict): Settings for current project.

"""
mapping = self.get_mappings(self.project_settings)
if not mapping:
return

log.info("Processing directory mapping ...")
self.on_enable_dirmap()

for k, sp in enumerate(mapping["source-path"]):
try:
print("{} -> {}".format(sp, mapping["destination-path"][k]))
self.dirmap_routine(sp, mapping["destination-path"][k])
except IndexError:
# missing corresponding destination path
log.error(("invalid dirmap mapping, missing corresponding"
" destination directory."))
break
except RuntimeError:
log.error("invalid path {} -> {}, mapping not registered".format( # noqa: E501
sp, mapping["destination-path"][k]
))
continue

def get_mappings(self, project_settings):
local_mapping = self._get_local_sync_dirmap(project_settings)
dirmap_label = "{}-dirmap".format(self.host_name)
if not self.project_settings[self.host_name].get(dirmap_label) and \
not local_mapping:
return []

mapping = local_mapping or \
self.project_settings[self.host_name][dirmap_label]["paths"] or {}
enbled = self.project_settings[self.host_name][dirmap_label]["enabled"]
mapping_enabled = enbled or bool(local_mapping)

if not mapping or not mapping_enabled:
return []
return mapping

def _get_local_sync_dirmap(self, project_settings):
"""
Returns dirmap if synch to local project is enabled.

Only valid mapping is from roots of remote site to local site set
in Local Settings.

Args:
project_settings (dict)
Returns:
dict : { "source-path": [XXX], "destination-path": [YYYY]}
"""
import json
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'json' imported but unused

mapping = {}

if not project_settings["global"]["sync_server"]["enabled"]:
log.debug("Site Sync not enabled")
return mapping

from openpype.settings.lib import get_site_local_overrides

if not self.sync_module:
from openpype.modules import ModulesManager
manager = ModulesManager()
self.sync_module = manager.modules_by_name["sync_server"]

project_name = os.getenv("AVALON_PROJECT")

active_site = self.sync_module.get_local_normalized_site(
self.sync_module.get_active_site(project_name))
remote_site = self.sync_module.get_local_normalized_site(
self.sync_module.get_remote_site(project_name))
log.debug("active {} - remote {}".format(active_site, remote_site))

if active_site == "local" \
and project_name in self.sync_module.get_enabled_projects()\
and active_site != remote_site:

sync_settings = self.sync_module.get_sync_project_setting(
os.getenv("AVALON_PROJECT"), exclude_locals=False,
cached=False)
log.debug(json.dumps(sync_settings, indent=4))

overrides = get_site_local_overrides(os.getenv("AVALON_PROJECT"),
active_site)
for root_name, value in overrides.items():
if os.path.isdir(value):
try:
mapping["destination-path"] = [value]
mapping["source-path"] = [sync_settings["sites"]
[remote_site]
["root"]
[root_name]]
except IndexError:
# missing corresponding destination path
log.debug("overrides".format(overrides))
log.error(
("invalid dirmap mapping, missing corresponding"
" destination directory."))
break

log.debug("local sync mapping:: {}".format(mapping))
return mapping
10 changes: 8 additions & 2 deletions openpype/settings/defaults/project_settings/nuke.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
"build_workfile": "ctrl+alt+b"
}
},
"nuke-dirmap": {
"enabled": true,
"paths": {
"source-path": [],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is any input processed by environment var solving or even solved by settings key as it is used in anatomy? So we could use {root} in source paths?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently no, but I would put that into new PR as it would be necessary to implement it also for Maya.

"destination-path": []
}
},
"create": {
"CreateWriteRender": {
"fpath_template": "{work}/renders/nuke/{subset}/{subset}.{frame}.{ext}",
Expand Down Expand Up @@ -130,8 +137,7 @@
},
"LoadClip": {
"enabled": true,
"_representations": [
],
"_representations": [],
"node_name_template": "{class_name}_{ext}"
}
},
Expand Down
Loading