mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge remote-tracking branch 'upstream/develop' into fusion_integration_v2
# Conflicts: # openpype/scripts/fusion_switch_shot.py
This commit is contained in:
commit
4c81d1d0b2
898 changed files with 57420 additions and 9022 deletions
2
.github/workflows/prerelease.yml
vendored
2
.github/workflows/prerelease.yml
vendored
|
|
@ -43,7 +43,7 @@ jobs:
|
|||
uses: heinrichreimer/github-changelog-generator-action@v2.2
|
||||
with:
|
||||
token: ${{ secrets.ADMIN_TOKEN }}
|
||||
addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}}'
|
||||
addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}, "refactor":{"prefix":"**🔀 Refactored code**", "labels":["refactor"]}}'
|
||||
issues: false
|
||||
issuesWoLabels: false
|
||||
sinceTag: "3.0.0"
|
||||
|
|
|
|||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
|
@ -39,7 +39,7 @@ jobs:
|
|||
uses: heinrichreimer/github-changelog-generator-action@v2.2
|
||||
with:
|
||||
token: ${{ secrets.ADMIN_TOKEN }}
|
||||
addSections: '{"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]},"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]}}'
|
||||
addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}, "refactor":{"prefix":"**🔀 Refactored code**", "labels":["refactor"]}}'
|
||||
issues: false
|
||||
issuesWoLabels: false
|
||||
sinceTag: "3.0.0"
|
||||
|
|
@ -81,7 +81,7 @@ jobs:
|
|||
uses: heinrichreimer/github-changelog-generator-action@v2.2
|
||||
with:
|
||||
token: ${{ secrets.ADMIN_TOKEN }}
|
||||
addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}}'
|
||||
addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}, "refactor":{"prefix":"**🔀 Refactored code**", "labels":["refactor"]}}'
|
||||
issues: false
|
||||
issuesWoLabels: false
|
||||
sinceTag: ${{ steps.version.outputs.last_release }}
|
||||
|
|
|
|||
11
.gitmodules
vendored
11
.gitmodules
vendored
|
|
@ -1,12 +1,3 @@
|
|||
[submodule "repos/avalon-core"]
|
||||
path = repos/avalon-core
|
||||
url = https://github.com/pypeclub/avalon-core.git
|
||||
[submodule "repos/avalon-unreal-integration"]
|
||||
path = repos/avalon-unreal-integration
|
||||
url = https://github.com/pypeclub/avalon-unreal-integration.git
|
||||
[submodule "openpype/modules/default_modules/ftrack/python2_vendor/arrow"]
|
||||
path = openpype/modules/default_modules/ftrack/python2_vendor/arrow
|
||||
url = https://github.com/arrow-py/arrow.git
|
||||
[submodule "openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api"]
|
||||
path = openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api
|
||||
url = https://bitbucket.org/ftrack/ftrack-python-api.git
|
||||
url = https://github.com/pypeclub/avalon-core.git
|
||||
170
CHANGELOG.md
170
CHANGELOG.md
|
|
@ -1,121 +1,89 @@
|
|||
# Changelog
|
||||
|
||||
## [3.9.0-nightly.3](https://github.com/pypeclub/OpenPype/tree/HEAD)
|
||||
## [3.9.1-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.8.2...HEAD)
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
- Loader: Remove default family states for hosts from code [\#2706](https://github.com/pypeclub/OpenPype/pull/2706)
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Update docusaurus to latest version [\#2760](https://github.com/pypeclub/OpenPype/pull/2760)
|
||||
- documentation: add example to `repack-version` command [\#2669](https://github.com/pypeclub/OpenPype/pull/2669)
|
||||
|
||||
**🆕 New features**
|
||||
|
||||
- Flame: loading clips to reels [\#2622](https://github.com/pypeclub/OpenPype/pull/2622)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- Pyblish Pype: Remove redundant new line in installed fonts printing [\#2758](https://github.com/pypeclub/OpenPype/pull/2758)
|
||||
- Flame: adding validator source clip [\#2746](https://github.com/pypeclub/OpenPype/pull/2746)
|
||||
- Work Files: Preserve subversion comment of current filename by default [\#2734](https://github.com/pypeclub/OpenPype/pull/2734)
|
||||
- Ftrack: Disable ftrack module by default [\#2732](https://github.com/pypeclub/OpenPype/pull/2732)
|
||||
- Project Manager: Disable add task, add asset and save button when not in a project [\#2727](https://github.com/pypeclub/OpenPype/pull/2727)
|
||||
- dropbox handle big file [\#2718](https://github.com/pypeclub/OpenPype/pull/2718)
|
||||
- Fusion Move PR: Minor tweaks to Fusion integration [\#2716](https://github.com/pypeclub/OpenPype/pull/2716)
|
||||
- Nuke: prerender with review knob [\#2691](https://github.com/pypeclub/OpenPype/pull/2691)
|
||||
- Maya configurable unit validator [\#2680](https://github.com/pypeclub/OpenPype/pull/2680)
|
||||
- General: Add settings for CleanUpFarm and disable the plugin by default [\#2679](https://github.com/pypeclub/OpenPype/pull/2679)
|
||||
- Project Manager: Only allow scroll wheel edits when spinbox is active [\#2678](https://github.com/pypeclub/OpenPype/pull/2678)
|
||||
- Ftrack: Sync description to assets [\#2670](https://github.com/pypeclub/OpenPype/pull/2670)
|
||||
- General: FFmpeg conversion also check attribute string length [\#2635](https://github.com/pypeclub/OpenPype/pull/2635)
|
||||
- Global: adding studio name/code to anatomy template formatting data [\#2630](https://github.com/pypeclub/OpenPype/pull/2630)
|
||||
- Houdini: Load Arnold .ass procedurals into Houdini [\#2606](https://github.com/pypeclub/OpenPype/pull/2606)
|
||||
- Deadline: Simplify GlobalJobPreLoad logic [\#2605](https://github.com/pypeclub/OpenPype/pull/2605)
|
||||
- Houdini: Implement Arnold .ass standin extraction from Houdini \(also support .ass.gz\) [\#2603](https://github.com/pypeclub/OpenPype/pull/2603)
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.0...HEAD)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- Loader UI: Fix right click in representation widget [\#2757](https://github.com/pypeclub/OpenPype/pull/2757)
|
||||
- Aftereffects 2022 and Deadline [\#2748](https://github.com/pypeclub/OpenPype/pull/2748)
|
||||
- Flame: bunch of bugs [\#2745](https://github.com/pypeclub/OpenPype/pull/2745)
|
||||
- Maya: Save current scene on workfile publish [\#2744](https://github.com/pypeclub/OpenPype/pull/2744)
|
||||
- Version Up: Preserve parts of filename after version number \(like subversion\) on version\_up [\#2741](https://github.com/pypeclub/OpenPype/pull/2741)
|
||||
- Loader UI: Multiple asset selection and underline colors fixed [\#2731](https://github.com/pypeclub/OpenPype/pull/2731)
|
||||
- General: Fix loading of unused chars in xml format [\#2729](https://github.com/pypeclub/OpenPype/pull/2729)
|
||||
- TVPaint: Set objectName with members [\#2725](https://github.com/pypeclub/OpenPype/pull/2725)
|
||||
- General: Don't use 'objectName' from loaded references [\#2715](https://github.com/pypeclub/OpenPype/pull/2715)
|
||||
- Settings: Studio Project anatomy is queried using right keys [\#2711](https://github.com/pypeclub/OpenPype/pull/2711)
|
||||
- Local Settings: Additional applications don't break UI [\#2710](https://github.com/pypeclub/OpenPype/pull/2710)
|
||||
- Houdini: Fix refactor of Houdini host move for CreateArnoldAss [\#2704](https://github.com/pypeclub/OpenPype/pull/2704)
|
||||
- LookAssigner: Fix imports after moving code to OpenPype repository [\#2701](https://github.com/pypeclub/OpenPype/pull/2701)
|
||||
- Multiple hosts: unify menu style across hosts [\#2693](https://github.com/pypeclub/OpenPype/pull/2693)
|
||||
- Maya Redshift fixes [\#2692](https://github.com/pypeclub/OpenPype/pull/2692)
|
||||
- Maya: fix fps validation popup [\#2685](https://github.com/pypeclub/OpenPype/pull/2685)
|
||||
- Houdini Explicitly collect correct frame name even in case of single frame render when `frameStart` is provided [\#2676](https://github.com/pypeclub/OpenPype/pull/2676)
|
||||
- hiero: fix effect collector name and order [\#2673](https://github.com/pypeclub/OpenPype/pull/2673)
|
||||
- Maya: Fix menu callbacks [\#2671](https://github.com/pypeclub/OpenPype/pull/2671)
|
||||
- Launcher: Fix access to 'data' attribute on actions [\#2659](https://github.com/pypeclub/OpenPype/pull/2659)
|
||||
- Houdini: fix usd family in loader and integrators [\#2631](https://github.com/pypeclub/OpenPype/pull/2631)
|
||||
- Harmony - fixed creator issue [\#2891](https://github.com/pypeclub/OpenPype/pull/2891)
|
||||
- General: Remove forgotten use of avalon Creator [\#2885](https://github.com/pypeclub/OpenPype/pull/2885)
|
||||
- General: Avoid circular import [\#2884](https://github.com/pypeclub/OpenPype/pull/2884)
|
||||
- Fixes for attaching loaded containers \(\#2837\) [\#2874](https://github.com/pypeclub/OpenPype/pull/2874)
|
||||
|
||||
**Merged pull requests:**
|
||||
**🔀 Refactored code**
|
||||
|
||||
- Harmony: Rendering in Deadline didn't work in other machines than submitter [\#2754](https://github.com/pypeclub/OpenPype/pull/2754)
|
||||
- Maya: set Deadline job/batch name to original source workfile name instead of published workfile [\#2733](https://github.com/pypeclub/OpenPype/pull/2733)
|
||||
- Fusion: Moved implementation into OpenPype [\#2713](https://github.com/pypeclub/OpenPype/pull/2713)
|
||||
- TVPaint: Plugin build without dependencies [\#2705](https://github.com/pypeclub/OpenPype/pull/2705)
|
||||
- Webpublisher: Photoshop create a beauty png [\#2689](https://github.com/pypeclub/OpenPype/pull/2689)
|
||||
- Ftrack: Hierarchical attributes are queried properly [\#2682](https://github.com/pypeclub/OpenPype/pull/2682)
|
||||
- Maya: Add Validate Frame Range settings [\#2661](https://github.com/pypeclub/OpenPype/pull/2661)
|
||||
- Harmony: move to Openpype [\#2657](https://github.com/pypeclub/OpenPype/pull/2657)
|
||||
- General: Show applications without integration in project [\#2656](https://github.com/pypeclub/OpenPype/pull/2656)
|
||||
- Maya: cleanup duplicate rendersetup code [\#2642](https://github.com/pypeclub/OpenPype/pull/2642)
|
||||
- General: Reduce style usage to OpenPype repository [\#2889](https://github.com/pypeclub/OpenPype/pull/2889)
|
||||
|
||||
## [3.9.0](https://github.com/pypeclub/OpenPype/tree/3.9.0) (2022-03-14)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.9.0-nightly.9...3.9.0)
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
- AssetCreator: Remove the tool [\#2845](https://github.com/pypeclub/OpenPype/pull/2845)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- General: Subset name filtering in ExtractReview outpus [\#2872](https://github.com/pypeclub/OpenPype/pull/2872)
|
||||
- NewPublisher: Descriptions and Icons in creator dialog [\#2867](https://github.com/pypeclub/OpenPype/pull/2867)
|
||||
- NewPublisher: Changing task on publishing instance [\#2863](https://github.com/pypeclub/OpenPype/pull/2863)
|
||||
- TrayPublisher: Choose project widget is more clear [\#2859](https://github.com/pypeclub/OpenPype/pull/2859)
|
||||
- New: Validation exceptions [\#2841](https://github.com/pypeclub/OpenPype/pull/2841)
|
||||
- Maya: add loaded containers to published instance [\#2837](https://github.com/pypeclub/OpenPype/pull/2837)
|
||||
- Ftrack: Can sync fps as string [\#2836](https://github.com/pypeclub/OpenPype/pull/2836)
|
||||
- General: Custom function for find executable [\#2822](https://github.com/pypeclub/OpenPype/pull/2822)
|
||||
- General: Color dialog UI fixes [\#2817](https://github.com/pypeclub/OpenPype/pull/2817)
|
||||
- global: letter box calculated on output as last process [\#2812](https://github.com/pypeclub/OpenPype/pull/2812)
|
||||
- Nuke: adding Reformat to baking mov plugin [\#2811](https://github.com/pypeclub/OpenPype/pull/2811)
|
||||
- Manager: Update all to latest button [\#2805](https://github.com/pypeclub/OpenPype/pull/2805)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- General: Missing time function [\#2877](https://github.com/pypeclub/OpenPype/pull/2877)
|
||||
- Deadline: Fix plugin name for tile assemble [\#2868](https://github.com/pypeclub/OpenPype/pull/2868)
|
||||
- Nuke: gizmo precollect fix [\#2866](https://github.com/pypeclub/OpenPype/pull/2866)
|
||||
- General: Fix hardlink for windows [\#2864](https://github.com/pypeclub/OpenPype/pull/2864)
|
||||
- General: ffmpeg was crashing on slate merge [\#2860](https://github.com/pypeclub/OpenPype/pull/2860)
|
||||
- WebPublisher: Video file was published with one too many frame [\#2858](https://github.com/pypeclub/OpenPype/pull/2858)
|
||||
- New Publisher: Error dialog got right styles [\#2857](https://github.com/pypeclub/OpenPype/pull/2857)
|
||||
- General: Fix getattr clalback on dynamic modules [\#2855](https://github.com/pypeclub/OpenPype/pull/2855)
|
||||
- Nuke: slate resolution to input video resolution [\#2853](https://github.com/pypeclub/OpenPype/pull/2853)
|
||||
- WebPublisher: Fix username stored in DB [\#2852](https://github.com/pypeclub/OpenPype/pull/2852)
|
||||
- WebPublisher: Fix wrong number of frames for video file [\#2851](https://github.com/pypeclub/OpenPype/pull/2851)
|
||||
- Nuke: Fix family test in validate\_write\_legacy to work with stillImage [\#2847](https://github.com/pypeclub/OpenPype/pull/2847)
|
||||
- Nuke: fix multiple baking profile farm publishing [\#2842](https://github.com/pypeclub/OpenPype/pull/2842)
|
||||
- Blender: Fixed parameters for FBX export of the camera [\#2840](https://github.com/pypeclub/OpenPype/pull/2840)
|
||||
- Maya: Stop creation of reviews for Cryptomattes [\#2832](https://github.com/pypeclub/OpenPype/pull/2832)
|
||||
- Deadline: Remove recreated event [\#2828](https://github.com/pypeclub/OpenPype/pull/2828)
|
||||
- Deadline: Added missing events folder [\#2827](https://github.com/pypeclub/OpenPype/pull/2827)
|
||||
- Settings: Missing document with OP versions may break start of OpenPype [\#2825](https://github.com/pypeclub/OpenPype/pull/2825)
|
||||
- Deadline: more detailed temp file name for environment json [\#2824](https://github.com/pypeclub/OpenPype/pull/2824)
|
||||
- General: Host name was formed from obsolete code [\#2821](https://github.com/pypeclub/OpenPype/pull/2821)
|
||||
- Settings UI: Fix "Apply from" action [\#2820](https://github.com/pypeclub/OpenPype/pull/2820)
|
||||
- Ftrack: Job killer with missing user [\#2819](https://github.com/pypeclub/OpenPype/pull/2819)
|
||||
- Nuke: Use AVALON\_APP to get value for "app" key [\#2818](https://github.com/pypeclub/OpenPype/pull/2818)
|
||||
- StandalonePublisher: use dynamic groups in subset names [\#2816](https://github.com/pypeclub/OpenPype/pull/2816)
|
||||
|
||||
**🔀 Refactored code**
|
||||
|
||||
- Refactor: move webserver tool to openpype [\#2876](https://github.com/pypeclub/OpenPype/pull/2876)
|
||||
- General: Move create logic from avalon to OpenPype [\#2854](https://github.com/pypeclub/OpenPype/pull/2854)
|
||||
- General: Add vendors from avalon [\#2848](https://github.com/pypeclub/OpenPype/pull/2848)
|
||||
- General: Basic event system [\#2846](https://github.com/pypeclub/OpenPype/pull/2846)
|
||||
- General: Move change context functions [\#2839](https://github.com/pypeclub/OpenPype/pull/2839)
|
||||
- Tools: Don't use avalon tools code [\#2829](https://github.com/pypeclub/OpenPype/pull/2829)
|
||||
- Move Unreal Implementation to OpenPype [\#2823](https://github.com/pypeclub/OpenPype/pull/2823)
|
||||
- General: Extract template formatting from anatomy [\#2766](https://github.com/pypeclub/OpenPype/pull/2766)
|
||||
|
||||
## [3.8.2](https://github.com/pypeclub/OpenPype/tree/3.8.2) (2022-02-07)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.8.2-nightly.3...3.8.2)
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Cosmetics: Fix common typos in openpype/website [\#2617](https://github.com/pypeclub/OpenPype/pull/2617)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- General: Project backup tools [\#2629](https://github.com/pypeclub/OpenPype/pull/2629)
|
||||
- nuke: adding clear button to write nodes [\#2627](https://github.com/pypeclub/OpenPype/pull/2627)
|
||||
- Ftrack: Family to Asset type mapping is in settings [\#2602](https://github.com/pypeclub/OpenPype/pull/2602)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- Fix pulling of cx\_freeze 6.10 [\#2628](https://github.com/pypeclub/OpenPype/pull/2628)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Docker: enhance dockerfiles with metadata, fix pyenv initialization [\#2647](https://github.com/pypeclub/OpenPype/pull/2647)
|
||||
- WebPublisher: fix instance duplicates [\#2641](https://github.com/pypeclub/OpenPype/pull/2641)
|
||||
- Fix - safer pulling of task name for webpublishing from PS [\#2613](https://github.com/pypeclub/OpenPype/pull/2613)
|
||||
|
||||
## [3.8.1](https://github.com/pypeclub/OpenPype/tree/3.8.1) (2022-02-01)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.8.1-nightly.3...3.8.1)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- Webpublisher: Thumbnail extractor [\#2600](https://github.com/pypeclub/OpenPype/pull/2600)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- Release/3.8.0 [\#2619](https://github.com/pypeclub/OpenPype/pull/2619)
|
||||
- hotfix: OIIO tool path - add extension on windows [\#2618](https://github.com/pypeclub/OpenPype/pull/2618)
|
||||
- Settings: Enum does not store empty string if has single item to select [\#2615](https://github.com/pypeclub/OpenPype/pull/2615)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Bump pillow from 8.4.0 to 9.0.0 [\#2595](https://github.com/pypeclub/OpenPype/pull/2595)
|
||||
|
||||
## [3.8.0](https://github.com/pypeclub/OpenPype/tree/3.8.0) (2022-01-24)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.8.0-nightly.7...3.8.0)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ from .lib import (
|
|||
Anatomy,
|
||||
filter_pyblish_plugins,
|
||||
set_plugin_attributes_from_settings,
|
||||
change_timer_to_current_context
|
||||
change_timer_to_current_context,
|
||||
register_event_callback,
|
||||
)
|
||||
|
||||
pyblish = avalon = _original_discover = None
|
||||
|
|
@ -58,10 +59,15 @@ def patched_discover(superclass):
|
|||
"""
|
||||
# run original discover and get plugins
|
||||
plugins = _original_discover(superclass)
|
||||
filtered_plugins = [
|
||||
plugin
|
||||
for plugin in plugins
|
||||
if issubclass(plugin, superclass)
|
||||
]
|
||||
|
||||
set_plugin_attributes_from_settings(plugins, superclass)
|
||||
set_plugin_attributes_from_settings(filtered_plugins, superclass)
|
||||
|
||||
return plugins
|
||||
return filtered_plugins
|
||||
|
||||
|
||||
@import_wrapper
|
||||
|
|
@ -69,6 +75,10 @@ def install():
|
|||
"""Install Pype to Avalon."""
|
||||
from pyblish.lib import MessageHandler
|
||||
from openpype.modules import load_modules
|
||||
from openpype.pipeline import (
|
||||
LegacyCreator,
|
||||
register_loader_plugin_path,
|
||||
)
|
||||
from avalon import pipeline
|
||||
|
||||
# Make sure modules are loaded
|
||||
|
|
@ -84,7 +94,7 @@ def install():
|
|||
log.info("Registering global plug-ins..")
|
||||
pyblish.register_plugin_path(PUBLISH_PATH)
|
||||
pyblish.register_discovery_filter(filter_pyblish_plugins)
|
||||
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
register_loader_plugin_path(LOAD_PATH)
|
||||
|
||||
project_name = os.environ.get("AVALON_PROJECT")
|
||||
|
||||
|
|
@ -112,8 +122,8 @@ def install():
|
|||
continue
|
||||
|
||||
pyblish.register_plugin_path(path)
|
||||
avalon.register_plugin_path(avalon.Loader, path)
|
||||
avalon.register_plugin_path(avalon.Creator, path)
|
||||
register_loader_plugin_path(path)
|
||||
avalon.register_plugin_path(LegacyCreator, path)
|
||||
avalon.register_plugin_path(avalon.InventoryAction, path)
|
||||
|
||||
# apply monkey patched discover to original one
|
||||
|
|
@ -122,20 +132,22 @@ def install():
|
|||
avalon.discover = patched_discover
|
||||
pipeline.discover = patched_discover
|
||||
|
||||
avalon.on("taskChanged", _on_task_change)
|
||||
register_event_callback("taskChanged", _on_task_change)
|
||||
|
||||
|
||||
def _on_task_change(*args):
|
||||
def _on_task_change():
|
||||
change_timer_to_current_context()
|
||||
|
||||
|
||||
@import_wrapper
|
||||
def uninstall():
|
||||
"""Uninstall Pype from Avalon."""
|
||||
from openpype.pipeline import deregister_loader_plugin_path
|
||||
|
||||
log.info("Deregistering global plug-ins..")
|
||||
pyblish.deregister_plugin_path(PUBLISH_PATH)
|
||||
pyblish.deregister_discovery_filter(filter_pyblish_plugins)
|
||||
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
deregister_loader_plugin_path(LOAD_PATH)
|
||||
log.info("Global plug-ins unregistred")
|
||||
|
||||
# restore original discover
|
||||
|
|
|
|||
|
|
@ -45,9 +45,6 @@ from .lib.avalon_context import (
|
|||
from . import resources
|
||||
|
||||
from .plugin import (
|
||||
PypeCreatorMixin,
|
||||
Creator,
|
||||
|
||||
Extractor,
|
||||
|
||||
ValidatePipelineOrder,
|
||||
|
|
@ -89,9 +86,6 @@ __all__ = [
|
|||
# Resources
|
||||
"resources",
|
||||
|
||||
# Pype creator mixin
|
||||
"PypeCreatorMixin",
|
||||
"Creator",
|
||||
# plugin classes
|
||||
"Extractor",
|
||||
# ordering
|
||||
|
|
|
|||
|
|
@ -42,6 +42,12 @@ def standalonepublisher():
|
|||
PypeCommands().launch_standalone_publisher()
|
||||
|
||||
|
||||
@main.command()
|
||||
def traypublisher():
|
||||
"""Show new OpenPype Standalone publisher UI."""
|
||||
PypeCommands().launch_traypublisher()
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option("-d", "--debug",
|
||||
is_flag=True, help=("Run pype tray in debug mode"))
|
||||
|
|
@ -371,10 +377,15 @@ def run(script):
|
|||
"--app_variant",
|
||||
help="Provide specific app variant for test, empty for latest",
|
||||
default=None)
|
||||
def runtests(folder, mark, pyargs, test_data_folder, persist, app_variant):
|
||||
@click.option("-t",
|
||||
"--timeout",
|
||||
help="Provide specific timeout value for test case",
|
||||
default=None)
|
||||
def runtests(folder, mark, pyargs, test_data_folder, persist, app_variant,
|
||||
timeout):
|
||||
"""Run all automatic tests after proper initialization via start.py"""
|
||||
PypeCommands().run_tests(folder, mark, pyargs, test_data_folder,
|
||||
persist, app_variant)
|
||||
persist, app_variant, timeout)
|
||||
|
||||
|
||||
@main.command()
|
||||
|
|
|
|||
|
|
@ -17,12 +17,13 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook):
|
|||
"nuke",
|
||||
"nukex",
|
||||
"hiero",
|
||||
"houdini",
|
||||
"nukestudio",
|
||||
"fusion",
|
||||
"blender",
|
||||
"photoshop",
|
||||
"tvpaint",
|
||||
"afftereffects"
|
||||
"aftereffects"
|
||||
]
|
||||
|
||||
def execute(self):
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class CopyTemplateWorkfile(PreLaunchHook):
|
|||
|
||||
# Before `AddLastWorkfileToLaunchArgs`
|
||||
order = 0
|
||||
app_groups = ["blender", "photoshop", "tvpaint", "afftereffects"]
|
||||
app_groups = ["blender", "photoshop", "tvpaint", "aftereffects"]
|
||||
|
||||
def execute(self):
|
||||
"""Check if can copy template for context and do it if possible.
|
||||
|
|
@ -44,7 +44,7 @@ class CopyTemplateWorkfile(PreLaunchHook):
|
|||
return
|
||||
|
||||
if os.path.exists(last_workfile):
|
||||
self.log.debug("Last workfile exits. Skipping {} process.".format(
|
||||
self.log.debug("Last workfile exists. Skipping {} process.".format(
|
||||
self.__class__.__name__
|
||||
))
|
||||
return
|
||||
|
|
@ -120,7 +120,7 @@ class CopyTemplateWorkfile(PreLaunchHook):
|
|||
f"Creating workfile from template: \"{template_path}\""
|
||||
)
|
||||
|
||||
# Copy template workfile to new destinantion
|
||||
# Copy template workfile to new destination
|
||||
shutil.copy2(
|
||||
os.path.normpath(template_path),
|
||||
os.path.normpath(last_workfile)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from openpype.api import Anatomy
|
|||
from openpype.lib import (
|
||||
PreLaunchHook,
|
||||
EnvironmentPrepData,
|
||||
prepare_host_environments,
|
||||
prepare_app_environments,
|
||||
prepare_context_environments
|
||||
)
|
||||
|
||||
|
|
@ -14,14 +14,6 @@ class GlobalHostDataHook(PreLaunchHook):
|
|||
|
||||
def execute(self):
|
||||
"""Prepare global objects to `data` that will be used for sure."""
|
||||
if not self.application.is_host:
|
||||
self.log.info(
|
||||
"Skipped hook {}. Application is not marked as host.".format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
self.prepare_global_data()
|
||||
|
||||
if not self.data.get("asset_doc"):
|
||||
|
|
@ -49,7 +41,7 @@ class GlobalHostDataHook(PreLaunchHook):
|
|||
"log": self.log
|
||||
})
|
||||
|
||||
prepare_host_environments(temp_data, self.launch_context.env_group)
|
||||
prepare_app_environments(temp_data, self.launch_context.env_group)
|
||||
prepare_context_environments(temp_data)
|
||||
|
||||
temp_data.pop("log")
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from Qt import QtCore
|
|||
from openpype.tools.utils import host_tools
|
||||
|
||||
from avalon import api
|
||||
from avalon.tools.webserver.app import WebServerTool
|
||||
from openpype.tools.adobe_webserver.app import WebServerTool
|
||||
|
||||
from .ws_stub import AfterEffectsServerStub
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,13 @@ from avalon import io, pipeline
|
|||
|
||||
from openpype import lib
|
||||
from openpype.api import Logger
|
||||
from openpype.pipeline import (
|
||||
LegacyCreator,
|
||||
register_loader_plugin_path,
|
||||
deregister_loader_plugin_path,
|
||||
)
|
||||
import openpype.hosts.aftereffects
|
||||
from openpype.lib import register_event_callback
|
||||
|
||||
from .launch_logic import get_stub
|
||||
|
||||
|
|
@ -65,21 +71,21 @@ def install():
|
|||
pyblish.api.register_host("aftereffects")
|
||||
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
||||
|
||||
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
register_loader_plugin_path(LOAD_PATH)
|
||||
avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH)
|
||||
log.info(PUBLISH_PATH)
|
||||
|
||||
pyblish.api.register_callback(
|
||||
"instanceToggled", on_pyblish_instance_toggled
|
||||
)
|
||||
|
||||
avalon.api.on("application.launched", application_launch)
|
||||
register_event_callback("application.launched", application_launch)
|
||||
|
||||
|
||||
def uninstall():
|
||||
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
|
||||
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
deregister_loader_plugin_path(LOAD_PATH)
|
||||
avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH)
|
||||
|
||||
|
||||
def on_pyblish_instance_toggled(instance, old_value, new_value):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import avalon.api
|
||||
from openpype.pipeline import LoaderPlugin
|
||||
from .launch_logic import get_stub
|
||||
|
||||
|
||||
class AfterEffectsLoader(avalon.api.Loader):
|
||||
class AfterEffectsLoader(LoaderPlugin):
|
||||
@staticmethod
|
||||
def get_stub():
|
||||
return get_stub()
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import logging
|
|||
import attr
|
||||
|
||||
from wsrpc_aiohttp import WebSocketAsync
|
||||
from avalon.tools.webserver.app import WebServerTool
|
||||
from openpype.tools.adobe_webserver.app import WebServerTool
|
||||
|
||||
|
||||
@attr.s
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
from avalon.api import CreatorError
|
||||
|
||||
import openpype.api
|
||||
from openpype.pipeline import create
|
||||
from openpype.pipeline import CreatorError
|
||||
from openpype.hosts.aftereffects.api import (
|
||||
get_stub,
|
||||
list_instances
|
||||
)
|
||||
|
||||
|
||||
class CreateRender(openpype.api.Creator):
|
||||
class CreateRender(create.LegacyCreator):
|
||||
"""Render folder for publish.
|
||||
|
||||
Creates subsets in format 'familyTaskSubsetname',
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import re
|
||||
|
||||
import avalon.api
|
||||
|
||||
from openpype.lib import (
|
||||
get_background_layers,
|
||||
get_unique_layer_name
|
||||
)
|
||||
from openpype.pipeline import get_representation_path
|
||||
from openpype.hosts.aftereffects.api import (
|
||||
AfterEffectsLoader,
|
||||
containerise
|
||||
|
|
@ -78,7 +77,7 @@ class BackgroundLoader(AfterEffectsLoader):
|
|||
else: # switching version - keep same name
|
||||
comp_name = container["namespace"]
|
||||
|
||||
path = avalon.api.get_representation_path(representation)
|
||||
path = get_representation_path(representation)
|
||||
|
||||
layers = get_background_layers(path)
|
||||
comp = stub.reload_background(container["members"][1],
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import re
|
||||
|
||||
import avalon.api
|
||||
from openpype import lib
|
||||
|
||||
from openpype.pipeline import get_representation_path
|
||||
from openpype.hosts.aftereffects.api import (
|
||||
AfterEffectsLoader,
|
||||
containerise
|
||||
|
|
@ -92,7 +92,7 @@ class FileLoader(AfterEffectsLoader):
|
|||
"{}_{}".format(context["asset"], context["subset"]))
|
||||
else: # switching version - keep same name
|
||||
layer_name = container["namespace"]
|
||||
path = avalon.api.get_representation_path(representation)
|
||||
path = get_representation_path(representation)
|
||||
# with aftereffects.maintained_selection(): # TODO
|
||||
stub.replace_item(layer.id, path, stub.LOADED_ICON + layer_name)
|
||||
stub.imprint(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<error id="main">
|
||||
<title>Subset context</title>
|
||||
<description>
|
||||
## Invalid subset context
|
||||
|
||||
Context of the given subset doesn't match your current scene.
|
||||
|
||||
### How to repair?
|
||||
|
||||
You can fix this with "repair" button on the right.
|
||||
</description>
|
||||
<detail>
|
||||
### __Detailed Info__ (optional)
|
||||
|
||||
This might happen if you are reuse old workfile and open it in different context.
|
||||
(Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.)
|
||||
</detail>
|
||||
</error>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<error id="main">
|
||||
<title>Scene setting</title>
|
||||
<description>
|
||||
## Invalid scene setting found
|
||||
|
||||
One of the settings in a scene doesn't match to asset settings in database.
|
||||
|
||||
{invalid_setting_str}
|
||||
|
||||
### How to repair?
|
||||
|
||||
Change values for {invalid_keys_str} in the scene OR change them in the asset database if they are wrong there.
|
||||
</description>
|
||||
<detail>
|
||||
### __Detailed Info__ (optional)
|
||||
|
||||
This error is shown when for example resolution in the scene doesn't match to resolution set on the asset in the database.
|
||||
Either value in the database or in the scene is wrong.
|
||||
</detail>
|
||||
</error>
|
||||
<error id="file_not_found">
|
||||
<title>Scene file doesn't exist</title>
|
||||
<description>
|
||||
## Scene file doesn't exist
|
||||
|
||||
Collected scene {scene_url} doesn't exist.
|
||||
|
||||
### How to repair?
|
||||
|
||||
Re-save file, start publish from the beginning again.
|
||||
</description>
|
||||
</error>
|
||||
</root>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from avalon import api
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline import PublishXmlValidationError
|
||||
from openpype.hosts.aftereffects.api import get_stub
|
||||
|
||||
|
||||
|
|
@ -53,9 +54,8 @@ class ValidateInstanceAsset(pyblish.api.InstancePlugin):
|
|||
current_asset = api.Session["AVALON_ASSET"]
|
||||
msg = (
|
||||
f"Instance asset {instance_asset} is not the same "
|
||||
f"as current context {current_asset}. PLEASE DO:\n"
|
||||
f"Repair with 'A' action to use '{current_asset}'.\n"
|
||||
f"If that's not correct value, close workfile and "
|
||||
f"reopen via Workfiles!"
|
||||
f"as current context {current_asset}."
|
||||
)
|
||||
assert instance_asset == current_asset, msg
|
||||
|
||||
if instance_asset != current_asset:
|
||||
raise PublishXmlValidationError(self, msg)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import re
|
|||
|
||||
import pyblish.api
|
||||
|
||||
from openpype.pipeline import PublishXmlValidationError
|
||||
from openpype.hosts.aftereffects.api import get_asset_settings
|
||||
|
||||
|
||||
|
|
@ -99,12 +100,14 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin):
|
|||
self.log.info("current_settings:: {}".format(current_settings))
|
||||
|
||||
invalid_settings = []
|
||||
invalid_keys = set()
|
||||
for key, value in expected_settings.items():
|
||||
if value != current_settings[key]:
|
||||
invalid_settings.append(
|
||||
"{} expected: {} found: {}".format(key, value,
|
||||
current_settings[key])
|
||||
)
|
||||
invalid_keys.add(key)
|
||||
|
||||
if ((expected_settings.get("handleStart")
|
||||
or expected_settings.get("handleEnd"))
|
||||
|
|
@ -116,7 +119,27 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin):
|
|||
msg = "Found invalid settings:\n{}".format(
|
||||
"\n".join(invalid_settings)
|
||||
)
|
||||
assert not invalid_settings, msg
|
||||
assert os.path.exists(instance.data.get("source")), (
|
||||
"Scene file not found (saved under wrong name)"
|
||||
)
|
||||
|
||||
if invalid_settings:
|
||||
invalid_keys_str = ",".join(invalid_keys)
|
||||
break_str = "<br/>"
|
||||
invalid_setting_str = "<b>Found invalid settings:</b><br/>{}".\
|
||||
format(break_str.join(invalid_settings))
|
||||
|
||||
formatting_data = {
|
||||
"invalid_setting_str": invalid_setting_str,
|
||||
"invalid_keys_str": invalid_keys_str
|
||||
}
|
||||
raise PublishXmlValidationError(self, msg,
|
||||
formatting_data=formatting_data)
|
||||
|
||||
if not os.path.exists(instance.data.get("source")):
|
||||
scene_url = instance.data.get("source")
|
||||
msg = "Scene file {} not found (saved under wrong name)".format(
|
||||
scene_url
|
||||
)
|
||||
formatting_data = {
|
||||
"scene_url": scene_url
|
||||
}
|
||||
raise PublishXmlValidationError(self, msg, key="file_not_found",
|
||||
formatting_data=formatting_data)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,16 @@ import avalon.api
|
|||
from avalon import io, schema
|
||||
from avalon.pipeline import AVALON_CONTAINER_ID
|
||||
|
||||
from openpype.pipeline import (
|
||||
LegacyCreator,
|
||||
register_loader_plugin_path,
|
||||
deregister_loader_plugin_path,
|
||||
)
|
||||
from openpype.api import Logger
|
||||
from openpype.lib import (
|
||||
register_event_callback,
|
||||
emit_event
|
||||
)
|
||||
import openpype.hosts.blender
|
||||
|
||||
HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.blender.__file__))
|
||||
|
|
@ -45,13 +54,14 @@ def install():
|
|||
pyblish.api.register_host("blender")
|
||||
pyblish.api.register_plugin_path(str(PUBLISH_PATH))
|
||||
|
||||
avalon.api.register_plugin_path(avalon.api.Loader, str(LOAD_PATH))
|
||||
avalon.api.register_plugin_path(avalon.api.Creator, str(CREATE_PATH))
|
||||
register_loader_plugin_path(str(LOAD_PATH))
|
||||
avalon.api.register_plugin_path(LegacyCreator, str(CREATE_PATH))
|
||||
|
||||
lib.append_user_scripts()
|
||||
|
||||
avalon.api.on("new", on_new)
|
||||
avalon.api.on("open", on_open)
|
||||
register_event_callback("new", on_new)
|
||||
register_event_callback("open", on_open)
|
||||
|
||||
_register_callbacks()
|
||||
_register_events()
|
||||
|
||||
|
|
@ -66,8 +76,8 @@ def uninstall():
|
|||
pyblish.api.deregister_host("blender")
|
||||
pyblish.api.deregister_plugin_path(str(PUBLISH_PATH))
|
||||
|
||||
avalon.api.deregister_plugin_path(avalon.api.Loader, str(LOAD_PATH))
|
||||
avalon.api.deregister_plugin_path(avalon.api.Creator, str(CREATE_PATH))
|
||||
deregister_loader_plugin_path(str(LOAD_PATH))
|
||||
avalon.api.deregister_plugin_path(LegacyCreator, str(CREATE_PATH))
|
||||
|
||||
if not IS_HEADLESS:
|
||||
ops.unregister()
|
||||
|
|
@ -113,22 +123,22 @@ def set_start_end_frames():
|
|||
scene.render.resolution_y = resolution_y
|
||||
|
||||
|
||||
def on_new(arg1, arg2):
|
||||
def on_new():
|
||||
set_start_end_frames()
|
||||
|
||||
|
||||
def on_open(arg1, arg2):
|
||||
def on_open():
|
||||
set_start_end_frames()
|
||||
|
||||
|
||||
@bpy.app.handlers.persistent
|
||||
def _on_save_pre(*args):
|
||||
avalon.api.emit("before_save", args)
|
||||
emit_event("before.save")
|
||||
|
||||
|
||||
@bpy.app.handlers.persistent
|
||||
def _on_save_post(*args):
|
||||
avalon.api.emit("save", args)
|
||||
emit_event("save")
|
||||
|
||||
|
||||
@bpy.app.handlers.persistent
|
||||
|
|
@ -136,9 +146,9 @@ def _on_load_post(*args):
|
|||
# Detect new file or opening an existing file
|
||||
if bpy.data.filepath:
|
||||
# Likely this was an open operation since it has a filepath
|
||||
avalon.api.emit("open", args)
|
||||
emit_event("open")
|
||||
else:
|
||||
avalon.api.emit("new", args)
|
||||
emit_event("new")
|
||||
|
||||
ops.OpenFileCacher.post_load()
|
||||
|
||||
|
|
@ -169,7 +179,7 @@ def _register_callbacks():
|
|||
log.info("Installed event handler _on_load_post...")
|
||||
|
||||
|
||||
def _on_task_changed(*args):
|
||||
def _on_task_changed():
|
||||
"""Callback for when the task in the context is changed."""
|
||||
|
||||
# TODO (jasper): Blender has no concept of projects or workspace.
|
||||
|
|
@ -186,7 +196,7 @@ def _on_task_changed(*args):
|
|||
def _register_events():
|
||||
"""Install callbacks for specific events."""
|
||||
|
||||
avalon.api.on("taskChanged", _on_task_changed)
|
||||
register_event_callback("taskChanged", _on_task_changed)
|
||||
log.info("Installed event callback for 'taskChanged'...")
|
||||
|
||||
|
||||
|
|
@ -202,13 +212,10 @@ def reload_pipeline(*args):
|
|||
avalon.api.uninstall()
|
||||
|
||||
for module in (
|
||||
"avalon.io",
|
||||
"avalon.lib",
|
||||
"avalon.pipeline",
|
||||
"avalon.tools.creator.app",
|
||||
"avalon.tools.manager.app",
|
||||
"avalon.api",
|
||||
"avalon.tools",
|
||||
"avalon.io",
|
||||
"avalon.lib",
|
||||
"avalon.pipeline",
|
||||
"avalon.api",
|
||||
):
|
||||
module = importlib.import_module(module)
|
||||
importlib.reload(module)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
import avalon.api
|
||||
from openpype.api import PypeCreatorMixin
|
||||
from openpype.pipeline import (
|
||||
LegacyCreator,
|
||||
LoaderPlugin,
|
||||
)
|
||||
from .pipeline import AVALON_CONTAINERS
|
||||
from .ops import (
|
||||
MainThreadItem,
|
||||
|
|
@ -129,7 +131,7 @@ def deselect_all():
|
|||
bpy.context.view_layer.objects.active = active
|
||||
|
||||
|
||||
class Creator(PypeCreatorMixin, avalon.api.Creator):
|
||||
class Creator(LegacyCreator):
|
||||
"""Base class for Creator plug-ins."""
|
||||
defaults = ['Main']
|
||||
|
||||
|
|
@ -145,13 +147,13 @@ class Creator(PypeCreatorMixin, avalon.api.Creator):
|
|||
return collection
|
||||
|
||||
|
||||
class Loader(avalon.api.Loader):
|
||||
class Loader(LoaderPlugin):
|
||||
"""Base class for Loader plug-ins."""
|
||||
|
||||
hosts = ["blender"]
|
||||
|
||||
|
||||
class AssetLoader(avalon.api.Loader):
|
||||
class AssetLoader(LoaderPlugin):
|
||||
"""A basic AssetLoader for Blender
|
||||
|
||||
This will implement the basic logic for linking/appending assets
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import get_representation_path
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
|
|
@ -178,7 +178,7 @@ class CacheModelLoader(plugin.AssetLoader):
|
|||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
libpath = Path(get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
|
|
|
|||
|
|
@ -5,9 +5,13 @@ from pathlib import Path
|
|||
from pprint import pformat
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from avalon import api, blender
|
||||
import bpy
|
||||
from openpype.pipeline import get_representation_path
|
||||
import openpype.hosts.blender.api.plugin
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
containerise_existing,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
logger = logging.getLogger("openpype").getChild("blender").getChild("load_action")
|
||||
|
||||
|
|
@ -49,7 +53,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
|
|||
|
||||
container = bpy.data.collections.new(lib_container)
|
||||
container.name = container_name
|
||||
blender.pipeline.containerise_existing(
|
||||
containerise_existing(
|
||||
container,
|
||||
name,
|
||||
namespace,
|
||||
|
|
@ -57,8 +61,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
|
|||
self.__class__.__name__,
|
||||
)
|
||||
|
||||
container_metadata = container.get(
|
||||
blender.pipeline.AVALON_PROPERTY)
|
||||
container_metadata = container.get(AVALON_PROPERTY)
|
||||
|
||||
container_metadata["libpath"] = libpath
|
||||
container_metadata["lib_container"] = lib_container
|
||||
|
|
@ -90,16 +93,16 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
|
|||
|
||||
anim_data.action.make_local()
|
||||
|
||||
if not obj.get(blender.pipeline.AVALON_PROPERTY):
|
||||
if not obj.get(AVALON_PROPERTY):
|
||||
|
||||
obj[blender.pipeline.AVALON_PROPERTY] = dict()
|
||||
obj[AVALON_PROPERTY] = dict()
|
||||
|
||||
avalon_info = obj[blender.pipeline.AVALON_PROPERTY]
|
||||
avalon_info = obj[AVALON_PROPERTY]
|
||||
avalon_info.update({"container_name": container_name})
|
||||
|
||||
objects_list.append(obj)
|
||||
|
||||
animation_container.pop(blender.pipeline.AVALON_PROPERTY)
|
||||
animation_container.pop(AVALON_PROPERTY)
|
||||
|
||||
# Save the list of objects in the metadata container
|
||||
container_metadata["objects"] = objects_list
|
||||
|
|
@ -128,7 +131,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
|
|||
container["objectName"]
|
||||
)
|
||||
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
libpath = Path(get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
logger.info(
|
||||
|
|
@ -153,8 +156,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
|
|||
f"Unsupported file: {libpath}"
|
||||
)
|
||||
|
||||
collection_metadata = collection.get(
|
||||
blender.pipeline.AVALON_PROPERTY)
|
||||
collection_metadata = collection.get(AVALON_PROPERTY)
|
||||
|
||||
collection_libpath = collection_metadata["libpath"]
|
||||
normalized_collection_libpath = (
|
||||
|
|
@ -225,16 +227,16 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
|
|||
strip.action = anim_data.action
|
||||
strip.action_frame_end = anim_data.action.frame_range[1]
|
||||
|
||||
if not obj.get(blender.pipeline.AVALON_PROPERTY):
|
||||
if not obj.get(AVALON_PROPERTY):
|
||||
|
||||
obj[blender.pipeline.AVALON_PROPERTY] = dict()
|
||||
obj[AVALON_PROPERTY] = dict()
|
||||
|
||||
avalon_info = obj[blender.pipeline.AVALON_PROPERTY]
|
||||
avalon_info = obj[AVALON_PROPERTY]
|
||||
avalon_info.update({"container_name": collection.name})
|
||||
|
||||
objects_list.append(obj)
|
||||
|
||||
anim_container.pop(blender.pipeline.AVALON_PROPERTY)
|
||||
anim_container.pop(AVALON_PROPERTY)
|
||||
|
||||
# Save the list of objects in the metadata container
|
||||
collection_metadata["objects"] = objects_list
|
||||
|
|
@ -266,8 +268,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
|
|||
"Nested collections are not supported."
|
||||
)
|
||||
|
||||
collection_metadata = collection.get(
|
||||
blender.pipeline.AVALON_PROPERTY)
|
||||
collection_metadata = collection.get(AVALON_PROPERTY)
|
||||
objects = collection_metadata["objects"]
|
||||
lib_container = collection_metadata["lib_container"]
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import get_representation_path
|
||||
from openpype.hosts.blender.api import plugin
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
|
|
@ -102,7 +102,7 @@ class AudioLoader(plugin.AssetLoader):
|
|||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
libpath = Path(get_representation_path(representation))
|
||||
|
||||
self.log.info(
|
||||
"Container: %s\nRepresentation: %s",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import get_representation_path
|
||||
from openpype.hosts.blender.api import plugin
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
|
|
@ -155,7 +155,7 @@ class BlendCameraLoader(plugin.AssetLoader):
|
|||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
libpath = Path(get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import get_representation_path
|
||||
from openpype.hosts.blender.api import plugin, lib
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
|
|
@ -143,7 +143,7 @@ class FbxCameraLoader(plugin.AssetLoader):
|
|||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
libpath = Path(get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import get_representation_path
|
||||
from openpype.hosts.blender.api import plugin, lib
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
|
|
@ -187,7 +187,7 @@ class FbxModelLoader(plugin.AssetLoader):
|
|||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
libpath = Path(get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
|
|
|
|||
|
|
@ -6,8 +6,11 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from openpype import lib
|
||||
from openpype.pipeline import (
|
||||
legacy_create,
|
||||
get_representation_path,
|
||||
)
|
||||
from openpype.hosts.blender.api import plugin
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
|
|
@ -159,7 +162,7 @@ class BlendLayoutLoader(plugin.AssetLoader):
|
|||
raise ValueError("Creator plugin \"CreateAnimation\" was "
|
||||
"not found.")
|
||||
|
||||
api.create(
|
||||
legacy_create(
|
||||
creator_plugin,
|
||||
name=local_obj.name.split(':')[-1] + "_animation",
|
||||
asset=asset,
|
||||
|
|
@ -308,7 +311,7 @@ class BlendLayoutLoader(plugin.AssetLoader):
|
|||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
libpath = Path(get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
|
|
|
|||
|
|
@ -7,8 +7,13 @@ from typing import Dict, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from openpype import lib
|
||||
from openpype.pipeline import (
|
||||
discover_loader_plugins,
|
||||
remove_container,
|
||||
load_container,
|
||||
get_representation_path,
|
||||
loaders_from_representation,
|
||||
)
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
AVALON_INSTANCES,
|
||||
AVALON_CONTAINERS,
|
||||
|
|
@ -34,7 +39,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
objects = list(asset_group.children)
|
||||
|
||||
for obj in objects:
|
||||
api.remove(obj.get(AVALON_PROPERTY))
|
||||
remove_container(obj.get(AVALON_PROPERTY))
|
||||
|
||||
def _remove_animation_instances(self, asset_group):
|
||||
instances = bpy.data.collections.get(AVALON_INSTANCES)
|
||||
|
|
@ -67,13 +72,13 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
with open(libpath, "r") as fp:
|
||||
data = json.load(fp)
|
||||
|
||||
all_loaders = api.discover(api.Loader)
|
||||
all_loaders = discover_loader_plugins()
|
||||
|
||||
for element in data:
|
||||
reference = element.get('reference')
|
||||
family = element.get('family')
|
||||
|
||||
loaders = api.loaders_from_representation(all_loaders, reference)
|
||||
loaders = loaders_from_representation(all_loaders, reference)
|
||||
loader = self._get_loader(loaders, family)
|
||||
|
||||
if not loader:
|
||||
|
|
@ -103,7 +108,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
# at this time it will not return anything. The assets will be
|
||||
# loaded in the next Blender cycle, so we use the options to
|
||||
# set the transform, parent and assign the action, if there is one.
|
||||
api.load(
|
||||
load_container(
|
||||
loader,
|
||||
reference,
|
||||
namespace=instance_name,
|
||||
|
|
@ -118,7 +123,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
# raise ValueError("Creator plugin \"CreateCamera\" was "
|
||||
# "not found.")
|
||||
|
||||
# api.create(
|
||||
# legacy_create(
|
||||
# creator_plugin,
|
||||
# name="camera",
|
||||
# # name=f"{unique_number}_{subset}_animation",
|
||||
|
|
@ -189,7 +194,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
libpath = Path(get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import os
|
|||
import json
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import get_representation_path
|
||||
from openpype.hosts.blender.api import plugin
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
containerise_existing,
|
||||
|
|
@ -140,7 +140,7 @@ class BlendLookLoader(plugin.AssetLoader):
|
|||
|
||||
def update(self, container: Dict, representation: Dict):
|
||||
collection = bpy.data.collections.get(container["objectName"])
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
libpath = Path(get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import get_representation_path
|
||||
from openpype.hosts.blender.api import plugin
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
|
|
@ -195,7 +195,7 @@ class BlendModelLoader(plugin.AssetLoader):
|
|||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
libpath = Path(get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
|
|
|
|||
|
|
@ -6,10 +6,15 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from avalon.blender import lib as avalon_lib
|
||||
from openpype import lib
|
||||
from openpype.hosts.blender.api import plugin
|
||||
from openpype.pipeline import (
|
||||
legacy_create,
|
||||
get_representation_path,
|
||||
)
|
||||
from openpype.hosts.blender.api import (
|
||||
plugin,
|
||||
get_selection,
|
||||
)
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
|
|
@ -248,7 +253,7 @@ class BlendRigLoader(plugin.AssetLoader):
|
|||
|
||||
animation_asset = options.get('animation_asset')
|
||||
|
||||
api.create(
|
||||
legacy_create(
|
||||
creator_plugin,
|
||||
name=namespace + "_animation",
|
||||
# name=f"{unique_number}_{subset}_animation",
|
||||
|
|
@ -262,7 +267,7 @@ class BlendRigLoader(plugin.AssetLoader):
|
|||
if anim_file:
|
||||
bpy.ops.import_scene.fbx(filepath=anim_file, anim_offset=0.0)
|
||||
|
||||
imported = avalon_lib.get_selection()
|
||||
imported = get_selection()
|
||||
|
||||
armature = [
|
||||
o for o in asset_group.children if o.type == 'ARMATURE'][0]
|
||||
|
|
@ -306,7 +311,7 @@ class BlendRigLoader(plugin.AssetLoader):
|
|||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
libpath = Path(get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ class ExtractCamera(api.Extractor):
|
|||
filepath=filepath,
|
||||
use_active_collection=False,
|
||||
use_selection=True,
|
||||
bake_anim_use_nla_strips=False,
|
||||
bake_anim_use_all_actions=False,
|
||||
add_leaf_bones=False,
|
||||
armature_nodetype='ROOT',
|
||||
object_types={'CAMERA'},
|
||||
bake_anim_simplify_factor=0.0
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import os
|
||||
import re
|
||||
import json
|
||||
import getpass
|
||||
|
||||
from avalon.vendor import requests
|
||||
import re
|
||||
import requests
|
||||
import pyblish.api
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@ from .workio import (
|
|||
)
|
||||
from .render_utils import (
|
||||
export_clip,
|
||||
get_preset_path_by_xml_name
|
||||
get_preset_path_by_xml_name,
|
||||
modify_preset_file
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
|
|
@ -140,5 +141,6 @@ __all__ = [
|
|||
|
||||
# render utils
|
||||
"export_clip",
|
||||
"get_preset_path_by_xml_name"
|
||||
"get_preset_path_by_xml_name",
|
||||
"modify_preset_file"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -527,6 +527,7 @@ def get_segment_attributes(segment):
|
|||
|
||||
# Add timeline segment to tree
|
||||
clip_data = {
|
||||
"shot_name": segment.shot_name.get_value(),
|
||||
"segment_name": segment.name.get_value(),
|
||||
"segment_comment": segment.comment.get_value(),
|
||||
"tape_name": segment.tape_name,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ from avalon import api as avalon
|
|||
from avalon.pipeline import AVALON_CONTAINER_ID
|
||||
from pyblish import api as pyblish
|
||||
from openpype.api import Logger
|
||||
from openpype.pipeline import (
|
||||
LegacyCreator,
|
||||
register_loader_plugin_path,
|
||||
deregister_loader_plugin_path,
|
||||
)
|
||||
from .lib import (
|
||||
set_segment_data_marker,
|
||||
set_publish_attribute,
|
||||
|
|
@ -32,8 +37,8 @@ def install():
|
|||
|
||||
pyblish.register_host("flame")
|
||||
pyblish.register_plugin_path(PUBLISH_PATH)
|
||||
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
register_loader_plugin_path(LOAD_PATH)
|
||||
avalon.register_plugin_path(LegacyCreator, CREATE_PATH)
|
||||
avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
|
||||
log.info("OpenPype Flame plug-ins registred ...")
|
||||
|
||||
|
|
@ -47,8 +52,8 @@ def uninstall():
|
|||
|
||||
log.info("Deregistering Flame plug-ins..")
|
||||
pyblish.deregister_plugin_path(PUBLISH_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
deregister_loader_plugin_path(LOAD_PATH)
|
||||
avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH)
|
||||
avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
|
||||
|
||||
# register callback for switching publishable
|
||||
|
|
|
|||
|
|
@ -2,13 +2,16 @@ import os
|
|||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from avalon.vendor import qargparse
|
||||
from xml.etree import ElementTree as ET
|
||||
import six
|
||||
import qargparse
|
||||
from Qt import QtWidgets, QtCore
|
||||
import openpype.api as openpype
|
||||
from openpype.pipeline import (
|
||||
LegacyCreator,
|
||||
LoaderPlugin,
|
||||
)
|
||||
from openpype import style
|
||||
import avalon.api as avalon
|
||||
from . import (
|
||||
lib as flib,
|
||||
pipeline as fpipeline,
|
||||
|
|
@ -299,7 +302,7 @@ class Spacer(QtWidgets.QWidget):
|
|||
self.setLayout(layout)
|
||||
|
||||
|
||||
class Creator(openpype.Creator):
|
||||
class Creator(LegacyCreator):
|
||||
"""Creator class wrapper
|
||||
"""
|
||||
clip_color = constants.COLOR_MAP["purple"]
|
||||
|
|
@ -361,6 +364,7 @@ class PublishableClip:
|
|||
vertical_sync_default = False
|
||||
driving_layer_default = ""
|
||||
index_from_segment_default = False
|
||||
use_shot_name_default = False
|
||||
|
||||
def __init__(self, segment, **kwargs):
|
||||
self.rename_index = kwargs["rename_index"]
|
||||
|
|
@ -376,6 +380,7 @@ class PublishableClip:
|
|||
# segment (clip) main attributes
|
||||
self.cs_name = self.clip_data["segment_name"]
|
||||
self.cs_index = int(self.clip_data["segment"])
|
||||
self.shot_name = self.clip_data["shot_name"]
|
||||
|
||||
# get track name and index
|
||||
self.track_index = int(self.clip_data["track"])
|
||||
|
|
@ -419,18 +424,21 @@ class PublishableClip:
|
|||
# deal with clip name
|
||||
new_name = self.marker_data.pop("newClipName")
|
||||
|
||||
if self.rename:
|
||||
if self.rename and not self.use_shot_name:
|
||||
# rename segment
|
||||
self.current_segment.name = str(new_name)
|
||||
self.marker_data["asset"] = str(new_name)
|
||||
elif self.use_shot_name:
|
||||
self.marker_data["asset"] = self.shot_name
|
||||
self.marker_data["hierarchyData"]["shot"] = self.shot_name
|
||||
else:
|
||||
self.marker_data["asset"] = self.cs_name
|
||||
self.marker_data["hierarchyData"]["shot"] = self.cs_name
|
||||
|
||||
if self.marker_data["heroTrack"] and self.review_layer:
|
||||
self.marker_data.update({"reviewTrack": self.review_layer})
|
||||
self.marker_data["reviewTrack"] = self.review_layer
|
||||
else:
|
||||
self.marker_data.update({"reviewTrack": None})
|
||||
self.marker_data["reviewTrack"] = None
|
||||
|
||||
# create pype tag on track_item and add data
|
||||
fpipeline.imprint(self.current_segment, self.marker_data)
|
||||
|
|
@ -463,6 +471,8 @@ class PublishableClip:
|
|||
# ui_inputs data or default values if gui was not used
|
||||
self.rename = self.ui_inputs.get(
|
||||
"clipRename", {}).get("value") or self.rename_default
|
||||
self.use_shot_name = self.ui_inputs.get(
|
||||
"useShotName", {}).get("value") or self.use_shot_name_default
|
||||
self.clip_name = self.ui_inputs.get(
|
||||
"clipName", {}).get("value") or self.clip_name_default
|
||||
self.hierarchy = self.ui_inputs.get(
|
||||
|
|
@ -652,7 +662,7 @@ class PublishableClip:
|
|||
# Publishing plugin functions
|
||||
# Loader plugin functions
|
||||
|
||||
class ClipLoader(avalon.Loader):
|
||||
class ClipLoader(LoaderPlugin):
|
||||
"""A basic clip loader for Flame
|
||||
|
||||
This will implement the basic behavior for a loader to inherit from that
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
|
||||
def export_clip(export_path, clip, preset_path, **kwargs):
|
||||
|
|
@ -123,3 +124,29 @@ def get_preset_path_by_xml_name(xml_preset_name):
|
|||
|
||||
# if nothing found then return False
|
||||
return False
|
||||
|
||||
|
||||
def modify_preset_file(xml_path, staging_dir, data):
|
||||
"""Modify xml preset with input data
|
||||
|
||||
Args:
|
||||
xml_path (str ): path for input xml preset
|
||||
staging_dir (str): staging dir path
|
||||
data (dict): data where key is xmlTag and value as string
|
||||
|
||||
Returns:
|
||||
str: _description_
|
||||
"""
|
||||
# create temp path
|
||||
dirname, basename = os.path.split(xml_path)
|
||||
temp_path = os.path.join(staging_dir, basename)
|
||||
|
||||
# change xml following data keys
|
||||
with open(xml_path, "r") as datafile:
|
||||
tree = ET.parse(datafile)
|
||||
for key, value in data.items():
|
||||
for element in tree.findall(".//{}".format(key)):
|
||||
element.text = str(value)
|
||||
tree.write(temp_path)
|
||||
|
||||
return temp_path
|
||||
|
|
|
|||
|
|
@ -420,13 +420,20 @@ class WireTapCom(object):
|
|||
RuntimeError: Not able to set colorspace policy
|
||||
"""
|
||||
color_policy = color_policy or "Legacy"
|
||||
|
||||
# check if the colour policy in custom dir
|
||||
if not os.path.exists(color_policy):
|
||||
color_policy = "/syncolor/policies/Autodesk/{}".format(
|
||||
color_policy)
|
||||
|
||||
# create arguments
|
||||
project_colorspace_cmd = [
|
||||
os.path.join(
|
||||
self.wiretap_tools_dir,
|
||||
"wiretap_duplicate_node"
|
||||
),
|
||||
"-s",
|
||||
"/syncolor/policies/Autodesk/{}".format(color_policy),
|
||||
color_policy,
|
||||
"-n",
|
||||
"/projects/{}/syncolor".format(project_name)
|
||||
]
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class FlamePrelaunch(PreLaunchHook):
|
|||
"FrameWidth": int(width),
|
||||
"FrameHeight": int(height),
|
||||
"AspectRatio": float((width / height) * _db_p_data["pixelAspect"]),
|
||||
"FrameRate": "{} fps".format(fps),
|
||||
"FrameRate": self._get_flame_fps(fps),
|
||||
"FrameDepth": str(imageio_flame["project"]["frameDepth"]),
|
||||
"FieldDominance": str(imageio_flame["project"]["fieldDominance"])
|
||||
}
|
||||
|
|
@ -101,6 +101,28 @@ class FlamePrelaunch(PreLaunchHook):
|
|||
|
||||
self.launch_context.launch_args.extend(app_arguments)
|
||||
|
||||
def _get_flame_fps(self, fps_num):
|
||||
fps_table = {
|
||||
float(23.976): "23.976 fps",
|
||||
int(25): "25 fps",
|
||||
int(24): "24 fps",
|
||||
float(29.97): "29.97 fps DF",
|
||||
int(30): "30 fps",
|
||||
int(50): "50 fps",
|
||||
float(59.94): "59.94 fps DF",
|
||||
int(60): "60 fps"
|
||||
}
|
||||
|
||||
match_key = min(fps_table.keys(), key=lambda x: abs(x - fps_num))
|
||||
|
||||
try:
|
||||
return fps_table[match_key]
|
||||
except KeyError as msg:
|
||||
raise KeyError((
|
||||
"Missing FPS key in conversion table. "
|
||||
"Following keys are available: {}".format(fps_table.keys())
|
||||
)) from msg
|
||||
|
||||
def _add_pythonpath(self):
|
||||
pythonpath = self.launch_context.env.get("PYTHONPATH")
|
||||
|
||||
|
|
|
|||
|
|
@ -87,41 +87,48 @@ class CreateShotClip(opfapi.Creator):
|
|||
"target": "tag",
|
||||
"toolTip": "Parents folder for shot root folder, Template filled with `Hierarchy Data` section", # noqa
|
||||
"order": 0},
|
||||
"useShotName": {
|
||||
"value": True,
|
||||
"type": "QCheckBox",
|
||||
"label": "Use Shot Name",
|
||||
"target": "ui",
|
||||
"toolTip": "Use name form Shot name clip attribute", # noqa
|
||||
"order": 1},
|
||||
"clipRename": {
|
||||
"value": False,
|
||||
"type": "QCheckBox",
|
||||
"label": "Rename clips",
|
||||
"target": "ui",
|
||||
"toolTip": "Renaming selected clips on fly", # noqa
|
||||
"order": 1},
|
||||
"order": 2},
|
||||
"clipName": {
|
||||
"value": "{sequence}{shot}",
|
||||
"type": "QLineEdit",
|
||||
"label": "Clip Name Template",
|
||||
"target": "ui",
|
||||
"toolTip": "template for creating shot namespaused for renaming (use rename: on)", # noqa
|
||||
"order": 2},
|
||||
"order": 3},
|
||||
"segmentIndex": {
|
||||
"value": True,
|
||||
"type": "QCheckBox",
|
||||
"label": "Segment index",
|
||||
"target": "ui",
|
||||
"toolTip": "Take number from segment index", # noqa
|
||||
"order": 3},
|
||||
"order": 4},
|
||||
"countFrom": {
|
||||
"value": 10,
|
||||
"type": "QSpinBox",
|
||||
"label": "Count sequence from",
|
||||
"target": "ui",
|
||||
"toolTip": "Set when the sequence number stafrom", # noqa
|
||||
"order": 4},
|
||||
"order": 5},
|
||||
"countSteps": {
|
||||
"value": 10,
|
||||
"type": "QSpinBox",
|
||||
"label": "Stepping number",
|
||||
"target": "ui",
|
||||
"toolTip": "What number is adding every new step", # noqa
|
||||
"order": 5},
|
||||
"order": 6},
|
||||
}
|
||||
},
|
||||
"hierarchyData": {
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ class LoadClip(opfapi.ClipLoader):
|
|||
# version_name = version.get("name", None)
|
||||
# colorspace = version_data.get("colorspace", None)
|
||||
# object_name = "{}_{}".format(name, namespace)
|
||||
# file = api.get_representation_path(representation).replace("\\", "/")
|
||||
# file = get_representation_path(representation).replace("\\", "/")
|
||||
# clip = track_item.source()
|
||||
|
||||
# # reconnect media to new path
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class ExtractSubsetResources(openpype.api.Extractor):
|
|||
"ext": "jpg",
|
||||
"xml_preset_file": "Jpeg (8-bit).xml",
|
||||
"xml_preset_dir": "",
|
||||
"export_type": "File Sequence",
|
||||
"colorspace_out": "Output - sRGB",
|
||||
"representation_add_range": False,
|
||||
"representation_tags": ["thumbnail"]
|
||||
|
|
@ -30,6 +31,7 @@ class ExtractSubsetResources(openpype.api.Extractor):
|
|||
"ext": "mov",
|
||||
"xml_preset_file": "Apple iPad (1920x1080).xml",
|
||||
"xml_preset_dir": "",
|
||||
"export_type": "Movie",
|
||||
"colorspace_out": "Output - Rec.709",
|
||||
"representation_add_range": True,
|
||||
"representation_tags": [
|
||||
|
|
@ -54,21 +56,35 @@ class ExtractSubsetResources(openpype.api.Extractor):
|
|||
):
|
||||
instance.data["representations"] = []
|
||||
|
||||
frame_start = instance.data["frameStart"]
|
||||
handle_start = instance.data["handleStart"]
|
||||
frame_start_handle = frame_start - handle_start
|
||||
source_first_frame = instance.data["sourceFirstFrame"]
|
||||
source_start_handles = instance.data["sourceStartH"]
|
||||
source_end_handles = instance.data["sourceEndH"]
|
||||
source_duration_handles = (
|
||||
source_end_handles - source_start_handles) + 1
|
||||
|
||||
# flame objects
|
||||
segment = instance.data["item"]
|
||||
sequence_clip = instance.context.data["flameSequence"]
|
||||
clip_data = instance.data["flameSourceClip"]
|
||||
clip = clip_data["PyClip"]
|
||||
|
||||
in_mark = (source_start_handles - source_first_frame) + 1
|
||||
out_mark = in_mark + source_duration_handles
|
||||
# segment's parent track name
|
||||
s_track_name = segment.parent.name.get_value()
|
||||
|
||||
# get configured workfile frame start/end (handles excluded)
|
||||
frame_start = instance.data["frameStart"]
|
||||
# get media source first frame
|
||||
source_first_frame = instance.data["sourceFirstFrame"]
|
||||
|
||||
# get timeline in/out of segment
|
||||
clip_in = instance.data["clipIn"]
|
||||
clip_out = instance.data["clipOut"]
|
||||
|
||||
# get handles value - take only the max from both
|
||||
handle_start = instance.data["handleStart"]
|
||||
handle_end = instance.data["handleStart"]
|
||||
handles = max(handle_start, handle_end)
|
||||
|
||||
# get media source range with handles
|
||||
source_end_handles = instance.data["sourceEndH"]
|
||||
source_start_handles = instance.data["sourceStartH"]
|
||||
source_end_handles = instance.data["sourceEndH"]
|
||||
|
||||
# create staging dir path
|
||||
staging_dir = self.staging_dir(instance)
|
||||
|
||||
# add default preset type for thumbnail and reviewable video
|
||||
|
|
@ -77,15 +93,52 @@ class ExtractSubsetResources(openpype.api.Extractor):
|
|||
export_presets = deepcopy(self.default_presets)
|
||||
export_presets.update(self.export_presets_mapping)
|
||||
|
||||
# with maintained duplication loop all presets
|
||||
with opfapi.maintained_object_duplication(clip) as duplclip:
|
||||
# loop all preset names and
|
||||
for unique_name, preset_config in export_presets.items():
|
||||
# loop all preset names and
|
||||
for unique_name, preset_config in export_presets.items():
|
||||
modify_xml_data = {}
|
||||
|
||||
# get all presets attributes
|
||||
preset_file = preset_config["xml_preset_file"]
|
||||
preset_dir = preset_config["xml_preset_dir"]
|
||||
export_type = preset_config["export_type"]
|
||||
repre_tags = preset_config["representation_tags"]
|
||||
color_out = preset_config["colorspace_out"]
|
||||
|
||||
# get frame range with handles for representation range
|
||||
frame_start_handle = frame_start - handle_start
|
||||
source_duration_handles = (
|
||||
source_end_handles - source_start_handles) + 1
|
||||
|
||||
# define in/out marks
|
||||
in_mark = (source_start_handles - source_first_frame) + 1
|
||||
out_mark = in_mark + source_duration_handles
|
||||
|
||||
# by default export source clips
|
||||
exporting_clip = clip
|
||||
|
||||
if export_type == "Sequence Publish":
|
||||
# change export clip to sequence
|
||||
exporting_clip = sequence_clip
|
||||
|
||||
# change in/out marks to timeline in/out
|
||||
in_mark = clip_in
|
||||
out_mark = clip_out
|
||||
|
||||
# add xml tags modifications
|
||||
modify_xml_data.update({
|
||||
"exportHandles": True,
|
||||
"nbHandles": handles,
|
||||
"startFrame": frame_start
|
||||
})
|
||||
|
||||
# with maintained duplication loop all presets
|
||||
with opfapi.maintained_object_duplication(
|
||||
exporting_clip) as duplclip:
|
||||
kwargs = {}
|
||||
preset_file = preset_config["xml_preset_file"]
|
||||
preset_dir = preset_config["xml_preset_dir"]
|
||||
repre_tags = preset_config["representation_tags"]
|
||||
color_out = preset_config["colorspace_out"]
|
||||
|
||||
if export_type == "Sequence Publish":
|
||||
# only keep visible layer where instance segment is child
|
||||
self.hide_other_tracks(duplclip, s_track_name)
|
||||
|
||||
# validate xml preset file is filled
|
||||
if preset_file == "":
|
||||
|
|
@ -108,10 +161,13 @@ class ExtractSubsetResources(openpype.api.Extractor):
|
|||
)
|
||||
|
||||
# create preset path
|
||||
preset_path = str(os.path.join(
|
||||
preset_orig_xml_path = str(os.path.join(
|
||||
preset_dir, preset_file
|
||||
))
|
||||
|
||||
preset_path = opfapi.modify_preset_file(
|
||||
preset_orig_xml_path, staging_dir, modify_xml_data)
|
||||
|
||||
# define kwargs based on preset type
|
||||
if "thumbnail" in unique_name:
|
||||
kwargs["thumb_frame_number"] = in_mark + (
|
||||
|
|
@ -122,6 +178,7 @@ class ExtractSubsetResources(openpype.api.Extractor):
|
|||
"out_mark": out_mark
|
||||
})
|
||||
|
||||
# get and make export dir paths
|
||||
export_dir_path = str(os.path.join(
|
||||
staging_dir, unique_name
|
||||
))
|
||||
|
|
@ -132,6 +189,7 @@ class ExtractSubsetResources(openpype.api.Extractor):
|
|||
export_dir_path, duplclip, preset_path, **kwargs)
|
||||
|
||||
extension = preset_config["ext"]
|
||||
|
||||
# create representation data
|
||||
representation_data = {
|
||||
"name": unique_name,
|
||||
|
|
@ -159,7 +217,12 @@ class ExtractSubsetResources(openpype.api.Extractor):
|
|||
# add files to represetation but add
|
||||
# imagesequence as list
|
||||
if (
|
||||
"movie_file" in preset_path
|
||||
# first check if path in files is not mov extension
|
||||
[
|
||||
f for f in files
|
||||
if os.path.splitext(f)[-1] == ".mov"
|
||||
]
|
||||
# then try if thumbnail is not in unique name
|
||||
or unique_name == "thumbnail"
|
||||
):
|
||||
representation_data["files"] = files.pop()
|
||||
|
|
@ -246,3 +309,19 @@ class ExtractSubsetResources(openpype.api.Extractor):
|
|||
)
|
||||
|
||||
return new_stage_dir, new_files_list
|
||||
|
||||
def hide_other_tracks(self, sequence_clip, track_name):
|
||||
"""Helper method used only if sequence clip is used
|
||||
|
||||
Args:
|
||||
sequence_clip (flame.Clip): sequence clip
|
||||
track_name (str): track name
|
||||
"""
|
||||
# create otio tracks and clips
|
||||
for ver in sequence_clip.versions:
|
||||
for track in ver.tracks:
|
||||
if len(track.segments) == 0 and track.hidden:
|
||||
continue
|
||||
|
||||
if track.name.get_value() != track_name:
|
||||
track.hidden = True
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
<fileType>Jpeg</fileType>
|
||||
<codec>923688</codec>
|
||||
<codecProfile></codecProfile>
|
||||
<namePattern><segment name></namePattern>
|
||||
<namePattern><shot name></namePattern>
|
||||
<compressionQuality>100</compressionQuality>
|
||||
<transferCharacteristic>2</transferCharacteristic>
|
||||
<colorimetricSpecification>4</colorimetricSpecification>
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
</sequence>
|
||||
<movie>
|
||||
<fileType>QuickTime</fileType>
|
||||
<namePattern><segment name></namePattern>
|
||||
<namePattern><shot name></namePattern>
|
||||
<yuvHeadroom>0</yuvHeadroom>
|
||||
<yuvColourSpace>PCS_709</yuvColourSpace>
|
||||
<operationalPattern>None</operationalPattern>
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
<targetVersion>2021</targetVersion>
|
||||
<pathSuffix>/profiles/.33622016/HDTV_720p_8Mbits.cdxprof</pathSuffix>
|
||||
</codecProfile>
|
||||
<namePattern><segment name>_<video codec></namePattern>
|
||||
<namePattern><shot name>_<video codec></namePattern>
|
||||
<compressionQuality>50</compressionQuality>
|
||||
<transferCharacteristic>2</transferCharacteristic>
|
||||
<colorimetricSpecification>4</colorimetricSpecification>
|
||||
|
|
@ -8,7 +8,7 @@ PLUGIN_DIR = os.path.dirname(os.path.dirname(__file__))
|
|||
EXPORT_PRESETS_DIR = os.path.join(PLUGIN_DIR, "export_preset")
|
||||
|
||||
CONFIG_DIR = os.path.join(os.path.expanduser(
|
||||
"~/.openpype"), "openpype_flame_to_ftrack")
|
||||
"~/.openpype"), "openpype_babypublisher")
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
|
@ -360,6 +360,8 @@ class FtrackComponentCreator:
|
|||
|
||||
|
||||
class FtrackEntityOperator:
|
||||
existing_tasks = []
|
||||
|
||||
def __init__(self, session, project_entity):
|
||||
self.session = session
|
||||
self.project_entity = project_entity
|
||||
|
|
@ -392,10 +394,7 @@ class FtrackEntityOperator:
|
|||
query = '{} where name is "{}" and project_id is "{}"'.format(
|
||||
type, name, self.project_entity["id"])
|
||||
|
||||
try:
|
||||
entity = session.query(query).one()
|
||||
except Exception:
|
||||
entity = None
|
||||
entity = session.query(query).first()
|
||||
|
||||
# if entity doesnt exist then create one
|
||||
if not entity:
|
||||
|
|
@ -430,10 +429,21 @@ class FtrackEntityOperator:
|
|||
return parents
|
||||
|
||||
def create_task(self, task_type, task_types, parent):
|
||||
existing_task = [
|
||||
_exising_tasks = [
|
||||
child for child in parent['children']
|
||||
if child.entity_type.lower() == 'task'
|
||||
if child['name'].lower() in task_type.lower()
|
||||
]
|
||||
|
||||
# add task into existing tasks if they are not already there
|
||||
for _t in _exising_tasks:
|
||||
if _t in self.existing_tasks:
|
||||
continue
|
||||
self.existing_tasks.append(_t)
|
||||
|
||||
existing_task = [
|
||||
task for task in self.existing_tasks
|
||||
if task['name'].lower() in task_type.lower()
|
||||
if task['parent'] == parent
|
||||
]
|
||||
|
||||
if existing_task:
|
||||
|
|
@ -445,4 +455,5 @@ class FtrackEntityOperator:
|
|||
})
|
||||
task["type"] = task_types[task_type]
|
||||
|
||||
self.existing_tasks.append(task)
|
||||
return task
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from PySide2 import QtWidgets, QtCore
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
import uiwidgets
|
||||
import app_utils
|
||||
|
|
@ -33,11 +33,12 @@ class MainWindow(QtWidgets.QWidget):
|
|||
self.panel_class.clear_temp_data()
|
||||
self.panel_class.close()
|
||||
clear_inner_modules()
|
||||
ftrack_lib.FtrackEntityOperator.existing_tasks = []
|
||||
# now the panel can be closed
|
||||
event.accept()
|
||||
|
||||
|
||||
class FlameToFtrackPanel(object):
|
||||
class FlameBabyPublisherPanel(object):
|
||||
session = None
|
||||
temp_data_dir = None
|
||||
processed_components = []
|
||||
|
|
@ -78,7 +79,7 @@ class FlameToFtrackPanel(object):
|
|||
|
||||
# creating ui
|
||||
self.window.setMinimumSize(1500, 600)
|
||||
self.window.setWindowTitle('Sequence Shots to Ftrack')
|
||||
self.window.setWindowTitle('OpenPype: Baby-publisher')
|
||||
self.window.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
|
||||
self.window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||
self.window.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
|
|
@ -469,10 +470,14 @@ class FlameToFtrackPanel(object):
|
|||
for sequence in self.selection:
|
||||
frame_rate = float(str(sequence.frame_rate)[:-4])
|
||||
for ver in sequence.versions:
|
||||
for tracks in ver.tracks:
|
||||
for segment in tracks.segments:
|
||||
for track in ver.tracks:
|
||||
if len(track.segments) == 0 and track.hidden:
|
||||
continue
|
||||
for segment in track.segments:
|
||||
print(segment.attributes)
|
||||
if str(segment.name)[1:-1] == "":
|
||||
if segment.name.get_value() == "":
|
||||
continue
|
||||
if segment.hidden.get_value() is True:
|
||||
continue
|
||||
# get clip frame duration
|
||||
record_duration = str(segment.record_duration)[1:-1]
|
||||
|
|
@ -492,11 +497,11 @@ class FlameToFtrackPanel(object):
|
|||
|
||||
# Add timeline segment to tree
|
||||
QtWidgets.QTreeWidgetItem(self.tree, [
|
||||
str(sequence.name)[1:-1], # seq
|
||||
str(segment.name)[1:-1], # shot
|
||||
sequence.name.get_value(), # seq name
|
||||
segment.shot_name.get_value(), # shot name
|
||||
str(clip_duration), # clip duration
|
||||
shot_description, # shot description
|
||||
str(segment.comment)[1:-1] # task description
|
||||
segment.comment.get_value() # task description
|
||||
]).setFlags(
|
||||
QtCore.Qt.ItemIsEditable
|
||||
| QtCore.Qt.ItemIsEnabled
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from PySide2 import QtWidgets, QtCore
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
|
||||
class FlameLabel(QtWidgets.QLabel):
|
||||
|
|
@ -16,10 +16,11 @@ def flame_panel_executor(selection):
|
|||
if "panel_app" in sys.modules.keys():
|
||||
print("panel_app module is already loaded")
|
||||
del sys.modules["panel_app"]
|
||||
import panel_app
|
||||
reload(panel_app) # noqa
|
||||
print("panel_app module removed from sys.modules")
|
||||
|
||||
import panel_app
|
||||
panel_app.FlameToFtrackPanel(selection)
|
||||
panel_app.FlameBabyPublisherPanel(selection)
|
||||
|
||||
|
||||
def scope_sequence(selection):
|
||||
|
|
@ -30,7 +31,7 @@ def scope_sequence(selection):
|
|||
def get_media_panel_custom_ui_actions():
|
||||
return [
|
||||
{
|
||||
"name": "OpenPype: Ftrack",
|
||||
"name": "OpenPype: Baby-publisher",
|
||||
"actions": [
|
||||
{
|
||||
"name": "Create Shots",
|
||||
|
|
@ -5,8 +5,8 @@ import contextlib
|
|||
|
||||
from Qt import QtGui
|
||||
|
||||
import avalon.api
|
||||
from avalon import io
|
||||
from openpype.pipeline import switch_container
|
||||
from .pipeline import get_current_comp, comp_lock_and_undo_chunk
|
||||
from openpype.api import (
|
||||
get_asset
|
||||
|
|
@ -167,7 +167,7 @@ def switch_item(container,
|
|||
assert representation, ("Could not find representation in the database "
|
||||
"with the name '%s'" % representation_name)
|
||||
|
||||
avalon.api.switch(container, representation)
|
||||
switch_container(container, representation)
|
||||
|
||||
return representation
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ import avalon.api
|
|||
from avalon.pipeline import AVALON_CONTAINER_ID
|
||||
|
||||
from openpype.api import Logger
|
||||
from openpype.pipeline import (
|
||||
LegacyCreator,
|
||||
register_loader_plugin_path,
|
||||
deregister_loader_plugin_path,
|
||||
)
|
||||
import openpype.hosts.fusion
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
|
@ -62,8 +67,8 @@ def install():
|
|||
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
||||
log.info("Registering Fusion plug-ins..")
|
||||
|
||||
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
register_loader_plugin_path(LOAD_PATH)
|
||||
avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH)
|
||||
|
||||
pyblish.api.register_callback(
|
||||
|
|
@ -86,8 +91,8 @@ def uninstall():
|
|||
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
|
||||
log.info("Deregistering Fusion plug-ins..")
|
||||
|
||||
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
deregister_loader_plugin_path(LOAD_PATH)
|
||||
avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH)
|
||||
avalon.api.deregister_plugin_path(
|
||||
avalon.api.InventoryAction, INVENTORY_PATH
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ import logging
|
|||
from Qt import QtWidgets, QtCore
|
||||
|
||||
import avalon.api
|
||||
from avalon import io, pipeline
|
||||
from avalon.vendor import qtawesome as qta
|
||||
from avalon import io
|
||||
import qtawesome as qta
|
||||
|
||||
from openpype import style
|
||||
from openpype.hosts.fusion import api
|
||||
from openpype.lib.avalon_context import get_workdir_from_session
|
||||
|
||||
log = logging.getLogger("Fusion Switch Shot")
|
||||
|
||||
|
|
@ -123,7 +124,7 @@ class App(QtWidgets.QWidget):
|
|||
|
||||
def _on_open_from_dir(self):
|
||||
|
||||
start_dir = self._get_context_directory()
|
||||
start_dir = get_workdir_from_session()
|
||||
comp_file, _ = QtWidgets.QFileDialog.getOpenFileName(
|
||||
self, "Choose comp", start_dir)
|
||||
|
||||
|
|
@ -157,17 +158,6 @@ class App(QtWidgets.QWidget):
|
|||
import colorbleed.scripts.fusion_switch_shot as switch_shot
|
||||
switch_shot.switch(asset_name=asset, filepath=file_name, new=True)
|
||||
|
||||
def _get_context_directory(self):
|
||||
|
||||
project = io.find_one({"type": "project",
|
||||
"name": avalon.api.Session["AVALON_PROJECT"]},
|
||||
projection={"config": True})
|
||||
|
||||
template = project["config"]["template"]["work"]
|
||||
dir = pipeline._format_work_template(template, avalon.api.Session)
|
||||
|
||||
return dir
|
||||
|
||||
def collect_slap_comps(self, directory):
|
||||
items = glob.glob("{}/*.comp".format(directory))
|
||||
return items
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import os
|
||||
|
||||
import openpype.api
|
||||
from openpype.pipeline import create
|
||||
from openpype.hosts.fusion.api import (
|
||||
get_current_comp,
|
||||
comp_lock_and_undo_chunk
|
||||
)
|
||||
|
||||
|
||||
class CreateOpenEXRSaver(openpype.api.Creator):
|
||||
class CreateOpenEXRSaver(create.LegacyCreator):
|
||||
|
||||
name = "openexrDefault"
|
||||
label = "Create OpenEXR Saver"
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
"""
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import load
|
||||
|
||||
|
||||
class FusionSetFrameRangeLoader(api.Loader):
|
||||
class FusionSetFrameRangeLoader(load.LoaderPlugin):
|
||||
"""Specific loader of Alembic for the avalon.animation family"""
|
||||
|
||||
families = ["animation",
|
||||
|
|
@ -39,7 +39,7 @@ class FusionSetFrameRangeLoader(api.Loader):
|
|||
lib.update_frame_range(start, end)
|
||||
|
||||
|
||||
class FusionSetFrameRangeWithHandlesLoader(api.Loader):
|
||||
class FusionSetFrameRangeWithHandlesLoader(load.LoaderPlugin):
|
||||
"""Specific loader of Alembic for the avalon.animation family"""
|
||||
|
||||
families = ["animation",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
import os
|
||||
import contextlib
|
||||
|
||||
from avalon import api, io
|
||||
from avalon import io
|
||||
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
from openpype.hosts.fusion.api import (
|
||||
imprint_container,
|
||||
get_current_comp,
|
||||
|
|
@ -117,7 +121,7 @@ def loader_shift(loader, frame, relative=True):
|
|||
return int(shift)
|
||||
|
||||
|
||||
class FusionLoadSequence(api.Loader):
|
||||
class FusionLoadSequence(load.LoaderPlugin):
|
||||
"""Load image sequence into Fusion"""
|
||||
|
||||
families = ["imagesequence", "review", "render"]
|
||||
|
|
@ -204,7 +208,7 @@ class FusionLoadSequence(api.Loader):
|
|||
assert tool.ID == "Loader", "Must be Loader"
|
||||
comp = tool.Comp()
|
||||
|
||||
root = os.path.dirname(api.get_representation_path(representation))
|
||||
root = os.path.dirname(get_representation_path(representation))
|
||||
path = self._get_first_image(root)
|
||||
|
||||
# Get start frame from version data
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ import logging
|
|||
|
||||
# Pipeline imports
|
||||
import avalon.api
|
||||
from avalon import io, pipeline
|
||||
from avalon import io
|
||||
|
||||
from openpype.lib import version_up
|
||||
from openpype.hosts.fusion import api
|
||||
from openpype.hosts.fusion.api import lib
|
||||
from openpype.lib.avalon_context import get_workdir_from_session
|
||||
|
||||
log = logging.getLogger("Update Slap Comp")
|
||||
|
||||
|
|
@ -44,16 +45,6 @@ def _format_version_folder(folder):
|
|||
return version_folder
|
||||
|
||||
|
||||
def _get_work_folder(session):
|
||||
"""Convenience function to get the work folder path of the current asset"""
|
||||
|
||||
# Get new filename, create path based on asset and work template
|
||||
template_work = self._project["config"]["template"]["work"]
|
||||
work_path = pipeline._format_work_template(template_work, session)
|
||||
|
||||
return os.path.normpath(work_path)
|
||||
|
||||
|
||||
def _get_fusion_instance():
|
||||
fusion = getattr(sys.modules["__main__"], "fusion", None)
|
||||
if fusion is None:
|
||||
|
|
@ -72,7 +63,7 @@ def _format_filepath(session):
|
|||
asset = session["AVALON_ASSET"]
|
||||
|
||||
# Save updated slap comp
|
||||
work_path = _get_work_folder(session)
|
||||
work_path = get_workdir_from_session(session)
|
||||
walk_to_dir = os.path.join(work_path, "scenes", "slapcomp")
|
||||
slapcomp_dir = os.path.abspath(walk_to_dir)
|
||||
|
||||
|
|
@ -112,7 +103,7 @@ def _update_savers(comp, session):
|
|||
None
|
||||
"""
|
||||
|
||||
new_work = _get_work_folder(session)
|
||||
new_work = get_workdir_from_session(session)
|
||||
renders = os.path.join(new_work, "renders")
|
||||
version_folder = _format_version_folder(renders)
|
||||
renders_version = os.path.join(renders, version_folder)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from Qt import QtWidgets
|
||||
from avalon.vendor import qtawesome
|
||||
import qtawesome
|
||||
from openpype.hosts.fusion.api import get_current_comp
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -575,7 +575,7 @@ replace_files = """function %s_replace_files(args)
|
|||
""" % (signature, signature)
|
||||
|
||||
|
||||
class ImageSequenceLoader(api.Loader):
|
||||
class ImageSequenceLoader(load.LoaderPlugin):
|
||||
"""Load images
|
||||
Stores the imported asset in a container named after the asset.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -272,8 +272,8 @@ function Client() {
|
|||
|
||||
app.avalonClient.send(
|
||||
{
|
||||
'module': 'avalon.api',
|
||||
'method': 'emit',
|
||||
'module': 'openpype.lib',
|
||||
'method': 'emit_event',
|
||||
'args': ['application.launched']
|
||||
}, false);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -361,7 +361,7 @@ def zip_and_move(source, destination):
|
|||
log.debug(f"Saved '{source}' to '{destination}'")
|
||||
|
||||
|
||||
def show(module_name):
|
||||
def show(tool_name):
|
||||
"""Call show on "module_name".
|
||||
|
||||
This allows to make a QApplication ahead of time and always "exec_" to
|
||||
|
|
@ -375,13 +375,6 @@ def show(module_name):
|
|||
# requests to be received properly.
|
||||
time.sleep(1)
|
||||
|
||||
# Get tool name from module name
|
||||
# TODO this is for backwards compatibility not sure if `TB_sceneOpened.js`
|
||||
# is automatically updated.
|
||||
# Previous javascript sent 'module_name' which contained whole tool import
|
||||
# string e.g. "avalon.tools.workfiles" now it should be only "workfiles"
|
||||
tool_name = module_name.split(".")[-1]
|
||||
|
||||
kwargs = {}
|
||||
if tool_name == "loader":
|
||||
kwargs["use_context"] = True
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@ import avalon.api
|
|||
from avalon.pipeline import AVALON_CONTAINER_ID
|
||||
|
||||
from openpype import lib
|
||||
from openpype.lib import register_event_callback
|
||||
from openpype.pipeline import (
|
||||
LegacyCreator,
|
||||
register_loader_plugin_path,
|
||||
deregister_loader_plugin_path,
|
||||
)
|
||||
import openpype.hosts.harmony
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
|
||||
|
|
@ -129,7 +135,7 @@ def check_inventory():
|
|||
harmony.send({"function": "PypeHarmony.message", "args": msg})
|
||||
|
||||
|
||||
def application_launch():
|
||||
def application_launch(event):
|
||||
"""Event that is executed after Harmony is launched."""
|
||||
# FIXME: This is breaking server <-> client communication.
|
||||
# It is now moved so it it manually called.
|
||||
|
|
@ -178,8 +184,8 @@ def install():
|
|||
|
||||
pyblish.api.register_host("harmony")
|
||||
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
register_loader_plugin_path(LOAD_PATH)
|
||||
avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH)
|
||||
log.info(PUBLISH_PATH)
|
||||
|
||||
# Register callbacks.
|
||||
|
|
@ -187,13 +193,13 @@ def install():
|
|||
"instanceToggled", on_pyblish_instance_toggled
|
||||
)
|
||||
|
||||
avalon.api.on("application.launched", application_launch)
|
||||
register_event_callback("application.launched", application_launch)
|
||||
|
||||
|
||||
def uninstall():
|
||||
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
|
||||
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
deregister_loader_plugin_path(LOAD_PATH)
|
||||
avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH)
|
||||
|
||||
|
||||
def on_pyblish_instance_toggled(instance, old_value, new_value):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import avalon.api
|
||||
from openpype.api import PypeCreatorMixin
|
||||
from openpype.pipeline import LegacyCreator
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
|
||||
|
||||
class Creator(PypeCreatorMixin, avalon.api.Creator):
|
||||
class Creator(LegacyCreator):
|
||||
"""Creator plugin to create instances in Harmony.
|
||||
|
||||
By default a Composite node is created to support any number of nodes in
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
from avalon import api
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
|
||||
sig = harmony.signature()
|
||||
|
|
@ -29,7 +32,7 @@ function %s(args)
|
|||
""" % (sig, sig)
|
||||
|
||||
|
||||
class ImportAudioLoader(api.Loader):
|
||||
class ImportAudioLoader(load.LoaderPlugin):
|
||||
"""Import audio."""
|
||||
|
||||
families = ["shot", "audio"]
|
||||
|
|
@ -37,7 +40,7 @@ class ImportAudioLoader(api.Loader):
|
|||
label = "Import Audio"
|
||||
|
||||
def load(self, context, name=None, namespace=None, data=None):
|
||||
wav_file = api.get_representation_path(context["representation"])
|
||||
wav_file = get_representation_path(context["representation"])
|
||||
harmony.send(
|
||||
{"function": func, "args": [context["subset"]["name"], wav_file]}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
import openpype.lib
|
||||
|
||||
|
|
@ -226,7 +229,7 @@ replace_files
|
|||
"""
|
||||
|
||||
|
||||
class BackgroundLoader(api.Loader):
|
||||
class BackgroundLoader(load.LoaderPlugin):
|
||||
"""Load images
|
||||
Stores the imported asset in a container named after the asset.
|
||||
"""
|
||||
|
|
@ -278,7 +281,7 @@ class BackgroundLoader(api.Loader):
|
|||
|
||||
def update(self, container, representation):
|
||||
|
||||
path = api.get_representation_path(representation)
|
||||
path = get_representation_path(representation)
|
||||
|
||||
with open(path) as json_file:
|
||||
data = json.load(json_file)
|
||||
|
|
@ -297,7 +300,7 @@ class BackgroundLoader(api.Loader):
|
|||
|
||||
bg_folder = os.path.dirname(path)
|
||||
|
||||
path = api.get_representation_path(representation)
|
||||
path = get_representation_path(representation)
|
||||
|
||||
print(container)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,15 @@ from pathlib import Path
|
|||
|
||||
import clique
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
import openpype.lib
|
||||
|
||||
|
||||
class ImageSequenceLoader(api.Loader):
|
||||
class ImageSequenceLoader(load.LoaderPlugin):
|
||||
"""Load image sequences.
|
||||
|
||||
Stores the imported asset in a container named after the asset.
|
||||
|
|
@ -79,7 +82,7 @@ class ImageSequenceLoader(api.Loader):
|
|||
self_name = self.__class__.__name__
|
||||
node = container.get("nodes").pop()
|
||||
|
||||
path = api.get_representation_path(representation)
|
||||
path = get_representation_path(representation)
|
||||
collections, remainder = clique.assemble(
|
||||
os.listdir(os.path.dirname(path))
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import os
|
||||
import shutil
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
|
||||
|
||||
class ImportPaletteLoader(api.Loader):
|
||||
class ImportPaletteLoader(load.LoaderPlugin):
|
||||
"""Import palettes."""
|
||||
|
||||
families = ["palette", "harmony.palette"]
|
||||
|
|
@ -31,7 +34,7 @@ class ImportPaletteLoader(api.Loader):
|
|||
scene_path = harmony.send(
|
||||
{"function": "scene.currentProjectPath"}
|
||||
)["result"]
|
||||
src = api.get_representation_path(representation)
|
||||
src = get_representation_path(representation)
|
||||
dst = os.path.join(
|
||||
scene_path,
|
||||
"palette-library",
|
||||
|
|
|
|||
|
|
@ -6,12 +6,15 @@ import os
|
|||
import shutil
|
||||
import uuid
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
import openpype.lib
|
||||
|
||||
|
||||
class TemplateLoader(api.Loader):
|
||||
class TemplateLoader(load.LoaderPlugin):
|
||||
"""Load Harmony template as container.
|
||||
|
||||
.. todo::
|
||||
|
|
@ -38,7 +41,7 @@ class TemplateLoader(api.Loader):
|
|||
# Load template.
|
||||
self_name = self.__class__.__name__
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
zip_file = api.get_representation_path(context["representation"])
|
||||
zip_file = get_representation_path(context["representation"])
|
||||
template_path = os.path.join(temp_dir, "temp.tpl")
|
||||
with zipfile.ZipFile(zip_file, "r") as zip_ref:
|
||||
zip_ref.extractall(template_path)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@ import zipfile
|
|||
import os
|
||||
import shutil
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
|
||||
|
||||
class ImportTemplateLoader(api.Loader):
|
||||
class ImportTemplateLoader(load.LoaderPlugin):
|
||||
"""Import templates."""
|
||||
|
||||
families = ["harmony.template", "workfile"]
|
||||
|
|
@ -17,7 +20,7 @@ class ImportTemplateLoader(api.Loader):
|
|||
def load(self, context, name=None, namespace=None, data=None):
|
||||
# Import template.
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
zip_file = api.get_representation_path(context["representation"])
|
||||
zip_file = get_representation_path(context["representation"])
|
||||
template_path = os.path.join(temp_dir, "temp.tpl")
|
||||
with zipfile.ZipFile(zip_file, "r") as zip_ref:
|
||||
zip_ref.extractall(template_path)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from pathlib import Path
|
|||
import attr
|
||||
from avalon import api
|
||||
|
||||
from openpype.lib import get_formatted_current_time
|
||||
import openpype.lib.abstract_collect_render
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
from openpype.lib.abstract_collect_render import RenderInstance
|
||||
|
|
@ -138,7 +139,7 @@ class CollectFarmRender(openpype.lib.abstract_collect_render.
|
|||
|
||||
render_instance = HarmonyRenderInstance(
|
||||
version=version,
|
||||
time=api.time(),
|
||||
time=get_formatted_current_time(),
|
||||
source=context.data["currentFile"],
|
||||
label=node.split("/")[1],
|
||||
subset=subset_name,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<error id="main">
|
||||
<title>Missing audio file</title>
|
||||
<description>
|
||||
## Cannot locate linked audio file
|
||||
|
||||
Audio file at {audio_url} cannot be found.
|
||||
|
||||
### How to repair?
|
||||
|
||||
Copy audio file to the highlighted location or remove audio link in the workfile.
|
||||
</description>
|
||||
</error>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<error id="main">
|
||||
<title>Subset context</title>
|
||||
<description>
|
||||
## Invalid subset context
|
||||
|
||||
Asset name found '{found}' in subsets, expected '{expected}'.
|
||||
|
||||
### How to repair?
|
||||
|
||||
You can fix this with `Repair` button on the right. This will use '{expected}' asset name and overwrite '{found}' asset name in scene metadata.
|
||||
|
||||
After that restart `Publish` with a `Reload button`.
|
||||
|
||||
If this is unwanted, close workfile and open again, that way different asset value would be used for context information.
|
||||
</description>
|
||||
<detail>
|
||||
### __Detailed Info__ (optional)
|
||||
|
||||
This might happen if you are reuse old workfile and open it in different context.
|
||||
(Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.)
|
||||
</detail>
|
||||
</error>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<error id="main">
|
||||
<title>Scene setting</title>
|
||||
<description>
|
||||
## Invalid scene setting found
|
||||
|
||||
One of the settings in a scene doesn't match to asset settings in database.
|
||||
|
||||
{invalid_setting_str}
|
||||
|
||||
### How to repair?
|
||||
|
||||
Change values for {invalid_keys_str} in the scene OR change them in the asset database if they are wrong there.
|
||||
</description>
|
||||
<detail>
|
||||
### __Detailed Info__ (optional)
|
||||
|
||||
This error is shown when for example resolution in the scene doesn't match to resolution set on the asset in the database.
|
||||
Either value in the database or in the scene is wrong.
|
||||
</detail>
|
||||
</error>
|
||||
<error id="file_not_found">
|
||||
<title>Scene file doesn't exist</title>
|
||||
<description>
|
||||
## Scene file doesn't exist
|
||||
|
||||
Collected scene {scene_url} doesn't exist.
|
||||
|
||||
### How to repair?
|
||||
|
||||
Re-save file, start publish from the beginning again.
|
||||
</description>
|
||||
</error>
|
||||
</root>
|
||||
|
|
@ -4,6 +4,8 @@ import pyblish.api
|
|||
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
|
||||
from openpype.pipeline import PublishXmlValidationError
|
||||
|
||||
|
||||
class ValidateAudio(pyblish.api.InstancePlugin):
|
||||
"""Ensures that there is an audio file in the scene.
|
||||
|
|
@ -42,4 +44,9 @@ class ValidateAudio(pyblish.api.InstancePlugin):
|
|||
|
||||
msg = "You are missing audio file:\n{}".format(audio_path)
|
||||
|
||||
assert os.path.isfile(audio_path), msg
|
||||
formatting_data = {
|
||||
"audio_url": audio_path
|
||||
}
|
||||
if os.path.isfile(audio_path):
|
||||
raise PublishXmlValidationError(self, msg,
|
||||
formatting_data=formatting_data)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import os
|
|||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline import PublishXmlValidationError
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
|
||||
|
||||
|
|
@ -45,4 +46,11 @@ class ValidateInstance(pyblish.api.InstancePlugin):
|
|||
"Instance asset is not the same as current asset:"
|
||||
f"\nInstance: {instance_asset}\nCurrent: {current_asset}"
|
||||
)
|
||||
assert instance_asset == current_asset, msg
|
||||
|
||||
formatting_data = {
|
||||
"found": instance_asset,
|
||||
"expected": current_asset
|
||||
}
|
||||
if instance_asset != current_asset:
|
||||
raise PublishXmlValidationError(self, msg,
|
||||
formatting_data=formatting_data)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import re
|
|||
import pyblish.api
|
||||
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
import openpype.hosts.harmony
|
||||
from openpype.pipeline import PublishXmlValidationError
|
||||
|
||||
|
||||
class ValidateSceneSettingsRepair(pyblish.api.Action):
|
||||
|
|
@ -19,12 +19,12 @@ class ValidateSceneSettingsRepair(pyblish.api.Action):
|
|||
|
||||
def process(self, context, plugin):
|
||||
"""Repair action entry point."""
|
||||
expected = openpype.hosts.harmony.api.get_asset_settings()
|
||||
expected = harmony.get_asset_settings()
|
||||
asset_settings = _update_frames(dict.copy(expected))
|
||||
asset_settings["frameStart"] = 1
|
||||
asset_settings["frameEnd"] = asset_settings["frameEnd"] + \
|
||||
asset_settings["handleEnd"]
|
||||
openpype.hosts.harmony.api.set_scene_settings(asset_settings)
|
||||
harmony.set_scene_settings(asset_settings)
|
||||
if not os.path.exists(context.data["scenePath"]):
|
||||
self.log.info("correcting scene name")
|
||||
scene_dir = os.path.dirname(context.data["currentFile"])
|
||||
|
|
@ -55,7 +55,7 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin):
|
|||
|
||||
def process(self, instance):
|
||||
"""Plugin entry point."""
|
||||
expected_settings = openpype.hosts.harmony.api.get_asset_settings()
|
||||
expected_settings = harmony.get_asset_settings()
|
||||
self.log.info("scene settings from DB:".format(expected_settings))
|
||||
|
||||
expected_settings = _update_frames(dict.copy(expected_settings))
|
||||
|
|
@ -102,13 +102,13 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin):
|
|||
self.log.debug("current scene settings {}".format(current_settings))
|
||||
|
||||
invalid_settings = []
|
||||
invalid_keys = set()
|
||||
for key, value in expected_settings.items():
|
||||
if value != current_settings[key]:
|
||||
invalid_settings.append({
|
||||
"name": key,
|
||||
"expected": value,
|
||||
"current": current_settings[key]
|
||||
})
|
||||
invalid_settings.append(
|
||||
"{} expected: {} found: {}".format(key, value,
|
||||
current_settings[key]))
|
||||
invalid_keys.add(key)
|
||||
|
||||
if ((expected_settings["handleStart"]
|
||||
or expected_settings["handleEnd"])
|
||||
|
|
@ -120,10 +120,30 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin):
|
|||
msg = "Found invalid settings:\n{}".format(
|
||||
json.dumps(invalid_settings, sort_keys=True, indent=4)
|
||||
)
|
||||
assert not invalid_settings, msg
|
||||
assert os.path.exists(instance.context.data.get("scenePath")), (
|
||||
"Scene file not found (saved under wrong name)"
|
||||
)
|
||||
|
||||
if invalid_settings:
|
||||
invalid_keys_str = ",".join(invalid_keys)
|
||||
break_str = "<br/>"
|
||||
invalid_setting_str = "<b>Found invalid settings:</b><br/>{}".\
|
||||
format(break_str.join(invalid_settings))
|
||||
|
||||
formatting_data = {
|
||||
"invalid_setting_str": invalid_setting_str,
|
||||
"invalid_keys_str": invalid_keys_str
|
||||
}
|
||||
raise PublishXmlValidationError(self, msg,
|
||||
formatting_data=formatting_data)
|
||||
|
||||
scene_url = instance.context.data.get("scenePath")
|
||||
if not os.path.exists(scene_url):
|
||||
msg = "Scene file {} not found (saved under wrong name)".format(
|
||||
scene_url
|
||||
)
|
||||
formatting_data = {
|
||||
"scene_url": scene_url
|
||||
}
|
||||
raise PublishXmlValidationError(self, msg, key="file_not_found",
|
||||
formatting_data=formatting_data)
|
||||
|
||||
|
||||
def _update_frames(expected_settings):
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import os
|
||||
import hiero.core.events
|
||||
import avalon.api as avalon
|
||||
from openpype.api import Logger
|
||||
from .lib import (
|
||||
sync_avalon_data_to_workfile,
|
||||
launch_workfiles_app,
|
||||
selection_changed_timeline,
|
||||
before_project_save
|
||||
before_project_save,
|
||||
register_event_callback
|
||||
)
|
||||
from .tags import add_tags_to_workfile
|
||||
from .menu import update_menu_task_label
|
||||
|
|
@ -126,5 +126,5 @@ def register_events():
|
|||
"""
|
||||
|
||||
# if task changed then change notext of hiero
|
||||
avalon.on("taskChanged", update_menu_task_label)
|
||||
register_event_callback("taskChanged", update_menu_task_label)
|
||||
log.info("Installed event callback for 'taskChanged'..")
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ self = sys.modules[__name__]
|
|||
self._change_context_menu = None
|
||||
|
||||
|
||||
def update_menu_task_label(*args):
|
||||
def update_menu_task_label():
|
||||
"""Update the task label in Avalon menu to current session"""
|
||||
|
||||
object_name = self._change_context_menu
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@ from avalon import api as avalon
|
|||
from avalon import schema
|
||||
from pyblish import api as pyblish
|
||||
from openpype.api import Logger
|
||||
from openpype.pipeline import (
|
||||
LegacyCreator,
|
||||
register_loader_plugin_path,
|
||||
deregister_loader_plugin_path,
|
||||
)
|
||||
from openpype.tools.utils import host_tools
|
||||
from . import lib, menu, events
|
||||
|
||||
|
|
@ -44,8 +49,8 @@ def install():
|
|||
log.info("Registering Hiero plug-ins..")
|
||||
pyblish.register_host("hiero")
|
||||
pyblish.register_plugin_path(PUBLISH_PATH)
|
||||
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
register_loader_plugin_path(LOAD_PATH)
|
||||
avalon.register_plugin_path(LegacyCreator, CREATE_PATH)
|
||||
avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
|
||||
|
||||
# register callback for switching publishable
|
||||
|
|
@ -66,8 +71,8 @@ def uninstall():
|
|||
log.info("Deregistering Hiero plug-ins..")
|
||||
pyblish.deregister_host("hiero")
|
||||
pyblish.deregister_plugin_path(PUBLISH_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
deregister_loader_plugin_path(LOAD_PATH)
|
||||
avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH)
|
||||
|
||||
# register callback for switching publishable
|
||||
pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
import re
|
||||
import os
|
||||
import hiero
|
||||
from Qt import QtWidgets, QtCore
|
||||
from avalon.vendor import qargparse
|
||||
import avalon.api as avalon
|
||||
import openpype.api as openpype
|
||||
from . import lib
|
||||
import re
|
||||
from copy import deepcopy
|
||||
|
||||
import hiero
|
||||
|
||||
from Qt import QtWidgets, QtCore
|
||||
import qargparse
|
||||
|
||||
import openpype.api as openpype
|
||||
from openpype.pipeline import LoaderPlugin, LegacyCreator
|
||||
from . import lib
|
||||
|
||||
log = openpype.Logger().get_logger(__name__)
|
||||
|
||||
|
||||
|
|
@ -303,7 +306,7 @@ def get_reference_node_parents(ref):
|
|||
return parents
|
||||
|
||||
|
||||
class SequenceLoader(avalon.Loader):
|
||||
class SequenceLoader(LoaderPlugin):
|
||||
"""A basic SequenceLoader for Resolve
|
||||
|
||||
This will implement the basic behavior for a loader to inherit from that
|
||||
|
|
@ -589,7 +592,7 @@ class ClipLoader:
|
|||
return track_item
|
||||
|
||||
|
||||
class Creator(openpype.Creator):
|
||||
class Creator(LegacyCreator):
|
||||
"""Creator class wrapper
|
||||
"""
|
||||
clip_color = "Purple"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from avalon import io, api
|
||||
from avalon import io
|
||||
from openpype.pipeline import get_representation_path
|
||||
import openpype.hosts.hiero.api as phiero
|
||||
# from openpype.hosts.hiero.api import plugin, lib
|
||||
# reload(lib)
|
||||
|
|
@ -112,7 +113,7 @@ class LoadClip(phiero.SequenceLoader):
|
|||
version_name = version.get("name", None)
|
||||
colorspace = version_data.get("colorspace", None)
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
file = api.get_representation_path(representation).replace("\\", "/")
|
||||
file = get_representation_path(representation).replace("\\", "/")
|
||||
clip = track_item.source()
|
||||
|
||||
# reconnect media to new path
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ from .lib import (
|
|||
lsattrs,
|
||||
read,
|
||||
|
||||
maintained_selection,
|
||||
unique_name
|
||||
maintained_selection
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -51,8 +50,7 @@ __all__ = [
|
|||
"lsattrs",
|
||||
"read",
|
||||
|
||||
"maintained_selection",
|
||||
"unique_name"
|
||||
"maintained_selection"
|
||||
]
|
||||
|
||||
# Backwards API compatibility
|
||||
|
|
|
|||
|
|
@ -99,65 +99,6 @@ def get_id_required_nodes():
|
|||
return list(nodes)
|
||||
|
||||
|
||||
def get_additional_data(container):
|
||||
"""Not implemented yet!"""
|
||||
return container
|
||||
|
||||
|
||||
def set_parameter_callback(node, parameter, language, callback):
|
||||
"""Link a callback to a parameter of a node
|
||||
|
||||
Args:
|
||||
node(hou.Node): instance of the nodee
|
||||
parameter(str): name of the parameter
|
||||
language(str): name of the language, e.g.: python
|
||||
callback(str): command which needs to be triggered
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
"""
|
||||
|
||||
template_grp = node.parmTemplateGroup()
|
||||
template = template_grp.find(parameter)
|
||||
if not template:
|
||||
return
|
||||
|
||||
script_language = (hou.scriptLanguage.Python if language == "python" else
|
||||
hou.scriptLanguage.Hscript)
|
||||
|
||||
template.setScriptCallbackLanguage(script_language)
|
||||
template.setScriptCallback(callback)
|
||||
|
||||
template.setTags({"script_callback": callback,
|
||||
"script_callback_language": language.lower()})
|
||||
|
||||
# Replace the existing template with the adjusted one
|
||||
template_grp.replace(parameter, template)
|
||||
|
||||
node.setParmTemplateGroup(template_grp)
|
||||
|
||||
|
||||
def set_parameter_callbacks(node, parameter_callbacks):
|
||||
"""Set callbacks for multiple parameters of a node
|
||||
|
||||
Args:
|
||||
node(hou.Node): instance of a hou.Node
|
||||
parameter_callbacks(dict): collection of parameter and callback data
|
||||
example: {"active" :
|
||||
{"language": "python",
|
||||
"callback": "print('hello world)'"}
|
||||
}
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
for parameter, data in parameter_callbacks.items():
|
||||
language = data["language"]
|
||||
callback = data["callback"]
|
||||
|
||||
set_parameter_callback(node, parameter, language, callback)
|
||||
|
||||
|
||||
def get_output_parameter(node):
|
||||
"""Return the render output parameter name of the given node
|
||||
|
||||
|
|
@ -189,19 +130,6 @@ def get_output_parameter(node):
|
|||
raise TypeError("Node type '%s' not supported" % node_type)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def attribute_values(node, data):
|
||||
|
||||
previous_attrs = {key: node.parm(key).eval() for key in data.keys()}
|
||||
try:
|
||||
node.setParms(data)
|
||||
yield
|
||||
except Exception as exc:
|
||||
pass
|
||||
finally:
|
||||
node.setParms(previous_attrs)
|
||||
|
||||
|
||||
def set_scene_fps(fps):
|
||||
hou.setFps(fps)
|
||||
|
||||
|
|
@ -349,10 +277,6 @@ def render_rop(ropnode):
|
|||
raise RuntimeError("Render failed: {0}".format(exc))
|
||||
|
||||
|
||||
def children_as_string(node):
|
||||
return [c.name() for c in node.children()]
|
||||
|
||||
|
||||
def imprint(node, data):
|
||||
"""Store attributes with value on a node
|
||||
|
||||
|
|
@ -473,53 +397,6 @@ def read(node):
|
|||
parameter in node.spareParms()}
|
||||
|
||||
|
||||
def unique_name(name, format="%03d", namespace="", prefix="", suffix="",
|
||||
separator="_"):
|
||||
"""Return unique `name`
|
||||
|
||||
The function takes into consideration an optional `namespace`
|
||||
and `suffix`. The suffix is included in evaluating whether a
|
||||
name exists - such as `name` + "_GRP" - but isn't included
|
||||
in the returned value.
|
||||
|
||||
If a namespace is provided, only names within that namespace
|
||||
are considered when evaluating whether the name is unique.
|
||||
|
||||
Arguments:
|
||||
format (str, optional): The `name` is given a number, this determines
|
||||
how this number is formatted. Defaults to a padding of 2.
|
||||
E.g. my_name01, my_name02.
|
||||
namespace (str, optional): Only consider names within this namespace.
|
||||
suffix (str, optional): Only consider names with this suffix.
|
||||
|
||||
Example:
|
||||
>>> name = hou.node("/obj").createNode("geo", name="MyName")
|
||||
>>> assert hou.node("/obj/MyName")
|
||||
True
|
||||
>>> unique = unique_name(name)
|
||||
>>> assert hou.node("/obj/{}".format(unique))
|
||||
False
|
||||
|
||||
"""
|
||||
|
||||
iteration = 1
|
||||
|
||||
parts = [prefix, name, format % iteration, suffix]
|
||||
if namespace:
|
||||
parts.insert(0, namespace)
|
||||
|
||||
unique = separator.join(parts)
|
||||
children = children_as_string(hou.node("/obj"))
|
||||
while unique in children:
|
||||
iteration += 1
|
||||
unique = separator.join(parts)
|
||||
|
||||
if suffix:
|
||||
return unique[:-len(suffix)]
|
||||
|
||||
return unique
|
||||
|
||||
|
||||
@contextmanager
|
||||
def maintained_selection():
|
||||
"""Maintain selection during context
|
||||
|
|
@ -542,3 +419,37 @@ def maintained_selection():
|
|||
if previous_selection:
|
||||
for node in previous_selection:
|
||||
node.setSelected(on=True)
|
||||
|
||||
|
||||
def reset_framerange():
|
||||
"""Set frame range to current asset"""
|
||||
|
||||
asset_name = api.Session["AVALON_ASSET"]
|
||||
asset = io.find_one({"name": asset_name, "type": "asset"})
|
||||
|
||||
frame_start = asset["data"].get("frameStart")
|
||||
frame_end = asset["data"].get("frameEnd")
|
||||
# Backwards compatibility
|
||||
if frame_start is None or frame_end is None:
|
||||
frame_start = asset["data"].get("edit_in")
|
||||
frame_end = asset["data"].get("edit_out")
|
||||
|
||||
if frame_start is None or frame_end is None:
|
||||
log.warning("No edit information found for %s" % asset_name)
|
||||
return
|
||||
|
||||
handles = asset["data"].get("handles") or 0
|
||||
handle_start = asset["data"].get("handleStart")
|
||||
if handle_start is None:
|
||||
handle_start = handles
|
||||
|
||||
handle_end = asset["data"].get("handleEnd")
|
||||
if handle_end is None:
|
||||
handle_end = handles
|
||||
|
||||
frame_start -= int(handle_start)
|
||||
frame_end += int(handle_end)
|
||||
|
||||
hou.playbar.setFrameRange(frame_start, frame_end)
|
||||
hou.playbar.setPlaybackRange(frame_start, frame_end)
|
||||
hou.setFrame(frame_start)
|
||||
|
|
|
|||
|
|
@ -4,17 +4,24 @@ import logging
|
|||
import contextlib
|
||||
|
||||
import hou
|
||||
import hdefereval
|
||||
|
||||
import pyblish.api
|
||||
import avalon.api
|
||||
from avalon.pipeline import AVALON_CONTAINER_ID
|
||||
from avalon.lib import find_submodule
|
||||
|
||||
from openpype.pipeline import (
|
||||
LegacyCreator,
|
||||
register_loader_plugin_path,
|
||||
)
|
||||
import openpype.hosts.houdini
|
||||
from openpype.hosts.houdini.api import lib
|
||||
|
||||
from openpype.lib import (
|
||||
any_outdated
|
||||
register_event_callback,
|
||||
emit_event,
|
||||
any_outdated,
|
||||
)
|
||||
|
||||
from .lib import get_asset_fps
|
||||
|
|
@ -46,15 +53,15 @@ def install():
|
|||
pyblish.api.register_host("hpython")
|
||||
|
||||
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
|
||||
avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH)
|
||||
register_loader_plugin_path(LOAD_PATH)
|
||||
avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH)
|
||||
|
||||
log.info("Installing callbacks ... ")
|
||||
# avalon.on("init", on_init)
|
||||
avalon.api.before("save", before_save)
|
||||
avalon.api.on("save", on_save)
|
||||
avalon.api.on("open", on_open)
|
||||
avalon.api.on("new", on_new)
|
||||
# register_event_callback("init", on_init)
|
||||
register_event_callback("before.save", before_save)
|
||||
register_event_callback("save", on_save)
|
||||
register_event_callback("open", on_open)
|
||||
register_event_callback("new", on_new)
|
||||
|
||||
pyblish.api.register_callback(
|
||||
"instanceToggled", on_pyblish_instance_toggled
|
||||
|
|
@ -66,9 +73,10 @@ def install():
|
|||
|
||||
sys.path.append(hou_pythonpath)
|
||||
|
||||
# Set asset FPS for the empty scene directly after launch of Houdini
|
||||
# so it initializes into the correct scene FPS
|
||||
_set_asset_fps()
|
||||
# Set asset settings for the empty scene directly after launch of Houdini
|
||||
# so it initializes into the correct scene FPS, Frame Range, etc.
|
||||
# todo: make sure this doesn't trigger when opening with last workfile
|
||||
_set_context_settings()
|
||||
|
||||
|
||||
def uninstall():
|
||||
|
|
@ -99,13 +107,13 @@ def _register_callbacks():
|
|||
|
||||
def on_file_event_callback(event):
|
||||
if event == hou.hipFileEventType.AfterLoad:
|
||||
avalon.api.emit("open", [event])
|
||||
emit_event("open")
|
||||
elif event == hou.hipFileEventType.AfterSave:
|
||||
avalon.api.emit("save", [event])
|
||||
emit_event("save")
|
||||
elif event == hou.hipFileEventType.BeforeSave:
|
||||
avalon.api.emit("before_save", [event])
|
||||
emit_event("before.save")
|
||||
elif event == hou.hipFileEventType.AfterClear:
|
||||
avalon.api.emit("new", [event])
|
||||
emit_event("new")
|
||||
|
||||
|
||||
def get_main_window():
|
||||
|
|
@ -227,11 +235,11 @@ def ls():
|
|||
yield data
|
||||
|
||||
|
||||
def before_save(*args):
|
||||
def before_save():
|
||||
return lib.validate_fps()
|
||||
|
||||
|
||||
def on_save(*args):
|
||||
def on_save():
|
||||
|
||||
log.info("Running callback on save..")
|
||||
|
||||
|
|
@ -240,7 +248,7 @@ def on_save(*args):
|
|||
lib.set_id(node, new_id, overwrite=False)
|
||||
|
||||
|
||||
def on_open(*args):
|
||||
def on_open():
|
||||
|
||||
if not hou.isUIAvailable():
|
||||
log.debug("Batch mode detected, ignoring `on_open` callbacks..")
|
||||
|
|
@ -277,20 +285,51 @@ def on_open(*args):
|
|||
dialog.show()
|
||||
|
||||
|
||||
def on_new(_):
|
||||
def on_new():
|
||||
"""Set project resolution and fps when create a new file"""
|
||||
|
||||
if hou.hipFile.isLoadingHipFile():
|
||||
# This event also triggers when Houdini opens a file due to the
|
||||
# new event being registered to 'afterClear'. As such we can skip
|
||||
# 'new' logic if the user is opening a file anyway
|
||||
log.debug("Skipping on new callback due to scene being opened.")
|
||||
return
|
||||
|
||||
log.info("Running callback on new..")
|
||||
_set_asset_fps()
|
||||
_set_context_settings()
|
||||
|
||||
# It seems that the current frame always gets reset to frame 1 on
|
||||
# new scene. So we enforce current frame to be at the start of the playbar
|
||||
# with execute deferred
|
||||
def _enforce_start_frame():
|
||||
start = hou.playbar.playbackRange()[0]
|
||||
hou.setFrame(start)
|
||||
|
||||
hdefereval.executeDeferred(_enforce_start_frame)
|
||||
|
||||
|
||||
def _set_asset_fps():
|
||||
"""Set Houdini scene FPS to the default required for current asset"""
|
||||
def _set_context_settings():
|
||||
"""Apply the project settings from the project definition
|
||||
|
||||
Settings can be overwritten by an asset if the asset.data contains
|
||||
any information regarding those settings.
|
||||
|
||||
Examples of settings:
|
||||
fps
|
||||
resolution
|
||||
renderer
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# Set new scene fps
|
||||
fps = get_asset_fps()
|
||||
print("Setting scene FPS to %i" % fps)
|
||||
lib.set_scene_fps(fps)
|
||||
|
||||
lib.reset_framerange()
|
||||
|
||||
|
||||
def on_pyblish_instance_toggled(instance, new_value, old_value):
|
||||
"""Toggle saver tool passthrough states on instance toggles."""
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@
|
|||
"""Houdini specific Avalon/Pyblish plugin definitions."""
|
||||
import sys
|
||||
import six
|
||||
import avalon.api
|
||||
from avalon.api import CreatorError
|
||||
|
||||
import hou
|
||||
from openpype.api import PypeCreatorMixin
|
||||
from openpype.pipeline import (
|
||||
CreatorError,
|
||||
LegacyCreator
|
||||
)
|
||||
from .lib import imprint
|
||||
|
||||
|
||||
|
|
@ -14,7 +15,7 @@ class OpenPypeCreatorError(CreatorError):
|
|||
pass
|
||||
|
||||
|
||||
class Creator(PypeCreatorMixin, avalon.api.Creator):
|
||||
class Creator(LegacyCreator):
|
||||
"""Creator plugin to create instances in Houdini
|
||||
|
||||
To support the wide range of node types for render output (Alembic, VDB,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
"""
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import load
|
||||
|
||||
|
||||
class SetFrameRangeLoader(api.Loader):
|
||||
class SetFrameRangeLoader(load.LoaderPlugin):
|
||||
"""Set Houdini frame range"""
|
||||
|
||||
families = [
|
||||
|
|
@ -29,8 +29,8 @@ class SetFrameRangeLoader(api.Loader):
|
|||
version = context["version"]
|
||||
version_data = version.get("data", {})
|
||||
|
||||
start = version_data.get("startFrame", None)
|
||||
end = version_data.get("endFrame", None)
|
||||
start = version_data.get("frameStart", None)
|
||||
end = version_data.get("frameEnd", None)
|
||||
|
||||
if start is None or end is None:
|
||||
print(
|
||||
|
|
@ -43,7 +43,7 @@ class SetFrameRangeLoader(api.Loader):
|
|||
hou.playbar.setPlaybackRange(start, end)
|
||||
|
||||
|
||||
class SetFrameRangeWithHandlesLoader(api.Loader):
|
||||
class SetFrameRangeWithHandlesLoader(load.LoaderPlugin):
|
||||
"""Set Maya frame range including pre- and post-handles"""
|
||||
|
||||
families = [
|
||||
|
|
@ -67,8 +67,8 @@ class SetFrameRangeWithHandlesLoader(api.Loader):
|
|||
version = context["version"]
|
||||
version_data = version.get("data", {})
|
||||
|
||||
start = version_data.get("startFrame", None)
|
||||
end = version_data.get("endFrame", None)
|
||||
start = version_data.get("frameStart", None)
|
||||
end = version_data.get("frameEnd", None)
|
||||
|
||||
if start is None or end is None:
|
||||
print(
|
||||
|
|
@ -78,9 +78,8 @@ class SetFrameRangeWithHandlesLoader(api.Loader):
|
|||
return
|
||||
|
||||
# Include handles
|
||||
handles = version_data.get("handles", 0)
|
||||
start -= handles
|
||||
end += handles
|
||||
start -= version_data.get("handleStart", 0)
|
||||
end += version_data.get("handleEnd", 0)
|
||||
|
||||
hou.playbar.setFrameRange(start, end)
|
||||
hou.playbar.setPlaybackRange(start, end)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import os
|
||||
from avalon import api
|
||||
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
from openpype.hosts.houdini.api import pipeline
|
||||
|
||||
|
||||
class AbcLoader(api.Loader):
|
||||
class AbcLoader(load.LoaderPlugin):
|
||||
"""Specific loader of Alembic for the avalon.animation family"""
|
||||
|
||||
families = ["model", "animation", "pointcache", "gpuCache"]
|
||||
|
|
@ -90,7 +92,7 @@ class AbcLoader(api.Loader):
|
|||
return
|
||||
|
||||
# Update the file path
|
||||
file_path = api.get_representation_path(representation)
|
||||
file_path = get_representation_path(representation)
|
||||
file_path = file_path.replace("\\", "/")
|
||||
|
||||
alembic_node.setParms({"fileName": file_path})
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
import os
|
||||
|
||||
from avalon import api
|
||||
from avalon.houdini import pipeline
|
||||
import clique
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
|
||||
from openpype.hosts.houdini.api import pipeline
|
||||
|
||||
|
||||
class AssLoader(api.Loader):
|
||||
class AssLoader(load.LoaderPlugin):
|
||||
"""Load .ass with Arnold Procedural"""
|
||||
|
||||
families = ["ass"]
|
||||
|
|
@ -88,7 +92,7 @@ class AssLoader(api.Loader):
|
|||
def update(self, container, representation):
|
||||
|
||||
# Update the file path
|
||||
file_path = api.get_representation_path(representation)
|
||||
file_path = get_representation_path(representation)
|
||||
file_path = file_path.replace("\\", "/")
|
||||
|
||||
procedural = container["node"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
from avalon import api
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
from openpype.hosts.houdini.api import pipeline
|
||||
|
||||
|
||||
|
|
@ -74,7 +77,7 @@ def transfer_non_default_values(src, dest, ignore=None):
|
|||
dest_parm.setFromParm(parm)
|
||||
|
||||
|
||||
class CameraLoader(api.Loader):
|
||||
class CameraLoader(load.LoaderPlugin):
|
||||
"""Specific loader of Alembic for the avalon.animation family"""
|
||||
|
||||
families = ["camera"]
|
||||
|
|
@ -129,7 +132,7 @@ class CameraLoader(api.Loader):
|
|||
node = container["node"]
|
||||
|
||||
# Update the file path
|
||||
file_path = api.get_representation_path(representation)
|
||||
file_path = get_representation_path(representation)
|
||||
file_path = file_path.replace("\\", "/")
|
||||
|
||||
# Update attributes
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from avalon import api
|
||||
|
||||
import os
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
from openpype.hosts.houdini.api import pipeline
|
||||
|
||||
|
||||
class HdaLoader(api.Loader):
|
||||
class HdaLoader(load.LoaderPlugin):
|
||||
"""Load Houdini Digital Asset file."""
|
||||
|
||||
families = ["hda"]
|
||||
|
|
@ -15,7 +18,6 @@ class HdaLoader(api.Loader):
|
|||
color = "orange"
|
||||
|
||||
def load(self, context, name=None, namespace=None, data=None):
|
||||
import os
|
||||
import hou
|
||||
|
||||
# Format file name, Houdini only wants forward slashes
|
||||
|
|
@ -49,7 +51,7 @@ class HdaLoader(api.Loader):
|
|||
import hou
|
||||
|
||||
hda_node = container["node"]
|
||||
file_path = api.get_representation_path(representation)
|
||||
file_path = get_representation_path(representation)
|
||||
file_path = file_path.replace("\\", "/")
|
||||
hou.hda.installFile(file_path)
|
||||
defs = hda_node.type().allInstalledDefinitions()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import os
|
||||
|
||||
from avalon import api
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
from openpype.hosts.houdini.api import lib, pipeline
|
||||
|
||||
import hou
|
||||
|
|
@ -37,7 +40,7 @@ def get_image_avalon_container():
|
|||
return image_container
|
||||
|
||||
|
||||
class ImageLoader(api.Loader):
|
||||
class ImageLoader(load.LoaderPlugin):
|
||||
"""Specific loader of Alembic for the avalon.animation family"""
|
||||
|
||||
families = ["colorbleed.imagesequence"]
|
||||
|
|
@ -87,7 +90,7 @@ class ImageLoader(api.Loader):
|
|||
node = container["node"]
|
||||
|
||||
# Update the file path
|
||||
file_path = api.get_representation_path(representation)
|
||||
file_path = get_representation_path(representation)
|
||||
file_path = file_path.replace("\\", "/")
|
||||
file_path = self._get_file_sequence(file_path)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
from avalon import api
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
from openpype.hosts.houdini.api import lib, pipeline
|
||||
|
||||
|
||||
class USDSublayerLoader(api.Loader):
|
||||
class USDSublayerLoader(load.LoaderPlugin):
|
||||
"""Sublayer USD file in Solaris"""
|
||||
|
||||
families = [
|
||||
|
|
@ -57,7 +60,7 @@ class USDSublayerLoader(api.Loader):
|
|||
node = container["node"]
|
||||
|
||||
# Update the file path
|
||||
file_path = api.get_representation_path(representation)
|
||||
file_path = get_representation_path(representation)
|
||||
file_path = file_path.replace("\\", "/")
|
||||
|
||||
# Update attributes
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
from avalon import api
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
from openpype.hosts.houdini.api import lib, pipeline
|
||||
|
||||
|
||||
class USDReferenceLoader(api.Loader):
|
||||
class USDReferenceLoader(load.LoaderPlugin):
|
||||
"""Reference USD file in Solaris"""
|
||||
|
||||
families = [
|
||||
|
|
@ -57,7 +60,7 @@ class USDReferenceLoader(api.Loader):
|
|||
node = container["node"]
|
||||
|
||||
# Update the file path
|
||||
file_path = api.get_representation_path(representation)
|
||||
file_path = get_representation_path(representation)
|
||||
file_path = file_path.replace("\\", "/")
|
||||
|
||||
# Update attributes
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import os
|
||||
import re
|
||||
from avalon import api
|
||||
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
get_representation_path,
|
||||
)
|
||||
from openpype.hosts.houdini.api import pipeline
|
||||
|
||||
|
||||
class VdbLoader(api.Loader):
|
||||
class VdbLoader(load.LoaderPlugin):
|
||||
"""Specific loader of Alembic for the avalon.animation family"""
|
||||
|
||||
families = ["vdbcache"]
|
||||
|
|
@ -96,7 +99,7 @@ class VdbLoader(api.Loader):
|
|||
return
|
||||
|
||||
# Update the file path
|
||||
file_path = api.get_representation_path(representation)
|
||||
file_path = get_representation_path(representation)
|
||||
file_path = self.format_path(file_path)
|
||||
|
||||
file_node.setParms({"fileName": file_path})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from avalon import api
|
||||
from openpype.pipeline import load
|
||||
|
||||
|
||||
class ShowInUsdview(api.Loader):
|
||||
class ShowInUsdview(load.LoaderPlugin):
|
||||
"""Open USD file in usdview"""
|
||||
|
||||
families = ["colorbleed.usd"]
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from collections import deque
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
|
||||
from openpype.pipeline import get_representation_path
|
||||
import openpype.hosts.houdini.api.usd as hou_usdlib
|
||||
from openpype.hosts.houdini.api.lib import render_rop
|
||||
|
||||
|
|
@ -308,7 +309,7 @@ class ExtractUSDLayered(openpype.api.Extractor):
|
|||
self.log.debug("No existing representation..")
|
||||
return False
|
||||
|
||||
old_file = api.get_representation_path(representation)
|
||||
old_file = get_representation_path(representation)
|
||||
if not os.path.exists(old_file):
|
||||
return False
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue