initialization of nukestudio integration

This commit is contained in:
Jakub Jezek 2019-04-24 11:31:29 +02:00
parent bb39f87f81
commit ca92c42cde
23 changed files with 1598 additions and 14 deletions

View file

@ -1,23 +1,20 @@
The base studio *config* for [Avalon](https://getavalon.github.io/)
he base studio _config_ for [Avalon](https://getavalon.github.io/)
Currently this config is dependent on our customised avalon instalation so it won't work with vanilla avalon core. We're working on open sourcing all of the necessary code though. You can still get inspiration or take our individual validators and scripts which should work just fine in other pipelines.
_This configuration acts as a starting point for all pype club clients wth avalon deployment._
### Code convention
Below are some of the standard practices applied to this repositories.
- **Etiquette: PEP8**
- All code is written in PEP8. It is recommended you use a linter as you work, flake8 and pylinter are both good options.
- **Etiquette: Napoleon docstrings**
- Any docstrings are made in Google Napoleon format. See [Napoleon](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) for details.
- **Etiquette: Semantic Versioning**
- This project follows [semantic versioning](http://semver.org).
- **Etiquette: Underscore means private**
- Anything prefixed with an underscore means that it is internal to wherever it is used. For example, a variable name is only ever used in the parent function or class. A module is not for use by the end-user. In contrast, anything without an underscore is public, but not necessarily part of the API. Members of the API resides in `api.py`.
- **API: Idempotence**
- A public function must be able to be called twice and produce the exact same result. This means no changing of state without restoring previous state when finishing. For example, if a function requires changing the current selection in Autodesk Maya, it must restore the previous selection prior to completing.
- **Etiquette: PEP8**
\- All code is written in PEP8. It is recommended you use a linter as you work, flake8 and pylinter are both good options.
- **Etiquette: Napoleon docstrings**
\- Any docstrings are made in Google Napoleon format. See [Napoleon](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) for details.
- **Etiquette: Semantic Versioning**
\- This project follows [semantic versioning](http://semver.org).
- **Etiquette: Underscore means private**
\- Anything prefixed with an underscore means that it is internal to wherever it is used. For example, a variable name is only ever used in the parent function or class. A module is not for use by the end-user. In contrast, anything without an underscore is public, but not necessarily part of the API. Members of the API resides in `api.py`.
- **API: Idempotence**
\- A public function must be able to be called twice and produce the exact same result. This means no changing of state without restoring previous state when finishing. For example, if a function requires changing the current selection in Autodesk Maya, it must restore the previous selection prior to completing.

181
pype/nukestudio/__init__.py Normal file
View file

@ -0,0 +1,181 @@
import os
import sys
from avalon import api as avalon
from pyblish import api as pyblish
from .. import api
from pype.nukestudio import menu
from .lib import (
show,
setup,
register_plugins,
add_to_filemenu
)
import nuke
from pypeapp import Logger
# #removing logger handler created in avalon_core
# for name, handler in [(handler.get_name(), handler)
# for handler in Logger.logging.root.handlers[:]]:
# if "pype" not in str(name).lower():
# Logger.logging.root.removeHandler(handler)
log = Logger().get_logger(__name__, "nuke")
# log = api.Logger.getLogger(__name__, "nuke")
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
PARENT_DIR = os.path.dirname(__file__)
PACKAGE_DIR = os.path.dirname(PARENT_DIR)
PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "nuke", "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "nuke", "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "nuke", "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "nuke", "inventory")
self = sys.modules[__name__]
self.nLogger = None
if os.getenv("PYBLISH_GUI", None):
pyblish.register_gui(os.getenv("PYBLISH_GUI", None))
# class NukeHandler(Logger.logging.Handler):
# '''
# Nuke Handler - emits logs into nuke's script editor.
# warning will emit nuke.warning()
# critical and fatal would popup msg dialog to alert of the error.
# '''
#
# def __init__(self):
# api.Logger.logging.Handler.__init__(self)
# self.set_name("Pype_Nuke_Handler")
#
# def emit(self, record):
# # Formated message:
# msg = self.format(record)
#
# if record.levelname.lower() in [
# # "warning",
# "critical",
# "fatal",
# "error"
# ]:
# nuke.message(msg)
#
# '''Adding Nuke Logging Handler'''
# nuke_handler = NukeHandler()
# if nuke_handler.get_name() \
# not in [handler.get_name()
# for handler in Logger.logging.root.handlers[:]]:
# api.Logger.logging.getLogger().addHandler(nuke_handler)
# api.Logger.logging.getLogger().setLevel(Logger.logging.INFO)
#
# if not self.nLogger:
# self.nLogger = Logger
def reload_config():
"""Attempt to reload pipeline at run-time.
CAUTION: This is primarily for development and debugging purposes.
"""
import importlib
for module in (
"app",
"app.api",
"{}.api".format(AVALON_CONFIG),
"{}.templates".format(AVALON_CONFIG),
"{}.nuke.actions".format(AVALON_CONFIG),
"{}.nuke.templates".format(AVALON_CONFIG),
"{}.nuke.menu".format(AVALON_CONFIG),
"{}.nuke.lib".format(AVALON_CONFIG),
):
log.info("Reloading module: {}...".format(module))
try:
module = importlib.import_module(module)
reload(module)
except Exception as e:
log.warning("Cannot reload module: {}".format(e))
importlib.reload(module)
def install():
# api.set_avalon_workdir()
# reload_config()
import sys
for path in sys.path:
if path.startswith("C:\\Users\\Public"):
sys.path.remove(path)
log.info("Registering Nuke plug-ins..")
pyblish.register_plugin_path(PUBLISH_PATH)
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled)
# Disable all families except for the ones we explicitly want to see
family_states = [
"write",
"review"
]
avalon.data["familiesStateDefault"] = False
avalon.data["familiesStateToggled"] = family_states
menu.install()
# load data from templates
# api.load_data_from_templates()
def uninstall():
log.info("Deregistering Nuke plug-ins..")
pyblish.deregister_plugin_path(PUBLISH_PATH)
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)
pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled)
# reset data from templates
api.reset_data_from_templates()
def on_pyblish_instance_toggled(instance, old_value, new_value):
"""Toggle node passthrough states on instance toggles."""
self.log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
instance, old_value, new_value))
from avalon.nuke import (
viewer_update_and_undo_stop,
add_publish_knob
)
# Whether instances should be passthrough based on new value
with viewer_update_and_undo_stop():
n = instance[0]
try:
n["publish"].value()
except ValueError:
n = add_publish_knob(n)
log.info(" `Publish` knob was added to write node..")
n["publish"].setValue(new_value)

View file

@ -0,0 +1,347 @@
import os
from pyblish import api
# Collection
collect_json_CollectJSON = api.CollectorOrder + 0.1
collect_source_CollectScene = api.CollectorOrder + 0.1
collect_scene_version_CollectSceneVersion = api.CollectorOrder + 0.1
collect_existing_files_CollectExistingFiles = api.CollectorOrder + 0.25
collect_reviews_CollectReviews = api.CollectorOrder + 0.3
collect_sorting_CollectSorting = api.CollectorOrder + 0.49
# Validation
persist_publish_state_PersistPublishState = api.ValidatorOrder
validate_executables_ValidateFFmpeg = api.ValidatorOrder
validate_processing_ValidateProcessing = api.ValidatorOrder
validate_scene_version_ValidateSceneVersion = api.ValidatorOrder
validate_review_ValidateReview = api.ValidatorOrder
# Extraction
extract_scene_save_ExtractSceneSave = api.ExtractorOrder - 0.49
extract_review_ExtractReview = api.ExtractorOrder
extract_review_ExtractReviewTranscode = api.ExtractorOrder + 0.02
extract_review_ExtractReviewTranscodeNukeStudio = (
api.ExtractorOrder + 0.02
)
# Integration
extract_json_ExtractJSON = api.IntegratorOrder + 1
copy_to_clipboard_action_Report = api.IntegratorOrder + 1
# AfterEffects
aftereffects_collect_render_items_CollectRenderItems = api.CollectorOrder
aftereffects_collect_scene_CollectScene = api.CollectorOrder
aftereffects_validate_output_path_ValidateOutputPath = api.ValidatorOrder
aftereffects_validate_scene_path_ValidateScenePath = api.ValidatorOrder
aftereffects_validate_unique_comp_renders_ValidateUniqueCompRenders = (
api.ValidatorOrder
)
aftereffects_append_deadline_data_AppendDeadlineData = api.ExtractorOrder
aftereffects_append_ftrack_audio_AppendFtrackAudio = api.ExtractorOrder
aftereffects_extract_local_ExtractLocal = api.ExtractorOrder
# CelAction
celaction_collect_scene_CollectScene = api.CollectorOrder
celaction_collect_render_CollectRender = api.CollectorOrder + 0.1
celaction_bait_append_ftrack_data_AppendFtrackData = (
api.CollectorOrder + 0.1
)
celaction_bait_append_ftrack_asset_name_AppendFtrackAssetName = (
api.CollectorOrder + 0.1
)
celaction_bait_validate_scene_path_ValidateScenePath = (
api.ValidatorOrder
)
celaction_bait_append_ftrack_data_AppendFtrackAudio = (
api.ExtractorOrder
)
celaction_extract_deadline_ExtractDeadline = api.ExtractorOrder
celaction_extract_render_images_ExtractRenderImages = api.ExtractorOrder
celaction_extract_render_images_ExtractRenderMovie = api.ExtractorOrder + 0.1
celaction_extract_deadline_movie_ExtractDeadlineMovie = (
api.ExtractorOrder + 0.4
)
celaction_bait_integrate_local_render_IntegrateLocal = (
api.IntegratorOrder
)
# Deadline
deadline_OnJobFinished_collect_output_CollectOutput = api.CollectorOrder
deadline_OnJobSubmitted_collect_movie_CollectMovie = api.CollectorOrder
deadline_OnJobSubmitted_collect_render_CollectRender = api.CollectorOrder
deadline_collect_family_CollectFamily = api.CollectorOrder + 0.1
deadline_collect_houdini_parameters_CollectHoudiniParameters = (
deadline_collect_family_CollectFamily + 0.01
)
deadline_collect_maya_parameters_CollectMayaParameters = (
deadline_collect_family_CollectFamily + 0.01
)
deadline_collect_nuke_parameters_CollectNukeParameters = (
deadline_collect_family_CollectFamily + 0.01
)
deadline_collect_houdini_render_CollectHoudiniRender = api.CollectorOrder + 0.4
deadline_validate_houdini_parameters_ValidateHoudiniParameters = (
api.ValidatorOrder
)
deadline_validate_maya_parameters_ValidateMayaParameters = api.ValidatorOrder
deadline_validate_nuke_parameters_ValidateNukeParameters = api.ValidatorOrder
deadline_extract_ftrack_path_ExtractFtrackPath = api.ExtractorOrder
deadline_extract_houdini_ExtractHoudini = api.ExtractorOrder
deadline_extract_job_name_ExtractJobName = api.ExtractorOrder
deadline_extract_maya_ExtractMaya = api.ExtractorOrder
deadline_extract_nuke_ExtractNuke = api.ExtractorOrder
deadline_extract_suspended_ExtractSuspended = api.ExtractorOrder
deadline_integrate_collection_IntegrateCollection = api.IntegratorOrder - 0.1
deadline_bait_integrate_ftrack_thumbnail_IntegrateFtrackThumbnail = (
api.IntegratorOrder
)
deadline_bait_update_ftrack_status_UpdateFtrackStatus = (
api.IntegratorOrder + 0.4
)
# Ftrack
ftrack_collect_nukestudio_CollectNukeStudioEntities = api.CollectorOrder + 0.1
ftrack_collect_nukestudio_CollectNukeStudioProjectData = (
api.CollectorOrder + 0.1
)
ftrack_collect_version_CollectVersion = api.CollectorOrder + 0.2
ftrack_collect_family_CollectFamily = api.CollectorOrder + 0.4
ftrack_validate_assets_ValidateAssets = api.ValidatorOrder
ftrack_validate_nuke_settings_ValidateNukeSettings = api.ValidatorOrder
ftrack_validate_nukestudio_ValidateNukeStudioProjectData = api.ValidatorOrder
ftrack_validate_nukestudio_tasks_ValidateNukeStudioTasks = api.ValidatorOrder
ftrack_extract_components_ExtractCache = api.ExtractorOrder
ftrack_extract_components_ExtractCamera = api.ExtractorOrder
ftrack_extract_components_ExtractGeometry = api.ExtractorOrder
ftrack_extract_components_ExtractGizmo = api.ExtractorOrder
ftrack_extract_components_ExtractImg = api.ExtractorOrder
ftrack_extract_components_ExtractLUT = api.ExtractorOrder
ftrack_extract_components_ExtractMovie = api.ExtractorOrder
ftrack_extract_components_ExtractAudio = api.ExtractorOrder
ftrack_extract_components_ExtractReview = api.ExtractorOrder
ftrack_extract_components_ExtractScene = api.ExtractorOrder
ftrack_extract_entities_ExtractProject = api.ExtractorOrder
ftrack_extract_entities_ExtractEpisode = (
ftrack_extract_entities_ExtractProject + 0.01
)
ftrack_extract_entities_ExtractSequence = (
ftrack_extract_entities_ExtractEpisode + 0.01
)
ftrack_extract_entities_ExtractShot = (
ftrack_extract_entities_ExtractSequence + 0.01
)
ftrack_extract_entities_ExtractLinkAssetbuilds = (
ftrack_extract_entities_ExtractShot + 0.01
)
ftrack_extract_entities_ExtractAssetDataNukeStudio = (
ftrack_extract_entities_ExtractShot + 0.01
)
ftrack_extract_entities_ExtractTasks = (
ftrack_extract_entities_ExtractShot + 0.01
)
ftrack_extract_entities_ExtractCommit = (
ftrack_extract_entities_ExtractTasks + 0.01
)
ftrack_extract_entities_ExtractNukeStudio = (
ftrack_extract_entities_ExtractTasks + 0.01
)
ftrack_extract_thumbnail_ExtractThumbnailImg = api.ExtractorOrder + 0.1
ftrack_extract_review_ExtractReview = api.ExtractorOrder + 0.2
ftrack_extract_components_ExtractComponents = api.ExtractorOrder + 0.4
ftrack_integrate_status_IntegrateStatus = api.IntegratorOrder
ftrack_other_link_source_OtherLinkSource = api.IntegratorOrder + 1
# Hiero
hiero_collect_items_CollectItems = api.CollectorOrder
hiero_validate_names_ValidateNames = api.ValidatorOrder
hiero_extract_transcode_BumpyboxExtractTranscodeH264 = api.ExtractorOrder - 0.1
hiero_extract_transcode_BumpyboxExtractTranscodeJPEG = api.ExtractorOrder - 0.1
hiero_extract_audio_ExtractAudio = api.ExtractorOrder
hiero_extract_ftrack_shot_ExtractFtrackShot = api.ExtractorOrder
hiero_extract_nuke_script_ExtractNukeScript = api.ExtractorOrder
hiero_extract_transcode_ExtractTranscode = api.ExtractorOrder
hiero_extract_ftrack_components_ExtractFtrackComponents = (
api.ExtractorOrder + 0.1
)
hiero_extract_ftrack_tasks_ExtractFtrackTasks = api.ExtractorOrder + 0.1
hiero_extract_ftrack_thumbnail_ExtractFtrackThumbnail = (
api.ExtractorOrder + 0.1
)
# Houdini
houdini_collect_Collect = api.CollectorOrder
houdini_validate_alembic_ValidateAlembic = api.ValidatorOrder
houdini_validate_dynamics_ValidateDynamics = api.ValidatorOrder
houdini_validate_geometry_ValidateGeometry = api.ValidatorOrder
houdini_validate_mantra_camera_ValidateMantraCamera = api.ValidatorOrder
houdini_validate_mantra_settings_ValidateMantraSettings = api.ValidatorOrder
houdini_validate_output_path_ValidateOutputPath = api.ValidatorOrder
houdini_extract_scene_save_ExtractSceneSave = api.ExtractorOrder - 0.1
houdini_extract_local_ExtractLocal = api.ExtractorOrder
# Maya
maya_collect_framerate_CollectFramerate = api.CollectorOrder - 0.5
maya_collect_files_CollectFiles = api.CollectorOrder
maya_collect_render_setups_CollectRenderSetups = api.CollectorOrder
maya_collect_sets_CollectSets = api.CollectorOrder
maya_collect_sets_CollectSetsProcess = maya_collect_sets_CollectSets + 0.01
maya_collect_sets_CollectSetsPublish = maya_collect_sets_CollectSets + 0.01
maya_collect_playblasts_CollectPlayblasts = api.CollectorOrder
maya_collect_playblasts_CollectPlayblastsProcess = (
maya_collect_playblasts_CollectPlayblasts + 0.01
)
maya_collect_playblasts_CollectPlayblastsPublish = (
maya_collect_playblasts_CollectPlayblasts + 0.01
)
maya_modeling_validate_intermediate_shapes_ValidateIntermediateShapes = (
api.ValidatorOrder
)
maya_modeling_validate_points_ValidatePoints = (
api.ValidatorOrder
)
maya_modeling_validate_hierarchy_ValidateHierarchy = (
api.ValidatorOrder
)
maya_modeling_validate_shape_name_ValidateShapeName = (
api.ValidatorOrder
)
maya_modeling_validate_transforms_ValidateTransforms = (
api.ValidatorOrder
)
maya_modeling_validate_display_layer_ValidateDisplayLayer = (
api.ValidatorOrder
)
maya_modeling_validate_smooth_display_ValidateSmoothDisplay = (
api.ValidatorOrder
)
maya_validate_arnold_setings_ValidateArnoldSettings = api.ValidatorOrder
maya_validate_name_ValidateName = api.ValidatorOrder
maya_validate_render_camera_ValidateRenderCamera = api.ValidatorOrder
maya_validate_render_layer_settings_ValidateRenderLayerSettings = (
api.ValidatorOrder
)
maya_validate_vray_settings_ValidateVraySettings = api.ValidatorOrder
maya_validate_scene_modified_ValidateSceneModified = api.ExtractorOrder - 0.49
maya_extract_alembic_ExtractAlembic = api.ExtractorOrder
maya_extract_formats_ExtractFormats = api.ExtractorOrder
maya_lookdev_extract_construction_history_ExtractConstructionHistory = (
maya_extract_formats_ExtractFormats - 0.01
)
maya_modeling_extract_construction_history_ExtractConstructionHistory = (
maya_extract_formats_ExtractFormats - 0.01
)
maya_rigging_extract_disconnect_animation_ExtractDisconnectAnimation = (
maya_extract_formats_ExtractFormats - 0.01
)
maya_extract_playblast_ExtractPlayblast = api.ExtractorOrder
maya_extract_render_layer_ExtractRenderLayer = api.ExtractorOrder
# Nuke
nuke_collect_selection_CollectSelection = api.CollectorOrder - 0.1
nuke_collect_backdrops_CollectBackdrops = api.CollectorOrder + 0.1
nuke_collect_framerate_CollectFramerate = api.CollectorOrder
nuke_collect_reads_CollectReads = api.CollectorOrder
nuke_collect_write_geo_CollectWriteGeo = api.CollectorOrder
nuke_collect_writes_CollectWrites = api.CollectorOrder
nuke_collect_write_geo_CollectCacheProcess = api.CollectorOrder + 0.01
nuke_collect_write_geo_CollectCachePublish = api.CollectorOrder + 0.01
nuke_collect_writes_CollectWritesProcess = api.CollectorOrder + 0.01
nuke_collect_writes_CollectWritesPublish = api.CollectorOrder + 0.01
nuke_collect_groups_CollectGroups = api.CollectorOrder + 0.1
nuke_validate_datatype_ValidateDatatype = api.ValidatorOrder
nuke_validate_frame_rate_ValidateFrameRate = api.ValidatorOrder
nuke_validate_group_node_ValidateGroupNode = api.ValidatorOrder
nuke_validate_proxy_mode_ValidateProxyMode = api.ValidatorOrder
nuke_validate_read_node_ValidateReadNode = api.ValidatorOrder
nuke_validate_write_node_ValidateWriteNode = api.ValidatorOrder
nuke_validate_write_node_ValidateReviewNodeDuplicate = api.ValidatorOrder
nuke_validate_writegeo_node_ValidateWriteGeoNode = api.ValidatorOrder
nuke_extract_output_directory_ExtractOutputDirectory = api.ExtractorOrder - 0.1
nuke_extract_backdrop_ExtractBackdrop = api.ExtractorOrder
nuke_extract_group_ExtractGroup = api.ExtractorOrder
nuke_extract_write_Extract = api.ExtractorOrder
nuke_extract_write_ExtractCache = api.ExtractorOrder
nuke_extract_write_ExtractCamera = api.ExtractorOrder
nuke_extract_write_ExtractGeometry = api.ExtractorOrder
nuke_extract_write_ExtractWrite = api.ExtractorOrder
nuke_extract_review_ExtractReview = api.ExtractorOrder + 0.01
# NukeStudio
nukestudio_collect_CollectFramerate = api.CollectorOrder
nukestudio_collect_CollectTrackItems = api.CollectorOrder
nukestudio_collect_CollectTasks = api.CollectorOrder + 0.01
nukestudio_validate_names_ValidateNames = api.ValidatorOrder
nukestudio_validate_names_ValidateNamesFtrack = api.ValidatorOrder
nukestudio_validate_projectroot_ValidateProjectRoot = api.ValidatorOrder
nukestudio_validate_resolved_paths_ValidateResolvedPaths = api.ValidatorOrder
nukestudio_validate_task_ValidateImageSequence = api.ValidatorOrder
nukestudio_validate_task_ValidateOutputRange = api.ValidatorOrder
nukestudio_validate_track_item_ValidateTrackItem = api.ValidatorOrder
nukestudio_validate_track_item_ValidateTrackItemFtrack = api.ValidatorOrder
nukestudio_validate_viewer_lut_ValidateViewerLut = api.ValidatorOrder
nukestudio_extract_review_ExtractReview = api.ExtractorOrder
nukestudio_extract_tasks_ExtractTasks = api.ExtractorOrder
# RoyalRender
royalrender_collect_CollectMayaSets = api.CollectorOrder + 0.1
royalrender_collect_CollectNukeWrites = api.CollectorOrder + 0.1
royalrender_extract_maya_ExtractMaya = api.ExtractorOrder
royalrender_extract_maya_alembic_ExtractMovie = api.ExtractorOrder
royalrender_extract_nuke_ExtractNuke = api.ExtractorOrder
# TVPaint
tvpaint_extract_deadline_ExtractDeadline = api.ExtractorOrder - 0.1
tvpaint_collect_scene_arg_CollectSceneArg = api.CollectorOrder - 0.05
tvpaint_collect_render_CollectRender = api.CollectorOrder + 0.1
tvpaint_validate_scene_path_ValidateScenePath = api.ValidatorOrder
tvpaint_extract_hobsoft_scene_ExtractHobsoftScene = api.ExtractorOrder
def get_order(module, name):
path = get_variable_name(module, name)
if path not in globals().keys():
raise KeyError("\"{0}\" could not be found in inventory.".format(path))
return globals()[path]
def get_variable_name(module, name):
plugins_directory = os.path.abspath(
os.path.join(__file__, "..", "plugins")
)
module = os.path.relpath(module, plugins_directory)
path = "{0}{1}".format(module, name)
path = path.replace(".py", "_")
path = path.replace(os.sep, "_")
return path

242
pype/nukestudio/lib.py Normal file
View file

@ -0,0 +1,242 @@
# Standard library
import os
import sys
# Pyblish libraries
import pyblish.api
# Host libraries
import hiero
from PySide2 import (QtWidgets, QtGui)
# Local libraries
import plugins
cached_process = None
self = sys.modules[__name__]
self._has_been_setup = False
self._has_menu = False
self._registered_gui = None
def setup(console=False, port=None, menu=True):
"""Setup integration
Registers Pyblish for Hiero plug-ins and appends an item to the File-menu
Arguments:
console (bool): Display console with GUI
port (int, optional): Port from which to start looking for an
available port to connect with Pyblish QML, default
provided by Pyblish Integration.
menu (bool, optional): Display file menu in Hiero.
"""
if self._has_been_setup:
teardown()
# register bumpybox plugins
pyblish.api.register_plugin_path(r"C:\Users\hubert\CODE\github\pyblish-bumpybox\pyblish_bumpybox\plugins\nukestudio")
register_plugins()
register_host()
add_submission()
if menu:
add_to_filemenu()
self._has_menu = True
self._has_been_setup = True
print("pyblish: Loaded successfully.")
def show():
"""Try showing the most desirable GUI
This function cycles through the currently registered
graphical user interfaces, if any, and presents it to
the user.
"""
return (_discover_gui() or _show_no_gui)()
def _discover_gui():
"""Return the most desirable of the currently registered GUIs"""
# Prefer last registered
guis = reversed(pyblish.api.registered_guis())
for gui in list(guis) + ["pyblish_lite"]:
try:
gui = __import__(gui).show
except (ImportError, AttributeError):
continue
else:
return gui
def teardown():
"""Remove integration"""
if not self._has_been_setup:
return
deregister_plugins()
deregister_host()
if self._has_menu:
remove_from_filemenu()
self._has_menu = False
self._has_been_setup = False
print("pyblish: Integration torn down successfully")
def remove_from_filemenu():
raise NotImplementedError("Implement me please.")
def deregister_plugins():
# De-register accompanying plugins
plugin_path = os.path.dirname(plugins.__file__)
pyblish.api.deregister_plugin_path(plugin_path)
print("pyblish: Deregistered %s" % plugin_path)
def register_host():
"""Register supported hosts"""
pyblish.api.register_host("nukestudio")
def deregister_host():
"""De-register supported hosts"""
pyblish.api.deregister_host("nukestudio")
def register_plugins():
# Register accompanying plugins
plugin_path = os.path.dirname(plugins.__file__)
pyblish.api.register_plugin_path(plugin_path)
def add_to_filemenu():
PublishAction()
class PyblishSubmission(hiero.exporters.FnSubmission.Submission):
def __init__(self):
hiero.exporters.FnSubmission.Submission.__init__(self)
def addToQueue(self):
# Add submission to Hiero module for retrieval in plugins.
hiero.submission = self
show()
def add_submission():
registry = hiero.core.taskRegistry
registry.addSubmission("Pyblish", PyblishSubmission)
class PublishAction(QtWidgets.QAction):
def __init__(self):
QtWidgets.QAction.__init__(self, "Publish", None)
self.triggered.connect(self.publish)
for interest in ["kShowContextMenu/kTimeline",
"kShowContextMenukBin",
"kShowContextMenu/kSpreadsheet"]:
hiero.core.events.registerInterest(interest, self.eventHandler)
self.setShortcut("Ctrl+Alt+P")
def publish(self):
import pyblish_nukestudio
# Removing "submission" attribute from hiero module, to prevent tasks
# from getting picked up when not using the "Export" dialog.
if hasattr(hiero, "submission"):
del hiero.submission
pyblish_nukestudio.show()
def eventHandler(self, event):
# Add the Menu to the right-click menu
event.menu.addAction(self)
def _show_no_gui():
"""Popup with information about how to register a new GUI
In the event of no GUI being registered or available,
this information dialog will appear to guide the user
through how to get set up with one.
"""
messagebox = QtWidgets.QMessageBox()
messagebox.setIcon(messagebox.Warning)
messagebox.setWindowIcon(QtGui.QIcon(os.path.join(
os.path.dirname(pyblish.__file__),
"icons",
"logo-32x32.svg"))
)
spacer = QtWidgets.QWidget()
spacer.setMinimumSize(400, 0)
spacer.setSizePolicy(QtWidgets.QSizePolicy.Minimum,
QtWidgets.QSizePolicy.Expanding)
layout = messagebox.layout()
layout.addWidget(spacer, layout.rowCount(), 0, 1, layout.columnCount())
messagebox.setWindowTitle("Uh oh")
messagebox.setText("No registered GUI found.")
if not pyblish.api.registered_guis():
messagebox.setInformativeText(
"In order to show you a GUI, one must first be registered. "
"Press \"Show details...\" below for information on how to "
"do that.")
messagebox.setDetailedText(
"Pyblish supports one or more graphical user interfaces "
"to be registered at once, the next acting as a fallback to "
"the previous."
"\n"
"\n"
"For example, to use Pyblish Lite, first install it:"
"\n"
"\n"
"$ pip install pyblish-lite"
"\n"
"\n"
"Then register it, like so:"
"\n"
"\n"
">>> import pyblish.api\n"
">>> pyblish.api.register_gui(\"pyblish_lite\")"
"\n"
"\n"
"The next time you try running this, Lite will appear."
"\n"
"See http://api.pyblish.com/register_gui.html for "
"more information.")
else:
messagebox.setInformativeText(
"None of the registered graphical user interfaces "
"could be found."
"\n"
"\n"
"Press \"Show details\" for more information.")
messagebox.setDetailedText(
"These interfaces are currently registered."
"\n"
"%s" % "\n".join(pyblish.api.registered_guis()))
messagebox.setStandardButtons(messagebox.Ok)
messagebox.exec_()

7
pype/nukestudio/menu.py Normal file
View file

@ -0,0 +1,7 @@
import nuke
from avalon.api import Session
from pype.nuke import lib
def install():

View file

@ -0,0 +1,188 @@
from pyblish import api
from pyblish_bumpybox import inventory
class CollectFramerate(api.ContextPlugin):
"""Collect framerate from selected sequence."""
order = inventory.get_order(__file__, "CollectFramerate")
label = "Framerate"
hosts = ["nukestudio"]
def process(self, context):
for item in context.data.get("selection", []):
context.data["framerate"] = item.sequence().framerate().toFloat()
return
class CollectTrackItems(api.ContextPlugin):
"""Collect all tasks from submission."""
order = inventory.get_order(__file__, "CollectTrackItems")
label = "Track Items"
hosts = ["nukestudio"]
def process(self, context):
import os
submission = context.data.get("submission", None)
data = {}
# Set handles
handles = 0
if submission:
for task in submission.getLeafTasks():
if task._cutHandles:
handles = task._cutHandles
# Skip audio track items
media_type = "core.Hiero.Python.TrackItem.MediaType.kAudio"
if str(task._item.mediaType()) == media_type:
continue
item = task._item
if item.name() not in data:
data[item.name()] = {"item": item, "tasks": [task]}
else:
data[item.name()]["tasks"].append(task)
data[item.name()]["startFrame"] = task.outputRange()[0]
data[item.name()]["endFrame"] = task.outputRange()[1]
else:
for item in context.data.get("selection", []):
# Skip audio track items
# Try/Except is to handle items types, like EffectTrackItem
try:
media_type = "core.Hiero.Python.TrackItem.MediaType.kVideo"
if str(item.mediaType()) != media_type:
continue
except:
continue
data[item.name()] = {
"item": item,
"tasks": [],
"startFrame": item.timelineIn(),
"endFrame": item.timelineOut()
}
for key, value in data.iteritems():
context.create_instance(
name=key,
item=value["item"],
family="trackItem",
tasks=value["tasks"],
startFrame=value["startFrame"] + handles,
endFrame=value["endFrame"] - handles,
handles=handles
)
context.create_instance(
name=key + "_review",
item=value["item"],
family="review",
families=["output"],
handles=handles,
output_path=os.path.abspath(
os.path.join(
context.data["activeProject"].path(),
"..",
"workspace",
key + ".mov"
)
)
)
class CollectTasks(api.ContextPlugin):
"""Collect all tasks from submission."""
order = inventory.get_order(__file__, "CollectTasks")
label = "Tasks"
hosts = ["nukestudio"]
def process(self, context):
import os
import re
import hiero.exporters as he
import clique
for parent in context:
if "trackItem" != parent.data["family"]:
continue
for task in parent.data["tasks"]:
asset_type = None
hiero_cls = he.FnSymLinkExporter.SymLinkExporter
if isinstance(task, hiero_cls):
asset_type = "img"
movie_formats = [".mov", ".R3D"]
ext = os.path.splitext(task.resolvedExportPath())[1]
if ext in movie_formats:
asset_type = "mov"
hiero_cls = he.FnTranscodeExporter.TranscodeExporter
if isinstance(task, hiero_cls):
asset_type = "img"
if task.resolvedExportPath().endswith(".mov"):
asset_type = "mov"
hiero_cls = he.FnNukeShotExporter.NukeShotExporter
if isinstance(task, hiero_cls):
asset_type = "scene"
hiero_cls = he.FnAudioExportTask.AudioExportTask
if isinstance(task, hiero_cls):
asset_type = "audio"
# Skip all non supported export types
if not asset_type:
continue
resolved_path = task.resolvedExportPath()
# Formatting the basename to not include frame padding or
# extension.
name = os.path.splitext(os.path.basename(resolved_path))[0]
name = name.replace(".", "")
name = name.replace("#", "")
name = re.sub(r"%.*d", "", name)
instance = context.create_instance(name=name, parent=parent)
instance.data["task"] = task
instance.data["item"] = parent.data["item"]
instance.data["family"] = "trackItem.task"
instance.data["families"] = [asset_type, "local", "task"]
label = "{1}/{0} - {2} - local".format(
name, parent, asset_type
)
instance.data["label"] = label
instance.data["handles"] = parent.data["handles"]
# Add collection or output
if asset_type == "img":
collection = None
if "#" in resolved_path:
head = resolved_path.split("#")[0]
padding = resolved_path.count("#")
tail = resolved_path.split("#")[-1]
collection = clique.Collection(
head=head, padding=padding, tail=tail
)
if "%" in resolved_path:
collection = clique.parse(
resolved_path, pattern="{head}{padding}{tail}"
)
instance.data["collection"] = collection
else:
instance.data["output_path"] = resolved_path

View file

@ -0,0 +1,13 @@
import pyblish.api
class CollectActiveProject(pyblish.api.ContextPlugin):
"""Inject the active project into context"""
order = pyblish.api.CollectorOrder - 0.2
def process(self, context):
import hiero
context.data["activeProject"] = hiero.ui.activeSequence().project()
self.log.info("activeProject: {}".format(context.data["activeProject"]))

View file

@ -0,0 +1,27 @@
import pyblish.api
class CollectProjectColorspace(pyblish.api.ContextPlugin):
"""get active project color settings"""
order = pyblish.api.CollectorOrder + 0.1
label = "Project's color settings"
def process(self, context):
import hiero
project = context.data["activeProject"]
colorspace = {}
colorspace["useOCIOEnvironmentOverride"] = project.useOCIOEnvironmentOverride()
colorspace["lutSetting16Bit"] = project.lutSetting16Bit()
colorspace["lutSetting8Bit"] = project.lutSetting8Bit()
colorspace["lutSettingFloat"] = project.lutSettingFloat()
colorspace["lutSettingLog"] = project.lutSettingLog()
colorspace["lutSettingViewer"] = project.lutSettingViewer()
colorspace["lutSettingWorkingSpace"] = project.lutSettingWorkingSpace()
colorspace["lutUseOCIOForExport"] = project.lutUseOCIOForExport()
colorspace["ocioConfigName"] = project.ocioConfigName()
colorspace["ocioConfigPath"] = project.ocioConfigPath()
context.data["colorspace"] = colorspace
self.log.info("context.data[colorspace]: {}".format(context.data["colorspace"]))

View file

@ -0,0 +1,14 @@
import pyblish.api
class CollectCurrentFile(pyblish.api.ContextPlugin):
"""Inject the current working file into context"""
order = pyblish.api.CollectorOrder
def process(self, context):
"""Todo, inject the current working file"""
project = context.data('activeProject')
context.set_data('currentFile', value=project.path())
self.log.info("currentFile: {}".format(context.data["currentFile"]))

View file

@ -0,0 +1,13 @@
import pyblish.api
class CollectHost(pyblish.api.ContextPlugin):
"""Inject the host into context"""
order = pyblish.api.CollectorOrder
def process(self, context):
import pyblish.api
context.set_data("host", pyblish.api.current_host())
self.log.info("current host: {}".format(pyblish.api.current_host()))

View file

@ -0,0 +1,12 @@
import pyblish.api
class CollectHostVersion(pyblish.api.ContextPlugin):
"""Inject the hosts version into context"""
order = pyblish.api.CollectorOrder
def process(self, context):
import nuke
context.set_data('hostVersion', value=nuke.NUKE_VERSION_STRING)

View file

@ -0,0 +1,15 @@
import pyblish.api
import hiero
class CollectSelection(pyblish.api.ContextPlugin):
"""Inject the selection in the context."""
order = pyblish.api.CollectorOrder - 0.1
label = "Selection"
def process(self, context):
selection = getattr(hiero, "selection")
self.log.debug("selection: {}".format(selection))
context.data["selection"] = hiero.selection

View file

@ -0,0 +1,14 @@
import pyblish.api
class CollectSubmission(pyblish.api.ContextPlugin):
"""Collect submisson children."""
order = pyblish.api.CollectorOrder - 0.1
def process(self, context):
import hiero
if hasattr(hiero, "submission"):
context.data["submission"] = hiero.submission
self.log.debug("__ submission: {}".format(context.data["submission"]))

View file

@ -0,0 +1,102 @@
from pyblish import api
from pyblish_bumpybox import inventory
class ExtractReview(api.InstancePlugin):
"""Extracts movie for review"""
order = inventory.get_order(__file__, "ExtractReview")
label = "NukeStudio Review"
optional = True
hosts = ["nukestudio"]
families = ["review"]
def process(self, instance):
import os
import time
import hiero.core
from hiero.exporters.FnExportUtil import writeSequenceAudioWithHandles
nukeWriter = hiero.core.nuke.ScriptWriter()
item = instance.data["item"]
handles = instance.data["handles"]
sequence = item.parent().parent()
output_path = os.path.abspath(
os.path.join(
instance.context.data["currentFile"], "..", "workspace"
)
)
# Generate audio
audio_file = os.path.join(
output_path, "{0}.wav".format(instance.data["name"])
)
writeSequenceAudioWithHandles(
audio_file,
sequence,
item.timelineIn(),
item.timelineOut(),
handles,
handles
)
# Generate Nuke script
root_node = hiero.core.nuke.RootNode(
item.timelineIn() - handles,
item.timelineOut() + handles,
fps=sequence.framerate()
)
root_node.addProjectSettings(instance.context.data["colorspace"])
nukeWriter.addNode(root_node)
item.addToNukeScript(
script=nukeWriter,
includeRetimes=True,
retimeMethod="Frame",
startHandle=handles,
endHandle=handles
)
movie_path = os.path.join(
output_path, "{0}.mov".format(instance.data["name"])
)
write_node = hiero.core.nuke.WriteNode(movie_path.replace("\\", "/"))
self.log.info("__ write_node: {0}".format(write_node))
write_node.setKnob("file_type", "mov")
write_node.setKnob("colorspace", instance.context.data["colorspace"]["lutSettingFloat"])
write_node.setKnob("meta_codec", "ap4h")
write_node.setKnob("mov64_codec", "ap4h")
write_node.setKnob("mov64_bitrate", 400000)
write_node.setKnob("mov64_bitrate_tolerance", 40000000)
write_node.setKnob("mov64_quality_min", 2)
write_node.setKnob("mov64_quality_max", 31)
write_node.setKnob("mov64_gop_size", 12)
write_node.setKnob("mov64_b_frames", 0)
write_node.setKnob("raw", True )
write_node.setKnob("mov64_audiofile", audio_file.replace("\\", "/"))
write_node.setKnob("mov32_fps", sequence.framerate())
nukeWriter.addNode(write_node)
nukescript_path = movie_path.replace(".mov", ".nk")
nukeWriter.writeToDisk(nukescript_path)
process = hiero.core.nuke.executeNukeScript(
nukescript_path,
open(movie_path.replace(".mov", ".log"), "w")
)
while process.poll() is None:
time.sleep(0.5)
assert os.path.exists(movie_path), "Creating review failed."
instance.data["output_path"] = movie_path
instance.data["review_family"] = "mov"

View file

@ -0,0 +1,125 @@
from pyblish import api
from pyblish_bumpybox import inventory
class ExtractTasks(api.InstancePlugin):
"""Extract tasks."""
order = inventory.get_order(__file__, "ExtractTasks")
label = "Tasks"
hosts = ["nukestudio"]
families = ["trackItem.task"]
optional = True
def filelink(self, src, dst):
import filecmp
import os
import shutil
import filelink
# Compare files to check whether they are the same.
if os.path.exists(dst) and filecmp.cmp(src, dst):
return
# Remove existing destination file.
if os.path.exists(dst):
os.remove(dst)
try:
filelink.create(src, dst, filelink.HARDLINK)
self.log.debug("Linking: \"{0}\" to \"{1}\"".format(src, dst))
except WindowsError as e:
if e.winerror == 17:
self.log.warning(
"File linking failed due to: \"{0}\". "
"Resorting to copying instead.".format(e)
)
shutil.copy(src, dst)
else:
raise e
def process(self, instance):
import time
import os
import hiero.core.nuke as nuke
import hiero.exporters as he
import clique
task = instance.data["task"]
hiero_cls = he.FnSymLinkExporter.SymLinkExporter
if isinstance(task, hiero_cls):
src = os.path.join(
task.filepath(),
task.fileName()
)
# Filelink each image file
if "img" in instance.data["families"]:
collection = clique.parse(src + " []")
for f in os.listdir(os.path.dirname(src)):
f = os.path.join(os.path.dirname(src), f)
frame_offset = task.outputRange()[0] - task.inputRange()[0]
input_range = (
int(task.inputRange()[0]), int(task.inputRange()[1]) + 1
)
for index in range(*input_range):
dst = task.resolvedExportPath() % (index + frame_offset)
self.filelink(src % index, dst)
# Filelink movie file
if "mov" in instance.data["families"]:
dst = task.resolvedExportPath()
self.filelink(src, dst)
hiero_cls = he.FnTranscodeExporter.TranscodeExporter
if isinstance(task, hiero_cls):
task.startTask()
while task.taskStep():
time.sleep(1)
script_path = task._scriptfile
log_path = script_path.replace(".nk", ".log")
log_file = open(log_path, "w")
process = nuke.executeNukeScript(script_path, log_file, True)
self.poll(process)
log_file.close()
if not task._preset.properties()["keepNukeScript"]:
os.remove(script_path)
os.remove(log_path)
hiero_cls = he.FnNukeShotExporter.NukeShotExporter
if isinstance(task, hiero_cls):
task.startTask()
while task.taskStep():
time.sleep(1)
hiero_cls = he.FnAudioExportTask.AudioExportTask
if isinstance(task, hiero_cls):
task.startTask()
while task.taskStep():
time.sleep(1)
# Fill collection with output
if "img" in instance.data["families"]:
collection = instance.data["collection"]
path = os.path.dirname(collection.format())
for f in os.listdir(path):
file_path = os.path.join(path, f).replace("\\", "/")
if collection.match(file_path):
collection.add(file_path)
def poll(self, process):
import time
returnCode = process.poll()
# if the return code hasn't been set, Nuke is still running
if returnCode is None:
time.sleep(1)
self.poll(process)

View file

@ -0,0 +1,43 @@
from pyblish import api
from pyblish_bumpybox import inventory
class ValidateNames(api.InstancePlugin):
"""Validate sequence, video track and track item names.
When creating output directories with the name of an item, ending with a
whitespace will fail the extraction.
Exact matching to optimize processing.
"""
order = inventory.get_order(__file__, "ValidateNames")
families = ["trackItem"]
match = api.Exact
label = "Names"
hosts = ["nukestudio"]
def process(self, instance):
item = instance.data["item"]
msg = "Track item \"{0}\" ends with a whitespace."
assert not item.name().endswith(" "), msg.format(item.name())
msg = "Video track \"{0}\" ends with a whitespace."
msg = msg.format(item.parent().name())
assert not item.parent().name().endswith(" "), msg
msg = "Sequence \"{0}\" ends with a whitespace."
msg = msg.format(item.parent().parent().name())
assert not item.parent().parent().name().endswith(" "), msg
class ValidateNamesFtrack(ValidateNames):
"""Validate sequence, video track and track item names.
Because we are matching the families exactly, we need this plugin to
accommodate for the ftrack family addition.
"""
order = inventory.get_order(__file__, "ValidateNamesFtrack")
families = ["trackItem", "ftrack"]

View file

@ -0,0 +1,53 @@
from pyblish import api
from pyblish_bumpybox import inventory
class RepairProjectRoot(api.Action):
label = "Repair"
icon = "wrench"
on = "failed"
def process(self, context, plugin):
import os
workspace = os.path.join(
os.path.dirname(context.data["currentFile"]),
"workspace"
).replace("\\", "/")
if not os.path.exists(workspace):
os.makedirs(workspace)
context.data["activeProject"].setProjectRoot(workspace)
# Need to manually fix the tasks "_projectRoot" attribute, because
# setting the project root is not enough.
submission = context.data.get("submission", None)
if submission:
for task in submission.getLeafTasks():
task._projectRoot = workspace
class ValidateProjectRoot(api.ContextPlugin):
"""Validate the project root to the workspace directory."""
order = inventory.get_order(__file__, "ValidateProjectRoot")
label = "Project Root"
hosts = ["nukestudio"]
actions = [RepairProjectRoot]
def process(self, context):
import os
workspace = os.path.join(
os.path.dirname(context.data["currentFile"]),
"workspace"
).replace("\\", "/")
project_root = context.data["activeProject"].projectRoot()
failure_message = (
'The project root needs to be "{0}", its currently: "{1}"'
).format(workspace, project_root)
assert project_root == workspace, failure_message

View file

@ -0,0 +1,29 @@
from pyblish import api
from pyblish_bumpybox import inventory
class ValidateResolvedPaths(api.ContextPlugin):
"""Validate there are no overlapping resolved paths."""
order = inventory.get_order(__file__, "ValidateResolvedPaths")
label = "Resolved Paths"
hosts = ["nukestudio"]
def process(self, context):
import os
import collections
paths = []
for instance in context:
if "trackItem.task" == instance.data["family"]:
paths.append(
os.path.abspath(instance.data["task"].resolvedExportPath())
)
duplicates = []
for item, count in collections.Counter(paths).items():
if count > 1:
duplicates.append(item)
msg = "Duplicate output paths found: {0}".format(duplicates)
assert not duplicates, msg

View file

@ -0,0 +1,58 @@
from pyblish import api
from pyblish_bumpybox import inventory
class ValidateOutputRange(api.InstancePlugin):
"""Validate the output range of the task.
This compares the output range and clip associated with the task, so see
whether there is a difference. This difference indicates that the user has
selected to export the clip length for the task which is very uncommon to
do.
"""
order = inventory.get_order(__file__, "ValidateOutputRange")
families = ["trackItem.task"]
label = "Output Range"
hosts = ["nukestudio"]
optional = True
def process(self, instance):
task = instance.data["task"]
item = instance.data["parent"]
output_range = task.outputRange()
first_frame = int(item.data["item"].source().sourceIn())
last_frame = int(item.data["item"].source().sourceOut())
clip_duration = last_frame - first_frame + 1
difference = clip_duration - output_range[1]
failure_message = (
'Looks like you are rendering the clip length for the task '
'rather than the cut length. If this is intended, just uncheck '
'this validator after resetting, else adjust the export range in '
'the "Handles" section of the export dialog.'
)
assert difference, failure_message
class ValidateImageSequence(api.InstancePlugin):
"""Validate image sequence output path is setup correctly."""
order = inventory.get_order(__file__, "ValidateImageSequence")
families = ["trackItem.task", "img"]
match = api.Subset
label = "Image Sequence"
hosts = ["nukestudio"]
optional = True
def process(self, instance):
resolved_path = instance.data["task"].resolvedExportPath()
msg = (
"Image sequence output is missing a padding. Please add \"####\" "
"or \"%04d\" to the output templates."
)
assert "#" in resolved_path or "%" in resolved_path, msg

View file

@ -0,0 +1,59 @@
from pyblish import api
from pyblish_bumpybox import inventory
class ValidateTrackItem(api.InstancePlugin):
"""Validate the track item to the sequence.
Exact matching to optimize processing.
"""
order = inventory.get_order(__file__, "ValidateTrackItem")
families = ["trackItem"]
match = api.Exact
label = "Track Item"
hosts = ["nukestudio"]
optional = True
def process(self, instance):
item = instance.data["item"]
self.log.info("__ item: {}".format(item))
media_source = item.source().mediaSource()
self.log.info("__ media_source: {}".format(media_source))
msg = (
'A setting does not match between track item "{0}" and sequence '
'"{1}".'.format(item.name(), item.sequence().name()) +
'\n\nSetting: "{0}".''\n\nTrack item: "{1}".\n\nSequence: "{2}".'
)
# Validate format settings.
fmt = item.sequence().format()
assert fmt.width() == media_source.width(), msg.format(
"width", fmt.width(), media_source.width()
)
assert fmt.height() == media_source.height(), msg.format(
"height", fmt.height(), media_source.height()
)
assert fmt.pixelAspect() == media_source.pixelAspect(), msg.format(
"pixelAspect", fmt.pixelAspect(), media_source.pixelAspect()
)
# Validate framerate setting.
sequence = item.sequence()
source_framerate = media_source.metadata()["foundry.source.framerate"]
assert sequence.framerate() == source_framerate, msg.format(
"framerate", source_framerate, sequence.framerate()
)
#
# class ValidateTrackItemFtrack(ValidateTrackItem):
# """Validate the track item to the sequence.
#
# Because we are matching the families exactly, we need this plugin to
# accommodate for the ftrack family addition.
# """
#
# order = inventory.get_order(__file__, "ValidateTrackItemFtrack")
# families = ["trackItem", "ftrack"]

View file

@ -0,0 +1,22 @@
from pyblish import api
from pyblish_bumpybox import inventory
class ValidateViewerLut(api.ContextPlugin):
"""Validate viewer lut in NukeStudio is the same as in Nuke."""
order = inventory.get_order(__file__, "ValidateViewerLut")
label = "Viewer LUT"
hosts = ["nukestudio"]
optional = True
def process(self, context):
import nuke
import hiero
# nuke_lut = nuke.ViewerProcess.node()["current"].value()
nukestudio_lut = context.data["activeProject"].lutSettingViewer()
self.log.info("__ nukestudio_lut: {}".format(nukestudio_lut))
msg = "Viewer LUT can only be RGB"
assert "RGB" in nukestudio_lut, msg

View file

@ -0,0 +1,14 @@
import traceback
try:
__import__("pype.nukestudio")
__import__("pyblish")
except ImportError as e:
print traceback.format_exc()
print("pyblish: Could not load integration: %s " % e)
else:
# Setup integration
import pype.nukestudio.lib
pype.nukestudio.lib.setup()

View file

@ -0,0 +1,9 @@
"""Puts the selection project into 'hiero.selection'"""
import hiero
def selectionChanged(event):
hiero.selection = event.sender.selection()
hiero.core.events.registerInterest('kSelectionChanged', selectionChanged)