From 7a0d446c1d8c842796ab6fdafd27a48d16358b48 Mon Sep 17 00:00:00 2001 From: antirotor Date: Mon, 13 May 2019 19:06:30 +0200 Subject: [PATCH 01/48] new(doc): Added basic support for generating documentation with Sphinx --- .gitignore | 8 + README.md | 6 +- docs/Makefile | 19 ++ docs/make.bat | 35 +++ docs/source/conf.py | 222 ++++++++++++++++++ docs/source/index.rst | 18 ++ docs/source/modules.rst | 7 + docs/source/pype.aport.rst | 20 ++ docs/source/pype.avalon_apps.rst | 20 ++ docs/source/pype.clockify.rst | 36 +++ docs/source/pype.ftrack.ftrack_server.rst | 36 +++ docs/source/pype.ftrack.lib.rst | 52 ++++ docs/source/pype.ftrack.rst | 52 ++++ docs/source/pype.fusion.rst | 27 +++ docs/source/pype.fusion.scripts.rst | 28 +++ docs/source/pype.houdini.rst | 20 ++ docs/source/pype.maya.rst | 52 ++++ docs/source/pype.nuke.rst | 44 ++++ docs/source/pype.premiere.rst | 20 ++ docs/source/pype.rst | 88 +++++++ docs/source/pype.scripts.rst | 28 +++ docs/source/pype.services.idle_manager.rst | 20 ++ docs/source/pype.services.rst | 17 ++ docs/source/pype.services.statics_server.rst | 20 ++ docs/source/pype.services.timers_manager.rst | 28 +++ .../pype.standalonepublish.resources.rst | 8 + docs/source/pype.standalonepublish.rst | 44 ++++ .../source/pype.standalonepublish.widgets.rst | 156 ++++++++++++ docs/source/pype.tools.assetcreator.rst | 36 +++ docs/source/pype.tools.rst | 15 ++ docs/source/pype.widgets.rst | 36 +++ docs/source/readme.rst | 1 + make_docs.bat | 46 ++++ 33 files changed, 1264 insertions(+), 1 deletion(-) create mode 100644 docs/Makefile create mode 100644 docs/make.bat create mode 100644 docs/source/conf.py create mode 100644 docs/source/index.rst create mode 100644 docs/source/modules.rst create mode 100644 docs/source/pype.aport.rst create mode 100644 docs/source/pype.avalon_apps.rst create mode 100644 docs/source/pype.clockify.rst create mode 100644 docs/source/pype.ftrack.ftrack_server.rst create mode 100644 docs/source/pype.ftrack.lib.rst create mode 100644 docs/source/pype.ftrack.rst create mode 100644 docs/source/pype.fusion.rst create mode 100644 docs/source/pype.fusion.scripts.rst create mode 100644 docs/source/pype.houdini.rst create mode 100644 docs/source/pype.maya.rst create mode 100644 docs/source/pype.nuke.rst create mode 100644 docs/source/pype.premiere.rst create mode 100644 docs/source/pype.rst create mode 100644 docs/source/pype.scripts.rst create mode 100644 docs/source/pype.services.idle_manager.rst create mode 100644 docs/source/pype.services.rst create mode 100644 docs/source/pype.services.statics_server.rst create mode 100644 docs/source/pype.services.timers_manager.rst create mode 100644 docs/source/pype.standalonepublish.resources.rst create mode 100644 docs/source/pype.standalonepublish.rst create mode 100644 docs/source/pype.standalonepublish.widgets.rst create mode 100644 docs/source/pype.tools.assetcreator.rst create mode 100644 docs/source/pype.tools.rst create mode 100644 docs/source/pype.widgets.rst create mode 100644 docs/source/readme.rst create mode 100644 make_docs.bat diff --git a/.gitignore b/.gitignore index f1656c7fab..baf7b918e2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,11 @@ __pycache__/ *.py[cod] *$py.class + +# Documentation +############### +/docs/build + +# Editor backup files # +####################### +*~ diff --git a/README.md b/README.md index 7cf8c4c0b6..ba5d458045 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +Pype +==== + The 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. @@ -7,7 +10,8 @@ _This configuration acts as a starting point for all pype club clients wth avalo -### Code convention +Code convention +--------------- Below are some of the standard practices applied to this repositories. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000000..69fe55ecfa --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,19 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000000..4d9eb83d9f --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000000..894425e56b --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,222 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) +import sys +import os +from pprint import pprint +from pypeapp.pypeLauncher import PypeLauncher +from pypeapp.storage import Storage +from pypeapp.deployment import Deployment + +pype_setup = os.getenv('PYPE_ROOT') +d = Deployment(pype_setup) +launcher = PypeLauncher() + +tools, config_path = d.get_environment_data() + +os.environ['PYPE_CONFIG'] = config_path +os.environ['TOOL_ENV'] = os.path.normpath(os.path.join(config_path, + 'environments')) +launcher._add_modules() +Storage().update_environment() +launcher._load_default_environments(tools=tools) + +# -- Project information ----------------------------------------------------- + +project = 'pype' +copyright = '2019, Orbi Tools' +author = 'Orbi Tools' + +# The short X.Y version +version = '' +# The full version, including alpha/beta/rc tags +release = '' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.mathjax', + 'sphinx.ext.viewcode', + 'sphinx.ext.autosummary', + 'recommonmark' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'friendly' + +# -- Options for autodoc ----------------------------------------------------- +autodoc_default_flags = ['members'] +autosummary_generate = True + + +# -- Options for HTML output ------------------------------------------------- + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +html_theme_options = { + 'collapse_navigation': False +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'pypedoc' + + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'pype.tex', 'pype Documentation', + 'OrbiTools', 'manual'), +] + + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'pype', 'pype Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'pype', 'pype Documentation', + author, 'pype', 'One line description of project.', + 'Miscellaneous'), +] + + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/': None} + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000000..b54d153894 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,18 @@ +.. pype documentation master file, created by + sphinx-quickstart on Mon May 13 17:18:23 2019. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to pype's documentation! +================================ + +.. toctree:: + readme + modules + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/source/modules.rst b/docs/source/modules.rst new file mode 100644 index 0000000000..a44fc918a5 --- /dev/null +++ b/docs/source/modules.rst @@ -0,0 +1,7 @@ +pype +==== + +.. toctree:: + :maxdepth: 6 + + pype diff --git a/docs/source/pype.aport.rst b/docs/source/pype.aport.rst new file mode 100644 index 0000000000..4a96b1e619 --- /dev/null +++ b/docs/source/pype.aport.rst @@ -0,0 +1,20 @@ +pype.aport package +================== + +.. automodule:: pype.aport + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.aport.api module +--------------------- + +.. automodule:: pype.aport.api + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.avalon_apps.rst b/docs/source/pype.avalon_apps.rst new file mode 100644 index 0000000000..a1835a51d2 --- /dev/null +++ b/docs/source/pype.avalon_apps.rst @@ -0,0 +1,20 @@ +pype.avalon\_apps package +========================= + +.. automodule:: pype.avalon_apps + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.avalon\_apps.avalon\_app module +------------------------------------ + +.. automodule:: pype.avalon_apps.avalon_app + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.clockify.rst b/docs/source/pype.clockify.rst new file mode 100644 index 0000000000..0a096d6c10 --- /dev/null +++ b/docs/source/pype.clockify.rst @@ -0,0 +1,36 @@ +pype.clockify package +===================== + +.. automodule:: pype.clockify + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.clockify.clockify module +----------------------------- + +.. automodule:: pype.clockify.clockify + :members: + :undoc-members: + :show-inheritance: + +pype.clockify.clockify\_api module +---------------------------------- + +.. automodule:: pype.clockify.clockify_api + :members: + :undoc-members: + :show-inheritance: + +pype.clockify.widget\_settings module +------------------------------------- + +.. automodule:: pype.clockify.widget_settings + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.ftrack.ftrack_server.rst b/docs/source/pype.ftrack.ftrack_server.rst new file mode 100644 index 0000000000..0e809ff30d --- /dev/null +++ b/docs/source/pype.ftrack.ftrack_server.rst @@ -0,0 +1,36 @@ +pype.ftrack.ftrack\_server package +================================== + +.. automodule:: pype.ftrack.ftrack_server + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.ftrack.ftrack\_server.event\_server module +----------------------------------------------- + +.. automodule:: pype.ftrack.ftrack_server.event_server + :members: + :undoc-members: + :show-inheritance: + +pype.ftrack.ftrack\_server.event\_server\_cli module +---------------------------------------------------- + +.. automodule:: pype.ftrack.ftrack_server.event_server_cli + :members: + :undoc-members: + :show-inheritance: + +pype.ftrack.ftrack\_server.ftrack\_server module +------------------------------------------------ + +.. automodule:: pype.ftrack.ftrack_server.ftrack_server + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.ftrack.lib.rst b/docs/source/pype.ftrack.lib.rst new file mode 100644 index 0000000000..32da8ee964 --- /dev/null +++ b/docs/source/pype.ftrack.lib.rst @@ -0,0 +1,52 @@ +pype.ftrack.lib package +======================= + +.. automodule:: pype.ftrack.lib + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.ftrack.lib.avalon\_sync module +----------------------------------- + +.. automodule:: pype.ftrack.lib.avalon_sync + :members: + :undoc-members: + :show-inheritance: + +pype.ftrack.lib.ftrack\_action\_handler module +---------------------------------------------- + +.. automodule:: pype.ftrack.lib.ftrack_action_handler + :members: + :undoc-members: + :show-inheritance: + +pype.ftrack.lib.ftrack\_app\_handler module +------------------------------------------- + +.. automodule:: pype.ftrack.lib.ftrack_app_handler + :members: + :undoc-members: + :show-inheritance: + +pype.ftrack.lib.ftrack\_base\_handler module +-------------------------------------------- + +.. automodule:: pype.ftrack.lib.ftrack_base_handler + :members: + :undoc-members: + :show-inheritance: + +pype.ftrack.lib.ftrack\_event\_handler module +--------------------------------------------- + +.. automodule:: pype.ftrack.lib.ftrack_event_handler + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.ftrack.rst b/docs/source/pype.ftrack.rst new file mode 100644 index 0000000000..0097da669a --- /dev/null +++ b/docs/source/pype.ftrack.rst @@ -0,0 +1,52 @@ +pype.ftrack package +=================== + +.. automodule:: pype.ftrack + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + + pype.ftrack.ftrack_server + pype.ftrack.lib + +Submodules +---------- + +pype.ftrack.credentials module +------------------------------ + +.. automodule:: pype.ftrack.credentials + :members: + :undoc-members: + :show-inheritance: + +pype.ftrack.ftrack\_module module +--------------------------------- + +.. automodule:: pype.ftrack.ftrack_module + :members: + :undoc-members: + :show-inheritance: + +pype.ftrack.login\_dialog module +-------------------------------- + +.. automodule:: pype.ftrack.login_dialog + :members: + :undoc-members: + :show-inheritance: + +pype.ftrack.login\_tools module +------------------------------- + +.. automodule:: pype.ftrack.login_tools + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.fusion.rst b/docs/source/pype.fusion.rst new file mode 100644 index 0000000000..39d13531c5 --- /dev/null +++ b/docs/source/pype.fusion.rst @@ -0,0 +1,27 @@ +pype.fusion package +=================== + +.. automodule:: pype.fusion + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + + pype.fusion.scripts + +Submodules +---------- + +pype.fusion.lib module +---------------------- + +.. automodule:: pype.fusion.lib + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.fusion.scripts.rst b/docs/source/pype.fusion.scripts.rst new file mode 100644 index 0000000000..e8878ff724 --- /dev/null +++ b/docs/source/pype.fusion.scripts.rst @@ -0,0 +1,28 @@ +pype.fusion.scripts package +=========================== + +.. automodule:: pype.fusion.scripts + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.fusion.scripts.fusion\_switch\_shot module +----------------------------------------------- + +.. automodule:: pype.fusion.scripts.fusion_switch_shot + :members: + :undoc-members: + :show-inheritance: + +pype.fusion.scripts.publish\_filesequence module +------------------------------------------------ + +.. automodule:: pype.fusion.scripts.publish_filesequence + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.houdini.rst b/docs/source/pype.houdini.rst new file mode 100644 index 0000000000..04dfac9070 --- /dev/null +++ b/docs/source/pype.houdini.rst @@ -0,0 +1,20 @@ +pype.houdini package +==================== + +.. automodule:: pype.houdini + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.houdini.lib module +----------------------- + +.. automodule:: pype.houdini.lib + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.maya.rst b/docs/source/pype.maya.rst new file mode 100644 index 0000000000..0b68a71d56 --- /dev/null +++ b/docs/source/pype.maya.rst @@ -0,0 +1,52 @@ +pype.maya package +================= + +.. automodule:: pype.maya + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.maya.action module +----------------------- + +.. automodule:: pype.maya.action + :members: + :undoc-members: + :show-inheritance: + +pype.maya.customize module +-------------------------- + +.. automodule:: pype.maya.customize + :members: + :undoc-members: + :show-inheritance: + +pype.maya.lib module +-------------------- + +.. automodule:: pype.maya.lib + :members: + :undoc-members: + :show-inheritance: + +pype.maya.menu module +--------------------- + +.. automodule:: pype.maya.menu + :members: + :undoc-members: + :show-inheritance: + +pype.maya.plugin module +----------------------- + +.. automodule:: pype.maya.plugin + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.nuke.rst b/docs/source/pype.nuke.rst new file mode 100644 index 0000000000..017b6db27b --- /dev/null +++ b/docs/source/pype.nuke.rst @@ -0,0 +1,44 @@ +pype.nuke package +================= + +.. automodule:: pype.nuke + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.nuke.actions module +------------------------ + +.. automodule:: pype.nuke.actions + :members: + :undoc-members: + :show-inheritance: + +pype.nuke.lib module +-------------------- + +.. automodule:: pype.nuke.lib + :members: + :undoc-members: + :show-inheritance: + +pype.nuke.menu module +--------------------- + +.. automodule:: pype.nuke.menu + :members: + :undoc-members: + :show-inheritance: + +pype.nuke.templates module +-------------------------- + +.. automodule:: pype.nuke.templates + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.premiere.rst b/docs/source/pype.premiere.rst new file mode 100644 index 0000000000..6caa25c580 --- /dev/null +++ b/docs/source/pype.premiere.rst @@ -0,0 +1,20 @@ +pype.premiere package +===================== + +.. automodule:: pype.premiere + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.premiere.templates module +------------------------------ + +.. automodule:: pype.premiere.templates + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.rst b/docs/source/pype.rst new file mode 100644 index 0000000000..7409ee62ee --- /dev/null +++ b/docs/source/pype.rst @@ -0,0 +1,88 @@ +pype package +============ + +.. automodule:: pype + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + + pype.aport + pype.avalon_apps + pype.clockify + pype.ftrack + pype.fusion + pype.houdini + pype.maya + pype.nuke + pype.premiere + pype.scripts + pype.services + pype.standalonepublish + pype.tools + pype.widgets + +Submodules +---------- + +pype.action module +------------------ + +.. automodule:: pype.action + :members: + :undoc-members: + :show-inheritance: + +pype.api module +--------------- + +.. automodule:: pype.api + :members: + :undoc-members: + :show-inheritance: + +pype.launcher\_actions module +----------------------------- + +.. automodule:: pype.launcher_actions + :members: + :undoc-members: + :show-inheritance: + +pype.lib module +--------------- + +.. automodule:: pype.lib + :members: + :undoc-members: + :show-inheritance: + +pype.plugin module +------------------ + +.. automodule:: pype.plugin + :members: + :undoc-members: + :show-inheritance: + +pype.setdress\_api module +------------------------- + +.. automodule:: pype.setdress_api + :members: + :undoc-members: + :show-inheritance: + +pype.templates module +--------------------- + +.. automodule:: pype.templates + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.scripts.rst b/docs/source/pype.scripts.rst new file mode 100644 index 0000000000..4ca6d25dac --- /dev/null +++ b/docs/source/pype.scripts.rst @@ -0,0 +1,28 @@ +pype.scripts package +==================== + +.. automodule:: pype.scripts + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.scripts.fusion\_switch\_shot module +---------------------------------------- + +.. automodule:: pype.scripts.fusion_switch_shot + :members: + :undoc-members: + :show-inheritance: + +pype.scripts.publish\_filesequence module +----------------------------------------- + +.. automodule:: pype.scripts.publish_filesequence + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.services.idle_manager.rst b/docs/source/pype.services.idle_manager.rst new file mode 100644 index 0000000000..92a083ed75 --- /dev/null +++ b/docs/source/pype.services.idle_manager.rst @@ -0,0 +1,20 @@ +pype.services.idle\_manager package +=================================== + +.. automodule:: pype.services.idle_manager + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.services.idle\_manager.idle\_manager module +------------------------------------------------ + +.. automodule:: pype.services.idle_manager.idle_manager + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.services.rst b/docs/source/pype.services.rst new file mode 100644 index 0000000000..285a83f53e --- /dev/null +++ b/docs/source/pype.services.rst @@ -0,0 +1,17 @@ +pype.services package +===================== + +.. automodule:: pype.services + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + + pype.services.idle_manager + pype.services.statics_server + pype.services.timers_manager + diff --git a/docs/source/pype.services.statics_server.rst b/docs/source/pype.services.statics_server.rst new file mode 100644 index 0000000000..7a336ebb1d --- /dev/null +++ b/docs/source/pype.services.statics_server.rst @@ -0,0 +1,20 @@ +pype.services.statics\_server package +===================================== + +.. automodule:: pype.services.statics_server + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.services.statics\_server.statics\_server module +---------------------------------------------------- + +.. automodule:: pype.services.statics_server.statics_server + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.services.timers_manager.rst b/docs/source/pype.services.timers_manager.rst new file mode 100644 index 0000000000..7404f10390 --- /dev/null +++ b/docs/source/pype.services.timers_manager.rst @@ -0,0 +1,28 @@ +pype.services.timers\_manager package +===================================== + +.. automodule:: pype.services.timers_manager + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.services.timers\_manager.timers\_manager module +---------------------------------------------------- + +.. automodule:: pype.services.timers_manager.timers_manager + :members: + :undoc-members: + :show-inheritance: + +pype.services.timers\_manager.widget\_user\_idle module +------------------------------------------------------- + +.. automodule:: pype.services.timers_manager.widget_user_idle + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.standalonepublish.resources.rst b/docs/source/pype.standalonepublish.resources.rst new file mode 100644 index 0000000000..414da97530 --- /dev/null +++ b/docs/source/pype.standalonepublish.resources.rst @@ -0,0 +1,8 @@ +pype.standalonepublish.resources package +======================================== + +.. automodule:: pype.standalonepublish.resources + :members: + :undoc-members: + :show-inheritance: + diff --git a/docs/source/pype.standalonepublish.rst b/docs/source/pype.standalonepublish.rst new file mode 100644 index 0000000000..5147039326 --- /dev/null +++ b/docs/source/pype.standalonepublish.rst @@ -0,0 +1,44 @@ +pype.standalonepublish package +============================== + +.. automodule:: pype.standalonepublish + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + + pype.standalonepublish.resources + pype.standalonepublish.widgets + +Submodules +---------- + +pype.standalonepublish.app module +--------------------------------- + +.. automodule:: pype.standalonepublish.app + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.publish module +------------------------------------- + +.. automodule:: pype.standalonepublish.publish + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.standalonepublish\_module module +------------------------------------------------------- + +.. automodule:: pype.standalonepublish.standalonepublish_module + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.standalonepublish.widgets.rst b/docs/source/pype.standalonepublish.widgets.rst new file mode 100644 index 0000000000..5bebbe46f5 --- /dev/null +++ b/docs/source/pype.standalonepublish.widgets.rst @@ -0,0 +1,156 @@ +pype.standalonepublish.widgets package +====================================== + +.. automodule:: pype.standalonepublish.widgets + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.standalonepublish.widgets.button\_from\_svgs module +-------------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.button_from_svgs + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.model\_asset module +-------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.model_asset + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.model\_filter\_proxy\_exact\_match module +------------------------------------------------------------------------ + +.. automodule:: pype.standalonepublish.widgets.model_filter_proxy_exact_match + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.model\_filter\_proxy\_recursive\_sort module +--------------------------------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.model_filter_proxy_recursive_sort + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.model\_node module +------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.model_node + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.model\_tasks\_template module +------------------------------------------------------------ + +.. automodule:: pype.standalonepublish.widgets.model_tasks_template + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.model\_tree module +------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.model_tree + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.model\_tree\_view\_deselectable module +--------------------------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.model_tree_view_deselectable + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.widget\_asset module +--------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.widget_asset + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.widget\_asset\_view module +--------------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.widget_asset_view + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.widget\_component\_item module +------------------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.widget_component_item + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.widget\_components module +-------------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.widget_components + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.widget\_components\_list module +-------------------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.widget_components_list + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.widget\_drop\_empty module +--------------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.widget_drop_empty + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.widget\_drop\_frame module +--------------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.widget_drop_frame + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.widget\_family module +---------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.widget_family + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.widget\_family\_desc module +---------------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.widget_family_desc + :members: + :undoc-members: + :show-inheritance: + +pype.standalonepublish.widgets.widget\_shadow module +---------------------------------------------------- + +.. automodule:: pype.standalonepublish.widgets.widget_shadow + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.tools.assetcreator.rst b/docs/source/pype.tools.assetcreator.rst new file mode 100644 index 0000000000..0775623687 --- /dev/null +++ b/docs/source/pype.tools.assetcreator.rst @@ -0,0 +1,36 @@ +pype.tools.assetcreator package +=============================== + +.. automodule:: pype.tools.assetcreator + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.tools.assetcreator.app module +---------------------------------- + +.. automodule:: pype.tools.assetcreator.app + :members: + :undoc-members: + :show-inheritance: + +pype.tools.assetcreator.model module +------------------------------------ + +.. automodule:: pype.tools.assetcreator.model + :members: + :undoc-members: + :show-inheritance: + +pype.tools.assetcreator.widget module +------------------------------------- + +.. automodule:: pype.tools.assetcreator.widget + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/pype.tools.rst b/docs/source/pype.tools.rst new file mode 100644 index 0000000000..fc6798fcc4 --- /dev/null +++ b/docs/source/pype.tools.rst @@ -0,0 +1,15 @@ +pype.tools package +================== + +.. automodule:: pype.tools + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + + pype.tools.assetcreator + diff --git a/docs/source/pype.widgets.rst b/docs/source/pype.widgets.rst new file mode 100644 index 0000000000..286b4acf32 --- /dev/null +++ b/docs/source/pype.widgets.rst @@ -0,0 +1,36 @@ +pype.widgets package +==================== + +.. automodule:: pype.widgets + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +pype.widgets.message\_window module +----------------------------------- + +.. automodule:: pype.widgets.message_window + :members: + :undoc-members: + :show-inheritance: + +pype.widgets.popup module +------------------------- + +.. automodule:: pype.widgets.popup + :members: + :undoc-members: + :show-inheritance: + +pype.widgets.project\_settings module +------------------------------------- + +.. automodule:: pype.widgets.project_settings + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/source/readme.rst b/docs/source/readme.rst new file mode 100644 index 0000000000..7592303fc2 --- /dev/null +++ b/docs/source/readme.rst @@ -0,0 +1 @@ +.. include:: ../../README.md diff --git a/make_docs.bat b/make_docs.bat new file mode 100644 index 0000000000..f0011086e5 --- /dev/null +++ b/make_docs.bat @@ -0,0 +1,46 @@ +@echo off +echo ^>^>^> Generating pype-setup documentation, please wait ... +call "C:\Users\Public\pype_env2\Scripts\activate.bat" + +setlocal enableextensions enabledelayedexpansion +set _OLD_PYTHONPATH=%PYTHONPATH% +echo ^>^>^> Adding repos path +rem add stuff in repos +call :ResolvePath repodir "..\" + +for /d %%d in ( %repodir%*) do ( +echo - adding path %%d +set PYTHONPATH=%%d;!PYTHONPATH! +) + +echo ^>^>^> Adding python vendors path +rem add python vendor paths +call :ResolvePath vendordir "..\..\vendor\python\" + +for /d %%d in ( %vendordir%*) do ( +echo - adding path %%d +set PYTHONPATH=%%d;!PYTHONPATH! +) + +echo ^>^>^> Setting PYPE_CONFIG +call :ResolvePath pypeconfig "..\pype-config" +set PYPE_CONFIG=%pypeconfig% +echo ^>^>^> Setting PYPE_ROOT +call :ResolvePath pyperoot "..\..\" +set PYPE_ROOT=%pyperoot% +set PYTHONPATH=%PYPE_ROOT%;%PYTHONPATH% +echo ^>^>^> Setting PYPE_ENV +set PYPE_ENV="C:\Users\Public\pype_env2" + +call "docs\make.bat" clean +sphinx-apidoc -M -f -d 6 --ext-autodoc --ext-intersphinx --ext-viewcode -o docs\source pype %PYPE_ROOT%\repos\pype\pype\vendor\* +call "docs\make.bat" html +echo ^>^>^> Doing cleanup ... +set PYTHONPATH=%_OLD_PYTHONPATH% +set PYPE_CONFIG= +call "C:\Users\Public\pype_env2\Scripts\deactivate.bat" +exit /b + +:ResolvePath + set %1=%~dpfn2 + exit /b From bdcc5b650c66f2a52a567b3284279a86cece5dc0 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 15 May 2019 16:32:54 +0100 Subject: [PATCH 02/48] started cleanup --- pype/plugins/global/publish/integrate.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pype/plugins/global/publish/integrate.py b/pype/plugins/global/publish/integrate.py index fce9d26220..d966258afc 100644 --- a/pype/plugins/global/publish/integrate.py +++ b/pype/plugins/global/publish/integrate.py @@ -1,6 +1,7 @@ import os import logging import shutil +import clique import errno import pyblish.api @@ -42,7 +43,10 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "review", "workfile", "scene", - "ass"] + "ass", + "render", + "imagesequence", + "write"] exclude_families = ["clip"] def process(self, instance): @@ -53,7 +57,9 @@ class IntegrateAsset(pyblish.api.InstancePlugin): self.register(instance) self.log.info("Integrating Asset in to the database ...") - self.integrate(instance) + if instance.data.get('transfer', True): + self.integrate(instance) + def register(self, instance): # Required environment variables From 521aa246eaff6c1c48bbe88a5d5365d015497dec Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 May 2019 12:40:37 +0200 Subject: [PATCH 03/48] feat(pype): adding functions from lib to api --- pype/api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/api.py b/pype/api.py index fcdcbce82b..0a5f21c202 100644 --- a/pype/api.py +++ b/pype/api.py @@ -43,7 +43,8 @@ from .lib import ( get_asset_data, modified_environ, add_tool_to_environment, - get_data_hierarchical_attr + get_data_hierarchical_attr, + get_avalon_project_template ) from .widgets.message_window import message @@ -83,6 +84,7 @@ __all__ = [ "set_hierarchy", "set_project_code", "get_data_hierarchical_attr", + "get_avalon_project_template", # preloaded templates "Anatomy", From 360586a7cfb86989b0bc976275fa137d5f1b2007 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 May 2019 12:42:44 +0200 Subject: [PATCH 04/48] feat(nukestudio): integrating Workfiles tool into menu --- pype/nukestudio/menu.py | 85 +++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/pype/nukestudio/menu.py b/pype/nukestudio/menu.py index b6e17aeab2..5244a0b527 100644 --- a/pype/nukestudio/menu.py +++ b/pype/nukestudio/menu.py @@ -43,50 +43,59 @@ def install(): else: menu = check_made_menu.menu() - actions = [{ - 'action': QAction('Set Context', None), - 'function': contextmanager.show, - 'icon': QIcon('icons:Position.png') - }, + actions = [ { - 'action': QAction('Create...', None), - 'function': creator.show, - 'icon': QIcon('icons:ColorAdd.png') - }, + 'action': QAction('Set Context', None), + 'function': contextmanager.show, + 'icon': QIcon('icons:Position.png') + }, + "separator", { - 'action': QAction('Load...', None), - 'function': cbloader.show, - 'icon': QIcon('icons:CopyRectangle.png') - }, + 'action': QAction("Work Files...", None), + 'function': (lambda: workfiles.show(os.environ["AVALON_WORKDIR"])), + 'icon': QIcon('icons:Position.png') + }, + "separator", { - 'action': QAction('Publish...', None), - 'function': publish.show, - 'icon': QIcon('icons:Output.png') - }, + 'action': QAction('Create...', None), + 'function': creator.show, + 'icon': QIcon('icons:ColorAdd.png') + }, { - 'action': QAction('Manage...', None), - 'function': cbsceneinventory.show, - 'icon': QIcon('icons:ModifyMetaData.png') - }, + 'action': QAction('Load...', None), + 'function': cbloader.show, + 'icon': QIcon('icons:CopyRectangle.png') + }, { - 'action': QAction('Library...', None), - 'function': libraryloader.show, - 'icon': QIcon('icons:ColorAdd.png') - }] - + 'action': QAction('Publish...', None), + 'function': publish.show, + 'icon': QIcon('icons:Output.png') + }, + { + 'action': QAction('Manage...', None), + 'function': cbsceneinventory.show, + 'icon': QIcon('icons:ModifyMetaData.png') + }, + { + 'action': QAction('Library...', None), + 'function': libraryloader.show, + 'icon': QIcon('icons:ColorAdd.png') + }] # Create menu items for a in actions: - pprint(a) - # create action - for k in a.keys(): - if 'action' in k: - action = a[k] - elif 'function' in k: - action.triggered.connect(a[k]) - elif 'icon' in k: - action.setIcon(a[k]) + if isinstance(a, dict): + # create action + for k in a.keys(): + if 'action' in k: + action = a[k] + elif 'function' in k: + action.triggered.connect(a[k]) + elif 'icon' in k: + action.setIcon(a[k]) - # add action to menu - menu.addAction(action) - hiero.ui.registerAction(action) + # add action to menu + menu.addAction(action) + hiero.ui.registerAction(action) + elif isinstance(a, str): + menu.addSeparator() From d80631851e2b478d59032ae6912d68953e2cea98 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 May 2019 12:43:28 +0200 Subject: [PATCH 05/48] feat(nukestudio): working on plugins --- pype/nukestudio/precomp_clip.py | 148 ++++++++++++++++++ .../publish/collect_active_project.py | 1 + .../publish/collect_hierarchy_context.py | 125 +++++++++------ .../publish/collect_project_root.py | 12 ++ .../nukestudio/publish/collect_tags.py | 2 +- .../hiero_plugin_path/Templates/hierarchy.png | Bin 0 -> 90074 bytes 6 files changed, 242 insertions(+), 46 deletions(-) create mode 100644 pype/nukestudio/precomp_clip.py create mode 100644 pype/plugins/nukestudio/publish/collect_project_root.py create mode 100644 setup/nukestudio/hiero_plugin_path/Templates/hierarchy.png diff --git a/pype/nukestudio/precomp_clip.py b/pype/nukestudio/precomp_clip.py new file mode 100644 index 0000000000..9920a2aa31 --- /dev/null +++ b/pype/nukestudio/precomp_clip.py @@ -0,0 +1,148 @@ +from hiero.core import * +from hiero.ui import * +import ft_utils +import re +import os + + +def create_nk_script_clips(script_lst, seq=None): + ''' + nk_scripts is list of dictionaries like: + [{ + 'path': 'P:/Jakub_testy_pipeline/test_v01.nk', + 'name': 'test', + 'timeline_frame_in': 10, + 'handles': 10, + 'source_start': 0, + 'source_end': 54, + 'task': 'Comp-tracking', + 'work_dir': 'VFX_PR', + 'shot': '00010' + }] + ''' + env = ft_utils.Env() + proj = projects()[-1] + root = proj.clipsBin() + + if not seq: + seq = Sequence('NewSequences') + root.addItem(BinItem(seq)) + # todo will ned to define this better + # track = seq[1] # lazy example to get a destination# track + clips_lst = [] + for nk in script_lst: + task_short = env.task_codes[nk['task']] + script_file = task_short + task_path = '/'.join([nk['work_dir'], nk['shot'], nk['task']]) + bin = create_bin_in_project(task_path, proj) + task_path += script_file + + if nk['task'] not in seq.videoTracks(): + track = hiero.core.VideoTrack(nk['task']) + seq.addTrack(track) + else: + track = seq.tracks(nk['task']) + + # create slip media + print nk['path'] + media = MediaSource(nk['path']) + print media + source = Clip(media) + print source + name = os.path.basename(os.path.splitext(nk['path'])[0]) + split_name = split_by_client_version(name, env)[0] or name + print split_name + # print source + # add to bin as clip item + items_in_bin = [b.name() for b in bin.items()] + if split_name not in items_in_bin: + binItem = BinItem(source) + bin.addItem(binItem) + print bin.items() + new_source = [ + item for item in bin.items() if split_name in item.name() + ][0].items()[0].item() + print new_source + # add to track as clip item + trackItem = TrackItem(split_name, TrackItem.kVideo) + trackItem.setSource(new_source) + trackItem.setSourceIn(nk['source_start'] + nk['handles']) + trackItem.setSourceOut(nk['source_end'] - nk['handles']) + trackItem.setTimelineIn(nk['source_start'] + nk['timeline_frame_in']) + trackItem.setTimelineOut( + (nk['source_end'] - (nk['handles'] * 2)) + nk['timeline_frame_in']) + track.addTrackItem(trackItem) + track.addTrackItem(trackItem) + clips_lst.append(trackItem) + + return clips_lst + + +def create_bin_in_project(bin_name='', project=''): + ''' + create bin in project and + if the bin_name is "bin1/bin2/bin3" it will create whole depth + ''' + + if not project: + # get the first loaded project + project = projects()[0] + if not bin_name: + return None + if '/' in bin_name: + bin_name = bin_name.split('/') + else: + bin_name = [bin_name] + + clipsBin = project.clipsBin() + + done_bin_lst = [] + for i, b in enumerate(bin_name): + if i == 0 and len(bin_name) > 1: + if b in [bin.name() for bin in clipsBin.bins()]: + bin = [bin for bin in clipsBin.bins() if b in bin.name()][0] + done_bin_lst.append(bin) + else: + create_bin = Bin(b) + clipsBin.addItem(create_bin) + done_bin_lst.append(create_bin) + + elif i >= 1 and i < len(bin_name) - 1: + if b in [bin.name() for bin in done_bin_lst[i - 1].bins()]: + bin = [ + bin for bin in done_bin_lst[i - 1].bins() + if b in bin.name() + ][0] + done_bin_lst.append(bin) + else: + create_bin = Bin(b) + done_bin_lst[i - 1].addItem(create_bin) + done_bin_lst.append(create_bin) + + elif i == len(bin_name) - 1: + if b in [bin.name() for bin in done_bin_lst[i - 1].bins()]: + bin = [ + bin for bin in done_bin_lst[i - 1].bins() + if b in bin.name() + ][0] + done_bin_lst.append(bin) + else: + create_bin = Bin(b) + done_bin_lst[i - 1].addItem(create_bin) + done_bin_lst.append(create_bin) + # print [bin.name() for bin in clipsBin.bins()] + return done_bin_lst[-1] + + +def split_by_client_version(string, env=None): + if not env: + env = ft_utils.Env() + + client_letter, client_digits = env.get_version_type('client') + regex = "[/_.]" + client_letter + "\d+" + try: + matches = re.findall(regex, string, re.IGNORECASE) + return string.split(matches[0]) + except Exception, e: + print e + return None diff --git a/pype/plugins/nukestudio/publish/collect_active_project.py b/pype/plugins/nukestudio/publish/collect_active_project.py index 0ac6192e4a..7fcd3c30a0 100644 --- a/pype/plugins/nukestudio/publish/collect_active_project.py +++ b/pype/plugins/nukestudio/publish/collect_active_project.py @@ -4,6 +4,7 @@ import pyblish.api class CollectActiveProject(pyblish.api.ContextPlugin): """Inject the active project into context""" + label = "Collect Active Project" order = pyblish.api.CollectorOrder - 0.2 def process(self, context): diff --git a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py index b421d31f79..6d780c62c8 100644 --- a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py +++ b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py @@ -2,7 +2,7 @@ import pyblish.api from avalon import api -class CollectHierarchyContext(pyblish.api.ContextPlugin): +class CollectHierarchyContext(pyblish.api.InstancePlugin): """Collecting hierarchy context from `parents` and `hierarchy` data present in `clip` family instances coming from the request json data file @@ -13,6 +13,7 @@ class CollectHierarchyContext(pyblish.api.ContextPlugin): label = "Collect Hierarchy Context" order = pyblish.api.CollectorOrder + 0.1 + families = ["clip"] def update_dict(self, ex_dict, new_dict): for key in ex_dict: @@ -22,51 +23,85 @@ class CollectHierarchyContext(pyblish.api.ContextPlugin): new_dict[key] = ex_dict[key] return new_dict - def process(self, context): - json_data = context.data.get("jsonData", None) - temp_context = {} - for instance in json_data['instances']: - if instance['family'] in 'projectfile': - continue + def convert_to_entity(self, key, value): + types = {"shot": "Shot", + "folder": "Folder", + "episode": "Episode", + "Sequence": "Sequence", + } + return {"entity_type": types.get(key, None), "entity_name": value} - in_info = {} - name = instance['name'] - # suppose that all instances are Shots - in_info['entity_type'] = 'Shot' + def process(self, instance): + context = instance.context + tags = instance.data.get("tags", None) + self.log.info(tags) + if tags: + for t in tags: + t_metadata = dict(t["metadata"]) + t_type = t_metadata.get("tag._type", "") + if "hierarchy" in t_type: + self.log.info("__ type: {}".format(t_type)) + d_metadata = dict() + for k, v in t_metadata.items(): + new_k = k.split(".")[1] + try: + d_metadata[new_k] = str(v).format(**context.data) + except Exception: + d_metadata[new_k] = v - instance_pyblish = [ - i for i in context.data["instances"] if i.data['asset'] in name][0] - in_info['custom_attributes'] = { - 'fend': instance_pyblish.data['endFrame'], - 'fstart': instance_pyblish.data['startFrame'], - 'fps': instance_pyblish.data['fps'] - } - in_info['tasks'] = instance['tasks'] + self.log.info("__ projectroot: {}".format(context.data["projectroot"])) + self.log.info("__ d_metadata: {}".format(d_metadata)) + self.log.info( + "__ hierarchy: {}".format(d_metadata["note"])) + # self.log.info("__ hierarchy.format: {}".format(d_metadata["note"].format( + # **d_metadata))) - parents = instance.get('parents', []) - - actual = {name: in_info} - - for parent in reversed(parents): - next_dict = {} - parent_name = parent["entityName"] - next_dict[parent_name] = {} - next_dict[parent_name]["entity_type"] = parent["entityType"] - next_dict[parent_name]["childs"] = actual - actual = next_dict - - temp_context = self.update_dict(temp_context, actual) - self.log.debug(temp_context) - - # TODO: 100% sure way of get project! Will be Name or Code? - project_name = api.Session["AVALON_PROJECT"] - final_context = {} - final_context[project_name] = {} - final_context[project_name]['entity_type'] = 'Project' - final_context[project_name]['childs'] = temp_context - - # adding hierarchy context to instance - context.data["hierarchyContext"] = final_context - self.log.debug("context.data[hierarchyContext] is: {}".format( - context.data["hierarchyContext"])) + # + # json_data = context.data.get("jsonData", None) + # temp_context = {} + # for instance in json_data['instances']: + # if instance['family'] in 'projectfile': + # continue + # + # in_info = {} + # name = instance['name'] + # # suppose that all instances are Shots + # in_info['entity_type'] = 'Shot' + # + # instance_pyblish = [ + # i for i in context.data["instances"] if i.data['asset'] in name][0] + # in_info['custom_attributes'] = { + # 'fend': instance_pyblish.data['endFrame'], + # 'fstart': instance_pyblish.data['startFrame'], + # 'fps': instance_pyblish.data['fps'] + # } + # + # in_info['tasks'] = instance['tasks'] + # + # parents = instance.get('parents', []) + # + # actual = {name: in_info} + # + # for parent in reversed(parents): + # next_dict = {} + # parent_name = parent["entityName"] + # next_dict[parent_name] = {} + # next_dict[parent_name]["entity_type"] = parent["entityType"] + # next_dict[parent_name]["childs"] = actual + # actual = next_dict + # + # temp_context = self.update_dict(temp_context, actual) + # self.log.debug(temp_context) + # + # # TODO: 100% sure way of get project! Will be Name or Code? + # project_name = api.Session["AVALON_PROJECT"] + # final_context = {} + # final_context[project_name] = {} + # final_context[project_name]['entity_type'] = 'Project' + # final_context[project_name]['childs'] = temp_context + # + # # adding hierarchy context to instance + # context.data["hierarchyContext"] = final_context + # self.log.debug("context.data[hierarchyContext] is: {}".format( + # context.data["hierarchyContext"])) diff --git a/pype/plugins/nukestudio/publish/collect_project_root.py b/pype/plugins/nukestudio/publish/collect_project_root.py new file mode 100644 index 0000000000..d6e1fbc560 --- /dev/null +++ b/pype/plugins/nukestudio/publish/collect_project_root.py @@ -0,0 +1,12 @@ +import pyblish.api + + +class CollectActiveProjectRoot(pyblish.api.ContextPlugin): + """Inject the active project into context""" + + label = "Collect Project Root" + order = pyblish.api.CollectorOrder - 0.1 + + def process(self, context): + project = context.data["activeProject"] + context.data["projectroot"] = project.projectRoot() diff --git a/pype/plugins/nukestudio/publish/collect_tags.py b/pype/plugins/nukestudio/publish/collect_tags.py index 9ae34d415f..8c1e12f2be 100644 --- a/pype/plugins/nukestudio/publish/collect_tags.py +++ b/pype/plugins/nukestudio/publish/collect_tags.py @@ -4,7 +4,7 @@ from pyblish import api class CollectClipTags(api.InstancePlugin): """Collect Tags from selected track items.""" - order = api.CollectorOrder + order = api.CollectorOrder + 0.005 label = "Collect Tags" hosts = ["nukestudio"] families = ['clip'] diff --git a/setup/nukestudio/hiero_plugin_path/Templates/hierarchy.png b/setup/nukestudio/hiero_plugin_path/Templates/hierarchy.png new file mode 100644 index 0000000000000000000000000000000000000000..461d2616040abfbd87f07eee4f1ce90476f7b82b GIT binary patch literal 90074 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ⅇ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 literal 0 HcmV?d00001 From 9056ade323dae6f3b6a29275754baf7dfb7c83f6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 May 2019 12:44:00 +0200 Subject: [PATCH 06/48] feat(nukestudio): adding Hierarchy Tag into default project tags --- .../Templates/SharedTags.hrox | 352 +++++++++--------- 1 file changed, 185 insertions(+), 167 deletions(-) diff --git a/setup/nukestudio/hiero_plugin_path/Templates/SharedTags.hrox b/setup/nukestudio/hiero_plugin_path/Templates/SharedTags.hrox index 128bde5456..ee67374df5 100644 --- a/setup/nukestudio/hiero_plugin_path/Templates/SharedTags.hrox +++ b/setup/nukestudio/hiero_plugin_path/Templates/SharedTags.hrox @@ -1,193 +1,211 @@ - - + + - + 2 70 0 0 13 - + - - + + - + + + + + + + + - + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -199,188 +217,188 @@ 0 0 - + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - - - + + + + + - - + + - - - - - + + + + + - - + + - - - - - + + + + + - - + + - - - - - + + + + + - - + + - - - - - + + + + + - - + + - - - - - + + + + + - - + + - - - - - + + + + + - - + + - - - - - + + + + + - - + + - - - + + + @@ -392,51 +410,51 @@ 0 0 - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -461,11 +479,11 @@ 0 0 2 - - - + + + - + From b26ca6888a9a377a5bec9789ed7c3d0082ea906a Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 20 May 2019 17:21:09 +0200 Subject: [PATCH 07/48] more integration merging --- .../standalonepublish/publish/integrate.py | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/pype/plugins/standalonepublish/publish/integrate.py b/pype/plugins/standalonepublish/publish/integrate.py index b6771a52e0..d6ca24add3 100644 --- a/pype/plugins/standalonepublish/publish/integrate.py +++ b/pype/plugins/standalonepublish/publish/integrate.py @@ -1,13 +1,12 @@ import os import logging import shutil +import clique import errno import pyblish.api from avalon import api, io from avalon.vendor import filelink -import clique - log = logging.getLogger(__name__) @@ -54,8 +53,11 @@ class IntegrateAsset(pyblish.api.InstancePlugin): self.register(instance) - self.log.info("Integrating Asset in to the database ...") - self.integrate(instance) + # self.log.info("Integrating Asset in to the database ...") + # self.log.info("instance.data: {}".format(instance.data)) + if instance.data.get('transfer', True): + self.integrate(instance) + def register(self, instance): # Required environment variables @@ -122,7 +124,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): if latest_version is not None: next_version += latest_version["name"] - self.log.info("Verifying version from assumed destination") + + # self.log.info("Verifying version from assumed destination") # assumed_data = instance.data["assumedTemplateData"] # assumed_version = assumed_data["version"] @@ -143,6 +146,11 @@ class IntegrateAsset(pyblish.api.InstancePlugin): self.log.debug("Creating version ...") version_id = io.insert_one(version).inserted_id instance.data['version'] = version['name'] + + if instance.data.get('version'): + next_version = int(instance.data.get('version')) + + instance.data['version'] = next_version # Write to disk # _ # | | @@ -167,13 +175,13 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "project": {"name": PROJECT, "code": project['data']['code']}, "silo": asset['silo'], + "task": api.Session["AVALON_TASK"], "asset": ASSET, "family": instance.data['family'], "subset": subset["name"], "version": int(version["name"]), "hierarchy": hierarchy} - template_publish = project["config"]["template"]["publish"] anatomy = instance.context.data['anatomy'] # Find the representations to transfer amongst the files @@ -195,9 +203,10 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # |_______| # - files = repre['files'] + files = repre['files' + stagingdir = repre['stagigDir'] - if len(files) > 1: + if isinstance(files, list): src_collections, remainder = clique.assemble(files) self.log.debug("dst_collections: {}".format(str(src_collections))) src_collection = src_collections[0] @@ -207,11 +216,11 @@ class IntegrateAsset(pyblish.api.InstancePlugin): test_dest_files = list() for i in [1, 2]: - template_data["representation"] = src_tail[1:] + template_data["representation"] = repre['name'] template_data["frame"] = src_collection.format( "{padding}") % i anatomy_filled = anatomy.format(template_data) - test_dest_files.append(anatomy_filled["publish"]["path"]) + test_dest_files.append(anatomy_filled[repre['anatomy_template']]["path"]) dst_collections, remainder = clique.assemble(test_dest_files) dst_collection = dst_collections[0] @@ -224,13 +233,14 @@ class IntegrateAsset(pyblish.api.InstancePlugin): src_padding = src_collection.format("{padding}") % i src_file_name = "{0}{1}{2}".format( src_head, src_padding, src_tail) + + dst_padding = dst_collection.format("{padding}") % i dst = "{0}{1}{2}".format(dst_head, dst_padding, dst_tail) - # src = os.path.join(stagingdir, src_file_name) - src = src_file_name + src = os.path.join(stagingdir, src_file_name) + # src = src_file_name self.log.debug("source: {}".format(src)) - instance.data["transfers"].append([src, dst]) else: @@ -242,16 +252,17 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # | | # |_______| # - fname = files[0] - # assert not os.path.isabs(fname), ( - # "Given file name is a full path" - # ) - # _, ext = os.path.splitext(fname) + template_data.pop("frame", None) + fname = files + assert not os.path.isabs(fname), ( + "Given file name is a full path" + ) + _, ext = os.path.splitext(fname) - template_data["representation"] = repre['representation'] + template_data["representation"] = repre['name'] - # src = os.path.join(stagingdir, fname) - src = fname + src = os.path.join(stagingdir, fname) + # src = fname anatomy_filled = anatomy.format(template_data) dst = anatomy_filled["publish"]["path"] From 6538e22f97bcbea214399c43131bd9e55155c785 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 20 May 2019 17:42:51 +0200 Subject: [PATCH 08/48] created wrapper for otio ffmpeg burnin adapter --- pype/scripts/otio_burnin.py | 176 ++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 pype/scripts/otio_burnin.py diff --git a/pype/scripts/otio_burnin.py b/pype/scripts/otio_burnin.py new file mode 100644 index 0000000000..e569e66e6f --- /dev/null +++ b/pype/scripts/otio_burnin.py @@ -0,0 +1,176 @@ +import os +import opentimelineio_contrib.adapters.ffmpeg_burnins as ffmpeg_burnins +# FFmpeg in PATH is required + + +class ModifiedBurnins(ffmpeg_burnins.Burnins): + TOP_CENTERED = ffmpeg_burnins.TOP_CENTERED + BOTTOM_CENTERED = ffmpeg_burnins.BOTTOM_CENTERED + TOP_LEFT = ffmpeg_burnins.TOP_LEFT + BOTTOM_LEFT = ffmpeg_burnins.BOTTOM_LEFT + TOP_RIGHT = ffmpeg_burnins.TOP_RIGHT + BOTTOM_RIGHT = ffmpeg_burnins.BOTTOM_RIGHT + + options_init = { + 'opacity': 1, + 'x_offset': 5, + 'y_offset': 5, + 'bg_padding': 5, + 'bg_opacity': 0.5, + 'font_size': 42 + } + + def __init__(self, source, streams=None, options_init=None): + super().__init__(source, streams) + if options_init: + self.options_init.update(options_init) + + def add_text(self, text, align, options=None): + """ + Adding static text to a filter. + + :param str text: text to apply to the drawtext + :param enum align: alignment, must use provided enum flags + :param dict options: recommended to use TextOptions + """ + if not options: + options = ffmpeg_burnins.TextOptions(**self.options_init) + self._add_burnin(text, align, options, ffmpeg_burnins.DRAWTEXT) + + def add_frame_numbers(self, align, options=None, start_frame=None): + """ + Convenience method to create the frame number expression. + + :param enum align: alignment, must use provided enum flags + :param dict options: recommended to use FrameNumberOptions + """ + if not options: + options = ffmpeg_burnins.FrameNumberOptions(**self.options_init) + if start_frame: + options['frame_offset'] = start_frame + + options['expression'] = r'%%{eif\:n+%d\:d}' % options['frame_offset'] + text = str(int(self.end_frame + options['frame_offset'])) + self._add_burnin(text, align, options, ffmpeg_burnins.DRAWTEXT) + + def add_timecode(self, align, options=None, start_frame=None): + """ + Convenience method to create the frame number expression. + + :param enum align: alignment, must use provided enum flags + :param dict options: recommended to use TimeCodeOptions + """ + if not options: + options = ffmpeg_burnins.TimeCodeOptions(**self.options_init) + if start_frame: + options['frame_offset'] = start_frame + + timecode = ffmpeg_burnins._frames_to_timecode( + options['frame_offset'], + self.frame_rate + ) + options = options.copy() + if not options.get('fps'): + options['fps'] = self.frame_rate + + self._add_burnin( + timecode.replace(':', r'\:'), + align, + options, + ffmpeg_burnins.TIMECODE + ) + + def _add_burnin(self, text, align, options, draw): + """ + Generic method for building the filter flags. + :param str text: text to apply to the drawtext + :param enum align: alignment, must use provided enum flags + :param dict options: + """ + resolution = self.resolution + data = { + 'text': options.get('expression') or text, + 'color': options['font_color'], + 'size': options['font_size'] + } + data.update(options) + data.update(ffmpeg_burnins._drawtext(align, resolution, text, options)) + if 'font' in data and ffmpeg_burnins._is_windows(): + data['font'] = data['font'].replace(os.sep, r'\\' + os.sep) + data['font'] = data['font'].replace(':', r'\:') + self.filters['drawtext'].append(draw % data) + + if options.get('bg_color') is not None: + box = ffmpeg_burnins.BOX % { + 'border': options['bg_padding'], + 'color': options['bg_color'], + 'opacity': options['bg_opacity'] + } + self.filters['drawtext'][-1] += ':%s' % box + + def command(self, output=None, args=None, overwrite=False): + """ + Generate the entire FFMPEG command. + + :param str output: output file + :param str args: additional FFMPEG arguments + :param bool overwrite: overwrite the output if it exists + :returns: completed command + :rtype: str + """ + output = output or '' + if overwrite: + output = '-y {}'.format(output) + + filters = '' + if self.filter_string: + filters = '-vf "{}"'.format(self.filter_string) + + return (ffmpeg_burnins.FFMPEG % { + 'input': self.source, + 'output': output, + 'args': '%s ' % args if args else '', + 'filters': filters + }).strip() + +def example(): + input = 'path/to/input/file' + output = 'path/to/output/file' + + options_init = { + 'opacity': 1, + 'x_offset': 10, + 'y_offset': 10, + 'bg_padding': 10, + 'bg_opacity': 0.5, + 'font_size': 52 + } + # First frame in burnin + start_frame = 2000 + # Options init sets burnin look + burnin = ModifiedBurnins(input, options_init=options_init) + # Static text + burnin.add_text('My Text', ModifiedBurnins.TOP_CENTERED) + # Frame number + burnin.add_frame_numbers(ModifiedBurnins.TOP_RIGHT, start_frame=start_frame) + # Timecode + burnin.add_timecode(ModifiedBurnins.TOP_LEFT, start_frame=start_frame) + # Start render (overwrite output file if exist) + burnin.render(output, overwrite=True) + + +''' +# TODO: implement image sequence +# Changes so OpenTimelineIo burnins is possible to render from image sequence. +# +# before input: +# # -start_number is number of first frame / -r is fps +# -start_number 375 -r 25 +# before output: +# # -c: set output codec (h264, ...) +# -c:v libx264 +# +# +# ffmpeg -loglevel panic -i image_sequence -vf "drawtext=text='Test':x=w/2-tw/2:y=0:fontcolor=white@1.0:fontsize=42:fontfile='C\:\\\WINDOWS\\\Fonts\\\arial.ttf':box=1:boxborderw=5:boxcolor=black@0.5,drawtext=text='%{eif\:n+1001\:d}':x=0:y=0:fontcolor=white@1.0:fontsize=42:fontfile='C\:\\\WINDOWS\\\Fonts\\\arial.ttf':box=1:boxborderw=5:boxcolor=black@0.5" C:\Users\jakub.trllo\Desktop\Tests\files\mov\render\test_output.mov' +# ffmpeg -loglevel panic -start_number 375 -r 25 -i "C:\Users\jakub.trllo\Desktop\Tests\files\exr\int_c022_lighting_v001_main_AO.%04d.exr" -vf "drawtext=text='Test':x=w/2-tw/2:y=0:fontcolor=white@1.0:fontsize=42:fontfile='C\:\\\WINDOWS\\\Fonts\\\arial.ttf':box=1:boxborderw=5:boxcolor=black@0.5,drawtext=text='%{eif\:n+1001\:d}':x=0:y=0:fontcolor=white@1.0:fontsize=42:fontfile='C\:\\\WINDOWS\\\Fonts\\\arial.ttf':box=1:boxborderw=5:boxcolor=black@0.5,colormatrix=bt601:bt709" -c:v libx264 "output_path.mov" +''' From 0096e7bb2a386fddd3318240083fc54b6ab6e43c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 May 2019 22:13:26 +0200 Subject: [PATCH 09/48] feat(nukestudio): creating Tags from presets --- pype/nukestudio/tags.py | 73 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 pype/nukestudio/tags.py diff --git a/pype/nukestudio/tags.py b/pype/nukestudio/tags.py new file mode 100644 index 0000000000..09a0d79b92 --- /dev/null +++ b/pype/nukestudio/tags.py @@ -0,0 +1,73 @@ +import hiero +import re +from pypeapp import config + + +def create_tag(key, value): + """ + Creating Tag object. + + Args: + key (str): name of tag + value (dict): parameters of tag + + Returns: + object: Tag object + """ + + tag = hiero.core.Tag(str(key)) + tag.setNote(value['note']) + tag.setIcon(value['icon']['path']) + mtd = tag.metadata() + pres_mtd = value.get('metadata', None) + if pres_mtd: + [mtd.setValue("tag.{}".format(k), v) + for k, v in pres_mtd.items()] + + return tag + + +def add_tags_from_presets(): + """ + Will create default tags from presets. + """ + + # get all presets + presets = config.get_presets() + + # get nukestudio tag.json from presets + nks_pres = presets['nukestudio'] + nks_pres_tags = nks_pres.get("tags", None) + + # get project and root bin object + project = hiero.core.projects()[-1] + root_bin = project.tagsBin() + + for _k, _val in nks_pres_tags.items(): + + pattern = re.compile(r"\[(.*)\]") + bin_find = pattern.findall(_k) + if bin_find: + # create Bin object + bin = hiero.core.Bin(str(bin_find[0])) + + for k, v in _val.items(): + # create Tag obj + tag = create_tag(k, v) + + # adding Tag to Bin + bin.addItem(tag) + + # adding Tag to Root Bin + root_bin.addItem(bin) + + else: + print(_k, _val) + # create Tag + tag = create_tag(_k, _val) + + # adding Tag to Root Bin + root_bin.addItem(tag) + + +add_tags_from_presets() From f7691b41216ba36a04ee7b3b963e09e516c8dd9e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 May 2019 22:14:02 +0200 Subject: [PATCH 10/48] feat(nukestudio): dealing with Qt.py --- pype/nukestudio/lib.py | 6 +++++- pype/nukestudio/menu.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pype/nukestudio/lib.py b/pype/nukestudio/lib.py index fba8572235..bb33bf5efb 100644 --- a/pype/nukestudio/lib.py +++ b/pype/nukestudio/lib.py @@ -8,7 +8,11 @@ import pyblish.api # Host libraries import hiero -from PySide2 import (QtWidgets, QtGui) +from pypeapp import Logger +log = Logger().get_logger(__name__, "nukestudio") + + +from avalon.vendor.Qt import (QtWidgets, QtGui) cached_process = None diff --git a/pype/nukestudio/menu.py b/pype/nukestudio/menu.py index 5244a0b527..9eb65ab202 100644 --- a/pype/nukestudio/menu.py +++ b/pype/nukestudio/menu.py @@ -26,7 +26,7 @@ def install(): libraryloader ) - menu_name = os.environ['PYPE_STUDIO_NAME'] + menu_name = os.environ['AVALON_LABEL'] # Grab Hiero's MenuBar M = hiero.ui.menuBar() From 8445e4cb9add4276f55f78c634861271ebb6c19c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 May 2019 22:14:26 +0200 Subject: [PATCH 11/48] fix(nuke): dealing with Qt.py --- pype/nuke/__init__.py | 6 ----- pype/nuke/lib.py | 55 +++++++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/pype/nuke/__init__.py b/pype/nuke/__init__.py index a2b1aeda6e..912585feb8 100644 --- a/pype/nuke/__init__.py +++ b/pype/nuke/__init__.py @@ -109,12 +109,6 @@ 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) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 20e7dfb210..06735fc8b0 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -3,7 +3,6 @@ import sys import os from collections import OrderedDict from pprint import pprint -from avalon.vendor.Qt import QtGui from avalon import api, io, lib import avalon.nuke import pype.api as pype @@ -20,6 +19,12 @@ self = sys.modules[__name__] self._project = None +for path in sys.path: + log.info(os.path.normpath(path)) + if "C:\\Users\\Public" in os.path.normpath(path): + log.info("_ removing from sys.path: `{}`".format(path)) + sys.path.remove(path) + def onScriptLoad(): if nuke.env['LINUX']: nuke.tcl('load ffmpegReader') @@ -472,30 +477,30 @@ def update_frame_range(start, end, root=None): else: nuke.root()[key].setValue(value) - -def get_additional_data(container): - """Get Nuke's related data for the container - - Args: - container(dict): the container found by the ls() function - - Returns: - dict - """ - - node = container["_tool"] - tile_color = node['tile_color'].value() - if tile_color is None: - return {} - - hex = '%08x' % tile_color - rgba = [ - float(int(hex[0:2], 16)) / 255.0, - float(int(hex[2:4], 16)) / 255.0, - float(int(hex[4:6], 16)) / 255.0 - ] - - return {"color": QtGui.QColor().fromRgbF(rgba[0], rgba[1], rgba[2])} +# +# def get_additional_data(container): +# """Get Nuke's related data for the container +# +# Args: +# container(dict): the container found by the ls() function +# +# Returns: +# dict +# """ +# +# node = container["_tool"] +# tile_color = node['tile_color'].value() +# if tile_color is None: +# return {} +# +# hex = '%08x' % tile_color +# rgba = [ +# float(int(hex[0:2], 16)) / 255.0, +# float(int(hex[2:4], 16)) / 255.0, +# float(int(hex[4:6], 16)) / 255.0 +# ] +# +# return {"color": Qt.QtGui.QColor().fromRgbF(rgba[0], rgba[1], rgba[2])} def get_write_node_template_attr(node): From 5a56919d1e64bb3bb6c4f61bd1dc539654fe8035 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 21 May 2019 15:04:06 +0200 Subject: [PATCH 12/48] added burnin example with presets --- pype/scripts/otio_burnin.py | 72 ++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/pype/scripts/otio_burnin.py b/pype/scripts/otio_burnin.py index e569e66e6f..034484a442 100644 --- a/pype/scripts/otio_burnin.py +++ b/pype/scripts/otio_burnin.py @@ -1,8 +1,13 @@ import os import opentimelineio_contrib.adapters.ffmpeg_burnins as ffmpeg_burnins +from pypeapp.lib import config +from pype import api as pype # FFmpeg in PATH is required +log = pype.Logger().get_logger("BurninWrapper", "burninwrap") + + class ModifiedBurnins(ffmpeg_burnins.Burnins): TOP_CENTERED = ffmpeg_burnins.TOP_CENTERED BOTTOM_CENTERED = ffmpeg_burnins.BOTTOM_CENTERED @@ -133,10 +138,8 @@ class ModifiedBurnins(ffmpeg_burnins.Burnins): 'filters': filters }).strip() -def example(): - input = 'path/to/input/file' - output = 'path/to/output/file' +def example(input_path, output_path): options_init = { 'opacity': 1, 'x_offset': 10, @@ -148,7 +151,7 @@ def example(): # First frame in burnin start_frame = 2000 # Options init sets burnin look - burnin = ModifiedBurnins(input, options_init=options_init) + burnin = ModifiedBurnins(input_path, options_init=options_init) # Static text burnin.add_text('My Text', ModifiedBurnins.TOP_CENTERED) # Frame number @@ -156,7 +159,66 @@ def example(): # Timecode burnin.add_timecode(ModifiedBurnins.TOP_LEFT, start_frame=start_frame) # Start render (overwrite output file if exist) - burnin.render(output, overwrite=True) + burnin.render(output_path, overwrite=True) + + +def example_with_presets(input_path, output_path, data): + presets = config.get_presets().get('tools', {}).get('burnins', {}) + options_init = presets.get('options') + + burnin = ModifiedBurnins(input_path, options_init=options_init) + + start_frame = data.get("start_frame") + for align_text, preset in presets.get('burnins', {}).items(): + align = None + if align_text == 'TOP_LEFT': + align = ModifiedBurnins.TOP_LEFT + elif align_text == 'TOP_CENTERED': + align = ModifiedBurnins.TOP_CENTERED + elif align_text == 'TOP_RIGHT': + align = ModifiedBurnins.TOP_RIGHT + elif align_text == 'BOTTOM_LEFT': + align = ModifiedBurnins.BOTTOM_LEFT + elif align_text == 'BOTTOM_CENTERED': + align = ModifiedBurnins.BOTTOM_CENTERED + elif align_text == 'BOTTOM_RIGHT': + align = ModifiedBurnins.BOTTOM_RIGHT + + bi_func = preset.get('function') + if not bi_func: + log.error( + 'Missing function for burnin!' + 'Burnins are not created!' + ) + return + + if ( + bi_func in ['frame_numbers', 'timecode'] and + not start_frame + ): + log.error( + 'start_frame is not set in entered data!' + 'Burnins are not created!' + ) + return + + if bi_func == 'frame_numbers': + burnin.add_frame_numbers(align, start_frame=start_frame) + elif bi_func == 'timecode': + burnin.add_timecode(align, start_frame=start_frame) + elif: bi_func == 'text': + if not preset.get('text'): + log.error('Text is not set for text function burnin!') + return + text = preset['text'].format(**data) + burnin.add_text(text, align) + else: + log.error( + 'Unknown function for burnins {}'.format(bi_func) + ) + return + + burnin.render(output_path, overwrite=True) ''' From ba94a577b00663cae685ec3b9f21817598e92cf4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 May 2019 17:38:30 +0200 Subject: [PATCH 13/48] feat(nukestudio): updating Tags workflow --- pype/nukestudio/__init__.py | 1 + pype/nukestudio/menu.py | 9 +- pype/nukestudio/tags.py | 67 ++- .../nukestudio/hiero_plugin_path/Icons/2D.png | Bin 0 -> 23980 bytes .../nukestudio/hiero_plugin_path/Icons/3D.png | Bin 0 -> 38657 bytes .../hiero_plugin_path/Icons/add_handles.png | Bin 0 -> 36038 bytes .../Icons/add_handles_end.png | Bin 0 -> 37252 bytes .../Icons/add_handles_start.png | Bin 0 -> 37509 bytes .../hiero_plugin_path/Icons/edit.png | Bin 0 -> 8031 bytes .../{Templates => Icons}/fusion.png | Bin .../hiero_plugin_path/Icons/hierarchy.png | Bin 0 -> 16086 bytes .../{Templates => Icons}/houdini.png | Bin .../{Templates => Icons}/maya.png | Bin .../{Templates => Icons}/nuke.png | Bin .../{Templates => Icons}/volume.png | Bin .../Templates/SharedTags.hrox | 490 ------------------ .../hiero_plugin_path/Templates/hierarchy.png | Bin 90074 -> 0 bytes 17 files changed, 58 insertions(+), 509 deletions(-) create mode 100644 setup/nukestudio/hiero_plugin_path/Icons/2D.png create mode 100644 setup/nukestudio/hiero_plugin_path/Icons/3D.png create mode 100644 setup/nukestudio/hiero_plugin_path/Icons/add_handles.png create mode 100644 setup/nukestudio/hiero_plugin_path/Icons/add_handles_end.png create mode 100644 setup/nukestudio/hiero_plugin_path/Icons/add_handles_start.png create mode 100644 setup/nukestudio/hiero_plugin_path/Icons/edit.png rename setup/nukestudio/hiero_plugin_path/{Templates => Icons}/fusion.png (100%) create mode 100644 setup/nukestudio/hiero_plugin_path/Icons/hierarchy.png rename setup/nukestudio/hiero_plugin_path/{Templates => Icons}/houdini.png (100%) rename setup/nukestudio/hiero_plugin_path/{Templates => Icons}/maya.png (100%) rename setup/nukestudio/hiero_plugin_path/{Templates => Icons}/nuke.png (100%) rename setup/nukestudio/hiero_plugin_path/{Templates => Icons}/volume.png (100%) delete mode 100644 setup/nukestudio/hiero_plugin_path/Templates/SharedTags.hrox delete mode 100644 setup/nukestudio/hiero_plugin_path/Templates/hierarchy.png diff --git a/pype/nukestudio/__init__.py b/pype/nukestudio/__init__.py index 36f3453cf7..ecee86c9fc 100644 --- a/pype/nukestudio/__init__.py +++ b/pype/nukestudio/__init__.py @@ -93,6 +93,7 @@ def install(config): api.load_data_from_templates() + def uninstall(): log.info("Deregistering NukeStudio plug-ins..") pyblish.deregister_host("nukestudio") diff --git a/pype/nukestudio/menu.py b/pype/nukestudio/menu.py index 9eb65ab202..2b038d69f1 100644 --- a/pype/nukestudio/menu.py +++ b/pype/nukestudio/menu.py @@ -1,6 +1,4 @@ import os -from avalon.api import Session -from pprint import pprint import hiero.core @@ -12,7 +10,7 @@ except Exception: from hiero.ui import findMenuAction - +from .tags import add_tags_from_presets # def install(): # here is the best place to add menu @@ -55,6 +53,11 @@ def install(): 'function': (lambda: workfiles.show(os.environ["AVALON_WORKDIR"])), 'icon': QIcon('icons:Position.png') }, + { + 'action': QAction('Create Default Tags..', None), + 'function': add_tags_from_presets, + 'icon': QIcon('icons:Position.png') + }, "separator", { 'action': QAction('Create...', None), diff --git a/pype/nukestudio/tags.py b/pype/nukestudio/tags.py index 09a0d79b92..863ef80eeb 100644 --- a/pype/nukestudio/tags.py +++ b/pype/nukestudio/tags.py @@ -26,6 +26,23 @@ def create_tag(key, value): return tag +def update_tag(tag, value): + """ + Fixing Tag object. + + Args: + tag (obj): Tag object + value (dict): parameters of tag + """ + + tag.setNote(value['note']) + tag.setIcon(value['icon']['path']) + mtd = tag.metadata() + pres_mtd = value.get('metadata', None) + if pres_mtd: + [mtd.setValue("tag.{}".format(k), v) + for k, v in pres_mtd.items()] + def add_tags_from_presets(): """ @@ -44,30 +61,48 @@ def add_tags_from_presets(): root_bin = project.tagsBin() for _k, _val in nks_pres_tags.items(): - pattern = re.compile(r"\[(.*)\]") bin_find = pattern.findall(_k) if bin_find: - # create Bin object - bin = hiero.core.Bin(str(bin_find[0])) + # check what is in root bin + bins = [b for b in root_bin.items() + if b.name() in str(bin_find[0])] + + if bins: + bin = bins[0] + else: + # create Bin object + bin = hiero.core.Bin(str(bin_find[0])) for k, v in _val.items(): - # create Tag obj - tag = create_tag(k, v) + tags = [t for t in bin.items() + if str(k) in t.name()] - # adding Tag to Bin - bin.addItem(tag) + if not tags: + # create Tag obj + tag = create_tag(k, v) - # adding Tag to Root Bin - root_bin.addItem(bin) + # adding Tag to Bin + bin.addItem(tag) + else: + # update Tag if already exists + update_tag(tags[0], v) + + if not bins: + # adding Tag to Root Bin + root_bin.addItem(bin) else: - print(_k, _val) - # create Tag - tag = create_tag(_k, _val) + tags = None + tags = [t for t in root_bin.items() + if str(_k) in t.name()] - # adding Tag to Root Bin - root_bin.addItem(tag) + if not tags: + # create Tag + tag = create_tag(_k, _val) - -add_tags_from_presets() + # adding Tag to Root Bin + root_bin.addItem(tag) + else: + # update Tag if already exists + update_tag(tags[0], _val) diff --git a/setup/nukestudio/hiero_plugin_path/Icons/2D.png b/setup/nukestudio/hiero_plugin_path/Icons/2D.png new file mode 100644 index 0000000000000000000000000000000000000000..69a22ad81500b9fbe4382e5af6a67bedf2b1ba2f GIT binary patch literal 23980 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 O29*YS6!9h|wcZ-u!Wnibek2XuJPDnA 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 literal 0 HcmV?d00001 diff --git a/setup/nukestudio/hiero_plugin_path/Icons/3D.png b/setup/nukestudio/hiero_plugin_path/Icons/3D.png new file mode 100644 index 0000000000000000000000000000000000000000..b3a4af9e24b389749a19eab921890ec083571d8c GIT binary patch literal 38657 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%0DmWNd9L*J>ubq}I2y)|*sh^FA<__tGTTWdVUb}0 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 z%0A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

Sb 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 literal 0 HcmV?d00001 diff --git a/setup/nukestudio/hiero_plugin_path/Icons/add_handles_start.png b/setup/nukestudio/hiero_plugin_path/Icons/add_handles_start.png new file mode 100644 index 0000000000000000000000000000000000000000..ecfd2c2621250502cbb5006627cf5b43c8edbbe2 GIT binary patch literal 37509 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 SC2)EKZfsz#U#oZZ#{U7UkM%kL literal 0 HcmV?d00001 diff --git a/setup/nukestudio/hiero_plugin_path/Templates/fusion.png b/setup/nukestudio/hiero_plugin_path/Icons/fusion.png similarity index 100% rename from setup/nukestudio/hiero_plugin_path/Templates/fusion.png rename to setup/nukestudio/hiero_plugin_path/Icons/fusion.png diff --git a/setup/nukestudio/hiero_plugin_path/Icons/hierarchy.png b/setup/nukestudio/hiero_plugin_path/Icons/hierarchy.png new file mode 100644 index 0000000000000000000000000000000000000000..55b4f9843bf09da248929e5f296396ff8d65c58a GIT binary patch literal 16086 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 - - - - - - 2 - 70 - 0 - 0 - 13 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2 - 70 - 0 - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 - 70 - 0 - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2 - 70 - 0 - 0 - - - 2 - 70 - 0 - 0 - 17 - - - 2 - 70 - 0 - 0 - 2 - - - - - - - - - diff --git a/setup/nukestudio/hiero_plugin_path/Templates/hierarchy.png b/setup/nukestudio/hiero_plugin_path/Templates/hierarchy.png deleted file mode 100644 index 461d2616040abfbd87f07eee4f1ce90476f7b82b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90074 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ⅇ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 From 693ad2ab420fecd18b6dd348809853d423a10973 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 22 May 2019 00:13:29 +0200 Subject: [PATCH 14/48] make ftrack integrators more robust and flexible --- .../plugins/standalonepublish/publish/collect_ftrack_api.py | 1 + .../standalonepublish/publish/integrate_ftrack_api.py | 2 +- .../standalonepublish/publish/integrate_ftrack_instances.py | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pype/plugins/standalonepublish/publish/collect_ftrack_api.py b/pype/plugins/standalonepublish/publish/collect_ftrack_api.py index 6df998350c..b15211e990 100644 --- a/pype/plugins/standalonepublish/publish/collect_ftrack_api.py +++ b/pype/plugins/standalonepublish/publish/collect_ftrack_api.py @@ -24,6 +24,7 @@ class CollectFtrackApi(pyblish.api.ContextPlugin): project = os.environ.get('AVALON_PROJECT', '') asset = os.environ.get('AVALON_ASSET', '') task = os.environ.get('AVALON_TASK', None) + self.log.debug(task) if task: result = session.query('Task where\ diff --git a/pype/plugins/standalonepublish/publish/integrate_ftrack_api.py b/pype/plugins/standalonepublish/publish/integrate_ftrack_api.py index 9eff10ba67..2c35b974f9 100644 --- a/pype/plugins/standalonepublish/publish/integrate_ftrack_api.py +++ b/pype/plugins/standalonepublish/publish/integrate_ftrack_api.py @@ -58,7 +58,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): session = instance.context.data["ftrackSession"] if instance.context.data.get("ftrackTask"): task = instance.context.data["ftrackTask"] - name = task['full_name'] + name = task parent = task["parent"] elif instance.context.data.get("ftrackEntity"): task = None diff --git a/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py b/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py index 0dc9bb137c..2605d95494 100644 --- a/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py +++ b/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py @@ -60,7 +60,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): if not comp.get('startFrameReview'): comp['startFrameReview'] = comp['startFrame'] if not comp.get('endFrameReview'): - comp['endFrameReview'] = instance.data['endFrame'] + comp['endFrameReview'] = comp['endFrame'] location = ft_session.query( 'Location where name is "ftrack.server"').one() component_data = { @@ -69,11 +69,11 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): "metadata": {'ftr_meta': json.dumps({ 'frameIn': int(comp['startFrameReview']), 'frameOut': int(comp['endFrameReview']), - 'frameRate': float(comp['frameRate')]})} + 'frameRate': comp['frameRate']})} } else: component_data = { - "name": comp['representation'] # Default component name is "main". + "name": comp['name'] } location = ft_session.query( 'Location where name is "ftrack.unmanaged"').one() From 4cc9d98c97508563425aa1cd16860536d2189859 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 22 May 2019 00:13:43 +0200 Subject: [PATCH 15/48] add avalon task to context --- pype/standalonepublish/publish.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pype/standalonepublish/publish.py b/pype/standalonepublish/publish.py index 4442f0243c..24ce6bc60b 100644 --- a/pype/standalonepublish/publish.py +++ b/pype/standalonepublish/publish.py @@ -30,7 +30,7 @@ pyblish.api.register_plugin_path(PUBLISH_PATH) # pyblish.api.register_plugin_path(PUBLISH_PATH) -def set_context(project, asset, app): +def set_context(project, asset, task, app): ''' Sets context for pyblish (must be done before pyblish is launched) :param project: Name of `Project` where instance should be published :type project: str @@ -41,6 +41,11 @@ def set_context(project, asset, app): io.Session["AVALON_PROJECT"] = project os.environ["AVALON_ASSET"] = asset io.Session["AVALON_ASSET"] = asset + if not task: + task = '' + os.environ["AVALON_TASK"] = task + io.Session["AVALON_TASK"] = task + io.install() From baa5ecba3a21c327c7e26d4e66df56e00dbf41cb Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 22 May 2019 00:14:16 +0200 Subject: [PATCH 16/48] add task to context and collect context json correctly --- .../publish/collect_context.py | 23 +++++++++---------- .../widgets/widget_components.py | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/pype/plugins/standalonepublish/publish/collect_context.py b/pype/plugins/standalonepublish/publish/collect_context.py index 6ac2dca936..055501a50d 100644 --- a/pype/plugins/standalonepublish/publish/collect_context.py +++ b/pype/plugins/standalonepublish/publish/collect_context.py @@ -1,9 +1,6 @@ import os import pyblish.api -from avalon import ( - io, - api as avalon -) +from avalon import io import json import logging import clique @@ -33,13 +30,12 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin): input_json_path = os.environ.get("SAPUBLISH_INPATH") output_json_path = os.environ.get("SAPUBLISH_OUTPATH") - context.data["stagingDir"] = os.path.dirname(input_json_path) + # context.data["stagingDir"] = os.path.dirname(input_json_path) context.data["returnJsonPath"] = output_json_path with open(input_json_path, "r") as f: in_data = json.load(f) - project_name = in_data['project'] asset_name = in_data['asset'] family = in_data['family'] subset = in_data['subset'] @@ -67,21 +63,24 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin): instance.data["files"] = list() instance.data['destination_list'] = list() instance.data['representations'] = list() + instance.data['source'] = 'standalone publisher' for component in in_data['representations']: - # instance.add(node) + component['destination'] = component['files'] + component['stagingDir'] = component['stagingDir'] + component['anatomy_template'] = 'render' collections, remainder = clique.assemble(component['files']) if collections: self.log.debug(collections) - instance.data['startFrame'] = component['startFrame'] - instance.data['endFrame'] = component['endFrame'] - instance.data['frameRate'] = component['frameRate'] + instance.data['startFrame'] = int(component['startFrame']) + instance.data['endFrame'] = int(component['endFrame']) + instance.data['frameRate'] = int(component['frameRate']) instance.data["files"].append(component) instance.data["representations"].append(component) - # "is_thumbnail": component['thumbnail'], - # "is_preview": component['preview'] + instance.data["thumbnail"] = component['thumbnail'] + instance.data["preview"] = component['preview'] self.log.info(in_data) diff --git a/pype/standalonepublish/widgets/widget_components.py b/pype/standalonepublish/widgets/widget_components.py index 1e1fdf88e3..90167f2fa6 100644 --- a/pype/standalonepublish/widgets/widget_components.py +++ b/pype/standalonepublish/widgets/widget_components.py @@ -117,7 +117,7 @@ class ComponentsWidget(QtWidgets.QWidget): try: data = self.parent_widget.collect_data() publish.set_context( - data['project'], data['asset'], 'standalonepublish' + data['project'], data['asset'], data['task'], 'standalonepublish' ) result = publish.publish(data) # Clear widgets from components list if publishing was successful From 46a129f59eff519b48ad1ec27f64f4a93eed8de2 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 22 May 2019 00:14:48 +0200 Subject: [PATCH 17/48] get all representation data from the instance.data['representations'] --- .../standalonepublish/publish/integrate.py | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/pype/plugins/standalonepublish/publish/integrate.py b/pype/plugins/standalonepublish/publish/integrate.py index d6ca24add3..4b2d539727 100644 --- a/pype/plugins/standalonepublish/publish/integrate.py +++ b/pype/plugins/standalonepublish/publish/integrate.py @@ -39,7 +39,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "yetiRig", "yeticache", "nukescript", - # "review", + "review", "workfile", "scene", "ass"] @@ -53,8 +53,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): self.register(instance) - # self.log.info("Integrating Asset in to the database ...") - # self.log.info("instance.data: {}".format(instance.data)) + self.log.info("Integrating Asset in to the database ...") + self.log.info("instance.data: {}".format(instance.data)) if instance.data.get('transfer', True): self.integrate(instance) @@ -88,13 +88,15 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # ^ # | # - # stagingdir = instance.data.get("stagingDir") - # assert stagingdir, ("Incomplete instance \"%s\": " - # "Missing reference to staging area." % instance) + stagingdir = instance.data.get("stagingDir") + if not stagingdir: + self.log.info("{} is missing reference to staging \ + directory Will try to get it from \ + representation".format(instance)) # extra check if stagingDir actually exists and is available - # self.log.debug("Establishing staging directory @ %s" % stagingdir) + self.log.debug("Establishing staging directory @ %s" % stagingdir) # Ensure at least one file is set up for transfer in staging dir. files = instance.data.get("files", []) @@ -124,6 +126,9 @@ class IntegrateAsset(pyblish.api.InstancePlugin): if latest_version is not None: next_version += latest_version["name"] + if instance.data.get('version'): + next_version = int(instance.data.get('version')) + # self.log.info("Verifying version from assumed destination") @@ -147,10 +152,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): version_id = io.insert_one(version).inserted_id instance.data['version'] = version['name'] - if instance.data.get('version'): - next_version = int(instance.data.get('version')) - - instance.data['version'] = next_version # Write to disk # _ # | | @@ -203,8 +204,10 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # |_______| # - files = repre['files' - stagingdir = repre['stagigDir'] + files = repre['files'] + if repre['stagingDir']: + stagingdir = repre['stagingDir'] + template = anatomy.templates[repre['anatomy_template']]["path"] if isinstance(files, list): src_collections, remainder = clique.assemble(files) @@ -216,7 +219,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): test_dest_files = list() for i in [1, 2]: - template_data["representation"] = repre['name'] + template_data["representation"] = repre['ext'] template_data["frame"] = src_collection.format( "{padding}") % i anatomy_filled = anatomy.format(template_data) @@ -259,22 +262,22 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ) _, ext = os.path.splitext(fname) - template_data["representation"] = repre['name'] + template_data["representation"] = repre['ext'] src = os.path.join(stagingdir, fname) # src = fname anatomy_filled = anatomy.format(template_data) - dst = anatomy_filled["publish"]["path"] + dst = anatomy_filled[repre['anatomy_template']]["path"] instance.data["transfers"].append([src, dst]) - template = anatomy.templates["publish"]["path"] + # template = anatomy.templates["publish"]["path"] instance.data["representations"][idx]['published_path'] = dst representation = { "schema": "pype:representation-2.0", "type": "representation", "parent": version_id, - "name": repre['representation'], + "name": repre['name'], "data": {'path': dst, 'template': template}, "dependencies": instance.data.get("dependencies", "").split(), @@ -428,18 +431,15 @@ class IntegrateAsset(pyblish.api.InstancePlugin): families += current_families self.log.debug("Registered root: {}".format(api.registered_root())) - # # create relative source path for DB - # try: - # source = instance.data['source'] - # except KeyError: - # source = context.data["currentFile"] - # - # relative_path = os.path.relpath(source, api.registered_root()) - # source = os.path.join("{root}", relative_path).replace("\\", "/") + # create relative source path for DB + try: + source = instance.data['source'] + except KeyError: + source = context.data["currentFile"] + relative_path = os.path.relpath(source, api.registered_root()) + source = os.path.join("{root}", relative_path).replace("\\", "/") - source = "standalone" - - # self.log.debug("Source: {}".format(source)) + self.log.debug("Source: {}".format(source)) version_data = {"families": families, "time": context.data["time"], "author": context.data["user"], From 4d92e51f91dc75a03382fa6dad42d86bddf4d324 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 22 May 2019 00:15:00 +0200 Subject: [PATCH 18/48] remove redundant integrator --- .../publish/integrate_rendered_frames.py | 436 ------------------ 1 file changed, 436 deletions(-) delete mode 100644 pype/plugins/standalonepublish/publish/integrate_rendered_frames.py diff --git a/pype/plugins/standalonepublish/publish/integrate_rendered_frames.py b/pype/plugins/standalonepublish/publish/integrate_rendered_frames.py deleted file mode 100644 index 43653ab0ed..0000000000 --- a/pype/plugins/standalonepublish/publish/integrate_rendered_frames.py +++ /dev/null @@ -1,436 +0,0 @@ -import os -import logging -import shutil -import clique - -import errno -import pyblish.api -from avalon import api, io - - -log = logging.getLogger(__name__) - - -class IntegrateFrames(pyblish.api.InstancePlugin): - """Resolve any dependency issies - - This plug-in resolves any paths which, if not updated might break - the published file. - - The order of families is important, when working with lookdev you want to - first publish the texture, update the texture paths in the nodes and then - publish the shading network. Same goes for file dependent assets. - """ - - label = "Integrate Frames" - order = pyblish.api.IntegratorOrder - families = [ - "imagesequence", - "render", - "write", - "source", - 'review'] - - family_targets = [".frames", ".local", ".review", "review", "imagesequence", "render", "source"] - exclude_families = ["clip"] - - def process(self, instance): - if [ef for ef in self.exclude_families - if instance.data["family"] in ef]: - return - - families = [f for f in instance.data["families"] - for search in self.family_targets - if search in f] - - if not families: - return - - self.register(instance) - - # self.log.info("Integrating Asset in to the database ...") - # self.log.info("instance.data: {}".format(instance.data)) - if instance.data.get('transfer', True): - self.integrate(instance) - - def register(self, instance): - - # Required environment variables - PROJECT = api.Session["AVALON_PROJECT"] - ASSET = instance.data.get("asset") or api.Session["AVALON_ASSET"] - LOCATION = api.Session["AVALON_LOCATION"] - - context = instance.context - # Atomicity - # - # Guarantee atomic publishes - each asset contains - # an identical set of members. - # __ - # / o - # / \ - # | o | - # \ / - # o __/ - # - assert all(result["success"] for result in context.data["results"]), ( - "Atomicity not held, aborting.") - - # Assemble - # - # | - # v - # ---> <---- - # ^ - # | - # - # stagingdir = instance.data.get("stagingDir") - # assert stagingdir, ("Incomplete instance \"%s\": " - # "Missing reference to staging area." % instance) - - # extra check if stagingDir actually exists and is available - - # self.log.debug("Establishing staging directory @ %s" % stagingdir) - - project = io.find_one({"type": "project"}) - - asset = io.find_one({"type": "asset", - "name": ASSET, - "parent": project["_id"]}) - - assert all([project, asset]), ("Could not find current project or " - "asset '%s'" % ASSET) - - subset = self.get_subset(asset, instance) - - # get next version - latest_version = io.find_one({"type": "version", - "parent": subset["_id"]}, - {"name": True}, - sort=[("name", -1)]) - - next_version = 1 - if latest_version is not None: - next_version += latest_version["name"] - - self.log.info("Verifying version from assumed destination") - - # assumed_data = instance.data["assumedTemplateData"] - # assumed_version = assumed_data["version"] - # if assumed_version != next_version: - # raise AttributeError("Assumed version 'v{0:03d}' does not match" - # "next version in database " - # "('v{1:03d}')".format(assumed_version, - # next_version)) - - if instance.data.get('version'): - next_version = int(instance.data.get('version')) - - instance.data['version'] = next_version - - self.log.debug("Next version: v{0:03d}".format(next_version)) - - version_data = self.create_version_data(context, instance) - version = self.create_version(subset=subset, - version_number=next_version, - locations=[LOCATION], - data=version_data) - - self.log.debug("Creating version ...") - version_id = io.insert_one(version).inserted_id - - # Write to disk - # _ - # | | - # _| |_ - # ____\ / - # |\ \ / \ - # \ \ v \ - # \ \________. - # \|________| - # - root = api.registered_root() - hierarchy = "" - parents = io.find_one({"type": 'asset', "name": ASSET})[ - 'data']['parents'] - if parents and len(parents) > 0: - # hierarchy = os.path.sep.join(hierarchy) - hierarchy = os.path.join(*parents) - - template_data = {"root": root, - "project": {"name": PROJECT, - "code": project['data']['code']}, - "silo": asset['silo'], - "task": api.Session["AVALON_TASK"], - "asset": ASSET, - "family": instance.data['family'], - "subset": subset["name"], - "version": int(version["name"]), - "hierarchy": hierarchy} - - # template_publish = project["config"]["template"]["publish"] - anatomy = instance.context.data['anatomy'] - - # Find the representations to transfer amongst the files - # Each should be a single representation (as such, a single extension) - representations = [] - destination_list = [] - - if 'transfers' not in instance.data: - instance.data['transfers'] = [] - - # for repre in instance.data["representations"]: - for idx, repre in enumerate(instance.data["representations"]): - # Collection - # _______ - # |______|\ - # | |\| - # | || - # | || - # | || - # |_______| - # - - files = repre['files'] - - if len(files) > 1: - - src_collections, remainder = clique.assemble(files) - self.log.debug("dst_collections: {}".format(str(src_collections))) - src_collection = src_collections[0] - # Assert that each member has identical suffix - src_head = src_collection.format("{head}") - src_tail = ext = src_collection.format("{tail}") - - test_dest_files = list() - for i in [1, 2]: - template_data["representation"] = repre['representation'] - template_data["frame"] = src_collection.format( - "{padding}") % i - anatomy_filled = anatomy.format(template_data) - test_dest_files.append(anatomy_filled["render"]["path"]) - - dst_collections, remainder = clique.assemble(test_dest_files) - dst_collection = dst_collections[0] - dst_head = dst_collection.format("{head}") - dst_tail = dst_collection.format("{tail}") - - instance.data["representations"][idx]['published_path'] = dst_collection.format() - - for i in src_collection.indexes: - src_padding = src_collection.format("{padding}") % i - src_file_name = "{0}{1}{2}".format( - src_head, src_padding, src_tail) - dst_padding = dst_collection.format("{padding}") % i - dst = "{0}{1}{2}".format(dst_head, dst_padding, dst_tail) - - # src = os.path.join(stagingdir, src_file_name) - src = src_file_name - self.log.debug("source: {}".format(src)) - - instance.data["transfers"].append([src, dst]) - - else: - # Single file - # _______ - # | |\ - # | | - # | | - # | | - # |_______| - # - - template_data.pop("frame", None) - - fname = files[0] - - self.log.info("fname: {}".format(fname)) - - # assert not os.path.isabs(fname), ( - # "Given file name is a full path" - # ) - # _, ext = os.path.splitext(fname) - - template_data["representation"] = repre['representation'] - - # src = os.path.join(stagingdir, fname) - src = src_file_name - - anatomy_filled = anatomy.format(template_data) - dst = anatomy_filled["render"]["path"] - - instance.data["transfers"].append([src, dst]) - instance.data["representations"][idx]['published_path'] = dst - - if repre['ext'] not in ["jpeg", "jpg", "mov", "mp4", "wav"]: - template_data["frame"] = "#" * int(anatomy_filled["render"]["padding"]) - - anatomy_filled = anatomy.format(template_data) - path_to_save = anatomy_filled["render"]["path"] - template = anatomy.templates["render"]["path"] - - self.log.debug("path_to_save: {}".format(path_to_save)) - - representation = { - "schema": "pype:representation-2.0", - "type": "representation", - "parent": version_id, - "name": repre['representation'], - "data": {'path': path_to_save, 'template': template}, - "dependencies": instance.data.get("dependencies", "").split(), - - # Imprint shortcut to context - # for performance reasons. - "context": { - "root": root, - "project": { - "name": PROJECT, - "code": project['data']['code'] - }, - "task": api.Session["AVALON_TASK"], - "silo": asset['silo'], - "asset": ASSET, - "family": instance.data['family'], - "subset": subset["name"], - "version": int(version["name"]), - "hierarchy": hierarchy, - "representation": repre['representation'] - } - } - - destination_list.append(dst) - instance.data['destination_list'] = destination_list - representations.append(representation) - - self.log.info("Registering {} items".format(len(representations))) - io.insert_many(representations) - - def integrate(self, instance): - """Move the files - - Through `instance.data["transfers"]` - - Args: - instance: the instance to integrate - """ - - transfers = instance.data["transfers"] - - for src, dest in transfers: - src = os.path.normpath(src) - dest = os.path.normpath(dest) - if src in dest: - continue - - self.log.info("Copying file .. {} -> {}".format(src, dest)) - self.copy_file(src, dest) - - def copy_file(self, src, dst): - """ Copy given source to destination - - Arguments: - src (str): the source file which needs to be copied - dst (str): the destination of the sourc file - Returns: - None - """ - - dirname = os.path.dirname(dst) - try: - os.makedirs(dirname) - except OSError as e: - if e.errno == errno.EEXIST: - pass - else: - self.log.critical("An unexpected error occurred.") - raise - - shutil.copy(src, dst) - - def get_subset(self, asset, instance): - - subset = io.find_one({"type": "subset", - "parent": asset["_id"], - "name": instance.data["subset"]}) - - if subset is None: - subset_name = instance.data["subset"] - self.log.info("Subset '%s' not found, creating.." % subset_name) - - _id = io.insert_one({ - "schema": "pype:subset-2.0", - "type": "subset", - "name": subset_name, - "data": {}, - "parent": asset["_id"] - }).inserted_id - - subset = io.find_one({"_id": _id}) - - return subset - - def create_version(self, subset, version_number, locations, data=None): - """ Copy given source to destination - - Args: - subset (dict): the registered subset of the asset - version_number (int): the version number - locations (list): the currently registered locations - - Returns: - dict: collection of data to create a version - """ - # Imprint currently registered location - version_locations = [location for location in locations if - location is not None] - - return {"schema": "pype:version-2.0", - "type": "version", - "parent": subset["_id"], - "name": version_number, - "locations": version_locations, - "data": data} - - def create_version_data(self, context, instance): - """Create the data collection for the version - - Args: - context: the current context - instance: the current instance being published - - Returns: - dict: the required information with instance.data as key - """ - - families = [] - current_families = instance.data.get("families", list()) - instance_family = instance.data.get("family", None) - - if instance_family is not None: - families.append(instance_family) - families += current_families - - # try: - # source = instance.data['source'] - # except KeyError: - # source = context.data["currentFile"] - # - # relative_path = os.path.relpath(source, api.registered_root()) - # source = os.path.join("{root}", relative_path).replace("\\", "/") - - source = "standalone" - - version_data = {"families": families, - "time": context.data["time"], - "author": context.data["user"], - "source": source, - "comment": context.data.get("comment")} - - # Include optional data if present in - optionals = ["startFrame", "endFrame", "step", - "handles", "colorspace", "fps", "outputDir"] - - for key in optionals: - if key in instance.data: - version_data[key] = instance.data.get(key, None) - - return version_data From 1bd87200177c21763a567c9f361d521667c7d346 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 May 2019 01:16:54 +0200 Subject: [PATCH 19/48] not video/image components can be loaded again --- pype/standalonepublish/widgets/widget_drop_frame.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pype/standalonepublish/widgets/widget_drop_frame.py b/pype/standalonepublish/widgets/widget_drop_frame.py index 4e99f697cb..de0acac238 100644 --- a/pype/standalonepublish/widgets/widget_drop_frame.py +++ b/pype/standalonepublish/widgets/widget_drop_frame.py @@ -243,12 +243,12 @@ class DropDataFrame(QtWidgets.QFrame): filepath = data['files'][0] ext = data['ext'] output = {} - probe_data = self.load_data_with_probe(filepath) if ( ext in self.presets['extensions']['image_file'] or ext in self.presets['extensions']['video_file'] ): + probe_data = self.load_data_with_probe(filepath) if 'frameRate' not in data: # default value frameRate = 25 @@ -268,13 +268,8 @@ class DropDataFrame(QtWidgets.QFrame): output['startFrame'] = startFrame output['endFrame'] = endFrame - file_info = None - if 'file_info' in data: - file_info = data['file_info'] - elif ext in ['.mov']: - file_info = probe_data.get('codec_name') - - output['file_info'] = file_info + if (ext == '.mov') and ('file_info' not in data): + output['file_info'] = probe_data.get('codec_name') return output From 15c448acb2566f38d203ba62c690b8a6707d9557 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 May 2019 01:21:57 +0200 Subject: [PATCH 20/48] file_info moved back (is important) --- pype/standalonepublish/widgets/widget_drop_frame.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pype/standalonepublish/widgets/widget_drop_frame.py b/pype/standalonepublish/widgets/widget_drop_frame.py index de0acac238..949f93feeb 100644 --- a/pype/standalonepublish/widgets/widget_drop_frame.py +++ b/pype/standalonepublish/widgets/widget_drop_frame.py @@ -244,6 +244,10 @@ class DropDataFrame(QtWidgets.QFrame): ext = data['ext'] output = {} + file_info = None + if 'file_info' in data: + file_info = data['file_info'] + if ( ext in self.presets['extensions']['image_file'] or ext in self.presets['extensions']['video_file'] @@ -268,8 +272,10 @@ class DropDataFrame(QtWidgets.QFrame): output['startFrame'] = startFrame output['endFrame'] = endFrame - if (ext == '.mov') and ('file_info' not in data): - output['file_info'] = probe_data.get('codec_name') + if (ext == '.mov') and (not file_info): + file_info = probe_data.get('codec_name') + + output['file_info'] = file_info return output From 3bf7f73c7de7daa2a690d856b0596553defd1c68 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 22 May 2019 01:22:28 +0200 Subject: [PATCH 21/48] extention is lowered so looking is not case sensitive --- pype/standalonepublish/widgets/widget_drop_frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/standalonepublish/widgets/widget_drop_frame.py b/pype/standalonepublish/widgets/widget_drop_frame.py index 949f93feeb..c792813a22 100644 --- a/pype/standalonepublish/widgets/widget_drop_frame.py +++ b/pype/standalonepublish/widgets/widget_drop_frame.py @@ -241,7 +241,7 @@ class DropDataFrame(QtWidgets.QFrame): def get_file_data(self, data): filepath = data['files'][0] - ext = data['ext'] + ext = data['ext'].lower() output = {} file_info = None From 201cc6b4d1fd9de1fea4303247660917d07c15f3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 May 2019 14:28:28 +0200 Subject: [PATCH 22/48] feat(nukestudio): menu set context with dynamic label trigger, reload pipeline added into Avalon menu --- pype/nukestudio/__init__.py | 51 +++++---------------------- pype/nukestudio/lib.py | 30 ++++++++++++++++ pype/nukestudio/menu.py | 69 +++++++++++++++++++++++++++++++++---- 3 files changed, 101 insertions(+), 49 deletions(-) diff --git a/pype/nukestudio/__init__.py b/pype/nukestudio/__init__.py index ecee86c9fc..c8cdc3e0f8 100644 --- a/pype/nukestudio/__init__.py +++ b/pype/nukestudio/__init__.py @@ -5,18 +5,13 @@ from pyblish import api as pyblish from .. import api -from .menu import install as menu_install - -from .lib import ( - show, - setup, - add_to_filemenu +from .menu import ( + install as menu_install, + _update_menu_task_label ) - from pypeapp import Logger - log = Logger().get_logger(__name__, "nukestudio") AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype") @@ -35,41 +30,9 @@ if os.getenv("PYBLISH_GUI", None): pyblish.register_gui(os.getenv("PYBLISH_GUI", None)) -def reload_config(): - """Attempt to reload pipeline at run-time. - - CAUTION: This is primarily for development and debugging purposes. - - """ - - import importlib - - for module in ( - "pypeapp", - "{}.api".format(AVALON_CONFIG), - "{}.templates".format(AVALON_CONFIG), - "{}.nukestudio.inventory".format(AVALON_CONFIG), - "{}.nukestudio.lib".format(AVALON_CONFIG), - "{}.nukestudio.menu".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(config): - # api.set_avalon_workdir() - # reload_config() - - # import sys - # for path in sys.path: - # if path.startswith("C:\\Users\\Public"): - # sys.path.remove(path) + _register_events() log.info("Registering NukeStudio plug-ins..") pyblish.register_host("nukestudio") @@ -93,7 +56,6 @@ def install(config): api.load_data_from_templates() - def uninstall(): log.info("Deregistering NukeStudio plug-ins..") pyblish.deregister_host("nukestudio") @@ -105,6 +67,11 @@ def uninstall(): api.reset_data_from_templates() +def _register_events(): + avalon.on("taskChanged", _update_menu_task_label) + log.info("Installed event callback for 'taskChanged'..") + + def ls(): """List available containers. diff --git a/pype/nukestudio/lib.py b/pype/nukestudio/lib.py index bb33bf5efb..6dc263e824 100644 --- a/pype/nukestudio/lib.py +++ b/pype/nukestudio/lib.py @@ -23,6 +23,36 @@ self._has_been_setup = False self._has_menu = False self._registered_gui = None +AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype") + +def reload_config(): + """Attempt to reload pipeline at run-time. + + CAUTION: This is primarily for development and debugging purposes. + + """ + + import importlib + + for module in ( + "avalon.api", + "avalon.lib", + "avalon.pipeline", + "pypeapp", + "{}.api".format(AVALON_CONFIG), + "{}.templates".format(AVALON_CONFIG), + "{}.nukestudio.lib".format(AVALON_CONFIG), + "{}.nukestudio.menu".format(AVALON_CONFIG), + "{}.nukestudio.tags".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 setup(console=False, port=None, menu=True): """Setup integration diff --git a/pype/nukestudio/menu.py b/pype/nukestudio/menu.py index 2b038d69f1..981c11e8d1 100644 --- a/pype/nukestudio/menu.py +++ b/pype/nukestudio/menu.py @@ -1,5 +1,5 @@ import os - +import sys import hiero.core try: @@ -10,8 +10,40 @@ except Exception: from hiero.ui import findMenuAction +from avalon.api import Session + from .tags import add_tags_from_presets -# + +from .lib import ( + reload_config +) +from pypeapp import Logger + +log = Logger().get_logger(__name__, "nukestudio") + +self = sys.modules[__name__] +self._change_context_menu = None + + +def _update_menu_task_label(*args): + """Update the task label in Avalon menu to current session""" + log.info("__ this is a test message showint this grigger works") + + object_name = self._change_context_menu + found_menu = findMenuAction(object_name) + + if not found_menu: + log.warning("Can't find menuItem: {}".format(object_name)) + return + + label = "{}, {}".format(Session["AVALON_ASSET"], + Session["AVALON_TASK"]) + + menu = found_menu.menu() + self._change_context_menu = label + menu.setTitle(label) + + def install(): # here is the best place to add menu from avalon.tools import ( @@ -25,12 +57,16 @@ def install(): ) menu_name = os.environ['AVALON_LABEL'] + + context_label = "{0}, {1}".format( + Session["AVALON_ASSET"], Session["AVALON_TASK"] + ) + + self._change_context_menu = context_label + # Grab Hiero's MenuBar M = hiero.ui.menuBar() - # Add a Menu to the MenuBar - file_action = None - try: check_made_menu = findMenuAction(menu_name) except Exception: @@ -43,6 +79,7 @@ def install(): actions = [ { + 'parent': context_label, 'action': QAction('Set Context', None), 'function': contextmanager.show, 'icon': QIcon('icons:Position.png') @@ -83,13 +120,31 @@ def install(): 'action': QAction('Library...', None), 'function': libraryloader.show, 'icon': QIcon('icons:ColorAdd.png') + }, + "separator", + { + 'action': QAction('Reload pipeline...', None), + 'function': reload_config, + 'icon': QIcon('icons:ColorAdd.png') }] + + # Create menu items for a in actions: + add_to_menu = menu if isinstance(a, dict): # create action for k in a.keys(): + if 'parent' in k: + submenus = [sm for sm in a[k].split('/')] + submenu = None + for sm in submenus: + if submenu: + submenu.addMenu(sm) + else: + submenu = menu.addMenu(sm) + add_to_menu = submenu if 'action' in k: action = a[k] elif 'function' in k: @@ -98,7 +153,7 @@ def install(): action.setIcon(a[k]) # add action to menu - menu.addAction(action) + add_to_menu.addAction(action) hiero.ui.registerAction(action) elif isinstance(a, str): - menu.addSeparator() + add_to_menu.addSeparator() From 514d6b7d2d51727b25c1cbb8fd28d402a5ab204e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 May 2019 14:35:57 +0200 Subject: [PATCH 23/48] fix(nukestudio): problem with reloading pipeline --- pype/nukestudio/lib.py | 1 - pype/nukestudio/menu.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pype/nukestudio/lib.py b/pype/nukestudio/lib.py index 6dc263e824..9b05d8af68 100644 --- a/pype/nukestudio/lib.py +++ b/pype/nukestudio/lib.py @@ -35,7 +35,6 @@ def reload_config(): import importlib for module in ( - "avalon.api", "avalon.lib", "avalon.pipeline", "pypeapp", diff --git a/pype/nukestudio/menu.py b/pype/nukestudio/menu.py index 981c11e8d1..fd63bec2a9 100644 --- a/pype/nukestudio/menu.py +++ b/pype/nukestudio/menu.py @@ -27,8 +27,7 @@ self._change_context_menu = None def _update_menu_task_label(*args): """Update the task label in Avalon menu to current session""" - log.info("__ this is a test message showint this grigger works") - + object_name = self._change_context_menu found_menu = findMenuAction(object_name) From 9257ded2ef81e0d851a0d39c1fe0150cc614a180 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 15:00:27 +0200 Subject: [PATCH 24/48] feat(nukestudio): hierarchy tag rework --- pype/nukestudio/tags.py | 49 ++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/pype/nukestudio/tags.py b/pype/nukestudio/tags.py index 863ef80eeb..8d1ec9e43a 100644 --- a/pype/nukestudio/tags.py +++ b/pype/nukestudio/tags.py @@ -1,6 +1,13 @@ import hiero import re -from pypeapp import config +from pypeapp import ( + config, + Logger +) + +log = Logger().get_logger(__name__, "nukestudio") + +_hierarchy_orig = 'hierarchy_orig' def create_tag(key, value): @@ -16,15 +23,9 @@ def create_tag(key, value): """ tag = hiero.core.Tag(str(key)) - tag.setNote(value['note']) - tag.setIcon(value['icon']['path']) - mtd = tag.metadata() - pres_mtd = value.get('metadata', None) - if pres_mtd: - [mtd.setValue("tag.{}".format(k), v) - for k, v in pres_mtd.items()] - return tag + return update_tag(tag, value) + def update_tag(tag, value): """ @@ -43,6 +44,8 @@ def update_tag(tag, value): [mtd.setValue("tag.{}".format(k), v) for k, v in pres_mtd.items()] + return tag + def add_tags_from_presets(): """ @@ -76,8 +79,8 @@ def add_tags_from_presets(): for k, v in _val.items(): tags = [t for t in bin.items() - if str(k) in t.name()] - + if str(k) in t.name() + if len(str(k)) == len(t.name())] if not tags: # create Tag obj tag = create_tag(k, v) @@ -85,7 +88,6 @@ def add_tags_from_presets(): # adding Tag to Bin bin.addItem(tag) else: - # update Tag if already exists update_tag(tags[0], v) if not bins: @@ -104,5 +106,26 @@ def add_tags_from_presets(): # adding Tag to Root Bin root_bin.addItem(tag) else: + # check if Hierarchy in name # update Tag if already exists - update_tag(tags[0], _val) + tag_names = [tg.name().lower() for tg in tags] + for _t in tags: + if 'hierarchy' not in _t.name().lower(): + # update only non hierarchy tags + # because hierarchy could be edited + update_tag(_t, _val) + elif _hierarchy_orig in _t.name().lower(): + # if hierarchy_orig already exists just + # sync with preset + update_tag(_t, _val) + else: + # if history tag already exist then create + # backup synchronisable original Tag + if (_hierarchy_orig not in tag_names): + # create Tag obj + tag = create_tag( + _hierarchy_orig.capitalize(), _val + ) + + # adding Tag to Bin + root_bin.addItem(tag) From 3a6871b30117170e486ebdedc633e6b45046fa71 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 15:01:20 +0200 Subject: [PATCH 25/48] feat(nukestudio): wrapping Workfiles with other settings to be altered --- pype/nukestudio/lib.py | 37 +++++++++++++++++++++++++++++++++++++ pype/nukestudio/menu.py | 8 ++++---- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/pype/nukestudio/lib.py b/pype/nukestudio/lib.py index 9b05d8af68..4c7dffdb38 100644 --- a/pype/nukestudio/lib.py +++ b/pype/nukestudio/lib.py @@ -11,6 +11,8 @@ import hiero from pypeapp import Logger log = Logger().get_logger(__name__, "nukestudio") +import avalon.api as avalon +import pype.api as pype from avalon.vendor.Qt import (QtWidgets, QtGui) @@ -25,6 +27,41 @@ self._registered_gui = None AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype") + +def set_workfiles(): + ''' Wrapping function for workfiles launcher ''' + from avalon.tools import workfiles + S = avalon.Session + active_project_root = os.path.normpath( + os.path.join(S['AVALON_PROJECTS'], S['AVALON_PROJECT']) + ) + workdir = os.environ["AVALON_WORKDIR"] + workfiles.show(workdir) + project = hiero.core.projects()[-1] + project.setProjectRoot(active_project_root) + + # get project data from avalon db + project_data = pype.get_project_data() + + # get format and fps property from avalon db on project + width = project_data['resolution_width'] + height = project_data['resolution_height'] + pixel_aspect = project_data['pixel_aspect'] + fps = project_data['fps'] + format_name = project_data['code'] + + # create new format in hiero project + format = hiero.core.Format(width, height, pixel_aspect, format_name) + project.setOutputFormat(format) + + # set fps to hiero project + project.setFramerate(fps) + + log.info("Project property has been synchronised with Avalon db") + + + + def reload_config(): """Attempt to reload pipeline at run-time. diff --git a/pype/nukestudio/menu.py b/pype/nukestudio/menu.py index fd63bec2a9..16b49b7b02 100644 --- a/pype/nukestudio/menu.py +++ b/pype/nukestudio/menu.py @@ -15,7 +15,8 @@ from avalon.api import Session from .tags import add_tags_from_presets from .lib import ( - reload_config + reload_config, + set_workfiles ) from pypeapp import Logger @@ -27,7 +28,7 @@ self._change_context_menu = None def _update_menu_task_label(*args): """Update the task label in Avalon menu to current session""" - + object_name = self._change_context_menu found_menu = findMenuAction(object_name) @@ -48,7 +49,6 @@ def install(): from avalon.tools import ( creator, publish, - workfiles, cbloader, cbsceneinventory, contextmanager, @@ -86,7 +86,7 @@ def install(): "separator", { 'action': QAction("Work Files...", None), - 'function': (lambda: workfiles.show(os.environ["AVALON_WORKDIR"])), + 'function': set_workfiles, 'icon': QIcon('icons:Position.png') }, { From 81aab9dfa9b9fd29cebd4b83245ed5247e421eae Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 15:02:06 +0200 Subject: [PATCH 26/48] fix(nukestudio): moving some plugins to _unused --- .../nukestudio/_unused/collect_subsets.py | 45 ++++++++++++++++++ .../_unused/validate_projectroot.py | 40 ++++++++++++++++ .../nukestudio/_unused/validate_track_item.py | 46 +++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 pype/plugins/nukestudio/_unused/collect_subsets.py create mode 100644 pype/plugins/nukestudio/_unused/validate_projectroot.py create mode 100644 pype/plugins/nukestudio/_unused/validate_track_item.py diff --git a/pype/plugins/nukestudio/_unused/collect_subsets.py b/pype/plugins/nukestudio/_unused/collect_subsets.py new file mode 100644 index 0000000000..b27a718f49 --- /dev/null +++ b/pype/plugins/nukestudio/_unused/collect_subsets.py @@ -0,0 +1,45 @@ +from pyblish import api + + +class CollectClipSubsets(api.InstancePlugin): + """Collect Subsets from selected Clips, Tags, Preset.""" + + order = api.CollectorOrder + 0.01 + label = "Collect Subsets" + hosts = ["nukestudio"] + families = ['clip'] + + def process(self, instance): + tags = instance.data.get('tags', None) + presets = instance.context.data['presets'][ + instance.context.data['host']] + if tags: + self.log.info(tags) + + if presets: + self.log.info(presets) + + # get presets and tags + # iterate tags and get task family + # iterate tags and get host family + # iterate tags and get handles family + + instance = instance.context.create_instance(instance_name) + + instance.data.update({ + "subset": subset_name, + "stagingDir": staging_dir, + "task": task, + "representation": ext[1:], + "host": host, + "asset": asset_name, + "label": label, + "name": name, + # "hierarchy": hierarchy, + # "parents": parents, + "family": family, + "families": [families, 'ftrack'], + "publish": True, + # "files": files_list + }) + instances.append(instance) diff --git a/pype/plugins/nukestudio/_unused/validate_projectroot.py b/pype/plugins/nukestudio/_unused/validate_projectroot.py new file mode 100644 index 0000000000..94315014c6 --- /dev/null +++ b/pype/plugins/nukestudio/_unused/validate_projectroot.py @@ -0,0 +1,40 @@ +from pyblish import api + + +class RepairProjectRoot(api.Action): + + label = "Repair" + icon = "wrench" + on = "failed" + + def process(self, context, plugin): + import os + + project_root = os.path.join( + os.path.dirname(context.data["currentFile"]) + ) + + context.data["activeProject"].setProjectRoot(project_root) + + +class ValidateProjectRoot(api.ContextPlugin): + """Validate the project root to the workspace directory.""" + + order = api.ValidatorOrder + label = "Project Root" + hosts = ["nukestudio"] + actions = [RepairProjectRoot] + + def process(self, context): + import os + + workspace = os.path.join( + os.path.dirname(context.data["currentFile"]) + ) + 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 diff --git a/pype/plugins/nukestudio/_unused/validate_track_item.py b/pype/plugins/nukestudio/_unused/validate_track_item.py new file mode 100644 index 0000000000..48f63b5608 --- /dev/null +++ b/pype/plugins/nukestudio/_unused/validate_track_item.py @@ -0,0 +1,46 @@ +from pyblish import api + +class ValidateClip(api.InstancePlugin): + """Validate the track item to the sequence. + + Exact matching to optimize processing. + """ + + order = api.ValidatorOrder + families = ["clip"] + # match = api.Exact + label = "Validate 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() + ) From 9717e0cd2a6e4fde04e186729194ac118bcc9641 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 15:02:48 +0200 Subject: [PATCH 27/48] feat(nukestudio): basic logic of Hierarchy collection --- .../publish/collect_hierarchy_context.py | 103 ++++++++++++++---- 1 file changed, 81 insertions(+), 22 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py index 6d780c62c8..53d95026cc 100644 --- a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py +++ b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py @@ -1,5 +1,6 @@ import pyblish.api from avalon import api +import re class CollectHierarchyContext(pyblish.api.InstancePlugin): @@ -24,38 +25,96 @@ class CollectHierarchyContext(pyblish.api.InstancePlugin): return new_dict def convert_to_entity(self, key, value): + # ftrack compatible entity types types = {"shot": "Shot", "folder": "Folder", "episode": "Episode", - "Sequence": "Sequence", + "sequence": "Sequence", + "track": "Sequence", } - return {"entity_type": types.get(key, None), "entity_name": value} + # convert to entity type + entity_type = types.get(key, None) + + # return if any + if entity_type: + return {"entity_type": entity_type, "entity_name": value} def process(self, instance): context = instance.context tags = instance.data.get("tags", None) - self.log.info(tags) - if tags: - for t in tags: - t_metadata = dict(t["metadata"]) - t_type = t_metadata.get("tag._type", "") - if "hierarchy" in t_type: - self.log.info("__ type: {}".format(t_type)) - d_metadata = dict() - for k, v in t_metadata.items(): - new_k = k.split(".")[1] - try: - d_metadata[new_k] = str(v).format(**context.data) - except Exception: - d_metadata[new_k] = v + clip = instance.data["item"] + asset = instance.data.get("asset") + # build data for inner nukestudio project property + data = { + "sequence": context.data['activeSequence'].name().replace(' ', '_'), + "track": clip.parent().name().replace(' ', '_'), + "clip": asset + } + self.log.info("__ data: {}".format(data)) - self.log.info("__ projectroot: {}".format(context.data["projectroot"])) - self.log.info("__ d_metadata: {}".format(d_metadata)) - self.log.info( - "__ hierarchy: {}".format(d_metadata["note"])) - # self.log.info("__ hierarchy.format: {}".format(d_metadata["note"].format( - # **d_metadata))) + # checking if tags are available + if not tags: + return + + # loop trough all tags + for t in tags: + t_metadata = dict(t["metadata"]) + t_type = t_metadata.get("tag.label", "") + t_note = t_metadata.get("tag.note", "") + + # and finding only hierarchical tag + if "hierarchy" in t_type.lower(): + d_metadata = dict() + parents = list() + + # take template from Tag.note and break it into parts + patern = re.compile(r"^\{([a-z]*?)\}") + par_split = [patern.findall(t)[0] + for t in t_note.split("/")] + + # format all {} in two layers + for k, v in t_metadata.items(): + new_k = k.split(".")[1] + try: + # first try all data and context data to + # add to individual properties + new_v = str(v).format( + **dict(context.data, **data)) + d_metadata[new_k] = new_v + + # create parents + # find matching index of order + p_match_i = [i for i, p in enumerate(par_split) + if new_k in p] + + # if any is matching then convert to entity_types + if p_match_i: + self.log.info("__ new_k: {}".format(new_k)) + self.log.info("__ new_v: {}".format(new_v)) + parent = self.convert_to_entity(new_k, new_v) + parents.insert(p_match_i[0], parent) + except Exception: + d_metadata[new_k] = v + + # lastly fill those individual properties itno + # main template from Tag.note + hierarchy = d_metadata["note"].format( + **d_metadata) + + # check if hierarchy attribute is already created + # it should not be so return warning if it is + hd = instance.data.get("hierarchy") + self.log.info("__ hd: {}".format(hd)) + assert not hd, "Only one Hierarchy Tag is \ + allowed. Clip: `{}`".format(asset) + + # add formated hierarchy path into instance data + instance.data["hierarchy"] = hierarchy + instance.data["parents"] = parents + self.log.info("__ hierarchy.format: {}".format(hierarchy)) + self.log.info("__ parents: {}".format(parents)) + self.log.info("__ d_metadata: {}".format(d_metadata)) # # json_data = context.data.get("jsonData", None) From e215113c136617db10fd3f4517fed764f4d8d7d8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 15:04:07 +0200 Subject: [PATCH 28/48] fix(nukestudio): projectroot should be project root rather then workfile directory --- pype/plugins/nukestudio/publish/collect_project_root.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_project_root.py b/pype/plugins/nukestudio/publish/collect_project_root.py index d6e1fbc560..1b21a6b641 100644 --- a/pype/plugins/nukestudio/publish/collect_project_root.py +++ b/pype/plugins/nukestudio/publish/collect_project_root.py @@ -1,5 +1,6 @@ import pyblish.api - +import avalon.api as avalon +import os class CollectActiveProjectRoot(pyblish.api.ContextPlugin): """Inject the active project into context""" @@ -8,5 +9,7 @@ class CollectActiveProjectRoot(pyblish.api.ContextPlugin): order = pyblish.api.CollectorOrder - 0.1 def process(self, context): - project = context.data["activeProject"] - context.data["projectroot"] = project.projectRoot() + S = avalon.Session + context.data["projectroot"] = os.path.normpath( + os.path.join(S['AVALON_PROJECTS'], S['AVALON_PROJECT']) + ) From e9dca465396ec5772330e6524e2c21e46f2b598b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 15:04:31 +0200 Subject: [PATCH 29/48] feat(nukestudio): adding collecting sequence --- pype/plugins/nukestudio/publish/collect_sequence.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 pype/plugins/nukestudio/publish/collect_sequence.py diff --git a/pype/plugins/nukestudio/publish/collect_sequence.py b/pype/plugins/nukestudio/publish/collect_sequence.py new file mode 100644 index 0000000000..f7d83d1ea0 --- /dev/null +++ b/pype/plugins/nukestudio/publish/collect_sequence.py @@ -0,0 +1,13 @@ +from pyblish import api +import hiero + + +class CollectSequence(api.ContextPlugin): + """Collect all Track items selection.""" + + order = api.CollectorOrder + label = "Collect Sequence" + hosts = ["nukestudio"] + + def process(self, context): + context.data['activeSequence'] = hiero.ui.activeSequence() From 2e22d7349114a5db9830ebf734a7756bff6f108f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 15:10:05 +0200 Subject: [PATCH 30/48] feat(nukestudio): validating hierarchy attr --- .../nukestudio/publish/collect_subsets.py | 45 ---------------- .../nukestudio/publish/validate_hierarchy.py | 18 +++++++ .../nukestudio/publish/validate_names.py | 11 ---- .../publish/validate_projectroot.py | 52 ------------------- .../nukestudio/publish/validate_track_item.py | 46 ---------------- 5 files changed, 18 insertions(+), 154 deletions(-) delete mode 100644 pype/plugins/nukestudio/publish/collect_subsets.py create mode 100644 pype/plugins/nukestudio/publish/validate_hierarchy.py delete mode 100644 pype/plugins/nukestudio/publish/validate_projectroot.py delete mode 100644 pype/plugins/nukestudio/publish/validate_track_item.py diff --git a/pype/plugins/nukestudio/publish/collect_subsets.py b/pype/plugins/nukestudio/publish/collect_subsets.py deleted file mode 100644 index b27a718f49..0000000000 --- a/pype/plugins/nukestudio/publish/collect_subsets.py +++ /dev/null @@ -1,45 +0,0 @@ -from pyblish import api - - -class CollectClipSubsets(api.InstancePlugin): - """Collect Subsets from selected Clips, Tags, Preset.""" - - order = api.CollectorOrder + 0.01 - label = "Collect Subsets" - hosts = ["nukestudio"] - families = ['clip'] - - def process(self, instance): - tags = instance.data.get('tags', None) - presets = instance.context.data['presets'][ - instance.context.data['host']] - if tags: - self.log.info(tags) - - if presets: - self.log.info(presets) - - # get presets and tags - # iterate tags and get task family - # iterate tags and get host family - # iterate tags and get handles family - - instance = instance.context.create_instance(instance_name) - - instance.data.update({ - "subset": subset_name, - "stagingDir": staging_dir, - "task": task, - "representation": ext[1:], - "host": host, - "asset": asset_name, - "label": label, - "name": name, - # "hierarchy": hierarchy, - # "parents": parents, - "family": family, - "families": [families, 'ftrack'], - "publish": True, - # "files": files_list - }) - instances.append(instance) diff --git a/pype/plugins/nukestudio/publish/validate_hierarchy.py b/pype/plugins/nukestudio/publish/validate_hierarchy.py new file mode 100644 index 0000000000..e8b8597daf --- /dev/null +++ b/pype/plugins/nukestudio/publish/validate_hierarchy.py @@ -0,0 +1,18 @@ +from pyblish import api + + +class ValidateHierarchy(api.InstancePlugin): + """Validate clip's hierarchy data. + + """ + + order = api.ValidatorOrder + families = ["clip"] + label = "Validate Hierarchy" + hosts = ["nukestudio"] + + def process(self, instance): + asset_name = instance.data.get("asset", None) + hierarchy = instance.data.get("hierarchy", None) + + assert hierarchy is not None, "Hierarchy Tag has to be set and added to clip `{}`".format(asset_name) diff --git a/pype/plugins/nukestudio/publish/validate_names.py b/pype/plugins/nukestudio/publish/validate_names.py index 52382e545d..8f7436cca0 100644 --- a/pype/plugins/nukestudio/publish/validate_names.py +++ b/pype/plugins/nukestudio/publish/validate_names.py @@ -29,14 +29,3 @@ class ValidateNames(api.InstancePlugin): 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 = api.ValidatorOrder - families = ["clip", "ftrack"] diff --git a/pype/plugins/nukestudio/publish/validate_projectroot.py b/pype/plugins/nukestudio/publish/validate_projectroot.py deleted file mode 100644 index b9b851e0d1..0000000000 --- a/pype/plugins/nukestudio/publish/validate_projectroot.py +++ /dev/null @@ -1,52 +0,0 @@ -from pyblish import api - - -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 = api.ValidatorOrder - 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 diff --git a/pype/plugins/nukestudio/publish/validate_track_item.py b/pype/plugins/nukestudio/publish/validate_track_item.py deleted file mode 100644 index 600bf58938..0000000000 --- a/pype/plugins/nukestudio/publish/validate_track_item.py +++ /dev/null @@ -1,46 +0,0 @@ -from pyblish import api - -class ValidateClip(api.InstancePlugin): - """Validate the track item to the sequence. - - Exact matching to optimize processing. - """ - - order = api.ValidatorOrder - families = ["clip"] - match = api.Exact - label = "Validate 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() - ) From d160836577fbc6aeb5e02ae8414d909ff8782cf3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 16:11:18 +0200 Subject: [PATCH 31/48] feat(nukestudio): collecting Tasks from Tags or Presets --- .../nukestudio/publish/collect_tag_tasks.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 pype/plugins/nukestudio/publish/collect_tag_tasks.py diff --git a/pype/plugins/nukestudio/publish/collect_tag_tasks.py b/pype/plugins/nukestudio/publish/collect_tag_tasks.py new file mode 100644 index 0000000000..d48939db88 --- /dev/null +++ b/pype/plugins/nukestudio/publish/collect_tag_tasks.py @@ -0,0 +1,41 @@ +from pyblish import api + + +class CollectClipTagTasks(api.InstancePlugin): + """Collect Tags from selected track items.""" + + order = api.CollectorOrder + 0.006 + label = "Collect Tag Tasks" + hosts = ["nukestudio"] + families = ['clip'] + + def process(self, instance): + # gets tags + tags = instance.data["tags"] + + # gets presets for nukestudio + presets = instance.context.data['presets'][ + instance.context.data['host']] + + # find preset for default task + default_tasks = presets['rules_tasks']['defaultTasks'] + + tasks = list() + for t in tags: + t_metadata = dict(t["metadata"]) + t_family = t_metadata.get("tag.family", "") + + # gets only task family tags and collect labels + if "task" in t_family: + t_task = t_metadata.get("tag.label", "") + tasks.append(t_task) + + if tasks: + instance.data["tasks"] = tasks + else: + # add tasks from presets if no task tag + instance.data["tasks"] = default_tasks + + self.log.info("Collected Tasks from Tags: `{}`".format( + instance.data["tasks"])) + return From bd97edb63f79d235389a3be7c3c00efc59b6d99a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 18:03:36 +0200 Subject: [PATCH 32/48] fix(nukestudio): convert timeIn and timeOut to Inteagers --- pype/plugins/nukestudio/publish/collect_clips.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_clips.py b/pype/plugins/nukestudio/publish/collect_clips.py index 69ec4814e9..6d443c03ec 100644 --- a/pype/plugins/nukestudio/publish/collect_clips.py +++ b/pype/plugins/nukestudio/publish/collect_clips.py @@ -23,9 +23,8 @@ class CollectClips(api.ContextPlugin): data[item.name()] = { "item": item, - "tasks": [], - "startFrame": item.timelineIn(), - "endFrame": item.timelineOut() + "startFrame": int(item.timelineIn()), + "endFrame": int(item.timelineOut()) } for key, value in data.items(): @@ -36,7 +35,6 @@ class CollectClips(api.ContextPlugin): asset=value["item"].name(), item=value["item"], family=family, - tasks=value["tasks"], startFrame=value["startFrame"], endFrame=value["endFrame"], handles=0 From d0353a12fb4cf2c7ed8b19ca2eebef0df6c62a22 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 18:04:16 +0200 Subject: [PATCH 33/48] fix(nukestudio): reformating framerate collector --- pype/plugins/nukestudio/publish/collect_framerate.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_framerate.py b/pype/plugins/nukestudio/publish/collect_framerate.py index 822be8fb9b..56b76b5011 100644 --- a/pype/plugins/nukestudio/publish/collect_framerate.py +++ b/pype/plugins/nukestudio/publish/collect_framerate.py @@ -3,11 +3,10 @@ from pyblish import api class CollectFramerate(api.ContextPlugin): """Collect framerate from selected sequence.""" - order = api.CollectorOrder + order = api.CollectorOrder + 0.01 label = "Collect Framerate" hosts = ["nukestudio"] def process(self, context): - for item in context.data.get("selection", []): - context.data["framerate"] = item.sequence().framerate().toFloat() - return + sequence = context.data["activeSequence"] + context.data["framerate"] = sequence.framerate().toFloat() From c5c275884bb2b6c85695e88d11a3026db36d33e0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 18:04:47 +0200 Subject: [PATCH 34/48] feat(nukestudio): collecting hierarchy finalised --- .../publish/collect_hierarchy_context.py | 160 ++++++++++-------- .../nukestudio/publish/validate_hierarchy.py | 6 +- 2 files changed, 95 insertions(+), 71 deletions(-) diff --git a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py index 53d95026cc..09676c344a 100644 --- a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py +++ b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py @@ -1,9 +1,9 @@ import pyblish.api -from avalon import api +import avalon.api as avalon import re -class CollectHierarchyContext(pyblish.api.InstancePlugin): +class CollectHierarchyInstance(pyblish.api.InstancePlugin): """Collecting hierarchy context from `parents` and `hierarchy` data present in `clip` family instances coming from the request json data file @@ -12,18 +12,10 @@ class CollectHierarchyContext(pyblish.api.InstancePlugin): don't exist yet """ - label = "Collect Hierarchy Context" + label = "Collect Hierarchy Clip" order = pyblish.api.CollectorOrder + 0.1 families = ["clip"] - def update_dict(self, ex_dict, new_dict): - for key in ex_dict: - if key in new_dict and isinstance(ex_dict[key], dict): - new_dict[key] = self.update_dict(ex_dict[key], new_dict[key]) - else: - new_dict[key] = ex_dict[key] - return new_dict - def convert_to_entity(self, key, value): # ftrack compatible entity types types = {"shot": "Shot", @@ -37,7 +29,7 @@ class CollectHierarchyContext(pyblish.api.InstancePlugin): # return if any if entity_type: - return {"entity_type": entity_type, "entity_name": value} + return {"entityType": entity_type, "entityName": value} def process(self, instance): context = instance.context @@ -49,9 +41,9 @@ class CollectHierarchyContext(pyblish.api.InstancePlugin): data = { "sequence": context.data['activeSequence'].name().replace(' ', '_'), "track": clip.parent().name().replace(' ', '_'), - "clip": asset + "shot": asset } - self.log.info("__ data: {}".format(data)) + self.log.debug("__ data: {}".format(data)) # checking if tags are available if not tags: @@ -68,10 +60,17 @@ class CollectHierarchyContext(pyblish.api.InstancePlugin): d_metadata = dict() parents = list() + # main template from Tag.note + template = t_note + + # if shot in template then remove it + if "shot" in template.lower(): + template = "/".join([t for t in template.split('/')][0:-1]) + # take template from Tag.note and break it into parts patern = re.compile(r"^\{([a-z]*?)\}") par_split = [patern.findall(t)[0] - for t in t_note.split("/")] + for t in template.split("/")] # format all {} in two layers for k, v in t_metadata.items(): @@ -83,6 +82,9 @@ class CollectHierarchyContext(pyblish.api.InstancePlugin): **dict(context.data, **data)) d_metadata[new_k] = new_v + if 'shot' in new_k: + instance.data["asset"] = new_v + # create parents # find matching index of order p_match_i = [i for i, p in enumerate(par_split) @@ -90,16 +92,14 @@ class CollectHierarchyContext(pyblish.api.InstancePlugin): # if any is matching then convert to entity_types if p_match_i: - self.log.info("__ new_k: {}".format(new_k)) - self.log.info("__ new_v: {}".format(new_v)) parent = self.convert_to_entity(new_k, new_v) parents.insert(p_match_i[0], parent) except Exception: d_metadata[new_k] = v # lastly fill those individual properties itno - # main template from Tag.note - hierarchy = d_metadata["note"].format( + # format the string with collected data + hierarchy = template.format( **d_metadata) # check if hierarchy attribute is already created @@ -112,55 +112,75 @@ class CollectHierarchyContext(pyblish.api.InstancePlugin): # add formated hierarchy path into instance data instance.data["hierarchy"] = hierarchy instance.data["parents"] = parents - self.log.info("__ hierarchy.format: {}".format(hierarchy)) - self.log.info("__ parents: {}".format(parents)) - self.log.info("__ d_metadata: {}".format(d_metadata)) + self.log.debug("__ hierarchy.format: {}".format(hierarchy)) + self.log.debug("__ parents: {}".format(parents)) + self.log.debug("__ d_metadata: {}".format(d_metadata)) - # - # json_data = context.data.get("jsonData", None) - # temp_context = {} - # for instance in json_data['instances']: - # if instance['family'] in 'projectfile': - # continue - # - # in_info = {} - # name = instance['name'] - # # suppose that all instances are Shots - # in_info['entity_type'] = 'Shot' - # - # instance_pyblish = [ - # i for i in context.data["instances"] if i.data['asset'] in name][0] - # in_info['custom_attributes'] = { - # 'fend': instance_pyblish.data['endFrame'], - # 'fstart': instance_pyblish.data['startFrame'], - # 'fps': instance_pyblish.data['fps'] - # } - # - # in_info['tasks'] = instance['tasks'] - # - # parents = instance.get('parents', []) - # - # actual = {name: in_info} - # - # for parent in reversed(parents): - # next_dict = {} - # parent_name = parent["entityName"] - # next_dict[parent_name] = {} - # next_dict[parent_name]["entity_type"] = parent["entityType"] - # next_dict[parent_name]["childs"] = actual - # actual = next_dict - # - # temp_context = self.update_dict(temp_context, actual) - # self.log.debug(temp_context) - # - # # TODO: 100% sure way of get project! Will be Name or Code? - # project_name = api.Session["AVALON_PROJECT"] - # final_context = {} - # final_context[project_name] = {} - # final_context[project_name]['entity_type'] = 'Project' - # final_context[project_name]['childs'] = temp_context - # - # # adding hierarchy context to instance - # context.data["hierarchyContext"] = final_context - # self.log.debug("context.data[hierarchyContext] is: {}".format( - # context.data["hierarchyContext"])) + +class CollectHierarchyContext(pyblish.api.ContextPlugin): + '''Collecting Hierarchy from instaces and building + context hierarchy tree + ''' + + label = "Collect Hierarchy Context" + order = pyblish.api.CollectorOrder + 0.101 + + def update_dict(self, ex_dict, new_dict): + for key in ex_dict: + if key in new_dict and isinstance(ex_dict[key], dict): + new_dict[key] = self.update_dict(ex_dict[key], new_dict[key]) + else: + new_dict[key] = ex_dict[key] + return new_dict + + def process(self, context): + instances = context[:] + # create hierarchyContext attr if context has none + self.log.debug("__ instances: {}".format(context[:])) + + temp_context = {} + for instance in instances: + if 'projectfile' in instance.data.get('family', ''): + continue + + + in_info = {} + name = instance.data["asset"] + # suppose that all instances are Shots + in_info['entity_type'] = 'Shot' + + # get custom attributes of the shot + in_info['custom_attributes'] = { + 'fend': instance.data['endFrame'], + 'fstart': instance.data['startFrame'], + 'fps': context.data["framerate"] + } + + in_info['tasks'] = instance.data['tasks'] + + parents = instance.data.get('parents', []) + + actual = {name: in_info} + + for parent in reversed(parents): + next_dict = {} + parent_name = parent["entityName"] + next_dict[parent_name] = {} + next_dict[parent_name]["entity_type"] = parent["entityType"] + next_dict[parent_name]["childs"] = actual + actual = next_dict + + temp_context = self.update_dict(temp_context, actual) + self.log.debug(temp_context) + + # TODO: 100% sure way of get project! Will be Name or Code? + project_name = avalon.Session["AVALON_PROJECT"] + final_context = {} + final_context[project_name] = {} + final_context[project_name]['entity_type'] = 'Project' + final_context[project_name]['childs'] = temp_context + + # adding hierarchy context to instance + context.data["hierarchyContext"] = final_context + self.log.debug("context.data[hierarchyContext] is: {}".format( + context.data["hierarchyContext"])) diff --git a/pype/plugins/nukestudio/publish/validate_hierarchy.py b/pype/plugins/nukestudio/publish/validate_hierarchy.py index e8b8597daf..2ddec1bcfc 100644 --- a/pype/plugins/nukestudio/publish/validate_hierarchy.py +++ b/pype/plugins/nukestudio/publish/validate_hierarchy.py @@ -14,5 +14,9 @@ class ValidateHierarchy(api.InstancePlugin): def process(self, instance): asset_name = instance.data.get("asset", None) hierarchy = instance.data.get("hierarchy", None) + parents = instance.data.get("parents", None) - assert hierarchy is not None, "Hierarchy Tag has to be set and added to clip `{}`".format(asset_name) + assert hierarchy, "Hierarchy Tag has to be set \ + and added to clip `{}`".format(asset_name) + assert parents, "Parents build from Hierarchy Tag has \ + to be set and added to clip `{}`".format(asset_name) From c59a136934e997cc4a72f8e2f50aaccab6ea605b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 19:05:31 +0200 Subject: [PATCH 35/48] fix(nukestudio): converting to new anatomy workflow --- .../nukestudio/publish/integrate_assumed_destination.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/plugins/nukestudio/publish/integrate_assumed_destination.py b/pype/plugins/nukestudio/publish/integrate_assumed_destination.py index c1936994e4..03113cd3fc 100644 --- a/pype/plugins/nukestudio/publish/integrate_assumed_destination.py +++ b/pype/plugins/nukestudio/publish/integrate_assumed_destination.py @@ -19,9 +19,10 @@ class IntegrateAssumedDestination(pyblish.api.InstancePlugin): # template = instance.data["template"] anatomy = instance.context.data['anatomy'] - # template = anatomy.publish.path + # self.log.info(anatomy.templates) anatomy_filled = anatomy.format(template_data) - mock_template = anatomy_filled.publish.path + # self.log.info(anatomy_filled) + mock_template = anatomy_filled["publish"]["path"] # For now assume resources end up in a "resources" folder in the # published folder From c6fa575104d08951ad9afdf7be540e0a606ff502 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 23 May 2019 19:06:06 +0200 Subject: [PATCH 36/48] fix(nukestudio): removing old premiere stuff --- .../publish/integrate_hierarchy_ftrack.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pype/plugins/nukestudio/publish/integrate_hierarchy_ftrack.py b/pype/plugins/nukestudio/publish/integrate_hierarchy_ftrack.py index d6d03e9722..377e2d816e 100644 --- a/pype/plugins/nukestudio/publish/integrate_hierarchy_ftrack.py +++ b/pype/plugins/nukestudio/publish/integrate_hierarchy_ftrack.py @@ -40,12 +40,9 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): input_data = context.data["hierarchyContext"] - # adding ftrack types from presets - ftrack_types = context.data['ftrackTypes'] + self.import_to_ftrack(input_data) - self.import_to_ftrack(input_data, ftrack_types) - - def import_to_ftrack(self, input_data, ftrack_types, parent=None): + def import_to_ftrack(self, input_data, parent=None): for entity_name in input_data: entity_data = input_data[entity_name] entity_type = entity_data['entity_type'].capitalize() @@ -82,7 +79,7 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): # CUSTOM ATTRIBUTES custom_attributes = entity_data.get('custom_attributes', []) instances = [ - i for i in self.context.data["instances"] if i.data['asset'] in entity['name']] + i for i in self.context[:] if i.data['asset'] in entity['name']] for key in custom_attributes: assert (key in entity['custom_attributes']), ( 'Missing custom attribute') @@ -111,14 +108,14 @@ class IntegrateHierarchyToFtrack(pyblish.api.ContextPlugin): for task in tasks_to_create: self.create_task( name=task, - task_type=ftrack_types[task], + task_type=task.capitalize(), parent=entity ) self.session.commit() if 'childs' in entity_data: self.import_to_ftrack( - entity_data['childs'], ftrack_types, entity) + entity_data['childs'], entity) def get_all_task_types(self, project): tasks = {} From 9a548ed4b49ff2930d7e69d89fad14d7d07a45ca Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 23 May 2019 23:53:51 +0200 Subject: [PATCH 37/48] add docstring and correct anatomy loading --- .../standalonepublish/publish/integrate.py | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/pype/plugins/standalonepublish/publish/integrate.py b/pype/plugins/standalonepublish/publish/integrate.py index 4b2d539727..9d585ab673 100644 --- a/pype/plugins/standalonepublish/publish/integrate.py +++ b/pype/plugins/standalonepublish/publish/integrate.py @@ -12,7 +12,7 @@ log = logging.getLogger(__name__) class IntegrateAsset(pyblish.api.InstancePlugin): - """Resolve any dependency issies + """Resolve any dependency issius This plug-in resolves any paths which, if not updated might break the published file. @@ -20,6 +20,16 @@ class IntegrateAsset(pyblish.api.InstancePlugin): The order of families is important, when working with lookdev you want to first publish the texture, update the texture paths in the nodes and then publish the shading network. Same goes for file dependent assets. + + Requirements for instance to be correctly integrated + + instance.data['representations'] - must be a list and each member + must be a dictionary with following data: + 'files': list of filenames for sequence, string for single file. + Only the filename is allowed, without the folder path. + 'stagingDir': "path/to/folder/with/files" + 'name': representation name (usually the same as extension) + 'ext': file extension """ label = "Integrate Asset" @@ -58,7 +68,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): if instance.data.get('transfer', True): self.integrate(instance) - def register(self, instance): # Required environment variables PROJECT = api.Session["AVALON_PROJECT"] @@ -90,19 +99,19 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # stagingdir = instance.data.get("stagingDir") if not stagingdir: - self.log.info("{} is missing reference to staging \ - directory Will try to get it from \ - representation".format(instance)) + self.log.info('''{} is missing reference to staging + directory Will try to get it from + representation'''.format(instance)) # extra check if stagingDir actually exists and is available self.log.debug("Establishing staging directory @ %s" % stagingdir) # Ensure at least one file is set up for transfer in staging dir. - files = instance.data.get("files", []) - assert files, "Instance has no files to transfer" - assert isinstance(files, (list, tuple)), ( - "Instance 'files' must be a list, got: {0}".format(files) + repres = instance.data.get("representations", None) + assert repres, "Instance has no files to transfer" + assert isinstance(repres, (list, tuple)), ( + "Instance 'files' must be a list, got: {0}".format(repres) ) project = io.find_one({"type": "project"}) @@ -129,7 +138,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): if instance.data.get('version'): next_version = int(instance.data.get('version')) - # self.log.info("Verifying version from assumed destination") # assumed_data = instance.data["assumedTemplateData"] @@ -189,6 +197,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Each should be a single representation (as such, a single extension) representations = [] destination_list = [] + template_name = 'publish' if 'transfers' not in instance.data: instance.data['transfers'] = [] @@ -205,13 +214,16 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # files = repre['files'] - if repre['stagingDir']: + if repre.get('stagingDir'): stagingdir = repre['stagingDir'] - template = anatomy.templates[repre['anatomy_template']]["path"] + if repre.get('anatomy_template'): + template_name = repre['anatomy_template'] + template = anatomy.templates[template_name]["path"] if isinstance(files, list): src_collections, remainder = clique.assemble(files) - self.log.debug("dst_collections: {}".format(str(src_collections))) + self.log.debug( + "dst_collections: {}".format(str(src_collections))) src_collection = src_collections[0] # Assert that each member has identical suffix src_head = src_collection.format("{head}") @@ -223,7 +235,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): template_data["frame"] = src_collection.format( "{padding}") % i anatomy_filled = anatomy.format(template_data) - test_dest_files.append(anatomy_filled[repre['anatomy_template']]["path"]) + test_dest_files.append( + anatomy_filled[template_name]["path"]) dst_collections, remainder = clique.assemble(test_dest_files) dst_collection = dst_collections[0] @@ -237,7 +250,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): src_file_name = "{0}{1}{2}".format( src_head, src_padding, src_tail) - dst_padding = dst_collection.format("{padding}") % i dst = "{0}{1}{2}".format(dst_head, dst_padding, dst_tail) @@ -267,7 +279,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): src = os.path.join(stagingdir, fname) # src = fname anatomy_filled = anatomy.format(template_data) - dst = anatomy_filled[repre['anatomy_template']]["path"] + dst = anatomy_filled[template_name]["path"] instance.data["transfers"].append([src, dst]) # template = anatomy.templates["publish"]["path"] @@ -287,14 +299,14 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "root": root, "project": {"name": PROJECT, "code": project['data']['code']}, - # 'task': api.Session["AVALON_TASK"], + 'task': api.Session["AVALON_TASK"], "silo": asset['silo'], "asset": ASSET, "family": instance.data['family'], "subset": subset["name"], "version": version["name"], "hierarchy": hierarchy, - # "representation": repre['representation'] + "representation": repre['ext'] } } From 15cf52009675019a9c9f26ca4f289fca69cade4b Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 24 May 2019 01:12:38 +0200 Subject: [PATCH 38/48] switch converted families to new integrator --- pype/plugins/global/publish/integrate.py | 12 +- pype/plugins/global/publish/integrate_new.py | 462 +++++++++++++++++++ 2 files changed, 463 insertions(+), 11 deletions(-) create mode 100644 pype/plugins/global/publish/integrate_new.py diff --git a/pype/plugins/global/publish/integrate.py b/pype/plugins/global/publish/integrate.py index d966258afc..b4ca38c334 100644 --- a/pype/plugins/global/publish/integrate.py +++ b/pype/plugins/global/publish/integrate.py @@ -25,25 +25,15 @@ class IntegrateAsset(pyblish.api.InstancePlugin): label = "Integrate Asset" order = pyblish.api.IntegratorOrder - families = ["animation", - "camera", - "look", - "mayaAscii", - "model", - "pointcache", + families = ["look", "vdbcache", - "setdress", "assembly", - "layout", - "rig", "vrayproxy", "yetiRig", "yeticache", "nukescript", "review", - "workfile", "scene", - "ass", "render", "imagesequence", "write"] diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py new file mode 100644 index 0000000000..357cd9fbbf --- /dev/null +++ b/pype/plugins/global/publish/integrate_new.py @@ -0,0 +1,462 @@ +import os +import logging +import shutil +import clique + +import errno +import pyblish.api +from avalon import api, io +from avalon.vendor import filelink + +log = logging.getLogger(__name__) + + +class IntegrateAssetNew(pyblish.api.InstancePlugin): + """Resolve any dependency issius + + This plug-in resolves any paths which, if not updated might break + the published file. + + The order of families is important, when working with lookdev you want to + first publish the texture, update the texture paths in the nodes and then + publish the shading network. Same goes for file dependent assets. + + Requirements for instance to be correctly integrated + + instance.data['representations'] - must be a list and each member + must be a dictionary with following data: + 'files': list of filenames for sequence, string for single file. + Only the filename is allowed, without the folder path. + 'stagingDir': "path/to/folder/with/files" + 'name': representation name (usually the same as extension) + 'ext': file extension + """ + + label = "Integrate Asset New" + order = pyblish.api.IntegratorOrder + families = ["workfile", + "pointcache", + "camera", + "animation", + "model", + "mayaAscii", + "setdress", + "layout", + "ass" + ] + exclude_families = ["clip"] + + def process(self, instance): + + if [ef for ef in self.exclude_families + if instance.data["family"] in ef]: + return + + self.register(instance) + + self.log.info("Integrating Asset in to the database ...") + self.log.info("instance.data: {}".format(instance.data)) + if instance.data.get('transfer', True): + self.integrate(instance) + + def register(self, instance): + # Required environment variables + PROJECT = api.Session["AVALON_PROJECT"] + ASSET = instance.data.get("asset") or api.Session["AVALON_ASSET"] + LOCATION = api.Session["AVALON_LOCATION"] + + context = instance.context + # Atomicity + # + # Guarantee atomic publishes - each asset contains + # an identical set of members. + # __ + # / o + # / \ + # | o | + # \ / + # o __/ + # + assert all(result["success"] for result in context.data["results"]), ( + "Atomicity not held, aborting.") + + # Assemble + # + # | + # v + # ---> <---- + # ^ + # | + # + stagingdir = instance.data.get("stagingDir") + if not stagingdir: + self.log.info('''{} is missing reference to staging + directory Will try to get it from + representation'''.format(instance)) + + # extra check if stagingDir actually exists and is available + + self.log.debug("Establishing staging directory @ %s" % stagingdir) + + # Ensure at least one file is set up for transfer in staging dir. + repres = instance.data.get("representations", None) + assert repres, "Instance has no files to transfer" + assert isinstance(repres, (list, tuple)), ( + "Instance 'files' must be a list, got: {0}".format(repres) + ) + + project = io.find_one({"type": "project"}) + + asset = io.find_one({"type": "asset", + "name": ASSET, + "parent": project["_id"]}) + + assert all([project, asset]), ("Could not find current project or " + "asset '%s'" % ASSET) + + subset = self.get_subset(asset, instance) + + # get next version + latest_version = io.find_one({"type": "version", + "parent": subset["_id"]}, + {"name": True}, + sort=[("name", -1)]) + + next_version = 1 + if latest_version is not None: + next_version += latest_version["name"] + + if instance.data.get('version'): + next_version = int(instance.data.get('version')) + + # self.log.info("Verifying version from assumed destination") + + # assumed_data = instance.data["assumedTemplateData"] + # assumed_version = assumed_data["version"] + # if assumed_version != next_version: + # raise AttributeError("Assumed version 'v{0:03d}' does not match" + # "next version in database " + # "('v{1:03d}')".format(assumed_version, + # next_version)) + + self.log.debug("Next version: v{0:03d}".format(next_version)) + + version_data = self.create_version_data(context, instance) + version = self.create_version(subset=subset, + version_number=next_version, + locations=[LOCATION], + data=version_data) + + self.log.debug("Creating version ...") + version_id = io.insert_one(version).inserted_id + instance.data['version'] = version['name'] + + # Write to disk + # _ + # | | + # _| |_ + # ____\ / + # |\ \ / \ + # \ \ v \ + # \ \________. + # \|________| + # + root = api.registered_root() + hierarchy = "" + parents = io.find_one({ + "type": 'asset', + "name": ASSET + })['data']['parents'] + if parents and len(parents) > 0: + # hierarchy = os.path.sep.join(hierarchy) + hierarchy = os.path.join(*parents) + + template_data = {"root": root, + "project": {"name": PROJECT, + "code": project['data']['code']}, + "silo": asset['silo'], + "task": api.Session["AVALON_TASK"], + "asset": ASSET, + "family": instance.data['family'], + "subset": subset["name"], + "version": int(version["name"]), + "hierarchy": hierarchy} + + anatomy = instance.context.data['anatomy'] + + # Find the representations to transfer amongst the files + # Each should be a single representation (as such, a single extension) + representations = [] + destination_list = [] + template_name = 'publish' + if 'transfers' not in instance.data: + instance.data['transfers'] = [] + + for idx, repre in enumerate(instance.data["representations"]): + + # Collection + # _______ + # |______|\ + # | |\| + # | || + # | || + # | || + # |_______| + # + + files = repre['files'] + if repre.get('stagingDir'): + stagingdir = repre['stagingDir'] + if repre.get('anatomy_template'): + template_name = repre['anatomy_template'] + template = anatomy.templates[template_name]["path"] + + if isinstance(files, list): + src_collections, remainder = clique.assemble(files) + self.log.debug( + "dst_collections: {}".format(str(src_collections))) + src_collection = src_collections[0] + # Assert that each member has identical suffix + src_head = src_collection.format("{head}") + src_tail = ext = src_collection.format("{tail}") + + test_dest_files = list() + for i in [1, 2]: + template_data["representation"] = repre['ext'] + template_data["frame"] = src_collection.format( + "{padding}") % i + anatomy_filled = anatomy.format(template_data) + test_dest_files.append( + anatomy_filled[template_name]["path"]) + + dst_collections, remainder = clique.assemble(test_dest_files) + dst_collection = dst_collections[0] + dst_head = dst_collection.format("{head}") + dst_tail = dst_collection.format("{tail}") + + instance.data["representations"][idx]['published_path'] = dst_collection.format() + + for i in src_collection.indexes: + src_padding = src_collection.format("{padding}") % i + src_file_name = "{0}{1}{2}".format( + src_head, src_padding, src_tail) + + dst_padding = dst_collection.format("{padding}") % i + dst = "{0}{1}{2}".format(dst_head, dst_padding, dst_tail) + + src = os.path.join(stagingdir, src_file_name) + # src = src_file_name + self.log.debug("source: {}".format(src)) + instance.data["transfers"].append([src, dst]) + + else: + # Single file + # _______ + # | |\ + # | | + # | | + # | | + # |_______| + # + template_data.pop("frame", None) + fname = files + assert not os.path.isabs(fname), ( + "Given file name is a full path" + ) + _, ext = os.path.splitext(fname) + + template_data["representation"] = repre['ext'] + + src = os.path.join(stagingdir, fname) + # src = fname + anatomy_filled = anatomy.format(template_data) + dst = anatomy_filled[template_name]["path"] + + instance.data["transfers"].append([src, dst]) + # template = anatomy.templates["publish"]["path"] + instance.data["representations"][idx]['published_path'] = dst + + representation = { + "schema": "pype:representation-2.0", + "type": "representation", + "parent": version_id, + "name": repre['name'], + "data": {'path': dst, 'template': template}, + "dependencies": instance.data.get("dependencies", "").split(), + + # Imprint shortcut to context + # for performance reasons. + "context": { + "root": root, + "project": {"name": PROJECT, + "code": project['data']['code']}, + 'task': api.Session["AVALON_TASK"], + "silo": asset['silo'], + "asset": ASSET, + "family": instance.data['family'], + "subset": subset["name"], + "version": version["name"], + "hierarchy": hierarchy, + "representation": repre['ext'] + } + } + + destination_list.append(dst) + instance.data['destination_list'] = destination_list + representations.append(representation) + + self.log.info("Registering {} items".format(len(representations))) + + io.insert_many(representations) + + def integrate(self, instance): + """Move the files + + Through `instance.data["transfers"]` + + Args: + instance: the instance to integrate + """ + + transfers = instance.data.get("transfers", list()) + + for src, dest in transfers: + self.log.info("Copying file .. {} -> {}".format(src, dest)) + self.copy_file(src, dest) + + # Produce hardlinked copies + # Note: hardlink can only be produced between two files on the same + # server/disk and editing one of the two will edit both files at once. + # As such it is recommended to only make hardlinks between static files + # to ensure publishes remain safe and non-edited. + hardlinks = instance.data.get("hardlinks", list()) + for src, dest in hardlinks: + self.log.info("Hardlinking file .. {} -> {}".format(src, dest)) + self.hardlink_file(src, dest) + + def copy_file(self, src, dst): + """ Copy given source to destination + + Arguments: + src (str): the source file which needs to be copied + dst (str): the destination of the sourc file + Returns: + None + """ + + dirname = os.path.dirname(dst) + try: + os.makedirs(dirname) + except OSError as e: + if e.errno == errno.EEXIST: + pass + else: + self.log.critical("An unexpected error occurred.") + raise + + shutil.copy(src, dst) + + def hardlink_file(self, src, dst): + + dirname = os.path.dirname(dst) + try: + os.makedirs(dirname) + except OSError as e: + if e.errno == errno.EEXIST: + pass + else: + self.log.critical("An unexpected error occurred.") + raise + + filelink.create(src, dst, filelink.HARDLINK) + + def get_subset(self, asset, instance): + + subset = io.find_one({"type": "subset", + "parent": asset["_id"], + "name": instance.data["subset"]}) + + if subset is None: + subset_name = instance.data["subset"] + self.log.info("Subset '%s' not found, creating.." % subset_name) + + _id = io.insert_one({ + "schema": "avalon-core:subset-2.0", + "type": "subset", + "name": subset_name, + "data": {}, + "parent": asset["_id"] + }).inserted_id + + subset = io.find_one({"_id": _id}) + + return subset + + def create_version(self, subset, version_number, locations, data=None): + """ Copy given source to destination + + Args: + subset (dict): the registered subset of the asset + version_number (int): the version number + locations (list): the currently registered locations + + Returns: + dict: collection of data to create a version + """ + # Imprint currently registered location + version_locations = [location for location in locations if + location is not None] + + return {"schema": "avalon-core:version-2.0", + "type": "version", + "parent": subset["_id"], + "name": version_number, + "locations": version_locations, + "data": data} + + def create_version_data(self, context, instance): + """Create the data collection for the version + + Args: + context: the current context + instance: the current instance being published + + Returns: + dict: the required information with instance.data as key + """ + + families = [] + current_families = instance.data.get("families", list()) + instance_family = instance.data.get("family", None) + + if instance_family is not None: + families.append(instance_family) + families += current_families + + self.log.debug("Registered root: {}".format(api.registered_root())) + # create relative source path for DB + try: + source = instance.data['source'] + except KeyError: + source = context.data["currentFile"] + relative_path = os.path.relpath(source, api.registered_root()) + source = os.path.join("{root}", relative_path).replace("\\", "/") + + self.log.debug("Source: {}".format(source)) + version_data = {"families": families, + "time": context.data["time"], + "author": context.data["user"], + "source": source, + "comment": context.data.get("comment"), + "machine": context.data.get("machine"), + "fps": context.data.get("fps")} + + # Include optional data if present in + optionals = [ + "startFrame", "endFrame", "step", "handles", "sourceHashes" + ] + for key in optionals: + if key in instance.data: + version_data[key] = instance.data[key] + + return version_data From c5d0f19385954bb361847d7ca141bfbb72d6edb2 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 24 May 2019 01:13:17 +0200 Subject: [PATCH 39/48] switch from 'files' to 'representations' integrator process --- .../plugins/houdini/publish/extract_alembic.py | 14 ++++++++++---- pype/plugins/maya/publish/collect_scene.py | 15 +++++++++------ pype/plugins/maya/publish/extract_animation.py | 12 +++++++++--- pype/plugins/maya/publish/extract_ass.py | 17 +++++++++++------ pype/plugins/maya/publish/extract_assproxy.py | 14 +++++++++----- .../maya/publish/extract_camera_alembic.py | 12 +++++++++--- .../maya/publish/extract_camera_mayaAscii.py | 12 +++++++++--- .../maya/publish/extract_maya_ascii_raw.py | 12 +++++++++--- pype/plugins/maya/publish/extract_model.py | 12 +++++++++--- .../plugins/maya/publish/extract_pointcache.py | 12 +++++++++--- pype/plugins/maya/publish/extract_quicktime.py | 18 +++++++++++++++--- pype/plugins/maya/publish/extract_rig.py | 13 ++++++++++--- 12 files changed, 118 insertions(+), 45 deletions(-) diff --git a/pype/plugins/houdini/publish/extract_alembic.py b/pype/plugins/houdini/publish/extract_alembic.py index f40f3d2d0e..4910c45aca 100644 --- a/pype/plugins/houdini/publish/extract_alembic.py +++ b/pype/plugins/houdini/publish/extract_alembic.py @@ -20,7 +20,7 @@ class ExtractAlembic(pype.api.Extractor): # Get the filename from the filename parameter output = ropnode.evalParm("filename") staging_dir = os.path.dirname(output) - instance.data["stagingDir"] = staging_dir + # instance.data["stagingDir"] = staging_dir file_name = os.path.basename(output) @@ -37,7 +37,13 @@ class ExtractAlembic(pype.api.Extractor): traceback.print_exc() raise RuntimeError("Render failed: {0}".format(exc)) - if "files" not in instance.data: - instance.data["files"] = [] + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data["files"].append(file_name) + representation = { + 'name': 'abc', + 'ext': '.abc', + 'files': file_name, + "stagingDir": staging_dir, + } + instance.data["representations"].append(representation) diff --git a/pype/plugins/maya/publish/collect_scene.py b/pype/plugins/maya/publish/collect_scene.py index 43b4c842e3..88c9ed7a47 100644 --- a/pype/plugins/maya/publish/collect_scene.py +++ b/pype/plugins/maya/publish/collect_scene.py @@ -37,16 +37,19 @@ class CollectMayaScene(pyblish.api.ContextPlugin): "label": subset, "publish": False, "family": 'workfile', - "representation": "ma", - "setMembers": [current_file], - "stagingDir": folder + "setMembers": [current_file] }) - data['files'] = [file] + data['representations'] = [{ + 'name': 'ma', + 'ext': '.ma', + 'files': file, + "stagingDir": folder, + }] instance.data.update(data) self.log.info('Collected instance: {}'.format(file)) self.log.info('Scene path: {}'.format(current_file)) - self.log.info('stagin Dir: {}'.format(folder)) - self.log.info('subset: {}'.format(filename)) + self.log.info('staging Dir: {}'.format(folder)) + self.log.info('subset: {}'.format(subset)) diff --git a/pype/plugins/maya/publish/extract_animation.py b/pype/plugins/maya/publish/extract_animation.py index 24d06ac182..30d5dae92b 100644 --- a/pype/plugins/maya/publish/extract_animation.py +++ b/pype/plugins/maya/publish/extract_animation.py @@ -77,9 +77,15 @@ class ExtractAnimation(pype.api.Extractor): endFrame=end, **options) - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data["files"].append(filename) + representation = { + 'name': 'abc', + 'ext': '.abc', + 'files': filename, + "stagingDir": dirname, + } + instance.data["representations"].append(representation) self.log.info("Extracted {} to {}".format(instance, dirname)) diff --git a/pype/plugins/maya/publish/extract_ass.py b/pype/plugins/maya/publish/extract_ass.py index 14b548b928..0c7ef02b4b 100644 --- a/pype/plugins/maya/publish/extract_ass.py +++ b/pype/plugins/maya/publish/extract_ass.py @@ -21,8 +21,8 @@ class ExtractAssStandin(pype.api.Extractor): def process(self, instance): staging_dir = self.staging_dir(instance) - file_name = "{}.ass".format(instance.name) - file_path = os.path.join(staging_dir, file_name) + filename = "{}.ass".format(instance.name) + file_path = os.path.join(staging_dir, filename) # Write out .ass file self.log.info("Writing: '%s'" % file_path) @@ -37,11 +37,16 @@ class ExtractAssStandin(pype.api.Extractor): boundingBox=True ) + if "representations" not in instance.data: + instance.data["representations"] = [] - if "files" not in instance.data: - instance.data["files"] = list() - - instance.data["files"].append(file_name) + representation = { + 'name': 'ass', + 'ext': '.ass', + 'files': filename, + "stagingDir": staging_dir + } + instance.data["representations"].append(representation) self.log.info("Extracted instance '%s' to: %s" % (instance.name, staging_dir)) diff --git a/pype/plugins/maya/publish/extract_assproxy.py b/pype/plugins/maya/publish/extract_assproxy.py index 87e7b35799..bc807be9b0 100644 --- a/pype/plugins/maya/publish/extract_assproxy.py +++ b/pype/plugins/maya/publish/extract_assproxy.py @@ -33,7 +33,6 @@ class ExtractAssProxy(pype.api.Extractor): else: yield - # Define extract output file path stagingdir = self.staging_dir(instance) filename = "{0}.ma".format(instance.name) @@ -64,10 +63,15 @@ class ExtractAssProxy(pype.api.Extractor): expressions=False, constructionHistory=False) + if "representations" not in instance.data: + instance.data["representations"] = [] - if "files" not in instance.data: - instance.data["files"] = list() - - instance.data["files"].append(filename) + representation = { + 'name': 'ma', + 'ext': '.ma', + 'files': filename, + "stagingDir": stagingdir + } + instance.data["representations"].append(representation) self.log.info("Extracted instance '%s' to: %s" % (instance.name, path)) diff --git a/pype/plugins/maya/publish/extract_camera_alembic.py b/pype/plugins/maya/publish/extract_camera_alembic.py index 90f4954823..01239fd1e8 100644 --- a/pype/plugins/maya/publish/extract_camera_alembic.py +++ b/pype/plugins/maya/publish/extract_camera_alembic.py @@ -70,10 +70,16 @@ class ExtractCameraAlembic(pype.api.Extractor): with avalon.maya.suspended_refresh(): cmds.AbcExport(j=job_str, verbose=False) - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data["files"].append(filename) + representation = { + 'name': 'abc', + 'ext': '.abc', + 'files': filename, + "stagingDir": dir_path, + } + instance.data["representations"].append(representation) self.log.info("Extracted instance '{0}' to: {1}".format( instance.name, path)) diff --git a/pype/plugins/maya/publish/extract_camera_mayaAscii.py b/pype/plugins/maya/publish/extract_camera_mayaAscii.py index 4a21e7bf58..152acb98fe 100644 --- a/pype/plugins/maya/publish/extract_camera_mayaAscii.py +++ b/pype/plugins/maya/publish/extract_camera_mayaAscii.py @@ -168,10 +168,16 @@ class ExtractCameraMayaAscii(pype.api.Extractor): massage_ma_file(path) - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data["files"].append(filename) + representation = { + 'name': 'ma', + 'ext': '.ma', + 'files': filename, + "stagingDir": dir_path, + } + instance.data["representations"].append(representation) self.log.info("Extracted instance '{0}' to: {1}".format( instance.name, path)) diff --git a/pype/plugins/maya/publish/extract_maya_ascii_raw.py b/pype/plugins/maya/publish/extract_maya_ascii_raw.py index 70c6f246f6..c8f10d5b9b 100644 --- a/pype/plugins/maya/publish/extract_maya_ascii_raw.py +++ b/pype/plugins/maya/publish/extract_maya_ascii_raw.py @@ -51,9 +51,15 @@ class ExtractMayaAsciiRaw(pype.api.Extractor): constraints=True, expressions=True) - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data["files"].append(filename) + representation = { + 'name': 'ma', + 'ext': '.ma', + 'files': filename, + "stagingDir": dir_path + } + instance.data["representations"].append(representation) self.log.info("Extracted instance '%s' to: %s" % (instance.name, path)) diff --git a/pype/plugins/maya/publish/extract_model.py b/pype/plugins/maya/publish/extract_model.py index 302a899793..f6d9681222 100644 --- a/pype/plugins/maya/publish/extract_model.py +++ b/pype/plugins/maya/publish/extract_model.py @@ -69,9 +69,15 @@ class ExtractModel(pype.api.Extractor): # Store reference for integration - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data["files"].append(filename) + representation = { + 'name': 'ma', + 'ext': '.ma', + 'files': filename, + "stagingDir": stagingdir, + } + instance.data["representations"].append(representation) self.log.info("Extracted instance '%s' to: %s" % (instance.name, path)) diff --git a/pype/plugins/maya/publish/extract_pointcache.py b/pype/plugins/maya/publish/extract_pointcache.py index 65982febd8..907dfe0e18 100644 --- a/pype/plugins/maya/publish/extract_pointcache.py +++ b/pype/plugins/maya/publish/extract_pointcache.py @@ -79,9 +79,15 @@ class ExtractAlembic(pype.api.Extractor): endFrame=end, **options) - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data["files"].append(filename) + representation = { + 'name': 'abc', + 'ext': '.abc', + 'files': filename, + "stagingDir": dirname + } + instance.data["representations"].append(representation) self.log.info("Extracted {} to {}".format(instance, dirname)) diff --git a/pype/plugins/maya/publish/extract_quicktime.py b/pype/plugins/maya/publish/extract_quicktime.py index 16c8c08e52..caabf9560c 100644 --- a/pype/plugins/maya/publish/extract_quicktime.py +++ b/pype/plugins/maya/publish/extract_quicktime.py @@ -123,9 +123,21 @@ class ExtractQuicktime(pype.api.Extractor): self.log.error(ffmpeg_error) raise RuntimeError(ffmpeg_error) - if "files" not in instance.data: - instance.data["files"] = list() - instance.data["files"].append(movieFile) + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'mov', + 'ext': '.mov', + 'files': movieFile, + "stagingDir": stagingdir, + 'startFrame': start, + 'endFrame': end, + 'frameRate': fps, + 'preview': True + } + instance.data["representations"].append(representation) + @contextlib.contextmanager diff --git a/pype/plugins/maya/publish/extract_rig.py b/pype/plugins/maya/publish/extract_rig.py index bc355cc925..713d5e2b59 100644 --- a/pype/plugins/maya/publish/extract_rig.py +++ b/pype/plugins/maya/publish/extract_rig.py @@ -34,9 +34,16 @@ class ExtractRig(pype.api.Extractor): expressions=True, constructionHistory=True) - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'ma', + 'ext': '.ma', + 'files': filename, + "stagingDir": dir_path + } + instance.data["representations"].append(representation) - instance.data["files"].append(filename) self.log.info("Extracted instance '%s' to: %s" % (instance.name, path)) From f98c8d3b71bbc075c8e78b675673462ec768ee46 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 24 May 2019 01:13:48 +0200 Subject: [PATCH 40/48] make sure we're collecting file sequences correctly --- .../publish/collect_context.py | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/pype/plugins/standalonepublish/publish/collect_context.py b/pype/plugins/standalonepublish/publish/collect_context.py index 055501a50d..73ae33ca18 100644 --- a/pype/plugins/standalonepublish/publish/collect_context.py +++ b/pype/plugins/standalonepublish/publish/collect_context.py @@ -5,7 +5,6 @@ import json import logging import clique - log = logging.getLogger("collector") @@ -13,12 +12,6 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin): """ Collecting temp json data sent from a host context and path for returning json data back to hostself. - - Setting avalon session into correct context - - Args: - context (obj): pyblish context session - """ label = "Collect Context - SA Publish" @@ -59,8 +52,9 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin): "families": [family, 'ftrack'], }) self.log.info("collected instance: {}".format(instance.data)) + self.log.info("parsing data: {}".format(in_data)) - instance.data["files"] = list() + # instance.data["files"] = list() instance.data['destination_list'] = list() instance.data['representations'] = list() instance.data['source'] = 'standalone publisher' @@ -70,17 +64,13 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin): component['destination'] = component['files'] component['stagingDir'] = component['stagingDir'] component['anatomy_template'] = 'render' - collections, remainder = clique.assemble(component['files']) - if collections: - self.log.debug(collections) + if isinstance(component['files'], list): + collections, remainder = clique.assemble(component['files']) + self.log.debug("collecting sequence: {}".format(collections)) instance.data['startFrame'] = int(component['startFrame']) instance.data['endFrame'] = int(component['endFrame']) instance.data['frameRate'] = int(component['frameRate']) - instance.data["files"].append(component) instance.data["representations"].append(component) - instance.data["thumbnail"] = component['thumbnail'] - instance.data["preview"] = component['preview'] - self.log.info(in_data) From 3657d4cfad0b3b2d12bcdd71709d16e1ad520194 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 24 May 2019 01:14:29 +0200 Subject: [PATCH 41/48] make thumbnail and preview data in representations optional --- .../publish/integrate_ftrack_instances.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py b/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py index 2605d95494..9d49250ae6 100644 --- a/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py +++ b/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py @@ -41,22 +41,16 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): componentList = [] ft_session = instance.context.data["ftrackSession"] - components = instance.data['representations'] - - for comp in components: + for comp in instance.data['representations']: self.log.debug('component {}'.format(comp)) - # filename, ext = os.path.splitext(file) - # self.log.debug('dest ext: ' + ext) - # ext = comp['Context'] - - if comp['thumbnail']: + if comp.get('thumbnail'): location = ft_session.query( 'Location where name is "ftrack.server"').one() component_data = { "name": "thumbnail" # Default component name is "main". } - elif comp['preview']: + elif comp.get('preview'): if not comp.get('startFrameReview'): comp['startFrameReview'] = comp['startFrame'] if not comp.get('endFrameReview'): From cd837f9b12214392469b4ebcf48188ac0b4a7411 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 24 May 2019 01:19:09 +0200 Subject: [PATCH 42/48] add more info to docstring --- pype/plugins/global/publish/integrate_new.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index 357cd9fbbf..e01c339143 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -30,6 +30,14 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): 'stagingDir': "path/to/folder/with/files" 'name': representation name (usually the same as extension) 'ext': file extension + optional data + 'anatomy_template': 'publish' or 'render', etc. + template from anatomy that should be used for + integrating this file. Only the first level can + be specified right now. + 'startFrame' + 'endFrame' + 'framerate' """ label = "Integrate Asset New" From 9fa7e893d80ddc2aff2e802ee27000c0803aabfd Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 27 May 2019 14:01:16 +0100 Subject: [PATCH 43/48] add support for more families in the new integrator --- .../global/publish/collect_filesequences.py | 9 ++- pype/plugins/global/publish/extract_jpeg.py | 13 ++++- .../global/publish/extract_quicktime.py | 13 ++++- pype/plugins/global/publish/integrate.py | 6 -- pype/plugins/global/publish/integrate_new.py | 11 +++- .../houdini/publish/extract_vdb_cache.py | 14 +++-- pype/plugins/maya/publish/extract_fbx.py | 17 ++++-- .../plugins/maya/publish/extract_thumbnail.py | 56 ++++--------------- .../plugins/maya/publish/extract_vrayproxy.py | 12 +++- pype/plugins/nuke/publish/collect_reads.py | 11 +++- pype/plugins/nuke/publish/collect_writes.py | 16 ++++-- pype/plugins/nuke/publish/extract_review.py | 28 +++++++--- pype/plugins/nuke/publish/extract_script.py | 16 ++++-- .../publish/collect_context.py | 1 - 14 files changed, 129 insertions(+), 94 deletions(-) diff --git a/pype/plugins/global/publish/collect_filesequences.py b/pype/plugins/global/publish/collect_filesequences.py index ce4c95d465..ce1ae2258e 100644 --- a/pype/plugins/global/publish/collect_filesequences.py +++ b/pype/plugins/global/publish/collect_filesequences.py @@ -179,7 +179,6 @@ class CollectFileSequences(pyblish.api.ContextPlugin): "subset": subset, "asset": data.get("asset", api.Session["AVALON_ASSET"]), "stagingDir": root, - "files": [list(collection)], "startFrame": start, "endFrame": end, "fps": fps, @@ -187,6 +186,14 @@ class CollectFileSequences(pyblish.api.ContextPlugin): }) instance.append(collection) + representation = { + 'name': 'jpg', + 'ext': '.jpg', + 'files': [list(collection)], + "stagingDir": root, + } + instance.data["representations"] = [representation] + if data.get('user'): context.data["user"] = data['user'] diff --git a/pype/plugins/global/publish/extract_jpeg.py b/pype/plugins/global/publish/extract_jpeg.py index 7720c9d56d..88e17f55c7 100644 --- a/pype/plugins/global/publish/extract_jpeg.py +++ b/pype/plugins/global/publish/extract_jpeg.py @@ -62,6 +62,13 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin): sub_proc = subprocess.Popen(subprocess_jpeg) sub_proc.wait() - if "files" not in instance.data: - instance.data["files"] = list() - instance.data["files"].append(jpegFile) + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'jpg', + 'ext': '.jpg', + 'files': jpegFile, + "stagingDir": stagingdir, + } + instance.data["representations"].append(representation) diff --git a/pype/plugins/global/publish/extract_quicktime.py b/pype/plugins/global/publish/extract_quicktime.py index fd34c46e5e..4c7db34e95 100644 --- a/pype/plugins/global/publish/extract_quicktime.py +++ b/pype/plugins/global/publish/extract_quicktime.py @@ -70,6 +70,13 @@ class ExtractQuicktimeEXR(pyblish.api.InstancePlugin): sub_proc = subprocess.Popen(subprocess_mov) sub_proc.wait() - if "files" not in instance.data: - instance.data["files"] = list() - instance.data["files"].append(movFile) + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'mov', + 'ext': '.mov', + 'files': movFile, + "stagingDir": stagingdir, + } + instance.data["representations"].append(representation) diff --git a/pype/plugins/global/publish/integrate.py b/pype/plugins/global/publish/integrate.py index b4ca38c334..d1a572fbfd 100644 --- a/pype/plugins/global/publish/integrate.py +++ b/pype/plugins/global/publish/integrate.py @@ -26,16 +26,10 @@ class IntegrateAsset(pyblish.api.InstancePlugin): label = "Integrate Asset" order = pyblish.api.IntegratorOrder families = ["look", - "vdbcache", "assembly", - "vrayproxy", "yetiRig", "yeticache", "nukescript", - "review", - "scene", - "render", - "imagesequence", "write"] exclude_families = ["clip"] diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index e01c339143..5f6fa7b674 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -34,7 +34,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): 'anatomy_template': 'publish' or 'render', etc. template from anatomy that should be used for integrating this file. Only the first level can - be specified right now. + be specified right now. 'startFrame' 'endFrame' 'framerate' @@ -50,7 +50,13 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "mayaAscii", "setdress", "layout", - "ass" + "ass", + "vdbcache", + "scene", + "vrayproxy", + "render", + "imagesequence", + "review" ] exclude_families = ["clip"] @@ -281,7 +287,6 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): dst = anatomy_filled[template_name]["path"] instance.data["transfers"].append([src, dst]) - # template = anatomy.templates["publish"]["path"] instance.data["representations"][idx]['published_path'] = dst representation = { diff --git a/pype/plugins/houdini/publish/extract_vdb_cache.py b/pype/plugins/houdini/publish/extract_vdb_cache.py index cfd9104744..29686ef2fd 100644 --- a/pype/plugins/houdini/publish/extract_vdb_cache.py +++ b/pype/plugins/houdini/publish/extract_vdb_cache.py @@ -35,9 +35,15 @@ class ExtractVDBCache(pype.api.Extractor): traceback.print_exc() raise RuntimeError("Render failed: {0}".format(exc)) - if "files" not in instance.data: - instance.data["files"] = [] - output = instance.data["frames"] - instance.data["files"].append(output) + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'mov', + 'ext': '.mov', + 'files': output, + "stagingDir": staging_dir, + } + instance.data["representations"].append(representation) diff --git a/pype/plugins/maya/publish/extract_fbx.py b/pype/plugins/maya/publish/extract_fbx.py index 48dd5a135c..93a99eea72 100644 --- a/pype/plugins/maya/publish/extract_fbx.py +++ b/pype/plugins/maya/publish/extract_fbx.py @@ -146,9 +146,9 @@ class ExtractFBX(pype.api.Extractor): cmds.loadPlugin("fbxmaya", quiet=True) # Define output path - directory = self.staging_dir(instance) + stagingDir = self.staging_dir(instance) filename = "{0}.fbx".format(instance.name) - path = os.path.join(directory, filename) + path = os.path.join(stagingDir, filename) # The export requires forward slashes because we need # to format it into a string in a mel expression @@ -208,9 +208,16 @@ class ExtractFBX(pype.api.Extractor): cmds.select(members, r=1, noExpand=True) mel.eval('FBXExport -f "{}" -s'.format(path)) - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'mov', + 'ext': '.mov', + 'files': filename, + "stagingDir": stagingDir, + } + instance.data["representations"].append(representation) - instance.data["files"].append(filename) self.log.info("Extract FBX successful to: {0}".format(path)) diff --git a/pype/plugins/maya/publish/extract_thumbnail.py b/pype/plugins/maya/publish/extract_thumbnail.py index 8bcd017729..355aa2b08b 100644 --- a/pype/plugins/maya/publish/extract_thumbnail.py +++ b/pype/plugins/maya/publish/extract_thumbnail.py @@ -110,9 +110,9 @@ class ExtractThumbnail(pype.api.Extractor): "depthOfField": cmds.getAttr("{0}.depthOfField".format(camera)), } - stagingdir = self.staging_dir(instance) + stagingDir = self.staging_dir(instance) filename = "{0}".format(instance.name) - path = os.path.join(stagingdir, filename) + path = os.path.join(stagingDir, filename) self.log.info("Outputting images to %s" % path) @@ -131,51 +131,19 @@ class ExtractThumbnail(pype.api.Extractor): _, thumbnail = os.path.split(playblast) self.log.info("file list {}".format(thumbnail)) - # self.log.info("Calculating HUD data overlay") - # stagingdir = "C:/Users/milan.kolar/AppData/Local/Temp/pyblish_tmp_ucsymm" - # collected_frames = os.listdir(stagingdir) - # collections, remainder = clique.assemble(collected_frames) - # input_path = os.path.join(stagingdir, collections[0].format('{head}{padding}{tail}')) - # self.log.info("input {}".format(input_path)) + if "representations" not in instance.data: + instance.data["representations"] = [] - # movieFile = filename + ".mov" - # full_movie_path = os.path.join(stagingdir, movieFile) - # self.log.info("output {}".format(full_movie_path)) - # fls = [os.path.join(stagingdir, filename).replace("\\","/") for f in os.listdir( dir_path ) if f.endswith(preset['compression'])] - # self.log.info("file list {}}".format(fls[0])) + representation = { + 'name': 'thumbnail', + 'ext': '.jpg', + 'files': thumbnail, + "stagingDir": stagingDir, + "thumbnail": True + } + instance.data["representations"].append(representation) - # out, err = ( - # ffmpeg - # .input(input_path, framerate=25) - # .output(full_movie_path) - # .run(overwrite_output=True) - # ) - - if "files" not in instance.data: - instance.data["files"] = list() - instance.data["files"].append(thumbnail) - - # ftrackStrings = fStrings.annotationData() - # nData = ftrackStrings.niceData - # nData['version'] = instance.context.data('version') - # fFrame = int(pm.playbackOptions( q = True, minTime = True)) - # eFrame = int(pm.playbackOptions( q = True, maxTime = True)) - # nData['frame'] = [(str("{0:05d}".format(f))) for f in range(fFrame, eFrame + 1)] - # soundOfst = int(float(nData['oFStart'])) - int(float(nData['handle'])) - fFrame - # soundFile = mu.giveMePublishedAudio() - # self.log.info("SOUND offset %s" % str(soundOfst)) - # self.log.info("SOUND source video to %s" % str(soundFile)) - # ann = dHUD.draftAnnotate() - # if soundFile: - # ann.addAnotation(seqFls = fls, outputMoviePth = movieFullPth, annotateDataArr = nData, soundFile = soundFile, soundOffset = soundOfst) - # else: - # ann.addAnotation(seqFls = fls, outputMoviePth = movieFullPth, annotateDataArr = nData) - - # for f in fls: - # os.remove(f) - - # playblast = (ann.expPth).replace("\\","/") @contextlib.contextmanager diff --git a/pype/plugins/maya/publish/extract_vrayproxy.py b/pype/plugins/maya/publish/extract_vrayproxy.py index 5c68f3ca81..b2c84db22b 100644 --- a/pype/plugins/maya/publish/extract_vrayproxy.py +++ b/pype/plugins/maya/publish/extract_vrayproxy.py @@ -54,10 +54,16 @@ class ExtractVRayProxy(pype.api.Extractor): ignoreHiddenObjects=True, createProxyNode=False) - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data["files"].append(file_name) + representation = { + 'name': 'vrmesh', + 'ext': '.vrmesh', + 'files': file_name, + "stagingDir": staging_dir, + } + instance.data["representations"].append(representation) self.log.info("Extracted instance '%s' to: %s" % (instance.name, staging_dir)) diff --git a/pype/plugins/nuke/publish/collect_reads.py b/pype/plugins/nuke/publish/collect_reads.py index 75ea2efa3a..1bba6198d2 100644 --- a/pype/plugins/nuke/publish/collect_reads.py +++ b/pype/plugins/nuke/publish/collect_reads.py @@ -76,7 +76,16 @@ class CollectNukeReads(pyblish.api.ContextPlugin): self.log.debug("collected_frames: {}".format(label)) - instance.data["files"] = [source_files] + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': ext, + 'ext': "." + ext, + 'files': source_files, + "stagingDir": source_dir, + } + instance.data["representations"].append(representation) transfer = False if "publish" in node.knobs(): diff --git a/pype/plugins/nuke/publish/collect_writes.py b/pype/plugins/nuke/publish/collect_writes.py index 68cd227280..fb66a29bc8 100644 --- a/pype/plugins/nuke/publish/collect_writes.py +++ b/pype/plugins/nuke/publish/collect_writes.py @@ -63,12 +63,16 @@ class CollectNukeWrites(pyblish.api.ContextPlugin): int(last_frame) ) - if "files" not in instance.data: - instance.data["files"] = list() - try: - collected_frames = os.listdir(output_dir) - self.log.debug("collected_frames: {}".format(label)) - instance.data["files"].append(collected_frames) + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': ext, + 'ext': "." + ext, + 'files': collected_frames, + "stagingDir": output_dir, + } + instance.data["representations"].append(representation) except Exception: self.log.debug("couldn't collect frames: {}".format(label)) diff --git a/pype/plugins/nuke/publish/extract_review.py b/pype/plugins/nuke/publish/extract_review.py index 16fb07a3fc..ed38893927 100644 --- a/pype/plugins/nuke/publish/extract_review.py +++ b/pype/plugins/nuke/publish/extract_review.py @@ -26,7 +26,7 @@ class ExtractDataForReview(pype.api.Extractor): # Deselect all nodes to prevent external connections [i["selected"].setValue(False) for i in nuke.allNodes()] self.log.debug("creating staging dir:") - self.staging_dir(instance) + self.stagingDir(instance) self.log.debug("instance: {}".format(instance)) self.log.debug("instance.data[families]: {}".format( @@ -50,10 +50,10 @@ class ExtractDataForReview(pype.api.Extractor): def transcode_mov(self, instance): collection = instance.data["collection"] - staging_dir = instance.data["stagingDir"].replace("\\", "/") + stagingDir = instance.data["stagingDir"].replace("\\", "/") file_name = collection.format("{head}mov") - review_mov = os.path.join(staging_dir, file_name).replace("\\", "/") + review_mov = os.path.join(stagingDir, file_name).replace("\\", "/") self.log.info("transcoding review mov: {0}".format(review_mov)) if instance.data.get("baked_colorspace_movie"): @@ -85,8 +85,8 @@ class ExtractDataForReview(pype.api.Extractor): import nuke temporary_nodes = [] - staging_dir = instance.data["stagingDir"].replace("\\", "/") - self.log.debug("StagingDir `{0}`...".format(staging_dir)) + stagingDir = instance.data["stagingDir"].replace("\\", "/") + self.log.debug("StagingDir `{0}`...".format(stagingDir)) collection = instance.data.get("collection", None) @@ -108,7 +108,7 @@ class ExtractDataForReview(pype.api.Extractor): node = previous_node = nuke.createNode("Read") node["file"].setValue( - os.path.join(staging_dir, fname).replace("\\", "/")) + os.path.join(stagingDir, fname).replace("\\", "/")) node["first"].setValue(first_frame) node["origfirst"].setValue(first_frame) @@ -147,7 +147,7 @@ class ExtractDataForReview(pype.api.Extractor): if representation in "mov": file = fhead + "baked.mov" - path = os.path.join(staging_dir, file).replace("\\", "/") + path = os.path.join(stagingDir, file).replace("\\", "/") self.log.debug("Path: {}".format(path)) instance.data["baked_colorspace_movie"] = path write_node["file"].setValue(path) @@ -158,7 +158,7 @@ class ExtractDataForReview(pype.api.Extractor): elif representation in "jpeg": file = fhead + "jpeg" - path = os.path.join(staging_dir, file).replace("\\", "/") + path = os.path.join(stagingDir, file).replace("\\", "/") instance.data["thumbnail"] = path write_node["file"].setValue(path) write_node["file_type"].setValue("jpeg") @@ -170,7 +170,17 @@ class ExtractDataForReview(pype.api.Extractor): first_frame = int(last_frame) / 2 last_frame = int(last_frame) / 2 # add into files for integration as representation - instance.data["files"].append(file) + + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'nk', + 'ext': '.nk', + 'files': file, + "stagingDir": stagingdir, + } + instance.data["representations"].append(representation) # Render frames nuke.execute(write_node.name(), int(first_frame), int(last_frame)) diff --git a/pype/plugins/nuke/publish/extract_script.py b/pype/plugins/nuke/publish/extract_script.py index f0ed438094..b54fa548a5 100644 --- a/pype/plugins/nuke/publish/extract_script.py +++ b/pype/plugins/nuke/publish/extract_script.py @@ -19,16 +19,22 @@ class ExtractScript(pype.api.Extractor): current_script = instance.context.data["currentFile"] # Define extract output file path - dir_path = self.staging_dir(instance) + stagingdir = self.staging_dir(instance) filename = "{0}".format(instance.data["name"]) - path = os.path.join(dir_path, filename) + path = os.path.join(stagingdir, filename) self.log.info("Performing extraction..") shutil.copy(current_script, path) - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data["files"].append(filename) + representation = { + 'name': 'nk', + 'ext': '.nk', + 'files': filename, + "stagingDir": stagingdir, + } + instance.data["representations"].append(representation) self.log.info("Extracted instance '%s' to: %s" % (instance.name, path)) diff --git a/pype/plugins/standalonepublish/publish/collect_context.py b/pype/plugins/standalonepublish/publish/collect_context.py index 73ae33ca18..f1a97ae7b6 100644 --- a/pype/plugins/standalonepublish/publish/collect_context.py +++ b/pype/plugins/standalonepublish/publish/collect_context.py @@ -54,7 +54,6 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin): self.log.info("collected instance: {}".format(instance.data)) self.log.info("parsing data: {}".format(in_data)) - # instance.data["files"] = list() instance.data['destination_list'] = list() instance.data['representations'] = list() instance.data['source'] = 'standalone publisher' From 9596e33b7756d7dfa66d411948c209a670e8854f Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 27 May 2019 17:34:26 +0100 Subject: [PATCH 44/48] ftrack uses new integration data --- .../ftrack/publish/collect_ftrack_api.py | 20 +++-- .../ftrack/publish/integrate_ftrack_api.py | 24 ++++-- .../publish/integrate_ftrack_instances.py | 56 +++++------- .../ftrack/publish/integrate_ftrack_review.py | 85 ------------------- 4 files changed, 54 insertions(+), 131 deletions(-) delete mode 100644 pype/plugins/ftrack/publish/integrate_ftrack_review.py diff --git a/pype/plugins/ftrack/publish/collect_ftrack_api.py b/pype/plugins/ftrack/publish/collect_ftrack_api.py index f5e1cfd950..b15211e990 100644 --- a/pype/plugins/ftrack/publish/collect_ftrack_api.py +++ b/pype/plugins/ftrack/publish/collect_ftrack_api.py @@ -23,11 +23,19 @@ class CollectFtrackApi(pyblish.api.ContextPlugin): project = os.environ.get('AVALON_PROJECT', '') asset = os.environ.get('AVALON_ASSET', '') - task = os.environ.get('AVALON_TASK', '') + task = os.environ.get('AVALON_TASK', None) + self.log.debug(task) - result = session.query('Task where\ - project.full_name is "{0}" and\ - name is "{1}" and\ - parent.name is "{2}"'.format(project, task, asset)).one() + if task: + result = session.query('Task where\ + project.full_name is "{0}" and\ + name is "{1}" and\ + parent.name is "{2}"'.format(project, task, asset)).one() + context.data["ftrackTask"] = result + else: + result = session.query('TypedContext where\ + project.full_name is "{0}" and\ + name is "{1}"'.format(project, asset)).one() + context.data["ftrackEntity"] = result - context.data["ftrackTask"] = result + self.log.info(result) diff --git a/pype/plugins/ftrack/publish/integrate_ftrack_api.py b/pype/plugins/ftrack/publish/integrate_ftrack_api.py index 709a08de9c..2c35b974f9 100644 --- a/pype/plugins/ftrack/publish/integrate_ftrack_api.py +++ b/pype/plugins/ftrack/publish/integrate_ftrack_api.py @@ -29,7 +29,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): if sys.version_info[0] < 3: for key, value in data.iteritems(): if not isinstance(value, (basestring, int)): - self.log.info(value) + self.log.info("value: {}".format(value)) if "id" in value.keys(): queries.append( "{0}.id is \"{1}\"".format(key, value["id"]) @@ -39,7 +39,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): else: for key, value in data.items(): if not isinstance(value, (str, int)): - self.log.info(value) + self.log.info("value: {}".format(value)) if "id" in value.keys(): queries.append( "{0}.id is \"{1}\"".format(key, value["id"]) @@ -56,7 +56,14 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): def process(self, instance): session = instance.context.data["ftrackSession"] - task = instance.context.data["ftrackTask"] + if instance.context.data.get("ftrackTask"): + task = instance.context.data["ftrackTask"] + name = task + parent = task["parent"] + elif instance.context.data.get("ftrackEntity"): + task = None + name = instance.context.data.get("ftrackEntity")['name'] + parent = instance.context.data.get("ftrackEntity") info_msg = "Created new {entity_type} with data: {data}" info_msg += ", metadata: {metadata}." @@ -68,6 +75,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): # Get existing entity. assettype_data = {"short": "upload"} assettype_data.update(data.get("assettype_data", {})) + self.log.debug("data: {}".format(data)) assettype_entity = session.query( self.query("AssetType", assettype_data) @@ -83,9 +91,9 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): # Asset # Get existing entity. asset_data = { - "name": task["name"], + "name": name, "type": assettype_entity, - "parent": task["parent"], + "parent": parent, } asset_data.update(data.get("asset_data", {})) @@ -93,7 +101,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): self.query("Asset", asset_data) ).first() - self.log.info(asset_entity) + self.log.info("asset entity: {}".format(asset_entity)) # Extracting metadata, and adding after entity creation. This is # due to a ftrack_api bug where you can't add metadata on creation. @@ -120,8 +128,10 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin): assetversion_data = { "version": 0, "asset": asset_entity, - "task": task } + if task: + assetversion_data['task'] = task + assetversion_data.update(data.get("assetversion_data", {})) assetversion_entity = session.query( diff --git a/pype/plugins/ftrack/publish/integrate_ftrack_instances.py b/pype/plugins/ftrack/publish/integrate_ftrack_instances.py index 75d9b6db15..a4624d7299 100644 --- a/pype/plugins/ftrack/publish/integrate_ftrack_instances.py +++ b/pype/plugins/ftrack/publish/integrate_ftrack_instances.py @@ -29,60 +29,50 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): def process(self, instance): self.log.debug('instance {}'.format(instance)) - assumed_data = instance.data["assumedTemplateData"] - assumed_version = assumed_data["version"] - version_number = int(assumed_version) + if instance.data.get('version'): version_number = int(instance.data.get('version')) family = instance.data['family'].lower() - asset_type = '' + asset_type = '' asset_type = self.family_mapping[family] componentList = [] - - dst_list = instance.data['destination_list'] - ft_session = instance.context.data["ftrackSession"] - for file in instance.data['destination_list']: - self.log.debug('file {}'.format(file)) + for comp in instance.data['representations']: + self.log.debug('component {}'.format(comp)) - for file in dst_list: - filename, ext = os.path.splitext(file) - self.log.debug('dest ext: ' + ext) - thumbnail = False - - if ext in ['.mov']: - if not instance.data.get('startFrameReview'): - instance.data['startFrameReview'] = instance.data['startFrame'] - if not instance.data.get('endFrameReview'): - instance.data['endFrameReview'] = instance.data['endFrame'] + if comp.get('thumbnail'): + location = ft_session.query( + 'Location where name is "ftrack.server"').one() + component_data = { + "name": "thumbnail" # Default component name is "main". + } + elif comp.get('preview'): + if not comp.get('startFrameReview'): + comp['startFrameReview'] = comp['startFrame'] + if not comp.get('endFrameReview'): + comp['endFrameReview'] = comp['endFrame'] location = ft_session.query( 'Location where name is "ftrack.server"').one() component_data = { # Default component name is "main". "name": "ftrackreview-mp4", "metadata": {'ftr_meta': json.dumps({ - 'frameIn': int(instance.data['startFrameReview']), - 'frameOut': int(instance.data['startFrameReview']), - 'frameRate': 25})} + 'frameIn': int(comp['startFrameReview']), + 'frameOut': int(comp['endFrameReview']), + 'frameRate': comp['frameRate']})} } - elif ext in [".jpg", ".jpeg"]: - component_data = { - "name": "thumbnail" # Default component name is "main". - } - thumbnail = True - location = ft_session.query( - 'Location where name is "ftrack.server"').one() + comp['thumbnail'] = False else: component_data = { - "name": ext[1:] # Default component name is "main". + "name": comp['name'] } - location = ft_session.query( 'Location where name is "ftrack.unmanaged"').one() + comp['thumbnail'] = False self.log.debug('location {}'.format(location)) @@ -96,10 +86,10 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): "version": version_number, }, "component_data": component_data, - "component_path": file, + "component_path": comp['published_path'], 'component_location': location, "component_overwrite": False, - "thumbnail": thumbnail + "thumbnail": comp['thumbnail'] } ) diff --git a/pype/plugins/ftrack/publish/integrate_ftrack_review.py b/pype/plugins/ftrack/publish/integrate_ftrack_review.py deleted file mode 100644 index a00100d1e1..0000000000 --- a/pype/plugins/ftrack/publish/integrate_ftrack_review.py +++ /dev/null @@ -1,85 +0,0 @@ -import pyblish.api -import os -import clique -import json - - -class IntegrateFtrackReview(pyblish.api.InstancePlugin): - """Collect ftrack component data - - Add ftrack component list to instance. - - - """ - - order = pyblish.api.IntegratorOrder + 0.48 - label = 'Integrate Ftrack Review' - families = ['review', 'ftrack'] - - family_mapping = {'review': 'mov' - } - - def process(self, instance): - - self.log.debug('instance {}'.format(instance)) - # - # assumed_data = instance.data["assumedTemplateData"] - # assumed_version = assumed_data["version"] - # version_number = int(assumed_version) - # family = instance.data['family'].lower() - # asset_type = '' - # - # asset_type = self.family_mapping[family] - # - # componentList = [] - # - # dst_list = instance.data['destination_list'] - # - # ft_session = instance.context.data["ftrackSession"] - # - # - # for file in instance.data['destination_list']: - # self.log.debug('file {}'.format(file)) - # - # for file in dst_list: - # filename, ext = os.path.splitext(file) - # self.log.debug('dest ext: ' + ext) - # - # if ext == '.mov': - # component_name = "ftrackreview-mp4" - # metadata = {'ftr_meta': json.dumps({ - # 'frameIn': int(instance.data["startFrame"]), - # 'frameOut': int(instance.data["startFrame"]), - # 'frameRate': 25})} - # thumbnail = False - # - # else: - # component_name = "thumbnail" - # thumbnail = True - # - # location = ft_session.query( - # 'Location where name is "ftrack.server"').one() - # - # componentList.append({"assettype_data": { - # "short": asset_type, - # }, - # "asset_data": { - # "name": instance.data["subset"], - # }, - # "assetversion_data": { - # "version": version_number, - # }, - # "component_data": { - # "name": component_name, # Default component name is "main". - # "metadata": metadata - # }, - # "component_path": file, - # 'component_location': location, - # "component_overwrite": False, - # "thumbnail": thumbnail - # } - # ) - # - # - # self.log.debug('componentsList: {}'.format(str(componentList))) - # instance.data["ftrackComponentsList"] = componentList From 1410c0023800997df405ca3c2fdce282428cc4cd Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 27 May 2019 20:49:37 +0100 Subject: [PATCH 45/48] merging all standalone plugins to globals --- .../publish/collect_context.py | 1 + .../publish/collect_ftrack_api.py | 41 -- .../publish/collect_templates.py | 17 - .../standalonepublish/publish/collect_time.py | 12 - .../standalonepublish/publish/integrate.py | 471 ------------------ .../publish/integrate_ftrack_api.py | 315 ------------ .../publish/integrate_ftrack_instances.py | 95 ---- pype/standalonepublish/publish.py | 16 +- 8 files changed, 9 insertions(+), 959 deletions(-) rename pype/plugins/{standalonepublish => global}/publish/collect_context.py (99%) delete mode 100644 pype/plugins/standalonepublish/publish/collect_ftrack_api.py delete mode 100644 pype/plugins/standalonepublish/publish/collect_templates.py delete mode 100644 pype/plugins/standalonepublish/publish/collect_time.py delete mode 100644 pype/plugins/standalonepublish/publish/integrate.py delete mode 100644 pype/plugins/standalonepublish/publish/integrate_ftrack_api.py delete mode 100644 pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py diff --git a/pype/plugins/standalonepublish/publish/collect_context.py b/pype/plugins/global/publish/collect_context.py similarity index 99% rename from pype/plugins/standalonepublish/publish/collect_context.py rename to pype/plugins/global/publish/collect_context.py index f1a97ae7b6..710d2294fa 100644 --- a/pype/plugins/standalonepublish/publish/collect_context.py +++ b/pype/plugins/global/publish/collect_context.py @@ -16,6 +16,7 @@ class CollectContextDataSAPublish(pyblish.api.ContextPlugin): label = "Collect Context - SA Publish" order = pyblish.api.CollectorOrder - 0.49 + hosts = ["shell"] def process(self, context): # get json paths from os and load them diff --git a/pype/plugins/standalonepublish/publish/collect_ftrack_api.py b/pype/plugins/standalonepublish/publish/collect_ftrack_api.py deleted file mode 100644 index b15211e990..0000000000 --- a/pype/plugins/standalonepublish/publish/collect_ftrack_api.py +++ /dev/null @@ -1,41 +0,0 @@ -import os -import pyblish.api - -try: - import ftrack_api_old as ftrack_api -except Exception: - import ftrack_api - - -class CollectFtrackApi(pyblish.api.ContextPlugin): - """ Collects an ftrack session and the current task id. """ - - order = pyblish.api.CollectorOrder - label = "Collect Ftrack Api" - - def process(self, context): - - # Collect session - session = ftrack_api.Session() - context.data["ftrackSession"] = session - - # Collect task - - project = os.environ.get('AVALON_PROJECT', '') - asset = os.environ.get('AVALON_ASSET', '') - task = os.environ.get('AVALON_TASK', None) - self.log.debug(task) - - if task: - result = session.query('Task where\ - project.full_name is "{0}" and\ - name is "{1}" and\ - parent.name is "{2}"'.format(project, task, asset)).one() - context.data["ftrackTask"] = result - else: - result = session.query('TypedContext where\ - project.full_name is "{0}" and\ - name is "{1}"'.format(project, asset)).one() - context.data["ftrackEntity"] = result - - self.log.info(result) diff --git a/pype/plugins/standalonepublish/publish/collect_templates.py b/pype/plugins/standalonepublish/publish/collect_templates.py deleted file mode 100644 index b59b20892b..0000000000 --- a/pype/plugins/standalonepublish/publish/collect_templates.py +++ /dev/null @@ -1,17 +0,0 @@ - -import pype.api as pype -from pypeapp import Anatomy - -import pyblish.api - - -class CollectTemplates(pyblish.api.ContextPlugin): - """Inject the current working file into context""" - - order = pyblish.api.CollectorOrder - label = "Collect Templates" - - def process(self, context): - # pype.load_data_from_templates() - context.data['anatomy'] = Anatomy() - self.log.info("Anatomy templates collected...") diff --git a/pype/plugins/standalonepublish/publish/collect_time.py b/pype/plugins/standalonepublish/publish/collect_time.py deleted file mode 100644 index e0adc7dfc3..0000000000 --- a/pype/plugins/standalonepublish/publish/collect_time.py +++ /dev/null @@ -1,12 +0,0 @@ -import pyblish.api -from avalon import api - - -class CollectTime(pyblish.api.ContextPlugin): - """Store global time at the time of publish""" - - label = "Collect Current Time" - order = pyblish.api.CollectorOrder - - def process(self, context): - context.data["time"] = api.time() diff --git a/pype/plugins/standalonepublish/publish/integrate.py b/pype/plugins/standalonepublish/publish/integrate.py deleted file mode 100644 index 9d585ab673..0000000000 --- a/pype/plugins/standalonepublish/publish/integrate.py +++ /dev/null @@ -1,471 +0,0 @@ -import os -import logging -import shutil -import clique - -import errno -import pyblish.api -from avalon import api, io -from avalon.vendor import filelink - -log = logging.getLogger(__name__) - - -class IntegrateAsset(pyblish.api.InstancePlugin): - """Resolve any dependency issius - - This plug-in resolves any paths which, if not updated might break - the published file. - - The order of families is important, when working with lookdev you want to - first publish the texture, update the texture paths in the nodes and then - publish the shading network. Same goes for file dependent assets. - - Requirements for instance to be correctly integrated - - instance.data['representations'] - must be a list and each member - must be a dictionary with following data: - 'files': list of filenames for sequence, string for single file. - Only the filename is allowed, without the folder path. - 'stagingDir': "path/to/folder/with/files" - 'name': representation name (usually the same as extension) - 'ext': file extension - """ - - label = "Integrate Asset" - order = pyblish.api.IntegratorOrder - families = ["animation", - "camera", - "look", - "mayaAscii", - "model", - "pointcache", - "vdbcache", - "setdress", - "assembly", - "layout", - "rig", - "vrayproxy", - "yetiRig", - "yeticache", - "nukescript", - "review", - "workfile", - "scene", - "ass"] - exclude_families = ["clip"] - - def process(self, instance): - - if [ef for ef in self.exclude_families - if instance.data["family"] in ef]: - return - - self.register(instance) - - self.log.info("Integrating Asset in to the database ...") - self.log.info("instance.data: {}".format(instance.data)) - if instance.data.get('transfer', True): - self.integrate(instance) - - def register(self, instance): - # Required environment variables - PROJECT = api.Session["AVALON_PROJECT"] - ASSET = instance.data.get("asset") or api.Session["AVALON_ASSET"] - LOCATION = api.Session["AVALON_LOCATION"] - - context = instance.context - # Atomicity - # - # Guarantee atomic publishes - each asset contains - # an identical set of members. - # __ - # / o - # / \ - # | o | - # \ / - # o __/ - # - assert all(result["success"] for result in context.data["results"]), ( - "Atomicity not held, aborting.") - - # Assemble - # - # | - # v - # ---> <---- - # ^ - # | - # - stagingdir = instance.data.get("stagingDir") - if not stagingdir: - self.log.info('''{} is missing reference to staging - directory Will try to get it from - representation'''.format(instance)) - - # extra check if stagingDir actually exists and is available - - self.log.debug("Establishing staging directory @ %s" % stagingdir) - - # Ensure at least one file is set up for transfer in staging dir. - repres = instance.data.get("representations", None) - assert repres, "Instance has no files to transfer" - assert isinstance(repres, (list, tuple)), ( - "Instance 'files' must be a list, got: {0}".format(repres) - ) - - project = io.find_one({"type": "project"}) - - asset = io.find_one({"type": "asset", - "name": ASSET, - "parent": project["_id"]}) - - assert all([project, asset]), ("Could not find current project or " - "asset '%s'" % ASSET) - - subset = self.get_subset(asset, instance) - - # get next version - latest_version = io.find_one({"type": "version", - "parent": subset["_id"]}, - {"name": True}, - sort=[("name", -1)]) - - next_version = 1 - if latest_version is not None: - next_version += latest_version["name"] - - if instance.data.get('version'): - next_version = int(instance.data.get('version')) - - # self.log.info("Verifying version from assumed destination") - - # assumed_data = instance.data["assumedTemplateData"] - # assumed_version = assumed_data["version"] - # if assumed_version != next_version: - # raise AttributeError("Assumed version 'v{0:03d}' does not match" - # "next version in database " - # "('v{1:03d}')".format(assumed_version, - # next_version)) - - self.log.debug("Next version: v{0:03d}".format(next_version)) - - version_data = self.create_version_data(context, instance) - version = self.create_version(subset=subset, - version_number=next_version, - locations=[LOCATION], - data=version_data) - - self.log.debug("Creating version ...") - version_id = io.insert_one(version).inserted_id - instance.data['version'] = version['name'] - - # Write to disk - # _ - # | | - # _| |_ - # ____\ / - # |\ \ / \ - # \ \ v \ - # \ \________. - # \|________| - # - root = api.registered_root() - hierarchy = "" - parents = io.find_one({ - "type": 'asset', - "name": ASSET - })['data']['parents'] - if parents and len(parents) > 0: - # hierarchy = os.path.sep.join(hierarchy) - hierarchy = os.path.join(*parents) - - template_data = {"root": root, - "project": {"name": PROJECT, - "code": project['data']['code']}, - "silo": asset['silo'], - "task": api.Session["AVALON_TASK"], - "asset": ASSET, - "family": instance.data['family'], - "subset": subset["name"], - "version": int(version["name"]), - "hierarchy": hierarchy} - - anatomy = instance.context.data['anatomy'] - - # Find the representations to transfer amongst the files - # Each should be a single representation (as such, a single extension) - representations = [] - destination_list = [] - template_name = 'publish' - if 'transfers' not in instance.data: - instance.data['transfers'] = [] - - for idx, repre in enumerate(instance.data["representations"]): - - # Collection - # _______ - # |______|\ - # | |\| - # | || - # | || - # | || - # |_______| - # - - files = repre['files'] - if repre.get('stagingDir'): - stagingdir = repre['stagingDir'] - if repre.get('anatomy_template'): - template_name = repre['anatomy_template'] - template = anatomy.templates[template_name]["path"] - - if isinstance(files, list): - src_collections, remainder = clique.assemble(files) - self.log.debug( - "dst_collections: {}".format(str(src_collections))) - src_collection = src_collections[0] - # Assert that each member has identical suffix - src_head = src_collection.format("{head}") - src_tail = ext = src_collection.format("{tail}") - - test_dest_files = list() - for i in [1, 2]: - template_data["representation"] = repre['ext'] - template_data["frame"] = src_collection.format( - "{padding}") % i - anatomy_filled = anatomy.format(template_data) - test_dest_files.append( - anatomy_filled[template_name]["path"]) - - dst_collections, remainder = clique.assemble(test_dest_files) - dst_collection = dst_collections[0] - dst_head = dst_collection.format("{head}") - dst_tail = dst_collection.format("{tail}") - - instance.data["representations"][idx]['published_path'] = dst_collection.format() - - for i in src_collection.indexes: - src_padding = src_collection.format("{padding}") % i - src_file_name = "{0}{1}{2}".format( - src_head, src_padding, src_tail) - - dst_padding = dst_collection.format("{padding}") % i - dst = "{0}{1}{2}".format(dst_head, dst_padding, dst_tail) - - src = os.path.join(stagingdir, src_file_name) - # src = src_file_name - self.log.debug("source: {}".format(src)) - instance.data["transfers"].append([src, dst]) - - else: - # Single file - # _______ - # | |\ - # | | - # | | - # | | - # |_______| - # - template_data.pop("frame", None) - fname = files - assert not os.path.isabs(fname), ( - "Given file name is a full path" - ) - _, ext = os.path.splitext(fname) - - template_data["representation"] = repre['ext'] - - src = os.path.join(stagingdir, fname) - # src = fname - anatomy_filled = anatomy.format(template_data) - dst = anatomy_filled[template_name]["path"] - - instance.data["transfers"].append([src, dst]) - # template = anatomy.templates["publish"]["path"] - instance.data["representations"][idx]['published_path'] = dst - - representation = { - "schema": "pype:representation-2.0", - "type": "representation", - "parent": version_id, - "name": repre['name'], - "data": {'path': dst, 'template': template}, - "dependencies": instance.data.get("dependencies", "").split(), - - # Imprint shortcut to context - # for performance reasons. - "context": { - "root": root, - "project": {"name": PROJECT, - "code": project['data']['code']}, - 'task': api.Session["AVALON_TASK"], - "silo": asset['silo'], - "asset": ASSET, - "family": instance.data['family'], - "subset": subset["name"], - "version": version["name"], - "hierarchy": hierarchy, - "representation": repre['ext'] - } - } - - destination_list.append(dst) - instance.data['destination_list'] = destination_list - representations.append(representation) - - self.log.info("Registering {} items".format(len(representations))) - - io.insert_many(representations) - - def integrate(self, instance): - """Move the files - - Through `instance.data["transfers"]` - - Args: - instance: the instance to integrate - """ - - transfers = instance.data.get("transfers", list()) - - for src, dest in transfers: - self.log.info("Copying file .. {} -> {}".format(src, dest)) - self.copy_file(src, dest) - - # Produce hardlinked copies - # Note: hardlink can only be produced between two files on the same - # server/disk and editing one of the two will edit both files at once. - # As such it is recommended to only make hardlinks between static files - # to ensure publishes remain safe and non-edited. - hardlinks = instance.data.get("hardlinks", list()) - for src, dest in hardlinks: - self.log.info("Hardlinking file .. {} -> {}".format(src, dest)) - self.hardlink_file(src, dest) - - def copy_file(self, src, dst): - """ Copy given source to destination - - Arguments: - src (str): the source file which needs to be copied - dst (str): the destination of the sourc file - Returns: - None - """ - - dirname = os.path.dirname(dst) - try: - os.makedirs(dirname) - except OSError as e: - if e.errno == errno.EEXIST: - pass - else: - self.log.critical("An unexpected error occurred.") - raise - - shutil.copy(src, dst) - - def hardlink_file(self, src, dst): - - dirname = os.path.dirname(dst) - try: - os.makedirs(dirname) - except OSError as e: - if e.errno == errno.EEXIST: - pass - else: - self.log.critical("An unexpected error occurred.") - raise - - filelink.create(src, dst, filelink.HARDLINK) - - def get_subset(self, asset, instance): - - subset = io.find_one({"type": "subset", - "parent": asset["_id"], - "name": instance.data["subset"]}) - - if subset is None: - subset_name = instance.data["subset"] - self.log.info("Subset '%s' not found, creating.." % subset_name) - - _id = io.insert_one({ - "schema": "avalon-core:subset-2.0", - "type": "subset", - "name": subset_name, - "data": {}, - "parent": asset["_id"] - }).inserted_id - - subset = io.find_one({"_id": _id}) - - return subset - - def create_version(self, subset, version_number, locations, data=None): - """ Copy given source to destination - - Args: - subset (dict): the registered subset of the asset - version_number (int): the version number - locations (list): the currently registered locations - - Returns: - dict: collection of data to create a version - """ - # Imprint currently registered location - version_locations = [location for location in locations if - location is not None] - - return {"schema": "avalon-core:version-2.0", - "type": "version", - "parent": subset["_id"], - "name": version_number, - "locations": version_locations, - "data": data} - - def create_version_data(self, context, instance): - """Create the data collection for the version - - Args: - context: the current context - instance: the current instance being published - - Returns: - dict: the required information with instance.data as key - """ - - families = [] - current_families = instance.data.get("families", list()) - instance_family = instance.data.get("family", None) - - if instance_family is not None: - families.append(instance_family) - families += current_families - - self.log.debug("Registered root: {}".format(api.registered_root())) - # create relative source path for DB - try: - source = instance.data['source'] - except KeyError: - source = context.data["currentFile"] - relative_path = os.path.relpath(source, api.registered_root()) - source = os.path.join("{root}", relative_path).replace("\\", "/") - - self.log.debug("Source: {}".format(source)) - version_data = {"families": families, - "time": context.data["time"], - "author": context.data["user"], - "source": source, - "comment": context.data.get("comment"), - "machine": context.data.get("machine"), - "fps": context.data.get("fps")} - - # Include optional data if present in - optionals = [ - "startFrame", "endFrame", "step", "handles", "sourceHashes" - ] - for key in optionals: - if key in instance.data: - version_data[key] = instance.data[key] - - return version_data diff --git a/pype/plugins/standalonepublish/publish/integrate_ftrack_api.py b/pype/plugins/standalonepublish/publish/integrate_ftrack_api.py deleted file mode 100644 index 2c35b974f9..0000000000 --- a/pype/plugins/standalonepublish/publish/integrate_ftrack_api.py +++ /dev/null @@ -1,315 +0,0 @@ -import os -import sys -import pyblish.api -import clique - - -class IntegrateFtrackApi(pyblish.api.InstancePlugin): - """ Commit components to server. """ - - order = pyblish.api.IntegratorOrder+0.499 - label = "Integrate Ftrack Api" - families = ["ftrack"] - - def query(self, entitytype, data): - """ Generate a query expression from data supplied. - - If a value is not a string, we'll add the id of the entity to the - query. - - Args: - entitytype (str): The type of entity to query. - data (dict): The data to identify the entity. - exclusions (list): All keys to exclude from the query. - - Returns: - str: String query to use with "session.query" - """ - queries = [] - if sys.version_info[0] < 3: - for key, value in data.iteritems(): - if not isinstance(value, (basestring, int)): - self.log.info("value: {}".format(value)) - if "id" in value.keys(): - queries.append( - "{0}.id is \"{1}\"".format(key, value["id"]) - ) - else: - queries.append("{0} is \"{1}\"".format(key, value)) - else: - for key, value in data.items(): - if not isinstance(value, (str, int)): - self.log.info("value: {}".format(value)) - if "id" in value.keys(): - queries.append( - "{0}.id is \"{1}\"".format(key, value["id"]) - ) - else: - queries.append("{0} is \"{1}\"".format(key, value)) - - query = ( - "select id from " + entitytype + " where " + " and ".join(queries) - ) - self.log.debug(query) - return query - - def process(self, instance): - - session = instance.context.data["ftrackSession"] - if instance.context.data.get("ftrackTask"): - task = instance.context.data["ftrackTask"] - name = task - parent = task["parent"] - elif instance.context.data.get("ftrackEntity"): - task = None - name = instance.context.data.get("ftrackEntity")['name'] - parent = instance.context.data.get("ftrackEntity") - - info_msg = "Created new {entity_type} with data: {data}" - info_msg += ", metadata: {metadata}." - - # Iterate over components and publish - for data in instance.data.get("ftrackComponentsList", []): - - # AssetType - # Get existing entity. - assettype_data = {"short": "upload"} - assettype_data.update(data.get("assettype_data", {})) - self.log.debug("data: {}".format(data)) - - assettype_entity = session.query( - self.query("AssetType", assettype_data) - ).first() - - # Create a new entity if none exits. - if not assettype_entity: - assettype_entity = session.create("AssetType", assettype_data) - self.log.debug( - "Created new AssetType with data: ".format(assettype_data) - ) - - # Asset - # Get existing entity. - asset_data = { - "name": name, - "type": assettype_entity, - "parent": parent, - } - asset_data.update(data.get("asset_data", {})) - - asset_entity = session.query( - self.query("Asset", asset_data) - ).first() - - self.log.info("asset entity: {}".format(asset_entity)) - - # Extracting metadata, and adding after entity creation. This is - # due to a ftrack_api bug where you can't add metadata on creation. - asset_metadata = asset_data.pop("metadata", {}) - - # Create a new entity if none exits. - if not asset_entity: - asset_entity = session.create("Asset", asset_data) - self.log.debug( - info_msg.format( - entity_type="Asset", - data=asset_data, - metadata=asset_metadata - ) - ) - - # Adding metadata - existing_asset_metadata = asset_entity["metadata"] - existing_asset_metadata.update(asset_metadata) - asset_entity["metadata"] = existing_asset_metadata - - # AssetVersion - # Get existing entity. - assetversion_data = { - "version": 0, - "asset": asset_entity, - } - if task: - assetversion_data['task'] = task - - assetversion_data.update(data.get("assetversion_data", {})) - - assetversion_entity = session.query( - self.query("AssetVersion", assetversion_data) - ).first() - - # Extracting metadata, and adding after entity creation. This is - # due to a ftrack_api bug where you can't add metadata on creation. - assetversion_metadata = assetversion_data.pop("metadata", {}) - - # Create a new entity if none exits. - if not assetversion_entity: - assetversion_entity = session.create( - "AssetVersion", assetversion_data - ) - self.log.debug( - info_msg.format( - entity_type="AssetVersion", - data=assetversion_data, - metadata=assetversion_metadata - ) - ) - - # Adding metadata - existing_assetversion_metadata = assetversion_entity["metadata"] - existing_assetversion_metadata.update(assetversion_metadata) - assetversion_entity["metadata"] = existing_assetversion_metadata - - # Have to commit the version and asset, because location can't - # determine the final location without. - session.commit() - - # Component - # Get existing entity. - component_data = { - "name": "main", - "version": assetversion_entity - } - component_data.update(data.get("component_data", {})) - - component_entity = session.query( - self.query("Component", component_data) - ).first() - - component_overwrite = data.get("component_overwrite", False) - location = data.get("component_location", session.pick_location()) - - # Overwrite existing component data if requested. - if component_entity and component_overwrite: - - origin_location = session.query( - "Location where name is \"ftrack.origin\"" - ).one() - - # Removing existing members from location - components = list(component_entity.get("members", [])) - components += [component_entity] - for component in components: - for loc in component["component_locations"]: - if location["id"] == loc["location_id"]: - location.remove_component( - component, recursive=False - ) - - # Deleting existing members on component entity - for member in component_entity.get("members", []): - session.delete(member) - del(member) - - session.commit() - - # Reset members in memory - if "members" in component_entity.keys(): - component_entity["members"] = [] - - # Add components to origin location - try: - collection = clique.parse(data["component_path"]) - except ValueError: - # Assume its a single file - # Changing file type - name, ext = os.path.splitext(data["component_path"]) - component_entity["file_type"] = ext - - origin_location.add_component( - component_entity, data["component_path"] - ) - else: - # Changing file type - component_entity["file_type"] = collection.format("{tail}") - - # Create member components for sequence. - for member_path in collection: - - size = 0 - try: - size = os.path.getsize(member_path) - except OSError: - pass - - name = collection.match(member_path).group("index") - - member_data = { - "name": name, - "container": component_entity, - "size": size, - "file_type": os.path.splitext(member_path)[-1] - } - - component = session.create( - "FileComponent", member_data - ) - origin_location.add_component( - component, member_path, recursive=False - ) - component_entity["members"].append(component) - - # Add components to location. - location.add_component( - component_entity, origin_location, recursive=True - ) - - data["component"] = component_entity - msg = "Overwriting Component with path: {0}, data: {1}, " - msg += "location: {2}" - self.log.info( - msg.format( - data["component_path"], - component_data, - location - ) - ) - - # Extracting metadata, and adding after entity creation. This is - # due to a ftrack_api bug where you can't add metadata on creation. - component_metadata = component_data.pop("metadata", {}) - - # Create new component if none exists. - new_component = False - if not component_entity: - component_entity = assetversion_entity.create_component( - data["component_path"], - data=component_data, - location=location - ) - data["component"] = component_entity - msg = "Created new Component with path: {0}, data: {1}" - msg += ", metadata: {2}, location: {3}" - self.log.info( - msg.format( - data["component_path"], - component_data, - component_metadata, - location - ) - ) - new_component = True - - # Adding metadata - existing_component_metadata = component_entity["metadata"] - existing_component_metadata.update(component_metadata) - component_entity["metadata"] = existing_component_metadata - - # if component_data['name'] = 'ftrackreview-mp4-mp4': - # assetversion_entity["thumbnail_id"] - - # Setting assetversion thumbnail - if data.get("thumbnail", False): - assetversion_entity["thumbnail_id"] = component_entity["id"] - - # Inform user about no changes to the database. - if (component_entity and not component_overwrite and - not new_component): - data["component"] = component_entity - self.log.info( - "Found existing component, and no request to overwrite. " - "Nothing has been changed." - ) - else: - # Commit changes. - session.commit() diff --git a/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py b/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py deleted file mode 100644 index 9d49250ae6..0000000000 --- a/pype/plugins/standalonepublish/publish/integrate_ftrack_instances.py +++ /dev/null @@ -1,95 +0,0 @@ -import pyblish.api -import os -import json - - -class IntegrateFtrackInstance(pyblish.api.InstancePlugin): - """Collect ftrack component data - - Add ftrack component list to instance. - - - """ - - order = pyblish.api.IntegratorOrder + 0.48 - label = 'Integrate Ftrack Component' - families = ["ftrack"] - - family_mapping = {'camera': 'cam', - 'look': 'look', - 'mayaAscii': 'scene', - 'model': 'geo', - 'rig': 'rig', - 'setdress': 'setdress', - 'pointcache': 'cache', - 'write': 'img', - 'render': 'render', - 'nukescript': 'comp', - 'review': 'mov'} - - def process(self, instance): - self.log.debug('instance {}'.format(instance)) - - if instance.data.get('version'): - version_number = int(instance.data.get('version')) - - family = instance.data['family'].lower() - - asset_type = '' - asset_type = self.family_mapping[family] - - componentList = [] - ft_session = instance.context.data["ftrackSession"] - - for comp in instance.data['representations']: - self.log.debug('component {}'.format(comp)) - - if comp.get('thumbnail'): - location = ft_session.query( - 'Location where name is "ftrack.server"').one() - component_data = { - "name": "thumbnail" # Default component name is "main". - } - elif comp.get('preview'): - if not comp.get('startFrameReview'): - comp['startFrameReview'] = comp['startFrame'] - if not comp.get('endFrameReview'): - comp['endFrameReview'] = comp['endFrame'] - location = ft_session.query( - 'Location where name is "ftrack.server"').one() - component_data = { - # Default component name is "main". - "name": "ftrackreview-mp4", - "metadata": {'ftr_meta': json.dumps({ - 'frameIn': int(comp['startFrameReview']), - 'frameOut': int(comp['endFrameReview']), - 'frameRate': comp['frameRate']})} - } - else: - component_data = { - "name": comp['name'] - } - location = ft_session.query( - 'Location where name is "ftrack.unmanaged"').one() - - self.log.debug('location {}'.format(location)) - - componentList.append({"assettype_data": { - "short": asset_type, - }, - "asset_data": { - "name": instance.data["subset"], - }, - "assetversion_data": { - "version": version_number, - }, - "component_data": component_data, - "component_path": comp['published_path'], - 'component_location': location, - "component_overwrite": False, - "thumbnail": comp['thumbnail'] - } - ) - - self.log.debug('componentsList: {}'.format(str(componentList))) - instance.data["ftrackComponentsList"] = componentList diff --git a/pype/standalonepublish/publish.py b/pype/standalonepublish/publish.py index 24ce6bc60b..13b505666c 100644 --- a/pype/standalonepublish/publish.py +++ b/pype/standalonepublish/publish.py @@ -16,19 +16,19 @@ import pyblish.api # Registers Global pyblish plugins -# pype.install() +pype.install() # Registers Standalone pyblish plugins -PUBLISH_PATH = os.path.sep.join( - [pype.PLUGINS_DIR, 'standalonepublish', 'publish'] -) -pyblish.api.register_plugin_path(PUBLISH_PATH) - -# # Registers Standalone pyblish plugins # PUBLISH_PATH = os.path.sep.join( -# [pype.PLUGINS_DIR, 'ftrack', 'publish'] +# [pype.PLUGINS_DIR, 'standalonepublish', 'publish'] # ) # pyblish.api.register_plugin_path(PUBLISH_PATH) +# Registers Standalone pyblish plugins +PUBLISH_PATH = os.path.sep.join( + [pype.PLUGINS_DIR, 'ftrack', 'publish'] +) +pyblish.api.register_plugin_path(PUBLISH_PATH) + def set_context(project, asset, task, app): ''' Sets context for pyblish (must be done before pyblish is launched) From 4ea25d3418e17047a4db584422a72ce088ad8c58 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 27 May 2019 23:49:17 +0100 Subject: [PATCH 46/48] implement nuke publish with the new integrator --- pype/plugins/global/publish/cleanup.py | 36 ++++++++--------- pype/plugins/global/publish/integrate.py | 4 +- pype/plugins/global/publish/integrate_new.py | 9 ++++- .../publish/integrate_rendered_frames.py | 2 +- pype/plugins/nuke/publish/collect_writes.py | 24 +++++++---- .../nuke/publish/extract_render_local.py | 19 ++++++--- pype/plugins/nuke/publish/extract_review.py | 38 ++++++++++++++---- .../nuke/publish/validate_collection.py | 40 +++++++++---------- 8 files changed, 107 insertions(+), 65 deletions(-) diff --git a/pype/plugins/global/publish/cleanup.py b/pype/plugins/global/publish/cleanup.py index 68ec9a48ad..3bb9ddc144 100644 --- a/pype/plugins/global/publish/cleanup.py +++ b/pype/plugins/global/publish/cleanup.py @@ -16,21 +16,21 @@ class CleanUp(pyblish.api.InstancePlugin): exclude_families = ["clip"] def process(self, instance): - if [ef for ef in self.exclude_families - if instance.data["family"] in ef]: - return - import tempfile - - staging_dir = instance.data.get("stagingDir", None) - if not staging_dir or not os.path.exists(staging_dir): - self.log.info("No staging directory found: %s" % staging_dir) - return - - temp_root = tempfile.gettempdir() - if not os.path.normpath(staging_dir).startswith(temp_root): - self.log.info("Skipping cleanup. Staging directory is not in the " - "temp folder: %s" % staging_dir) - return - - self.log.info("Removing temporary folder ...") - shutil.rmtree(staging_dir) + # if [ef for ef in self.exclude_families + # if instance.data["family"] in ef]: + # return + # import tempfile + # + # staging_dir = instance.data.get("stagingDir", None) + # if not staging_dir or not os.path.exists(staging_dir): + # self.log.info("No staging directory found: %s" % staging_dir) + # return + # + # temp_root = tempfile.gettempdir() + # if not os.path.normpath(staging_dir).startswith(temp_root): + # self.log.info("Skipping cleanup. Staging directory is not in the " + # "temp folder: %s" % staging_dir) + # return + # + # self.log.info("Removing temporary folder ...") + # shutil.rmtree(staging_dir) diff --git a/pype/plugins/global/publish/integrate.py b/pype/plugins/global/publish/integrate.py index d1a572fbfd..208bc027d5 100644 --- a/pype/plugins/global/publish/integrate.py +++ b/pype/plugins/global/publish/integrate.py @@ -28,9 +28,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): families = ["look", "assembly", "yetiRig", - "yeticache", - "nukescript", - "write"] + "yeticache"] exclude_families = ["clip"] def process(self, instance): diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index 5f6fa7b674..0ffe84987b 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -56,7 +56,10 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "vrayproxy", "render", "imagesequence", - "review" + "review", + "nukescript", + "render", + "write" ] exclude_families = ["clip"] @@ -228,7 +231,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): if isinstance(files, list): src_collections, remainder = clique.assemble(files) self.log.debug( - "dst_collections: {}".format(str(src_collections))) + "src_collections: {}".format(str(src_collections))) src_collection = src_collections[0] # Assert that each member has identical suffix src_head = src_collection.format("{head}") @@ -242,6 +245,8 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): anatomy_filled = anatomy.format(template_data) test_dest_files.append( anatomy_filled[template_name]["path"]) + self.log.debug( + "test_dest_files: {}".format(str(test_dest_files))) dst_collections, remainder = clique.assemble(test_dest_files) dst_collection = dst_collections[0] diff --git a/pype/plugins/global/publish/integrate_rendered_frames.py b/pype/plugins/global/publish/integrate_rendered_frames.py index e814e31640..ff8d6adc71 100644 --- a/pype/plugins/global/publish/integrate_rendered_frames.py +++ b/pype/plugins/global/publish/integrate_rendered_frames.py @@ -24,7 +24,7 @@ class IntegrateFrames(pyblish.api.InstancePlugin): label = "Integrate Frames" order = pyblish.api.IntegratorOrder - families = ["imagesequence", "render", "write", "source"] + families = ["imagesequence", "source"] family_targets = [".frames", ".local", ".review", "imagesequence", "render", "source"] exclude_families = ["clip"] diff --git a/pype/plugins/nuke/publish/collect_writes.py b/pype/plugins/nuke/publish/collect_writes.py index fb66a29bc8..68bc2fd5d4 100644 --- a/pype/plugins/nuke/publish/collect_writes.py +++ b/pype/plugins/nuke/publish/collect_writes.py @@ -64,19 +64,27 @@ class CollectNukeWrites(pyblish.api.ContextPlugin): ) if "representations" not in instance.data: - instance.data["representations"] = [] + instance.data["representations"] = list() + try: + collected_frames = os.listdir(output_dir) - representation = { - 'name': ext, - 'ext': "." + ext, - 'files': collected_frames, - "stagingDir": output_dir, - } - instance.data["representations"].append(representation) + representation = { + 'name': ext, + 'ext': ext, + 'files': collected_frames, + "stagingDir": output_dir, + "anatomy_template": "render" + } + instance.data["representations"].append(representation) except Exception: self.log.debug("couldn't collect frames: {}".format(label)) + + + # except Exception: + # self.log.debug("couldn't collect frames: {}".format(label)) + instance.data.update({ "path": path, "outputDir": output_dir, diff --git a/pype/plugins/nuke/publish/extract_render_local.py b/pype/plugins/nuke/publish/extract_render_local.py index 5ac1c77059..18a1aa1f54 100644 --- a/pype/plugins/nuke/publish/extract_render_local.py +++ b/pype/plugins/nuke/publish/extract_render_local.py @@ -49,18 +49,27 @@ class NukeRenderLocal(pype.api.Extractor): # swap path back to publish path path = node['file'].value() node['file'].setValue(path.replace(temp_dir, output_dir)) + ext = node["file_type"].value() - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data["files"] = [os.listdir(temp_dir)] + collected_frames = os.listdir(temp_dir) + repre = { + 'name': ext, + 'ext': ext, + 'files': collected_frames, + "stagingDir": temp_dir, + "anatomy_template": "render" + } + instance.data["representations"].append(repre) self.log.info("Extracted instance '{0}' to: {1}".format( instance.name, - output_dir + temp_dir )) - collections, remainder = clique.assemble(*instance.data['files']) + collections, remainder = clique.assemble(collected_frames) self.log.info('collections: {}'.format(str(collections))) if collections: diff --git a/pype/plugins/nuke/publish/extract_review.py b/pype/plugins/nuke/publish/extract_review.py index ed38893927..2eabbd4e87 100644 --- a/pype/plugins/nuke/publish/extract_review.py +++ b/pype/plugins/nuke/publish/extract_review.py @@ -26,7 +26,7 @@ class ExtractDataForReview(pype.api.Extractor): # Deselect all nodes to prevent external connections [i["selected"].setValue(False) for i in nuke.allNodes()] self.log.debug("creating staging dir:") - self.stagingDir(instance) + self.staging_dir(instance) self.log.debug("instance: {}".format(instance)) self.log.debug("instance.data[families]: {}".format( @@ -75,13 +75,28 @@ class ExtractDataForReview(pype.api.Extractor): instance.data["baked_colorspace_movie"])) os.remove(instance.data["baked_colorspace_movie"]) - instance.data["files"].append(file_name) + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'mov', + 'ext': 'mov', + 'files': file_name, + "stagingDir": stagingDir, + "anatomy_template": "render", + "thumbnail": False, + "preview": True, + 'startFrameReview': instance.data['startFrame'], + 'endFrameReview': instance.data['endFrame'], + 'frameRate': instance.context.data["framerate"] + } + instance.data["representations"].append(representation) def render_review_representation(self, instance, representation="mov"): - assert instance.data['files'], "Instance data files should't be empty!" + assert instance.data['representations'][0]['files'], "Instance data files should't be empty!" import nuke temporary_nodes = [] @@ -155,6 +170,8 @@ class ExtractDataForReview(pype.api.Extractor): write_node["raw"].setValue(1) write_node.setInput(0, previous_node) temporary_nodes.append(write_node) + thumbnail = False + preview = True elif representation in "jpeg": file = fhead + "jpeg" @@ -165,6 +182,8 @@ class ExtractDataForReview(pype.api.Extractor): write_node["raw"].setValue(1) write_node.setInput(0, previous_node) temporary_nodes.append(write_node) + thumbnail = True + preview = False # retime for first_frame = int(last_frame) / 2 @@ -174,13 +193,16 @@ class ExtractDataForReview(pype.api.Extractor): if "representations" not in instance.data: instance.data["representations"] = [] - representation = { - 'name': 'nk', - 'ext': '.nk', + repre = { + 'name': representation, + 'ext': representation, 'files': file, - "stagingDir": stagingdir, + "stagingDir": stagingDir, + "anatomy_template": "render", + "thumbnail": thumbnail, + "preview": preview } - instance.data["representations"].append(representation) + instance.data["representations"].append(repre) # Render frames nuke.execute(write_node.name(), int(first_frame), int(last_frame)) diff --git a/pype/plugins/nuke/publish/validate_collection.py b/pype/plugins/nuke/publish/validate_collection.py index c402927373..1d0e1b260e 100644 --- a/pype/plugins/nuke/publish/validate_collection.py +++ b/pype/plugins/nuke/publish/validate_collection.py @@ -30,33 +30,33 @@ class ValidatePrerenderedFrames(pyblish.api.InstancePlugin): hosts = ["nuke"] actions = [RepairCollectionAction] - def process(self, instance): - self.log.debug('instance.data["files"]: {}'.format(instance.data['files'])) - assert instance.data.get('files'), "no frames were collected, you need to render them" + for repre in instance.data.get('representations'): - collections, remainder = clique.assemble(*instance.data['files']) - self.log.info('collections: {}'.format(str(collections))) + assert repre.get('files'), "no frames were collected, you need to render them" - collection = collections[0] + collections, remainder = clique.assemble(repre["files"]) + self.log.info('collections: {}'.format(str(collections))) - frame_length = instance.data["endFrame"] \ - - instance.data["startFrame"] + 1 + collection = collections[0] - if frame_length is not 1: - assert len(collections) == 1, "There are multiple collections in the folder" - assert collection.is_contiguous(), "Some frames appear to be missing" + frame_length = instance.data["endFrame"] \ + - instance.data["startFrame"] + 1 - assert remainder is not None, "There are some extra files in folder" + if frame_length != 1: + assert len(collections) == 1, "There are multiple collections in the folder" + assert collection.is_contiguous(), "Some frames appear to be missing" - self.log.info('frame_length: {}'.format(frame_length)) - self.log.info('len(collection.indexes): {}'.format( - len(collection.indexes))) + assert remainder is not None, "There are some extra files in folder" - assert len( - collection.indexes - ) is frame_length, "{} missing frames. Use " - "repair to render all frames".format(__name__) + self.log.info('frame_length: {}'.format(frame_length)) + self.log.info('len(collection.indexes): {}'.format( + len(collection.indexes))) - instance.data['collection'] = collection + assert len( + collection.indexes + ) is frame_length, "{} missing frames. Use " + "repair to render all frames".format(__name__) + + instance.data['collection'] = collection From d339cade16b8040a979fc900d66bcfe212baee17 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 27 May 2019 23:51:41 +0100 Subject: [PATCH 47/48] return disabled cleanup plugin --- pype/plugins/global/publish/cleanup.py | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pype/plugins/global/publish/cleanup.py b/pype/plugins/global/publish/cleanup.py index 3bb9ddc144..68ec9a48ad 100644 --- a/pype/plugins/global/publish/cleanup.py +++ b/pype/plugins/global/publish/cleanup.py @@ -16,21 +16,21 @@ class CleanUp(pyblish.api.InstancePlugin): exclude_families = ["clip"] def process(self, instance): - # if [ef for ef in self.exclude_families - # if instance.data["family"] in ef]: - # return - # import tempfile - # - # staging_dir = instance.data.get("stagingDir", None) - # if not staging_dir or not os.path.exists(staging_dir): - # self.log.info("No staging directory found: %s" % staging_dir) - # return - # - # temp_root = tempfile.gettempdir() - # if not os.path.normpath(staging_dir).startswith(temp_root): - # self.log.info("Skipping cleanup. Staging directory is not in the " - # "temp folder: %s" % staging_dir) - # return - # - # self.log.info("Removing temporary folder ...") - # shutil.rmtree(staging_dir) + if [ef for ef in self.exclude_families + if instance.data["family"] in ef]: + return + import tempfile + + staging_dir = instance.data.get("stagingDir", None) + if not staging_dir or not os.path.exists(staging_dir): + self.log.info("No staging directory found: %s" % staging_dir) + return + + temp_root = tempfile.gettempdir() + if not os.path.normpath(staging_dir).startswith(temp_root): + self.log.info("Skipping cleanup. Staging directory is not in the " + "temp folder: %s" % staging_dir) + return + + self.log.info("Removing temporary folder ...") + shutil.rmtree(staging_dir) From dcdbd070feadc04bfefe326492d03030539f5dd8 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 29 May 2019 17:20:43 +0200 Subject: [PATCH 48/48] include changes from jakub --- pype/plugins/global/publish/integrate_new.py | 26 +++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index 0ffe84987b..104833dadf 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -59,7 +59,8 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "review", "nukescript", "render", - "write" + "write", + "plates" ] exclude_families = ["clip"] @@ -80,6 +81,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): # Required environment variables PROJECT = api.Session["AVALON_PROJECT"] ASSET = instance.data.get("asset") or api.Session["AVALON_ASSET"] + TASK = instance.data.get("task") or api.Session["AVALON_TASK"] LOCATION = api.Session["AVALON_LOCATION"] context = instance.context @@ -192,7 +194,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "project": {"name": PROJECT, "code": project['data']['code']}, "silo": asset['silo'], - "task": api.Session["AVALON_TASK"], + "task": TASK, "asset": ASSET, "family": instance.data['family'], "subset": subset["name"], @@ -226,12 +228,13 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): stagingdir = repre['stagingDir'] if repre.get('anatomy_template'): template_name = repre['anatomy_template'] - template = anatomy.templates[template_name]["path"] + template = os.path.normpath( + anatomy.templates[template_name]["path"]) if isinstance(files, list): src_collections, remainder = clique.assemble(files) self.log.debug( - "src_collections: {}".format(str(src_collections))) + "src_tail_collections: {}".format(str(src_collections))) src_collection = src_collections[0] # Assert that each member has identical suffix src_head = src_collection.format("{head}") @@ -243,10 +246,11 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): template_data["frame"] = src_collection.format( "{padding}") % i anatomy_filled = anatomy.format(template_data) + test_dest_files.append( - anatomy_filled[template_name]["path"]) - self.log.debug( - "test_dest_files: {}".format(str(test_dest_files))) + os.path.normpath( + anatomy_filled[template_name]["path"]) + ) dst_collections, remainder = clique.assemble(test_dest_files) dst_collection = dst_collections[0] @@ -262,10 +266,9 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): dst_padding = dst_collection.format("{padding}") % i dst = "{0}{1}{2}".format(dst_head, dst_padding, dst_tail) - + self.log.debug("destination: `{}`".format(dst)) src = os.path.join(stagingdir, src_file_name) - # src = src_file_name - self.log.debug("source: {}".format(src)) + self.log.debug("source: `{}`".format(src)) instance.data["transfers"].append([src, dst]) else: @@ -287,7 +290,6 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): template_data["representation"] = repre['ext'] src = os.path.join(stagingdir, fname) - # src = fname anatomy_filled = anatomy.format(template_data) dst = anatomy_filled[template_name]["path"] @@ -308,7 +310,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "root": root, "project": {"name": PROJECT, "code": project['data']['code']}, - 'task': api.Session["AVALON_TASK"], + 'task': TASK, "silo": asset['silo'], "asset": ASSET, "family": instance.data['family'],