diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index dea7e3c57f..7a39103859 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -35,6 +35,19 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
+ - 3.16.6-nightly.1
+ - 3.16.5
+ - 3.16.5-nightly.5
+ - 3.16.5-nightly.4
+ - 3.16.5-nightly.3
+ - 3.16.5-nightly.2
+ - 3.16.5-nightly.1
+ - 3.16.4
+ - 3.16.4-nightly.3
+ - 3.16.4-nightly.2
+ - 3.16.4-nightly.1
+ - 3.16.3
+ - 3.16.3-nightly.5
- 3.16.3-nightly.4
- 3.16.3-nightly.3
- 3.16.3-nightly.2
@@ -122,19 +135,6 @@ body:
- 3.14.9-nightly.4
- 3.14.9-nightly.3
- 3.14.9-nightly.2
- - 3.14.9-nightly.1
- - 3.14.8
- - 3.14.8-nightly.4
- - 3.14.8-nightly.3
- - 3.14.8-nightly.2
- - 3.14.8-nightly.1
- - 3.14.7
- - 3.14.7-nightly.8
- - 3.14.7-nightly.7
- - 3.14.7-nightly.6
- - 3.14.7-nightly.5
- - 3.14.7-nightly.4
- - 3.14.7-nightly.3
validations:
required: true
- type: dropdown
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f2930d45eb..c4f9ff57ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,1821 @@
# Changelog
+## [3.16.5](https://github.com/ynput/OpenPype/tree/3.16.5)
+
+
+[Full Changelog](https://github.com/ynput/OpenPype/compare/3.16.4...3.16.5)
+
+### **🆕 New features**
+
+
+
+Attribute Definitions: Multiselection enum def #5547
+
+Added `multiselection` option to `EnumDef`.
+
+
+___
+
+
+
+### **🚀 Enhancements**
+
+
+
+Farm: adding target collector #5494
+
+Enhancing farm publishing workflow.
+
+
+___
+
+
+
+
+
+Maya: Optimize validate plug-in path attributes #5522
+
+- Optimize query (use `cmds.ls` once)
+- Add Select Invalid action
+- Improve validation report
+- Avoid "Unknown object type" errors
+
+
+___
+
+
+
+
+
+Maya: Remove Validate Instance Attributes plug-in #5525
+
+Remove Validate Instance Attributes plug-in.
+
+
+___
+
+
+
+
+
+Enhancement: Tweak logging for artist facing reports #5537
+
+Tweak the logging of publishing for global, deadline, maya and a fusion plugin to have a cleaner artist-facing report.
+- Fix context being reported correctly from CollectContext
+- Fix ValidateMeshArnoldAttributes: fix when arnold is not loaded, fix applying settings, fix for when ai attributes do not exist
+
+
+___
+
+
+
+
+
+AYON: Update settings #5544
+
+Updated settings in AYON addons and conversion of AYON settings in OpenPype.
+
+
+___
+
+
+
+
+
+Chore: Removed Ass export script #5560
+
+Removed Arnold render script, which was obsolete and unused.
+
+
+___
+
+
+
+
+
+Nuke: Allow for knob values to be validated against multiple values. #5042
+
+Knob values can now be validated against multiple values, so you can allow write nodes to be `exr` and `png`, or `16-bit` and `32-bit`.
+
+
+___
+
+
+
+
+
+Enhancement: Cosmetics for Higher version of publish already exists validation error #5190
+
+Fix double spaces in message.Example output **after** the PR:
+
+
+___
+
+
+
+
+
+Nuke: publish existing frames on farm #5409
+
+This PR proposes adding a fourth option in Nuke render publish called "Use Existing Frames - Farm". This would be useful when the farm is busy or when the artist lacks enough farm licenses. Additionally, some artists prefer rendering on the farm but still want to check frames before publishing.By adding the "Use Existing Frames - Farm" option, artists will have more flexibility and control over their render publishing process. This enhancement will streamline the workflow and improve efficiency for Nuke users.
+
+
+___
+
+
+
+
+
+Unreal: Create project in temp location and move to final when done #5476
+
+Create Unreal project in local temporary folder and when done, move it to final destination.
+
+
+___
+
+
+
+
+
+TrayPublisher: adding audio product type into default presets #5489
+
+Adding Audio product type into default presets so anybody can publish audio to their shots.
+
+
+___
+
+
+
+
+
+Global: avoiding cleanup of flagged representation #5502
+
+Publishing folder can be flagged as persistent at representation level.
+
+
+___
+
+
+
+
+
+General: missing tag could raise error #5511
+
+- avoiding potential situation where missing Tag key could raise error
+
+
+___
+
+
+
+
+
+Chore: Queued event system #5514
+
+Implemented event system with more expected behavior of event system. If an event is triggered during other event callback, it is not processed immediately but waits until all callbacks of previous events are done. The event system also allows to not trigger events directly once `emit_event` is called which gives option to process events in custom loops.
+
+
+___
+
+
+
+
+
+Publisher: Tweak log message to provide plugin name after "Plugin" #5521
+
+Fix logged message for settings automatically applied to plugin attributes
+
+
+___
+
+
+
+
+
+Houdini: Improve VDB Selection #5523
+
+Improves VDB selection if selection is `SopNode`: return the selected sop nodeif selection is `ObjNode`: get the output node with the minimum 'outputidx' or the node with display flag
+
+
+___
+
+
+
+
+
+Maya: Refactor/tweak Validate Instance In same Context plug-in #5526
+
+- Chore/Refactor: Re-use existing select invalid and repair actions
+- Enhancement: provide more elaborate PublishValidationError report
+- Bugfix: fix "optional" support by using `OptionalPyblishPluginMixin` base class.
+
+
+___
+
+
+
+
+
+Enhancement: Update houdini main menu #5527
+
+This PR adds two updates:
+- dynamic main menu
+- dynamic asset name and task
+
+
+___
+
+
+
+
+
+Houdini: Reset FPS when clicking Set Frame Range #5528
+
+_Similar to Maya,_ Make `Set Frame Range` resets FPS, issue https://github.com/ynput/OpenPype/issues/5516
+
+
+___
+
+
+
+
+
+Enhancement: Deadline plugins optimize, cleanup and fix optional support for validate deadline pools #5531
+
+- Fix optional support of validate deadline pools
+- Query deadline webservice only once per URL for verification, and once for available deadline pools instead of for every instance
+- Use `deadlineUrl` in `instance.data` when validating pools if it is set.
+- Code cleanup: Re-use existing `requests_get` implementation
+
+
+___
+
+
+
+
+
+Chore: PowerShell script for docker build #5535
+
+Added PowerShell script to run docker build.
+
+
+___
+
+
+
+
+
+AYON: Deadline expand userpaths in executables list #5540
+
+Expande `~` paths in executables list.
+
+
+___
+
+
+
+
+
+Chore: Use correct git url #5542
+
+Fixed github url in README.md.
+
+
+___
+
+
+
+
+
+Chore: Create plugin does not expect system settings #5553
+
+System settings are not passed to initialization of create plugin initialization (and `apply_settings`).
+
+
+___
+
+
+
+
+
+Chore: Allow custom Qt scale factor rounding policy #5555
+
+Do not force `PassThrough` rounding policy if different policy is defined via env variable.
+
+
+___
+
+
+
+
+
+Houdini: Fix outdated containers pop-up on opening last workfile on launch #5567
+
+Fix Houdini not showing outdated containers pop-up on scene open when launching with last workfile argument
+
+
+___
+
+
+
+
+
+Houdini: Improve errors e.g. raise PublishValidationError or cosmetics #5568
+
+Improve errors e.g. raise PublishValidationError or cosmeticsThis also fixes the Increment Current File plug-in since due to an invalid import it was previously broken
+
+
+___
+
+
+
+
+
+Fusion: Code updates #5569
+
+Update fusion code which contains obsolete code. Removed `switch_ui.py` script from fusion with related script in scripts.
+
+
+___
+
+
+
+### **🐛 Bug fixes**
+
+
+
+Maya: Validate Shape Zero fix repair action + provide informational artist-facing report #5524
+
+Refactor to PublishValidationError to allow the RepairAction to work + provide informational report message
+
+
+___
+
+
+
+
+
+Maya: Fix attribute definitions for `CreateYetiCache` #5574
+
+Fix attribute definitions for `CreateYetiCache`
+
+
+___
+
+
+
+
+
+Max: Optional Renderable Camera Validator for Render Instance #5286
+
+Optional validation to check on renderable camera being set up correctly for deadline submission.If not being set up correctly, it wont pass the validation and user can perform repair actions.
+
+
+___
+
+
+
+
+
+Max: Adding custom modifiers back to the loaded objects #5378
+
+The custom parameters OpenpypeData doesn't show in the loaded container when it is being loaded through the loader.
+
+
+___
+
+
+
+
+
+Houdini: Use default_variant to Houdini Node TAB Creator #5421
+
+Use the default variant of the creator plugins on the interactive creator from the TAB node search instead of hard-coding it to `Main`.
+
+
+___
+
+
+
+
+
+Nuke: adding inherited colorspace from instance #5454
+
+Thumbnails are extracted with inherited colorspace collected from rendering write node.
+
+
+___
+
+
+
+
+
+Add kitsu credentials to deadline publish job #5455
+
+This PR hopefully fixes this issue #5440
+
+
+___
+
+
+
+
+
+AYON: Fill entities during editorial #5475
+
+Fill entities and update template data on instances during extract AYON hierarchy.
+
+
+___
+
+
+
+
+
+Ftrack: Fix version 0 when integrating to Ftrack - OP-6595 #5477
+
+Fix publishing version 0 to Ftrack.
+
+
+___
+
+
+
+
+
+OCIO: windows unc path support in Nuke and Hiero #5479
+
+Hiero and Nuke is not supporting windows unc path formatting in OCIO environment variable.
+
+
+___
+
+
+
+
+
+Deadline: Added super call to init #5480
+
+DL 10.3 requires plugin inheriting from DeadlinePlugin to call super's **init** explicitly.
+
+
+___
+
+
+
+
+
+Nuke: fixing thumbnail and monitor out root attributes #5483
+
+Nuke Root Colorspace settings for Thumbnail and Monitor Out schema was gradually changed between version 12, 13, 14 and we needed to address those changes individually for particular version.
+
+
+___
+
+
+
+
+
+Nuke: fixing missing `instance_id` error #5484
+
+Workfiles with Instances created in old publisher workflow were rising error during converting method since they were missing `instance_id` key introduced in new publisher workflow.
+
+
+___
+
+
+
+
+
+Nuke: existing frames validator is repairing render target #5486
+
+Nuke is now correctly repairing render target after the existing frames validator finds missing frames and repair action is used.
+
+
+___
+
+
+
+
+
+added UE to extract burnins families #5487
+
+This PR fixes missing burnins in reviewables when rendering from UE.
+___
+
+
+
+
+
+Harmony: refresh code for current Deadline #5493
+
+- Added support in Deadline Plug-in for new versions of Harmony, in particular version 21 and 22.
+- Remove review=False flag on render instance
+- Add farm=True flag on render instance
+- Fix is_in_tests function call in Harmony Deadline submission plugin
+- Force HarmonyOpenPype.py Deadline Python plug-in to py3
+- Fix cosmetics/hound in HarmonyOpenPype.py Deadline Python plug-in
+
+
+___
+
+
+
+
+
+Publisher: Fix multiselection value #5505
+
+Selection of multiple instances in Publisher does not cause that all instances change all publish attributes to the same value.
+
+
+___
+
+
+
+
+
+Publisher: Avoid warnings on thumbnails if source image also has alpha channel #5510
+
+Avoids the following warning from `ExtractThumbnailFromSource`:
+```
+// pyblish.ExtractThumbnailFromSource : oiiotool WARNING: -o : Can't save 4 channels to jpeg... saving only R,G,B
+```
+
+
+
+___
+
+
+
+
+
+Update ayon-python-api #5512
+
+Update ayon python api and related callbacks.
+
+
+___
+
+
+
+
+
+Max: Fixing the bug of falling back to use workfile for Arnold or any renderers except Redshift #5520
+
+Fix the bug of falling back to use workfile for Arnold
+
+
+___
+
+
+
+
+
+General: Fix Validate Publish Dir Validator #5534
+
+Nonsensical "family" key was used instead of real value (as 'render' etc.) which would result in wrong translation of intermediate family names.Updated docstring.
+
+
+___
+
+
+
+
+
+have the addons loading respect a custom AYON_ADDONS_DIR #5539
+
+When using a custom AYON_ADDONS_DIR environment variable that variable is used in the launcher correctly and downloads and extracts addons to there, however when running Ayon does not respect this environment variable
+
+
+___
+
+
+
+
+
+Deadline: files on representation cannot be single item list #5545
+
+Further logic expects that single item files will be only 'string' not 'list' (eg. repre["files"] = "abc.exr" not repre["files"] = ["abc.exr"].This would cause an issue in ExtractReview later.This could happen if DL rendered single frame file with different frame value.
+
+
+___
+
+
+
+
+
+Webpublisher: better encode list values for click #5546
+
+Targets could be a list, original implementation pushed it as a separate items, it must be added as `--targets webpulish --targets filepublish`.`wepublish_routes` handles triggering from UI, changes in `publish_functions` handle triggering from cmd (for tests, api access).
+
+
+___
+
+
+
+
+
+Houdini: Introduce imprint function for correct version in hda loader #5548
+
+Resolve #5478
+
+
+___
+
+
+
+
+
+AYON: Fill entities during editorial (2) #5549
+
+Fix changes made in https://github.com/ynput/OpenPype/pull/5475.
+
+
+___
+
+
+
+
+
+Max: OP Data updates in Loaders #5563
+
+Fix the bug on the loaders not being able to load the objects when iterating key and values with the dict.Max prefers list over the list in dict.
+
+
+___
+
+
+
+
+
+Create Plugins: Better check of overriden '__init__' method #5571
+
+Create plugins do not log warning messages about each create plugin because of wrong `__init__` method check.
+
+
+___
+
+
+
+### **Merged pull requests**
+
+
+
+Tests: fix unit tests #5533
+
+Fixed failing tests.Updated Unreal's validator to match removed general one which had a couple of issues fixed.
+
+
+___
+
+
+
+
+
+
+## [3.16.4](https://github.com/ynput/OpenPype/tree/3.16.4)
+
+
+[Full Changelog](https://github.com/ynput/OpenPype/compare/3.16.3...3.16.4)
+
+### **🆕 New features**
+
+
+
+Feature: Download last published workfile specify version #4998
+
+Setting `workfile_version` key to hook's `self.launch_context.data` allow you to specify the workfile version you want sync service to download if none is matched locally. This is helpful if the last version hasn't been correctly published/synchronized, and you want to recover the previous one (or some you'd like).Version could be set in two ways:
+- OP's absolute version, matching the `version` index in DB.
+- Relative version in reverse order from the last one: `-2`, `-3`...I don't know where I should write documentation about that.
+
+
+___
+
+
+
+### **🚀 Enhancements**
+
+
+
+Maya: allow not creation of group for Import loaders #5427
+
+This PR enhances previous one. All ReferenceLoaders could not wrap imported products into explicit group.Also `Import` Loaders have same options. Control for this is separate in Settings, eg. Reference might wrap loaded items in group, `Import` might not.
+
+
+___
+
+
+
+
+
+3dsMax: Settings for Ayon #5388
+
+Max Addon Setting for Ayon
+
+
+___
+
+
+
+
+
+General: Navigation to Folder from Launcher #5404
+
+Adds an action in launcher to open the directory of the asset.
+
+
+___
+
+
+
+
+
+Chore: Default variant in create plugin #5429
+
+Attribute `default_variant` on create plugins always returns string and if default variant is not filled other ways how to get one are implemented.
+
+
+___
+
+
+
+
+
+Publisher: Thumbnail widget enhancements #5439
+
+Thumbnails widget in Publisher has new 3 options to choose from: Paste (from clipboard), Take screenshot and Browse. Clear button and new options are not visible by default, user must expand options button to show them.
+
+
+___
+
+
+
+
+
+AYON: Update ayon api to '0.3.5' #5460
+
+Updated ayon-python-api to 0.3.5.
+
+
+___
+
+
+
+### **🐛 Bug fixes**
+
+
+
+AYON: Apply unknown ayon settings first #5435
+
+Settings of custom addons are available in converted settings.
+
+
+___
+
+
+
+
+
+Maya: Fix wrong subset name of render family in deadline #5442
+
+New Publisher is creating different subset names than previously which resulted in duplication of `render` string in final subset name of `render` family published on Deadline.This PR solves that, it also fixes issues with legacy instances from old publisher, it matches the subset name as was before.This solves same issue in Max implementation.
+
+
+___
+
+
+
+
+
+Maya: Fix setting of version to workfile instance #5452
+
+If there are multiple instances of renderlayer published, previous logic resulted in unpredictable rewrite of instance family to 'workfile' if `Sync render version with workfile` was on.
+
+
+___
+
+
+
+
+
+Maya: Context plugin shouldn't be tied to family #5464
+
+`Maya Current File` collector was tied to `workfile` unnecessary. It should run even if `workile` instance is not being published.
+
+
+___
+
+
+
+
+
+Unreal: Fix loading hero version for static and skeletal meshes #5393
+
+Fixed a problem with loading hero versions for static ans skeletal meshes.
+
+
+___
+
+
+
+
+
+TVPaint: Fix 'repeat' behavior #5412
+
+Calculation of frames for repeat behavior is working correctly.
+
+
+___
+
+
+
+
+
+AYON: Thumbnails cache and api prep #5437
+
+Moved thumbnails cache from ayon python api to OpenPype and prepare AYON thumbnail resolver for new api functions. Current implementation should work with old and new ayon-python-api.
+
+
+___
+
+
+
+
+
+Nuke: Name of the Read Node should be updated correctly when switching versions or assets. #5444
+
+Bug fixing of the read node's name not being updated correctly when setting version or switching asset.
+
+
+___
+
+
+
+
+
+Farm publishing: asymmetric handles fixed #5446
+
+Handles are now set correctly on farm published product version if asymmetric were set to shot attributes.
+
+
+___
+
+
+
+
+
+Scene Inventory: Provider icons fix #5450
+
+Fix how provider icons are accessed in scene inventory.
+
+
+___
+
+
+
+
+
+Fix typo on Deadline OP plugin name #5453
+
+Surprised that no one has hit this bug yet... but it seems like there was a typo on the name of the OP Deadline plugin when submitting jobs to it.
+
+
+___
+
+
+
+
+
+AYON: Fix version attributes update #5472
+
+Fixed updates of attribs in AYON mode.
+
+
+___
+
+
+
+### **Merged pull requests**
+
+
+
+Added missing defaults for import_loader #5447
+
+
+___
+
+
+
+
+
+Bug: Local settings don't open on 3.14.7 #5220
+
+### Before posting a new ticket, have you looked through the documentation to find an answer?
+
+Yes I have
+
+### Have you looked through the existing tickets to find any related issues ?
+
+Not yet
+
+### Author of the bug
+
+@FadyFS
+
+### Version
+
+3.15.11-nightly.3
+
+### What platform you are running OpenPype on?
+
+Linux / Centos
+
+### Current Behavior:
+
+the previous behavior (bug) :
+
+
+
+### Expected Behavior:
+
+
+
+
+### What type of bug is it ?
+
+Happened only once in a particular configuration
+
+### Which project / workfile / asset / ...
+
+open settings with 3.14.7
+
+### Steps To Reproduce:
+
+1. Run openpype on the 3.15.11-nightly.3 version
+2. Open settings in 3.14.7 version
+
+### Relevant log output:
+
+_No response_
+
+### Additional context:
+
+_No response_
+
+___
+
+
+
+
+
+Tests: Add automated targets for tests #5443
+
+Without it plugins with 'automated' targets won't be triggered (eg `CloseAE` etc.)
+
+
+___
+
+
+
+
+
+
+## [3.16.3](https://github.com/ynput/OpenPype/tree/3.16.3)
+
+
+[Full Changelog](https://github.com/ynput/OpenPype/compare/3.16.2...3.16.3)
+
+### **🆕 New features**
+
+
+
+AYON: 3rd party addon usage #5300
+
+Prepare OpenPype code to be able use `ayon-third-party` addon which supply ffmpeg and OpenImageIO executables. Because they both can support to define custom arguments (more than one) a new functions were needed to supply.New functions are `get_ffmpeg_tool_args` and `get_oiio_tool_args`. They work similar to previous but instead of string are returning list of strings. All places using previous functions `get_ffmpeg_tool_path` and `get_oiio_tool_path` are now using new ones. They should be backwards compatible and even with addon if returns single argument.
+
+
+___
+
+
+
+
+
+AYON: Addon settings in OpenPype #5347
+
+Moved settings addons to OpenPype server addon. Modified create package to create zip files for server for each settings addon and for openpype addon.
+
+
+___
+
+
+
+
+
+AYON: Add folder to template data #5417
+
+Added `folder` to template data, so `{folder[name]}` can be used in templates.
+
+
+___
+
+
+
+
+
+Option to start versioning from 0 #5262
+
+This PR adds a settings option to start all versioning from 0.This PR will replace #4455.
+
+
+___
+
+
+
+
+
+Ayon: deadline implementation #5321
+
+Quick implementation of deadline in Ayon. New Ayon plugin added for Deadline repository
+
+
+___
+
+
+
+
+
+AYON: Remove AYON launch logic from OpenPype #5348
+
+Removed AYON launch logic from OpenPype. The logic is outdated at this moment and is replaced by `ayon-launcher`.
+
+
+___
+
+
+
+### **🚀 Enhancements**
+
+
+
+Bug: Error on multiple instance rig with maya #5310
+
+I change endswith method by startswith method because the set are automacaly name out_SET, out_SET1, out_SET2 ...
+
+
+___
+
+
+
+
+
+Applications: Use prelaunch hooks to extract environments #5387
+
+Environment variable preparation is based on prelaunch hooks. This should allow to pass OCIO environment variables to farm jobs.
+
+
+___
+
+
+
+
+
+Applications: Launch hooks cleanup #5395
+
+Use `set` instead of `list` for filtering attributes in launch hooks. Celaction hooks dir does not contain `__init__.py`. Celaction prelaunch hook is reusing `CELACTION_ROOT_DIR`. Launch hooks are using full import from `openpype.lib.applications`.
+
+
+___
+
+
+
+
+
+Applications: Environment variables order #5245
+
+Changed order of set environment variables. First are set context environment variables and then project environment overrides. Also asset and task environemnt variables are optional.
+
+
+___
+
+
+
+
+
+Autosave preferences can be read after Nuke opens the script #5295
+
+Looks like I need to open the script in Nuke to be able to correctly load the autosave preferences.This PR reads the Nuke script in context, and offers owerwriting the current script with autosaved one if autosave exists.
+
+
+___
+
+
+
+
+
+Resolve: Update with compatible resolve version and latest docs #5317
+
+Missing information about compatible Resolve version and latest docs from https://github.com/ynput/OpenPype/tree/develop/openpype/hosts/resolve
+
+
+___
+
+
+
+
+
+Chore: Remove deprecated functions #5323
+
+Removed functions/classes that are deprecated and marked to be removed.
+
+
+___
+
+
+
+
+
+Nuke Render and Prerender nodes Process Order - OP-3555 #5332
+
+This PR exposes control over the order of processing of the instances, by sorting the instances created. The sorting happens on the `render_order` and subset name. If the knob `render_order` is found on the instance, we'll sort by that first before sorting by subset name.`render_order` instances are processed before nodes without `render_order`. This could be extended in the future by querying other knobs but I dont know of a usecase for this.Hardcoded the creator `order` attribute of the `prerender` class to be before the `render`. Could be exposed to the user/studio but dont know of a use case for this.
+
+
+___
+
+
+
+
+
+Unreal: Python Environment Improvements #5344
+
+Automatically set `UE_PYTHONPATH` as `PYTHONPATH` when launching Unreal.
+
+
+___
+
+
+
+
+
+Unreal: Custom location for Unreal Ayon Plugin #5346
+
+Added a new environment variable `AYON_BUILT_UNREAL_PLUGIN` to set an already existing and built Ayon Plugin for Unreal.
+
+
+___
+
+
+
+
+
+Unreal: Better handling of Exceptions in UE Worker threads #5349
+
+Implemented a new `UEWorker` base class to handle exception during the execution of UE Workers.
+
+
+___
+
+
+
+
+
+Houdini: Add farm toggle on creation menu #5350
+
+Deadline Farm publishing and Rendering for Houdini was possible with this PR #4825 farm publishing is enabled by default some ROP nodes which may surprise new users (like me).I think adding a toggle (on by default) on creation UI is better so that users will be aware that there's a farm option for this publish instance.ROPs Modified :
+- [x] Mantra ROP
+- [x] Karma ROP
+- [x] Arnold ROP
+- [x] Redshift ROP
+- [x] Vray ROP
+
+
+___
+
+
+
+
+
+Ftrack: Sync to avalon settings #5353
+
+Added roles settings for sync to avalon action.
+
+
+___
+
+
+
+
+
+Chore: Schemas inside OpenPype #5354
+
+Moved/copied schemas from repository root inside openpype/pipeline.
+
+
+___
+
+
+
+
+
+AYON: Addons creation enhancements #5356
+
+Enhanced AYON addons creation. Fix issue with `Pattern` typehint. Zip filenames contain version. OpenPype package is skipping modules that are already separated in AYON. Updated settings of addons.
+
+
+___
+
+
+
+
+
+AYON: Update staging icons #5372
+
+Updated staging icons for staging mode.
+
+
+___
+
+
+
+
+
+Enhancement: Houdini Update pointcache labels #5373
+
+To me it's logical to find pointcaches types listed one after another, but they were named differentlySo, I made this PR to update their labels
+
+
+___
+
+
+
+
+
+nuke: split write node product instance features #5389
+
+Improving Write node product instances by allowing precise activation of specific features.
+
+
+___
+
+
+
+
+
+Max: Use the empty modifiers in container to store AYON Parameter #5396
+
+Instead of adding AYON/OP Parameter along with other attributes inside the container, empty modifiers would be created to store AYON/OP custom attributes
+
+
+___
+
+
+
+
+
+AfterEffects: Removed unused imports #5397
+
+Removed unused import from extract local render plugin file.
+
+
+___
+
+
+
+
+
+Nuke: adding BBox knob type to settings #5405
+
+Nuke knob types in settings having new `Box` type for reposition nodes like Crop or Reformat.
+
+
+___
+
+
+
+
+
+SyncServer: Existence of module is optional #5413
+
+Existence of SyncServer module is optional and not required. Added `sync_server` module back to ignored modules when openpype addon is created for AYON. Command `syncserver` is marked as deprecated and redirected to sync server cli.
+
+
+___
+
+
+
+
+
+Webpublisher: Self contain test publish logic #5414
+
+Moved test logic of publishing to webpublisher. Simplified `remote_publish` to remove webpublisher specific logic.
+
+
+___
+
+
+
+
+
+Webpublisher: Cleanup targets #5418
+
+Removed `remote` target from webpublisher and replaced it with 2 targets `webpublisher` and `automated`.
+
+
+___
+
+
+
+
+
+nuke: update server addon settings with box #5419
+
+updtaing nuke ayon server settings for Box option in knob types.
+
+
+___
+
+
+
+### **🐛 Bug fixes**
+
+
+
+Maya: fix validate frame range on review attached to other instances #5296
+
+Fixes situation where frame range validator can't be turned off on models if they are attached to reviewable camera in Maya.
+
+
+___
+
+
+
+
+
+Maya: Apply project settings to creators #5303
+
+Project settings were not applied to the creators.
+
+
+___
+
+
+
+
+
+Maya: Validate Model Content #5336
+
+`assemblies` in `cmds.ls` does not seem to work;
+```python
+
+from maya import cmds
+
+
+content_instance = ['|group2|pSphere1_GEO', '|group2|pSphere1_GEO|pSphere1_GEOShape', '|group1|pSphere1_GEO', '|group1|pSphere1_GEO|pSphere1_GEOShape']
+assemblies = cmds.ls(content_instance, assemblies=True, long=True)
+print(assemblies)
+```
+
+Fixing with string splitting instead.
+
+
+___
+
+
+
+
+
+Bugfix: Maya update defaults variable #5368
+
+So, something was forgotten while moving out from `LegacyCreator` to `NewCreator``LegacyCreator` used `defaults` to list suggested subset names which was changed into `default_variants` in the the `NewCreator`and setting `defaults` to any values has no effect!This update affects:
+- [x] Model
+- [x] Set Dress
+
+
+___
+
+
+
+
+
+Chore: Python 2 support fix #5375
+
+Fix Python 2 support by adding `click` into python 2 dependencies and removing f-string from maya.
+
+
+___
+
+
+
+
+
+Maya: do not create top level group on reference #5402
+
+This PR allows to not wrapping loaded referenced assets in top level group either explicitly for artist or by configuration in Settings.Artists can control group creation in ReferenceLoader options.Default no group creation could be set by emptying `Group Name` in `project_settings/maya/load/reference_loader`
+
+
+___
+
+
+
+
+
+Settings: Houdini & Maya create plugin settings #5436
+
+Fixes related to Maya and Houdini settings. Renamed `defaults` to `default_variants` in plugin settings to match attribute name on create plugin in both OpenPype and AYON settings. Fixed Houdini AYON settings where were missing settings for defautlt varaints and fixed Maya AYON settings where default factory had wrong assignment.
+
+
+___
+
+
+
+
+
+Maya: Hide CreateAnimation #5297
+
+When converting `animation` family or loading a `rig` family, need to include the `animation` creator but hide it in creator context.
+
+
+___
+
+
+
+
+
+Nuke Anamorphic slate - Read pixel aspect from input #5304
+
+When asset pixel aspect differs from rendered pixel aspect, Nuke slate pixel aspect is not longer taken from asset, but is readed via ffprobe.
+
+
+___
+
+
+
+
+
+Nuke - Allow ExtractReviewDataMov with no timecode knob #5305
+
+ExtractReviewDataMov allows to specify file type. Trying to write some other extension than mov fails on generate_mov assuming that mov64_write_timecode knob exists.
+
+
+___
+
+
+
+
+
+Nuke: removing settings schema with defaults for OpenPype #5306
+
+continuation of https://github.com/ynput/OpenPype/pull/5275
+
+
+___
+
+
+
+
+
+Bugfix: Dependency without 'inputLinks' not downloaded #5337
+
+Remove condition that avoids downloading dependency without `inputLinks`.
+
+
+___
+
+
+
+
+
+Bugfix: Houdini Creator use selection even if it was toggled off #5359
+
+When creating many product types (families) one after another without refreshing the creator window manually if you toggled `Use selection` once, all the later product types will use selection even if it was toggled offHere's Before it will keep use selection even if it was toggled off, unless you refresh window manuallyhttps://github.com/ynput/OpenPype/assets/20871534/8b890122-5b53-4c6b-897d-6a2f3aa3388aHere's After it works as expectedhttps://github.com/ynput/OpenPype/assets/20871534/6b1db990-de1b-428e-8828-04ab59a44e28
+
+
+___
+
+
+
+
+
+Houdini: Correct camera selection for karma renderer when using selected node #5360
+
+When user creates the karma rop with selected camera by use selection, it will give the error message of "no render camera found in selection".This PR is to fix the bug of creating karma rop when using selected camera node in Houdini
+
+
+___
+
+
+
+
+
+AYON: Environment variables and functions #5361
+
+Prepare code for ayon-launcher compatibility. Fix ayon launcher subprocess calls, added more checks for `AYON_SERVER_ENABLED`, use ayon launcher suitable environment variables in AYON mode and changed outputs of some functions. Replaced usages of `OPENPYPE_REPOS_ROOT` environment variable with `PACKAGE_DIR` variable -> correct paths are used.
+
+
+___
+
+
+
+
+
+Nuke: farm rendering of prerender ignore roots in nuke #5366
+
+`prerender` family was using wrong subset, same as `render` which should be different.
+
+
+___
+
+
+
+
+
+Bugfix: Houdini update defaults variable #5367
+
+So, something was forgotten while moving out from `LegacyCreator` to `NewCreator``LegacyCreator` used `defaults` to list suggested subset names which was changed into `default_variants` in the the `NewCreator`and setting `defaults` to any values has no effect!This update affects:
+- [x] Arnold ASS
+- [x] Arnold ROP
+- [x] Karma ROP
+- [x] Mantra ROP
+- [x] Redshift ROP
+- [x] VRay ROP
+
+
+___
+
+
+
+
+
+Publisher: Fix create/publish animation #5369
+
+Use geometry movement instead of changing min/max width.
+
+
+___
+
+
+
+
+
+Unreal: Move unreal splash screen to unreal #5370
+
+Moved splash screen code to unreal integration and removed import from Igniter.
+
+
+___
+
+
+
+
+
+Nuke: returned not cleaning of renders folder on the farm #5374
+
+Previous PR enabled explicit cleanup of `renders` folder after farm publishing. This is not matching customer's workflows. Customer wants to have access to files in `renders` folder and potentially redo some frames for long frame sequences.This PR extends logic of marking rendered files for deletion only if instance doesn't have `stagingDir_persistent`.For backwards compatibility all Nuke instances have `stagingDir_persistent` set to True, eg. `renders` folder won't be cleaned after farm publish.
+
+
+___
+
+
+
+
+
+Nuke: loading sequences is working #5376
+
+Loading image sequences was broken after the latest release, version 3.16. However, I am pleased to inform you that it is now functioning as expected.
+
+
+___
+
+
+
+
+
+AYON: Fix settings conversion for ayon addons #5377
+
+AYON addon settings are available in system settings and does not have available the same values in `"modules"` subkey.
+
+
+___
+
+
+
+
+
+Nuke: OCIO env var workflow #5379
+
+The OCIO environment variable needs to be consistently handled across all platforms. Nuke resolves the custom OCIO config path differently depending on the platform, so we included the ocio config path in the workfile with a partial replacement using an environment variable. Additionally, for Windows sessions, we replaced backward slashes with a TCL expression.
+
+
+___
+
+
+
+
+
+Unreal: Fix Unreal build script #5381
+
+Define 'AYON_UNREAL_ROOT' environment variable in unreal addon.
+
+
+___
+
+
+
+
+
+3dsMax: Use relative path to MAX_HOST_DIR #5382
+
+Use `MAX_HOST_DIR` to calculate startup script path instead of use relative path to `OPENPYPE_ROOT` environment variable.
+
+
+___
+
+
+
+
+
+Bugfix: Houdini abc validator error message #5386
+
+When ABC path validator fails, it prints node objects not node paths or namesThis bug happened because of updating `get_invalid` method to return nodes instead of node pathsBeforeAfter
+
+
+___
+
+
+
+
+
+Nuke: node name influence product (subset) name #5392
+
+Nuke now allows users to duplicate publishing instances, making the workflow easier. By duplicating a node and changing its name, users can set the product (subset) name in the publishing context.Users now have the ability to change the variant name in Publisher, which will automatically rename the associated instance node.
+
+
+___
+
+
+
+
+
+Houdini: delete redundant bgeo sop validator #5394
+
+I found out that this `Validate BGEO SOP Path` validator is redundant, it catches two cases that are already implemented in "Validate Output Node". "Validate Output Node" works with `bgeo` as well as `abc` because `"pointcache"` is listed in its families
+
+
+___
+
+
+
+
+
+Nuke: workfile is not reopening after change of context #5399
+
+Nuke no longer reopens the latest workfile when the context is changed to a different task using the Workfile tool. The issue also affected the Script Clean (from Nuke File menu) and Close feature, but it has now been fixed.
+
+
+___
+
+
+
+
+
+Bugfix: houdini hard coded project settings #5400
+
+I made this PR to solve the issue with hard-coded settings in houdini
+
+
+___
+
+
+
+
+
+AYON: 3dsMax settings #5401
+
+Keep `adsk_3dsmax` group in applications settings.
+
+
+___
+
+
+
+
+
+Bugfix: update defaults to default_variants in maya and houdini OP DCC settings #5407
+
+On moving out to new creator in Maya and Houdini updating settings was missed.
+
+
+___
+
+
+
+
+
+Applications: Attributes creation #5408
+
+Applications addon does not cause infinite server restart loop.
+
+
+___
+
+
+
+
+
+Max: fix the bug of handling Object deletion in OP Parameter #5410
+
+If the object is added to the OP parameter and user delete it in the scene thereafter, it will error out the container with OP attributes. This PR resolves the bug.This PR also fixes the bug of not adding the attribute into OP parameter correctly when the user enables "use selections" to link the object into the OP parameter.
+
+
+___
+
+
+
+
+
+Colorspace: including environments from launcher process #5411
+
+Fixed bug in GitHub PR where the OCIO config template was not properly formatting environment variables from System Settings `general/environment`.
+
+
+___
+
+
+
+
+
+Nuke: workfile template fixes #5428
+
+Some bunch of small bugs needed to be fixed
+
+
+___
+
+
+
+
+
+Houdini, Max: Fix missed function interface change #5430
+
+This PR https://github.com/ynput/OpenPype/pull/5321/files from @kalisp missed updating the `add_render_job_env_var` in Houdini and Max as they are passing an extra arg:
+```
+TypeError: add_render_job_env_var() takes 1 positional argument but 2 were given
+```
+
+
+___
+
+
+
+
+
+Scene Inventory: Fix issue with 'sync_server' #5431
+
+Fix accesss to `sync_server` attribute in scene inventory.
+
+
+___
+
+
+
+
+
+Unpack project: Fix import issue #5433
+
+Added `load_json_file`, `replace_project_documents` and `store_project_documents` to mongo init.
+
+
+___
+
+
+
+
+
+Chore: Versions post fixes #5441
+
+Fixed issues caused by my fault. Filled right version value to anatomy data.
+
+
+___
+
+
+
+### **📃 Testing**
+
+
+
+Tests: Copy file_handler as it will be removed by purging ayon code #5357
+
+Ayon code will get purged in the future from this repo/addon, therefore all `ayon_common` will be gone. `file_handler` gets internalized to tests as it is not used anywhere else.
+
+
+___
+
+
+
+
+
+
## [3.16.2](https://github.com/ynput/OpenPype/tree/3.16.2)
@@ -357,7 +2172,7 @@ ___
Add functional base for API Documentation using Sphinx and AutoAPI.
-After unsuccessful #2512, #834 and #210 this is yet another try. But this time without ambition to solve the whole issue. This is making Shinx script to work and nothing else. Any changes and improvements in API docs should be made in subsequent PRs.
+After unsuccessful #2512, #834 and #210 this is yet another try. But this time without ambition to solve the whole issue. This is making Shinx script to work and nothing else. Any changes and improvements in API docs should be made in subsequent PRs.
## How to use it
@@ -368,7 +2183,7 @@ cd .\docs
make.bat html
```
-or
+or
```sh
cd ./docs
@@ -383,7 +2198,7 @@ During the build you'll see tons of red errors that are pointing to our issues:
Invalid import are usually wrong relative imports (too deep) or circular imports.
2) **Invalid doc-strings**
- Doc-strings to be processed into documentation needs to follow some syntax - this can be checked by running
+ Doc-strings to be processed into documentation needs to follow some syntax - this can be checked by running
`pydocstyle` that is already included with OpenPype
3) **Invalid markdown/rst files**
md/rst files can be included inside rst files using `.. include::` directive. But they have to be properly formatted.
@@ -1570,11 +3385,11 @@ ___
Houdini: Redshift ROP image format bug #5218
-Problem :
-"RS_outputFileFormat" parm value was missing
-and there were more "image_format" than redshift rop supports
+Problem :
+"RS_outputFileFormat" parm value was missing
+and there were more "image_format" than redshift rop supports
-Fix:
+Fix:
1) removed unnecessary formats from `image_format_enum`
2) add the selected format value to `RS_outputFileFormat`
___
@@ -3751,7 +5566,7 @@ ___
Maya Load References - Add Display Handle Setting #4904
-When we load a reference in Maya using OpenPype loader, display handle is checked by default and prevent us to select easily the object in the viewport. I understand that some productions like to keep this option, so I propose to add display handle to the reference loader settings.
+When we load a reference in Maya using OpenPype loader, display handle is checked by default and prevent us to select easily the object in the viewport. I understand that some productions like to keep this option, so I propose to add display handle to the reference loader settings.
___
@@ -3859,7 +5674,7 @@ ___
Patchelf version locked #4853
-For Centos dockerfile it is necessary to lock the patchelf version to the older, otherwise the build process fails.
+For Centos dockerfile it is necessary to lock the patchelf version to the older, otherwise the build process fails.
___
diff --git a/README.md b/README.md
index 6caed8061c..ce98f845e6 100644
--- a/README.md
+++ b/README.md
@@ -62,7 +62,7 @@ development tools like [CMake](https://cmake.org/) and [Visual Studio](https://v
#### Clone repository:
```sh
-git clone --recurse-submodules git@github.com:Pypeclub/OpenPype.git
+git clone --recurse-submodules git@github.com:ynput/OpenPype.git
```
#### To build OpenPype:
@@ -144,6 +144,10 @@ sudo ./tools/docker_build.sh centos7
If all is successful, you'll find built OpenPype in `./build/` folder.
+Docker build can be also started from Windows machine, just use `./tools/docker_build.ps1` instead of shell script.
+
+This could be used even for building linux build (with argument `centos7` or `debian`)
+
#### Manual build
You will need [Python >= 3.9](https://www.python.org/downloads/) and [git](https://git-scm.com/downloads). You'll also need [curl](https://curl.se) on systems that doesn't have one preinstalled.
diff --git a/openpype/cli.py b/openpype/cli.py
index 22ad16e937..0df277fb0a 100644
--- a/openpype/cli.py
+++ b/openpype/cli.py
@@ -196,47 +196,6 @@ def publish(paths, targets, gui):
PypeCommands.publish(list(paths), targets, gui)
-@main.command()
-@click.argument("path")
-@click.option("-h", "--host", help="Host")
-@click.option("-u", "--user", help="User email address")
-@click.option("-p", "--project", help="Project")
-@click.option("-t", "--targets", help="Targets", default=None,
- multiple=True)
-def remotepublishfromapp(project, path, host, user=None, targets=None):
- """Start CLI publishing.
-
- Publish collects json from paths provided as an argument.
- More than one path is allowed.
- """
-
- if AYON_SERVER_ENABLED:
- raise RuntimeError(
- "AYON does not support 'remotepublishfromapp' command."
- )
- PypeCommands.remotepublishfromapp(
- project, path, host, user, targets=targets
- )
-
-
-@main.command()
-@click.argument("path")
-@click.option("-u", "--user", help="User email address")
-@click.option("-p", "--project", help="Project")
-@click.option("-t", "--targets", help="Targets", default=None,
- multiple=True)
-def remotepublish(project, path, user=None, targets=None):
- """Start CLI publishing.
-
- Publish collects json from paths provided as an argument.
- More than one path is allowed.
- """
-
- if AYON_SERVER_ENABLED:
- raise RuntimeError("AYON does not support 'remotepublish' command.")
- PypeCommands.remotepublish(project, path, user, targets=targets)
-
-
@main.command(context_settings={"ignore_unknown_options": True})
def projectmanager():
if AYON_SERVER_ENABLED:
diff --git a/openpype/client/mongo/__init__.py b/openpype/client/mongo/__init__.py
index 5c5143a731..9f62d7a9cf 100644
--- a/openpype/client/mongo/__init__.py
+++ b/openpype/client/mongo/__init__.py
@@ -6,6 +6,9 @@ from .mongo import (
OpenPypeMongoConnection,
get_project_database,
get_project_connection,
+ load_json_file,
+ replace_project_documents,
+ store_project_documents,
)
@@ -17,4 +20,7 @@ __all__ = (
"OpenPypeMongoConnection",
"get_project_database",
"get_project_connection",
+ "load_json_file",
+ "replace_project_documents",
+ "store_project_documents",
)
diff --git a/openpype/client/server/conversion_utils.py b/openpype/client/server/conversion_utils.py
index 42df337b6d..f67a1ef9c4 100644
--- a/openpype/client/server/conversion_utils.py
+++ b/openpype/client/server/conversion_utils.py
@@ -663,10 +663,13 @@ def convert_v4_representation_to_v3(representation):
if isinstance(context, six.string_types):
context = json.loads(context)
- if "folder" in context:
- _c_folder = context.pop("folder")
+ if "asset" not in context and "folder" in context:
+ _c_folder = context["folder"]
context["asset"] = _c_folder["name"]
+ elif "asset" in context and "folder" not in context:
+ context["folder"] = {"name": context["asset"]}
+
if "product" in context:
_c_product = context.pop("product")
context["family"] = _c_product["type"]
@@ -959,9 +962,11 @@ def convert_create_representation_to_v4(representation, con):
converted_representation["files"] = new_files
context = representation["context"]
- context["folder"] = {
- "name": context.pop("asset", None)
- }
+ if "folder" not in context:
+ context["folder"] = {
+ "name": context.get("asset")
+ }
+
context["product"] = {
"type": context.pop("family", None),
"name": context.pop("subset", None),
@@ -1074,7 +1079,7 @@ def convert_update_folder_to_v4(project_name, asset_id, update_data, con):
parent_id = None
tasks = None
new_data = {}
- attribs = {}
+ attribs = full_update_data.pop("attrib", {})
if "type" in update_data:
new_update_data["active"] = update_data["type"] == "asset"
@@ -1113,6 +1118,9 @@ def convert_update_folder_to_v4(project_name, asset_id, update_data, con):
print("Folder has new data: {}".format(new_data))
new_update_data["data"] = new_data
+ if attribs:
+ new_update_data["attrib"] = attribs
+
if has_task_changes:
raise ValueError("Task changes of folder are not implemented")
@@ -1126,7 +1134,7 @@ def convert_update_subset_to_v4(project_name, subset_id, update_data, con):
full_update_data = _from_flat_dict(update_data)
data = full_update_data.get("data")
new_data = {}
- attribs = {}
+ attribs = full_update_data.pop("attrib", {})
if data:
if "family" in data:
family = data.pop("family")
@@ -1148,9 +1156,6 @@ def convert_update_subset_to_v4(project_name, subset_id, update_data, con):
elif value is not REMOVED_VALUE:
new_data[key] = value
- if attribs:
- new_update_data["attribs"] = attribs
-
if "name" in update_data:
new_update_data["name"] = update_data["name"]
@@ -1165,6 +1170,9 @@ def convert_update_subset_to_v4(project_name, subset_id, update_data, con):
new_update_data["folderId"] = update_data["parent"]
flat_data = _to_flat_dict(new_update_data)
+ if attribs:
+ flat_data["attrib"] = attribs
+
if new_data:
print("Subset has new data: {}".format(new_data))
flat_data["data"] = new_data
@@ -1179,7 +1187,7 @@ def convert_update_version_to_v4(project_name, version_id, update_data, con):
full_update_data = _from_flat_dict(update_data)
data = full_update_data.get("data")
new_data = {}
- attribs = {}
+ attribs = full_update_data.pop("attrib", {})
if data:
if "author" in data:
new_update_data["author"] = data.pop("author")
@@ -1196,9 +1204,6 @@ def convert_update_version_to_v4(project_name, version_id, update_data, con):
elif value is not REMOVED_VALUE:
new_data[key] = value
- if attribs:
- new_update_data["attribs"] = attribs
-
if "name" in update_data:
new_update_data["version"] = update_data["name"]
@@ -1213,6 +1218,9 @@ def convert_update_version_to_v4(project_name, version_id, update_data, con):
new_update_data["productId"] = update_data["parent"]
flat_data = _to_flat_dict(new_update_data)
+ if attribs:
+ flat_data["attrib"] = attribs
+
if new_data:
print("Version has new data: {}".format(new_data))
flat_data["data"] = new_data
@@ -1252,7 +1260,7 @@ def convert_update_representation_to_v4(
data = full_update_data.get("data")
new_data = {}
- attribs = {}
+ attribs = full_update_data.pop("attrib", {})
if data:
for key, value in data.items():
if key in folder_attributes:
@@ -1282,7 +1290,7 @@ def convert_update_representation_to_v4(
if "context" in update_data:
context = update_data["context"]
- if "asset" in context:
+ if "folder" not in context and "asset" in context:
context["folder"] = {"name": context.pop("asset")}
if "family" in context or "subset" in context:
@@ -1309,6 +1317,9 @@ def convert_update_representation_to_v4(
new_update_data["files"] = new_files
flat_data = _to_flat_dict(new_update_data)
+ if attribs:
+ flat_data["attrib"] = attribs
+
if new_data:
print("Representation has new data: {}".format(new_data))
flat_data["data"] = new_data
diff --git a/openpype/client/server/entities.py b/openpype/client/server/entities.py
index 9579f13add..39322627bb 100644
--- a/openpype/client/server/entities.py
+++ b/openpype/client/server/entities.py
@@ -83,10 +83,10 @@ def _get_subsets(
project_name,
subset_ids,
subset_names,
- folder_ids,
- names_by_folder_ids,
- active,
- fields
+ folder_ids=folder_ids,
+ names_by_folder_ids=names_by_folder_ids,
+ active=active,
+ fields=fields,
):
yield convert_v4_subset_to_v3(subset)
diff --git a/openpype/vendor/python/common/ayon_api/thumbnails.py b/openpype/client/server/thumbnails.py
similarity index 92%
rename from openpype/vendor/python/common/ayon_api/thumbnails.py
rename to openpype/client/server/thumbnails.py
index 50acd94dcb..dc649b9651 100644
--- a/openpype/vendor/python/common/ayon_api/thumbnails.py
+++ b/openpype/client/server/thumbnails.py
@@ -1,3 +1,11 @@
+"""Cache of thumbnails downloaded from AYON server.
+
+Thumbnails are cached to appdirs to predefined directory.
+
+This should be moved to thumbnails logic in pipeline but because it would
+overflow OpenPype logic it's here for now.
+"""
+
import os
import time
import collections
@@ -10,7 +18,7 @@ FileInfo = collections.namedtuple(
)
-class ThumbnailCache:
+class AYONThumbnailCache:
"""Cache of thumbnails on local storage.
Thumbnails are cached to appdirs to predefined directory. Each project has
@@ -32,13 +40,14 @@ class ThumbnailCache:
# Lifetime of thumbnails (in seconds)
# - default 3 days
- days_alive = 3 * 24 * 60 * 60
+ days_alive = 3
# Max size of thumbnail directory (in bytes)
# - default 2 Gb
max_filesize = 2 * 1024 * 1024 * 1024
def __init__(self, cleanup=True):
self._thumbnails_dir = None
+ self._days_alive_secs = self.days_alive * 24 * 60 * 60
if cleanup:
self.cleanup()
@@ -50,7 +59,8 @@ class ThumbnailCache:
"""
if self._thumbnails_dir is None:
- directory = appdirs.user_data_dir("ayon", "ynput")
+ # TODO use generic function
+ directory = appdirs.user_data_dir("AYON", "Ynput")
self._thumbnails_dir = os.path.join(directory, "thumbnails")
return self._thumbnails_dir
@@ -121,7 +131,7 @@ class ThumbnailCache:
for filename in filenames:
path = os.path.join(root, filename)
modification_time = os.path.getmtime(path)
- if current_time - modification_time > self.days_alive:
+ if current_time - modification_time > self._days_alive_secs:
os.remove(path)
def _max_size_cleanup(self, thumbnails_dir):
diff --git a/openpype/hooks/pre_ocio_hook.py b/openpype/hooks/pre_ocio_hook.py
index 1307ed9f76..add3a0adaf 100644
--- a/openpype/hooks/pre_ocio_hook.py
+++ b/openpype/hooks/pre_ocio_hook.py
@@ -45,6 +45,9 @@ class OCIOEnvHook(PreLaunchHook):
if config_data:
ocio_path = config_data["path"]
+ if self.host_name in ["nuke", "hiero"]:
+ ocio_path = ocio_path.replace("\\", "/")
+
self.log.info(
f"Setting OCIO environment to config path: {ocio_path}")
diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py
index fa79fac78f..fbe600ae68 100644
--- a/openpype/hosts/aftereffects/plugins/create/create_render.py
+++ b/openpype/hosts/aftereffects/plugins/create/create_render.py
@@ -28,7 +28,6 @@ class RenderCreator(Creator):
create_allow_context_change = True
# Settings
- default_variants = []
mark_for_review = True
def create(self, subset_name_from_ui, data, pre_create_data):
@@ -165,12 +164,16 @@ class RenderCreator(Creator):
api.get_stub().rename_item(comp_id,
new_comp_name)
- def apply_settings(self, project_settings, system_settings):
+ def apply_settings(self, project_settings):
plugin_settings = (
project_settings["aftereffects"]["create"]["RenderCreator"]
)
self.mark_for_review = plugin_settings["mark_for_review"]
+ self.default_variants = plugin_settings.get(
+ "default_variants",
+ plugin_settings.get("defaults") or []
+ )
def get_detail_description(self):
return """Creator for Render instances
diff --git a/openpype/hosts/aftereffects/plugins/publish/closeAE.py b/openpype/hosts/aftereffects/plugins/publish/closeAE.py
index eff2573e8f..0be20d9f05 100644
--- a/openpype/hosts/aftereffects/plugins/publish/closeAE.py
+++ b/openpype/hosts/aftereffects/plugins/publish/closeAE.py
@@ -15,7 +15,7 @@ class CloseAE(pyblish.api.ContextPlugin):
active = True
hosts = ["aftereffects"]
- targets = ["remotepublish"]
+ targets = ["automated"]
def process(self, context):
self.log.info("CloseAE")
diff --git a/openpype/hosts/aftereffects/plugins/publish/collect_render.py b/openpype/hosts/aftereffects/plugins/publish/collect_render.py
index aa46461915..49874d6cff 100644
--- a/openpype/hosts/aftereffects/plugins/publish/collect_render.py
+++ b/openpype/hosts/aftereffects/plugins/publish/collect_render.py
@@ -138,7 +138,6 @@ class CollectAERender(publish.AbstractCollectRender):
fam = "render.farm"
if fam not in instance.families:
instance.families.append(fam)
- instance.toBeRenderedOn = "deadline"
instance.renderer = "aerender"
instance.farm = True # to skip integrate
if "review" in instance.families:
diff --git a/openpype/hosts/blender/plugins/load/load_blend.py b/openpype/hosts/blender/plugins/load/load_blend.py
index 99f291a5a7..fa41f4374b 100644
--- a/openpype/hosts/blender/plugins/load/load_blend.py
+++ b/openpype/hosts/blender/plugins/load/load_blend.py
@@ -119,7 +119,7 @@ class BlendLoader(plugin.AssetLoader):
context: Full parenthood of representation to load
options: Additional settings dictionary
"""
- libpath = self.fname
+ libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]
diff --git a/openpype/hosts/blender/plugins/load/load_camera_abc.py b/openpype/hosts/blender/plugins/load/load_camera_abc.py
index e5afecff66..05d3fb764d 100644
--- a/openpype/hosts/blender/plugins/load/load_camera_abc.py
+++ b/openpype/hosts/blender/plugins/load/load_camera_abc.py
@@ -100,7 +100,7 @@ class AbcCameraLoader(plugin.AssetLoader):
asset_group = bpy.data.objects.new(group_name, object_data=None)
avalon_container.objects.link(asset_group)
- objects = self._process(libpath, asset_group, group_name)
+ self._process(libpath, asset_group, group_name)
objects = []
nodes = list(asset_group.children)
diff --git a/openpype/hosts/blender/plugins/load/load_camera_fbx.py b/openpype/hosts/blender/plugins/load/load_camera_fbx.py
index b9d05dda0a..3cca6e7fd3 100644
--- a/openpype/hosts/blender/plugins/load/load_camera_fbx.py
+++ b/openpype/hosts/blender/plugins/load/load_camera_fbx.py
@@ -103,7 +103,7 @@ class FbxCameraLoader(plugin.AssetLoader):
asset_group = bpy.data.objects.new(group_name, object_data=None)
avalon_container.objects.link(asset_group)
- objects = self._process(libpath, asset_group, group_name)
+ self._process(libpath, asset_group, group_name)
objects = []
nodes = list(asset_group.children)
diff --git a/openpype/hosts/blender/plugins/publish/extract_abc.py b/openpype/hosts/blender/plugins/publish/extract_abc.py
index f4babc94d3..87159e53f0 100644
--- a/openpype/hosts/blender/plugins/publish/extract_abc.py
+++ b/openpype/hosts/blender/plugins/publish/extract_abc.py
@@ -21,8 +21,6 @@ class ExtractABC(publish.Extractor):
filename = f"{instance.name}.abc"
filepath = os.path.join(stagingdir, filename)
- context = bpy.context
-
# Perform extraction
self.log.info("Performing extraction..")
diff --git a/openpype/hosts/blender/plugins/publish/extract_abc_animation.py b/openpype/hosts/blender/plugins/publish/extract_abc_animation.py
index e141ccaa44..44b2ba3761 100644
--- a/openpype/hosts/blender/plugins/publish/extract_abc_animation.py
+++ b/openpype/hosts/blender/plugins/publish/extract_abc_animation.py
@@ -20,8 +20,6 @@ class ExtractAnimationABC(publish.Extractor):
filename = f"{instance.name}.abc"
filepath = os.path.join(stagingdir, filename)
- context = bpy.context
-
# Perform extraction
self.log.info("Performing extraction..")
diff --git a/openpype/hosts/blender/plugins/publish/extract_camera_abc.py b/openpype/hosts/blender/plugins/publish/extract_camera_abc.py
index a21a59b151..036be7bf3c 100644
--- a/openpype/hosts/blender/plugins/publish/extract_camera_abc.py
+++ b/openpype/hosts/blender/plugins/publish/extract_camera_abc.py
@@ -21,16 +21,11 @@ class ExtractCameraABC(publish.Extractor):
filename = f"{instance.name}.abc"
filepath = os.path.join(stagingdir, filename)
- context = bpy.context
-
# Perform extraction
self.log.info("Performing extraction..")
plugin.deselect_all()
- selected = []
- active = None
-
asset_group = None
for obj in instance:
if obj.get(AVALON_PROPERTY):
diff --git a/openpype/hosts/flame/plugins/load/load_clip.py b/openpype/hosts/flame/plugins/load/load_clip.py
index 338833b449..ca4eab0f63 100644
--- a/openpype/hosts/flame/plugins/load/load_clip.py
+++ b/openpype/hosts/flame/plugins/load/load_clip.py
@@ -48,7 +48,6 @@ class LoadClip(opfapi.ClipLoader):
self.fpd = fproject.current_workspace.desktop
# load clip to timeline and get main variables
- namespace = namespace
version = context['version']
version_data = version.get("data", {})
version_name = version.get("name", None)
diff --git a/openpype/hosts/flame/plugins/load/load_clip_batch.py b/openpype/hosts/flame/plugins/load/load_clip_batch.py
index ca43b94ee9..1f3a017d72 100644
--- a/openpype/hosts/flame/plugins/load/load_clip_batch.py
+++ b/openpype/hosts/flame/plugins/load/load_clip_batch.py
@@ -45,7 +45,6 @@ class LoadClipBatch(opfapi.ClipLoader):
self.batch = options.get("batch") or flame.batch
# load clip to timeline and get main variables
- namespace = namespace
version = context['version']
version_data = version.get("data", {})
version_name = version.get("name", None)
diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py
index 23fdf5e785..e14f960a2b 100644
--- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py
+++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py
@@ -325,7 +325,6 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
def _create_shot_instance(self, context, clip_name, **data):
master_layer = data.get("heroTrack")
hierarchy_data = data.get("hierarchyData")
- asset = data.get("asset")
if not master_layer:
return
diff --git a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/backgrounds_selected_to32bit.py b/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/backgrounds_selected_to32bit.py
deleted file mode 100644
index 1a0a9911ea..0000000000
--- a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/backgrounds_selected_to32bit.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from openpype.hosts.fusion.api import (
- comp_lock_and_undo_chunk,
- get_current_comp
-)
-
-
-def main():
- comp = get_current_comp()
- """Set all selected backgrounds to 32 bit"""
- with comp_lock_and_undo_chunk(comp, 'Selected Backgrounds to 32bit'):
- tools = comp.GetToolList(True, "Background").values()
- for tool in tools:
- tool.Depth = 5
-
-
-main()
diff --git a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/backgrounds_to32bit.py b/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/backgrounds_to32bit.py
deleted file mode 100644
index c2eea505e5..0000000000
--- a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/backgrounds_to32bit.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from openpype.hosts.fusion.api import (
- comp_lock_and_undo_chunk,
- get_current_comp
-)
-
-
-def main():
- comp = get_current_comp()
- """Set all backgrounds to 32 bit"""
- with comp_lock_and_undo_chunk(comp, 'Backgrounds to 32bit'):
- tools = comp.GetToolList(False, "Background").values()
- for tool in tools:
- tool.Depth = 5
-
-
-main()
diff --git a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/loaders_selected_to32bit.py b/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/loaders_selected_to32bit.py
deleted file mode 100644
index 2118767f4d..0000000000
--- a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/loaders_selected_to32bit.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from openpype.hosts.fusion.api import (
- comp_lock_and_undo_chunk,
- get_current_comp
-)
-
-
-def main():
- comp = get_current_comp()
- """Set all selected loaders to 32 bit"""
- with comp_lock_and_undo_chunk(comp, 'Selected Loaders to 32bit'):
- tools = comp.GetToolList(True, "Loader").values()
- for tool in tools:
- tool.Depth = 5
-
-
-main()
diff --git a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/loaders_to32bit.py b/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/loaders_to32bit.py
deleted file mode 100644
index 7dd1f66a5e..0000000000
--- a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/32bit/loaders_to32bit.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from openpype.hosts.fusion.api import (
- comp_lock_and_undo_chunk,
- get_current_comp
-)
-
-
-def main():
- comp = get_current_comp()
- """Set all loaders to 32 bit"""
- with comp_lock_and_undo_chunk(comp, 'Loaders to 32bit'):
- tools = comp.GetToolList(False, "Loader").values()
- for tool in tools:
- tool.Depth = 5
-
-
-main()
diff --git a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/switch_ui.py b/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/switch_ui.py
deleted file mode 100644
index 87322235f5..0000000000
--- a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/switch_ui.py
+++ /dev/null
@@ -1,200 +0,0 @@
-import os
-import sys
-import glob
-import logging
-
-from qtpy import QtWidgets, QtCore
-
-import qtawesome as qta
-
-from openpype.client import get_assets
-from openpype import style
-from openpype.pipeline import (
- install_host,
- get_current_project_name,
-)
-from openpype.hosts.fusion import api
-from openpype.pipeline.context_tools import get_workdir_from_session
-
-log = logging.getLogger("Fusion Switch Shot")
-
-
-class App(QtWidgets.QWidget):
-
- def __init__(self, parent=None):
-
- ################################################
- # |---------------------| |------------------| #
- # |Comp | |Asset | #
- # |[..][ v]| |[ v]| #
- # |---------------------| |------------------| #
- # | Update existing comp [ ] | #
- # |------------------------------------------| #
- # | Switch | #
- # |------------------------------------------| #
- ################################################
-
- QtWidgets.QWidget.__init__(self, parent)
-
- layout = QtWidgets.QVBoxLayout()
-
- # Comp related input
- comp_hlayout = QtWidgets.QHBoxLayout()
- comp_label = QtWidgets.QLabel("Comp file")
- comp_label.setFixedWidth(50)
- comp_box = QtWidgets.QComboBox()
-
- button_icon = qta.icon("fa.folder", color="white")
- open_from_dir = QtWidgets.QPushButton()
- open_from_dir.setIcon(button_icon)
-
- comp_box.setFixedHeight(25)
- open_from_dir.setFixedWidth(25)
- open_from_dir.setFixedHeight(25)
-
- comp_hlayout.addWidget(comp_label)
- comp_hlayout.addWidget(comp_box)
- comp_hlayout.addWidget(open_from_dir)
-
- # Asset related input
- asset_hlayout = QtWidgets.QHBoxLayout()
- asset_label = QtWidgets.QLabel("Shot")
- asset_label.setFixedWidth(50)
-
- asset_box = QtWidgets.QComboBox()
- asset_box.setLineEdit(QtWidgets.QLineEdit())
- asset_box.setFixedHeight(25)
-
- refresh_icon = qta.icon("fa.refresh", color="white")
- refresh_btn = QtWidgets.QPushButton()
- refresh_btn.setIcon(refresh_icon)
-
- asset_box.setFixedHeight(25)
- refresh_btn.setFixedWidth(25)
- refresh_btn.setFixedHeight(25)
-
- asset_hlayout.addWidget(asset_label)
- asset_hlayout.addWidget(asset_box)
- asset_hlayout.addWidget(refresh_btn)
-
- # Options
- options = QtWidgets.QHBoxLayout()
- options.setAlignment(QtCore.Qt.AlignLeft)
-
- current_comp_check = QtWidgets.QCheckBox()
- current_comp_check.setChecked(True)
- current_comp_label = QtWidgets.QLabel("Use current comp")
-
- options.addWidget(current_comp_label)
- options.addWidget(current_comp_check)
-
- accept_btn = QtWidgets.QPushButton("Switch")
-
- layout.addLayout(options)
- layout.addLayout(comp_hlayout)
- layout.addLayout(asset_hlayout)
- layout.addWidget(accept_btn)
-
- self._open_from_dir = open_from_dir
- self._comps = comp_box
- self._assets = asset_box
- self._use_current = current_comp_check
- self._accept_btn = accept_btn
- self._refresh_btn = refresh_btn
-
- self.setWindowTitle("Fusion Switch Shot")
- self.setLayout(layout)
-
- self.resize(260, 140)
- self.setMinimumWidth(260)
- self.setFixedHeight(140)
-
- self.connections()
-
- # Update ui to correct state
- self._on_use_current_comp()
- self._refresh()
-
- def connections(self):
- self._use_current.clicked.connect(self._on_use_current_comp)
- self._open_from_dir.clicked.connect(self._on_open_from_dir)
- self._refresh_btn.clicked.connect(self._refresh)
- self._accept_btn.clicked.connect(self._on_switch)
-
- def _on_use_current_comp(self):
- state = self._use_current.isChecked()
- self._open_from_dir.setEnabled(not state)
- self._comps.setEnabled(not state)
-
- def _on_open_from_dir(self):
-
- start_dir = get_workdir_from_session()
- comp_file, _ = QtWidgets.QFileDialog.getOpenFileName(
- self, "Choose comp", start_dir)
-
- if not comp_file:
- return
-
- # Create completer
- self.populate_comp_box([comp_file])
- self._refresh()
-
- def _refresh(self):
- # Clear any existing items
- self._assets.clear()
-
- asset_names = self.collect_asset_names()
- completer = QtWidgets.QCompleter(asset_names)
-
- self._assets.setCompleter(completer)
- self._assets.addItems(asset_names)
-
- def _on_switch(self):
-
- if not self._use_current.isChecked():
- file_name = self._comps.itemData(self._comps.currentIndex())
- else:
- comp = api.get_current_comp()
- file_name = comp.GetAttrs("COMPS_FileName")
-
- asset = self._assets.currentText()
-
- import colorbleed.scripts.fusion_switch_shot as switch_shot
- switch_shot.switch(asset_name=asset, filepath=file_name, new=True)
-
- def collect_slap_comps(self, directory):
- items = glob.glob("{}/*.comp".format(directory))
- return items
-
- def collect_asset_names(self):
- project_name = get_current_project_name()
- asset_docs = get_assets(project_name, fields=["name"])
- asset_names = {
- asset_doc["name"]
- for asset_doc in asset_docs
- }
- return list(asset_names)
-
- def populate_comp_box(self, files):
- """Ensure we display the filename only but the path is stored as well
-
- Args:
- files (list): list of full file path [path/to/item/item.ext,]
-
- Returns:
- None
- """
-
- for f in files:
- filename = os.path.basename(f)
- self._comps.addItem(filename, userData=f)
-
-
-if __name__ == '__main__':
- install_host(api)
-
- app = QtWidgets.QApplication(sys.argv)
- window = App()
- window.setStyleSheet(style.load_stylesheet())
- window.show()
- sys.exit(app.exec_())
diff --git a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/update_loader_ranges.py b/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/update_loader_ranges.py
deleted file mode 100644
index 3d2d1ecfa6..0000000000
--- a/openpype/hosts/fusion/deploy/Scripts/Comp/OpenPype/update_loader_ranges.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""Forces Fusion to 'retrigger' the Loader to update.
-
-Warning:
- This might change settings like 'Reverse', 'Loop', trims and other
- settings of the Loader. So use this at your own risk.
-
-"""
-from openpype.hosts.fusion.api.pipeline import (
- get_current_comp,
- comp_lock_and_undo_chunk
-)
-
-
-def update_loader_ranges():
- comp = get_current_comp()
- with comp_lock_and_undo_chunk(comp, "Reload clip time ranges"):
- tools = comp.GetToolList(True, "Loader").values()
- for tool in tools:
-
- # Get tool attributes
- tool_a = tool.GetAttrs()
- clipTable = tool_a['TOOLST_Clip_Name']
- altclipTable = tool_a['TOOLST_AltClip_Name']
- startTime = tool_a['TOOLNT_Clip_Start']
- old_global_in = tool.GlobalIn[comp.CurrentTime]
-
- # Reapply
- for index, _ in clipTable.items():
- time = startTime[index]
- tool.Clip[time] = tool.Clip[time]
-
- for index, _ in altclipTable.items():
- time = startTime[index]
- tool.ProxyFilename[time] = tool.ProxyFilename[time]
-
- tool.GlobalIn[comp.CurrentTime] = old_global_in
-
-
-if __name__ == '__main__':
- update_loader_ranges()
diff --git a/openpype/hosts/fusion/deploy/fusion_shared.prefs b/openpype/hosts/fusion/deploy/fusion_shared.prefs
index b379ea7c66..93b08aa886 100644
--- a/openpype/hosts/fusion/deploy/fusion_shared.prefs
+++ b/openpype/hosts/fusion/deploy/fusion_shared.prefs
@@ -5,7 +5,7 @@ Global = {
Map = {
["OpenPype:"] = "$(OPENPYPE_FUSION)/deploy",
["Config:"] = "UserPaths:Config;OpenPype:Config",
- ["Scripts:"] = "UserPaths:Scripts;Reactor:System/Scripts;OpenPype:Scripts",
+ ["Scripts:"] = "UserPaths:Scripts;Reactor:System/Scripts",
},
},
Script = {
diff --git a/openpype/hosts/fusion/plugins/create/create_saver.py b/openpype/hosts/fusion/plugins/create/create_saver.py
index 04898d0a45..39edca4de3 100644
--- a/openpype/hosts/fusion/plugins/create/create_saver.py
+++ b/openpype/hosts/fusion/plugins/create/create_saver.py
@@ -30,10 +30,6 @@ class CreateSaver(NewCreator):
instance_attributes = [
"reviewable"
]
- default_variants = [
- "Main",
- "Mask"
- ]
# TODO: This should be renamed together with Nuke so it is aligned
temp_rendering_path_template = (
@@ -250,11 +246,7 @@ class CreateSaver(NewCreator):
label="Review",
)
- def apply_settings(
- self,
- project_settings,
- system_settings
- ):
+ def apply_settings(self, project_settings):
"""Method called on initialization of plugin to apply settings."""
# plugin settings
diff --git a/openpype/hosts/fusion/plugins/publish/collect_instances.py b/openpype/hosts/fusion/plugins/publish/collect_instances.py
index 6016baa2a9..4d6da79b77 100644
--- a/openpype/hosts/fusion/plugins/publish/collect_instances.py
+++ b/openpype/hosts/fusion/plugins/publish/collect_instances.py
@@ -85,5 +85,5 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
# Add review family if the instance is marked as 'review'
# This could be done through a 'review' Creator attribute.
if instance.data.get("review", False):
- self.log.info("Adding review family..")
+ self.log.debug("Adding review family..")
instance.data["families"].append("review")
diff --git a/openpype/hosts/fusion/plugins/publish/collect_render.py b/openpype/hosts/fusion/plugins/publish/collect_render.py
index a20a142701..341f3f191a 100644
--- a/openpype/hosts/fusion/plugins/publish/collect_render.py
+++ b/openpype/hosts/fusion/plugins/publish/collect_render.py
@@ -108,7 +108,6 @@ class CollectFusionRender(
fam = "render.farm"
if fam not in instance.families:
instance.families.append(fam)
- instance.toBeRenderedOn = "deadline"
instance.farm = True # to skip integrate
if "review" in instance.families:
# to skip ExtractReview locally
diff --git a/openpype/hosts/harmony/plugins/load/load_template.py b/openpype/hosts/harmony/plugins/load/load_template.py
index f3c69a9104..a78a1bf1ec 100644
--- a/openpype/hosts/harmony/plugins/load/load_template.py
+++ b/openpype/hosts/harmony/plugins/load/load_template.py
@@ -82,7 +82,6 @@ class TemplateLoader(load.LoaderPlugin):
node = harmony.find_node_by_name(node_name, "GROUP")
self_name = self.__class__.__name__
- update_and_replace = False
if is_representation_from_latest(representation):
self._set_green(node)
else:
diff --git a/openpype/hosts/harmony/plugins/publish/collect_farm_render.py b/openpype/hosts/harmony/plugins/publish/collect_farm_render.py
index 5e9b9094a7..af825c052a 100644
--- a/openpype/hosts/harmony/plugins/publish/collect_farm_render.py
+++ b/openpype/hosts/harmony/plugins/publish/collect_farm_render.py
@@ -147,13 +147,13 @@ class CollectFarmRender(publish.AbstractCollectRender):
attachTo=False,
setMembers=[node],
publish=info[4],
- review=False,
renderer=None,
priority=50,
name=node.split("/")[1],
family="render.farm",
families=["render.farm"],
+ farm=True,
resolutionWidth=context.data["resolutionWidth"],
resolutionHeight=context.data["resolutionHeight"],
@@ -174,7 +174,6 @@ class CollectFarmRender(publish.AbstractCollectRender):
outputFormat=info[1],
outputStartFrame=info[3],
leadingZeros=info[2],
- toBeRenderedOn='deadline',
ignoreFrameHandleCheck=True
)
diff --git a/openpype/hosts/hiero/api/plugin.py b/openpype/hosts/hiero/api/plugin.py
index 65a4009756..52f96261b2 100644
--- a/openpype/hosts/hiero/api/plugin.py
+++ b/openpype/hosts/hiero/api/plugin.py
@@ -317,20 +317,6 @@ class Spacer(QtWidgets.QWidget):
self.setLayout(layout)
-def get_reference_node_parents(ref):
- """Return all parent reference nodes of reference node
-
- Args:
- ref (str): reference node.
-
- Returns:
- list: The upstream parent reference nodes.
-
- """
- parents = []
- return parents
-
-
class SequenceLoader(LoaderPlugin):
"""A basic SequenceLoader for Resolve
diff --git a/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py b/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py
index d455ad4a4e..fcb1ab27a0 100644
--- a/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py
+++ b/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py
@@ -43,7 +43,6 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
if review and review_track_index == _track_index:
continue
for sitem in sub_track_items:
- effect = None
# make sure this subtrack item is relative of track item
if ((track_item not in sitem.linkedItems())
and (len(sitem.linkedItems()) > 0)):
@@ -53,7 +52,6 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
continue
effect = self.add_effect(_track_index, sitem)
-
if effect:
effects.update(effect)
diff --git a/openpype/hosts/houdini/api/colorspace.py b/openpype/hosts/houdini/api/colorspace.py
index 7047644225..cc40b9df1c 100644
--- a/openpype/hosts/houdini/api/colorspace.py
+++ b/openpype/hosts/houdini/api/colorspace.py
@@ -1,7 +1,7 @@
import attr
import hou
from openpype.hosts.houdini.api.lib import get_color_management_preferences
-
+from openpype.pipeline.colorspace import get_display_view_colorspace_name
@attr.s
class LayerMetadata(object):
@@ -54,3 +54,16 @@ class ARenderProduct(object):
)
]
return colorspace_data
+
+
+def get_default_display_view_colorspace():
+ """Returns the colorspace attribute of the default (display, view) pair.
+
+ It's used for 'ociocolorspace' parm in OpenGL Node."""
+
+ prefs = get_color_management_preferences()
+ return get_display_view_colorspace_name(
+ config_path=prefs["config"],
+ display=prefs["display"],
+ view=prefs["view"]
+ )
diff --git a/openpype/hosts/houdini/api/creator_node_shelves.py b/openpype/hosts/houdini/api/creator_node_shelves.py
index 7c6122cffe..1f9fef7417 100644
--- a/openpype/hosts/houdini/api/creator_node_shelves.py
+++ b/openpype/hosts/houdini/api/creator_node_shelves.py
@@ -57,28 +57,31 @@ def create_interactive(creator_identifier, **kwargs):
list: The created instances.
"""
-
- # TODO Use Qt instead
- result, variant = hou.ui.readInput('Define variant name',
- buttons=("Ok", "Cancel"),
- initial_contents='Main',
- title="Define variant",
- help="Set the variant for the "
- "publish instance",
- close_choice=1)
- if result == 1:
- # User interrupted
- return
- variant = variant.strip()
- if not variant:
- raise RuntimeError("Empty variant value entered.")
-
host = registered_host()
context = CreateContext(host)
creator = context.manual_creators.get(creator_identifier)
if not creator:
- raise RuntimeError("Invalid creator identifier: "
- "{}".format(creator_identifier))
+ raise RuntimeError("Invalid creator identifier: {}".format(
+ creator_identifier)
+ )
+
+ # TODO Use Qt instead
+ result, variant = hou.ui.readInput(
+ "Define variant name",
+ buttons=("Ok", "Cancel"),
+ initial_contents=creator.get_default_variant(),
+ title="Define variant",
+ help="Set the variant for the publish instance",
+ close_choice=1
+ )
+
+ if result == 1:
+ # User interrupted
+ return
+
+ variant = variant.strip()
+ if not variant:
+ raise RuntimeError("Empty variant value entered.")
# TODO: Once more elaborate unique create behavior should exist per Creator
# instead of per network editor area then we should move this from here
diff --git a/openpype/hosts/houdini/api/lib.py b/openpype/hosts/houdini/api/lib.py
index b03f8c8fc1..75c7ff9fee 100644
--- a/openpype/hosts/houdini/api/lib.py
+++ b/openpype/hosts/houdini/api/lib.py
@@ -22,9 +22,12 @@ log = logging.getLogger(__name__)
JSON_PREFIX = "JSON:::"
-def get_asset_fps():
+def get_asset_fps(asset_doc=None):
"""Return current asset fps."""
- return get_current_project_asset()["data"].get("fps")
+
+ if asset_doc is None:
+ asset_doc = get_current_project_asset(fields=["data.fps"])
+ return asset_doc["data"]["fps"]
def set_id(node, unique_id, overwrite=False):
@@ -472,14 +475,19 @@ def maintained_selection():
def reset_framerange():
- """Set frame range to current asset"""
+ """Set frame range and FPS to current asset"""
+ # Get asset data
project_name = get_current_project_name()
asset_name = get_current_asset_name()
# Get the asset ID from the database for the asset of current context
asset_doc = get_asset_by_name(project_name, asset_name)
asset_data = asset_doc["data"]
+ # Get FPS
+ fps = get_asset_fps(asset_doc)
+
+ # Get Start and End Frames
frame_start = asset_data.get("frameStart")
frame_end = asset_data.get("frameEnd")
@@ -493,6 +501,9 @@ def reset_framerange():
frame_start -= int(handle_start)
frame_end += int(handle_end)
+ # Set frame range and FPS
+ print("Setting scene FPS to {}".format(int(fps)))
+ set_scene_fps(fps)
hou.playbar.setFrameRange(frame_start, frame_end)
hou.playbar.setPlaybackRange(frame_start, frame_end)
hou.setFrame(frame_start)
diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py
index 8a26bbb504..c9ae801af5 100644
--- a/openpype/hosts/houdini/api/pipeline.py
+++ b/openpype/hosts/houdini/api/pipeline.py
@@ -25,7 +25,6 @@ from openpype.lib import (
emit_event,
)
-from .lib import get_asset_fps
log = logging.getLogger("openpype.hosts.houdini")
@@ -304,6 +303,28 @@ def on_save():
lib.set_id(node, new_id, overwrite=False)
+def _show_outdated_content_popup():
+ # Get main window
+ parent = lib.get_main_window()
+ if parent is None:
+ log.info("Skipping outdated content pop-up "
+ "because Houdini window can't be found.")
+ else:
+ from openpype.widgets import popup
+
+ # Show outdated pop-up
+ def _on_show_inventory():
+ from openpype.tools.utils import host_tools
+ host_tools.show_scene_inventory(parent=parent)
+
+ dialog = popup.Popup(parent=parent)
+ dialog.setWindowTitle("Houdini scene has outdated content")
+ dialog.setMessage("There are outdated containers in "
+ "your Houdini scene.")
+ dialog.on_clicked.connect(_on_show_inventory)
+ dialog.show()
+
+
def on_open():
if not hou.isUIAvailable():
@@ -317,28 +338,18 @@ def on_open():
lib.validate_fps()
if any_outdated_containers():
- from openpype.widgets import popup
-
- log.warning("Scene has outdated content.")
-
- # Get main window
parent = lib.get_main_window()
if parent is None:
- log.info("Skipping outdated content pop-up "
- "because Houdini window can't be found.")
+ # When opening Houdini with last workfile on launch the UI hasn't
+ # initialized yet completely when the `on_open` callback triggers.
+ # We defer the dialog popup to wait for the UI to become available.
+ # We assume it will open because `hou.isUIAvailable()` returns True
+ import hdefereval
+ hdefereval.executeDeferred(_show_outdated_content_popup)
else:
+ _show_outdated_content_popup()
- # Show outdated pop-up
- def _on_show_inventory():
- from openpype.tools.utils import host_tools
- host_tools.show_scene_inventory(parent=parent)
-
- dialog = popup.Popup(parent=parent)
- dialog.setWindowTitle("Houdini scene has outdated content")
- dialog.setMessage("There are outdated containers in "
- "your Houdini scene.")
- dialog.on_clicked.connect(_on_show_inventory)
- dialog.show()
+ log.warning("Scene has outdated content.")
def on_new():
@@ -385,11 +396,6 @@ def _set_context_settings():
None
"""
- # Set new scene fps
- fps = get_asset_fps()
- print("Setting scene FPS to %i" % fps)
- lib.set_scene_fps(fps)
-
lib.reset_framerange()
diff --git a/openpype/hosts/houdini/api/plugin.py b/openpype/hosts/houdini/api/plugin.py
index 70c837205e..730a627dc3 100644
--- a/openpype/hosts/houdini/api/plugin.py
+++ b/openpype/hosts/houdini/api/plugin.py
@@ -296,7 +296,7 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
"""
return [hou.ropNodeTypeCategory()]
- def apply_settings(self, project_settings, system_settings):
+ def apply_settings(self, project_settings):
"""Method called on initialization of plugin to apply settings."""
settings_name = self.settings_name
diff --git a/openpype/hosts/houdini/plugins/create/create_review.py b/openpype/hosts/houdini/plugins/create/create_review.py
index ab06b30c35..60c34a358b 100644
--- a/openpype/hosts/houdini/plugins/create/create_review.py
+++ b/openpype/hosts/houdini/plugins/create/create_review.py
@@ -3,6 +3,9 @@
from openpype.hosts.houdini.api import plugin
from openpype.lib import EnumDef, BoolDef, NumberDef
+import os
+import hou
+
class CreateReview(plugin.HoudiniCreator):
"""Review with OpenGL ROP"""
@@ -13,7 +16,6 @@ class CreateReview(plugin.HoudiniCreator):
icon = "video-camera"
def create(self, subset_name, instance_data, pre_create_data):
- import hou
instance_data.pop("active", None)
instance_data.update({"node_type": "opengl"})
@@ -82,6 +84,11 @@ class CreateReview(plugin.HoudiniCreator):
instance_node.setParms(parms)
+ # Set OCIO Colorspace to the default output colorspace
+ # if there's OCIO
+ if os.getenv("OCIO"):
+ self.set_colorcorrect_to_default_view_space(instance_node)
+
to_lock = ["id", "family"]
self.lock_parameters(instance_node, to_lock)
@@ -123,3 +130,23 @@ class CreateReview(plugin.HoudiniCreator):
minimum=0.0001,
decimals=3)
]
+
+ def set_colorcorrect_to_default_view_space(self,
+ instance_node):
+ """Set ociocolorspace to the default output space."""
+ from openpype.hosts.houdini.api.colorspace import get_default_display_view_colorspace # noqa
+
+ # set Color Correction parameter to OpenColorIO
+ instance_node.setParms({"colorcorrect": 2})
+
+ # Get default view space for ociocolorspace parm.
+ default_view_space = get_default_display_view_colorspace()
+ instance_node.setParms(
+ {"ociocolorspace": default_view_space}
+ )
+
+ self.log.debug(
+ "'OCIO Colorspace' parm on '{}' has been set to "
+ "the default view color space '{}'"
+ .format(instance_node, default_view_space)
+ )
diff --git a/openpype/hosts/houdini/plugins/create/create_vbd_cache.py b/openpype/hosts/houdini/plugins/create/create_vbd_cache.py
index c015cebd49..9c96e48e3a 100644
--- a/openpype/hosts/houdini/plugins/create/create_vbd_cache.py
+++ b/openpype/hosts/houdini/plugins/create/create_vbd_cache.py
@@ -33,7 +33,7 @@ class CreateVDBCache(plugin.HoudiniCreator):
}
if self.selected_nodes:
- parms["soppath"] = self.selected_nodes[0].path()
+ parms["soppath"] = self.get_sop_node_path(self.selected_nodes[0])
instance_node.setParms(parms)
@@ -42,3 +42,63 @@ class CreateVDBCache(plugin.HoudiniCreator):
hou.ropNodeTypeCategory(),
hou.sopNodeTypeCategory()
]
+
+ def get_sop_node_path(self, selected_node):
+ """Get Sop Path of the selected node.
+
+ Although Houdini allows ObjNode path on `sop_path` for the
+ the ROP node, we prefer it set to the SopNode path explicitly.
+ """
+
+ # Allow sop level paths (e.g. /obj/geo1/box1)
+ if isinstance(selected_node, hou.SopNode):
+ self.log.debug(
+ "Valid SopNode selection, 'SOP Path' in ROP will"
+ " be set to '%s'.", selected_node.path()
+ )
+ return selected_node.path()
+
+ # Allow object level paths to Geometry nodes (e.g. /obj/geo1)
+ # but do not allow other object level nodes types like cameras, etc.
+ elif isinstance(selected_node, hou.ObjNode) and \
+ selected_node.type().name() == "geo":
+
+ # Try to find output node.
+ sop_node = self.get_obj_output(selected_node)
+ if sop_node:
+ self.log.debug(
+ "Valid ObjNode selection, 'SOP Path' in ROP will "
+ "be set to the child path '%s'.", sop_node.path()
+ )
+ return sop_node.path()
+
+ self.log.debug(
+ "Selection isn't valid. 'SOP Path' in ROP will be empty."
+ )
+ return ""
+
+ def get_obj_output(self, obj_node):
+ """Try to find output node.
+
+ If any output nodes are present, return the output node with
+ the minimum 'outputidx'
+ If no output nodes are present, return the node with display flag
+ If no nodes are present at all, return None
+ """
+
+ outputs = obj_node.subnetOutputs()
+
+ # if obj_node is empty
+ if not outputs:
+ return
+
+ # if obj_node has one output child whether its
+ # sop output node or a node with the render flag
+ elif len(outputs) == 1:
+ return outputs[0]
+
+ # if there are more than one, then it has multiple output nodes
+ # return the one with the minimum 'outputidx'
+ else:
+ return min(outputs,
+ key=lambda node: node.evalParm('outputidx'))
diff --git a/openpype/hosts/houdini/plugins/load/load_bgeo.py b/openpype/hosts/houdini/plugins/load/load_bgeo.py
index 22680178c0..489bf944ed 100644
--- a/openpype/hosts/houdini/plugins/load/load_bgeo.py
+++ b/openpype/hosts/houdini/plugins/load/load_bgeo.py
@@ -34,7 +34,6 @@ class BgeoLoader(load.LoaderPlugin):
# Create a new geo node
container = obj.createNode("geo", node_name=node_name)
- is_sequence = bool(context["representation"]["context"].get("frame"))
# Remove the file node, it only loads static meshes
# Houdini 17 has removed the file node from the geo node
diff --git a/openpype/hosts/houdini/plugins/load/load_hda.py b/openpype/hosts/houdini/plugins/load/load_hda.py
index 57edc341a3..9630716253 100644
--- a/openpype/hosts/houdini/plugins/load/load_hda.py
+++ b/openpype/hosts/houdini/plugins/load/load_hda.py
@@ -59,6 +59,9 @@ class HdaLoader(load.LoaderPlugin):
def_paths = [d.libraryFilePath() for d in defs]
new = def_paths.index(file_path)
defs[new].setIsPreferred(True)
+ hda_node.setParms({
+ "representation": str(representation["_id"])
+ })
def remove(self, container):
node = container["node"]
diff --git a/openpype/hosts/houdini/plugins/publish/collect_output_node.py b/openpype/hosts/houdini/plugins/publish/collect_output_node.py
index 601ed17b39..0b27678ed0 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_output_node.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_output_node.py
@@ -1,5 +1,7 @@
import pyblish.api
+from openpype.pipeline.publish import KnownPublishError
+
class CollectOutputSOPPath(pyblish.api.InstancePlugin):
"""Collect the out node's SOP/COP Path value."""
@@ -58,8 +60,8 @@ class CollectOutputSOPPath(pyblish.api.InstancePlugin):
elif node_type == "Redshift_Proxy_Output":
out_node = node.parm("RS_archive_sopPath").evalAsNode()
else:
- raise ValueError(
- "ROP node type '%s' is" " not supported." % node_type
+ raise KnownPublishError(
+ "ROP node type '{}' is not supported.".format(node_type)
)
if not out_node:
diff --git a/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py b/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py
index d4fe37f993..277f922ba4 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py
@@ -80,14 +80,9 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin):
def get_beauty_render_product(self, prefix, suffix=""):
"""Return the beauty output filename if render element enabled
"""
+ # Remove aov suffix from the product: `prefix.aov_suffix` -> `prefix`
aov_parm = ".{}".format(suffix)
- beauty_product = None
- if aov_parm in prefix:
- beauty_product = prefix.replace(aov_parm, "")
- else:
- beauty_product = prefix
-
- return beauty_product
+ return prefix.replace(aov_parm, "")
def get_render_element_name(self, node, prefix, suffix=""):
"""Return the output filename using the AOV prefix and suffix
diff --git a/openpype/hosts/houdini/plugins/publish/increment_current_file.py b/openpype/hosts/houdini/plugins/publish/increment_current_file.py
index 2493b28bc1..3569de7693 100644
--- a/openpype/hosts/houdini/plugins/publish/increment_current_file.py
+++ b/openpype/hosts/houdini/plugins/publish/increment_current_file.py
@@ -2,7 +2,7 @@ import pyblish.api
from openpype.lib import version_up
from openpype.pipeline import registered_host
-from openpype.action import get_errored_plugins_from_data
+from openpype.pipeline.publish import get_errored_plugins_from_context
from openpype.hosts.houdini.api import HoudiniHost
from openpype.pipeline.publish import KnownPublishError
@@ -27,7 +27,7 @@ class IncrementCurrentFile(pyblish.api.ContextPlugin):
def process(self, context):
- errored_plugins = get_errored_plugins_from_data(context)
+ errored_plugins = get_errored_plugins_from_context(context)
if any(
plugin.__name__ == "HoudiniSubmitPublishDeadline"
for plugin in errored_plugins
@@ -40,9 +40,10 @@ class IncrementCurrentFile(pyblish.api.ContextPlugin):
# Filename must not have changed since collecting
host = registered_host() # type: HoudiniHost
current_file = host.current_file()
- assert (
- context.data["currentFile"] == current_file
- ), "Collected filename mismatches from current scene name."
+ if context.data["currentFile"] != current_file:
+ raise KnownPublishError(
+ "Collected filename mismatches from current scene name."
+ )
new_filepath = version_up(current_file)
host.save_workfile(new_filepath)
diff --git a/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py b/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py
index 4878738ed3..79387fbef5 100644
--- a/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py
+++ b/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py
@@ -1,5 +1,6 @@
import pyblish.api
+from openpype.pipeline.publish import PublishValidationError
from openpype.hosts.houdini.api import lib
import hou
@@ -30,7 +31,7 @@ class ValidateAnimationSettings(pyblish.api.InstancePlugin):
invalid = self.get_invalid(instance)
if invalid:
- raise RuntimeError(
+ raise PublishValidationError(
"Output settings do no match for '%s'" % instance
)
diff --git a/openpype/hosts/houdini/plugins/publish/validate_remote_publish.py b/openpype/hosts/houdini/plugins/publish/validate_remote_publish.py
index 4e8e5fc0e8..4f71d79382 100644
--- a/openpype/hosts/houdini/plugins/publish/validate_remote_publish.py
+++ b/openpype/hosts/houdini/plugins/publish/validate_remote_publish.py
@@ -36,11 +36,11 @@ class ValidateRemotePublishOutNode(pyblish.api.ContextPlugin):
if node.parm("shellexec").eval():
self.raise_error("Must not execute in shell")
if node.parm("prerender").eval() != cmd:
- self.raise_error(("REMOTE_PUBLISH node does not have "
- "correct prerender script."))
+ self.raise_error("REMOTE_PUBLISH node does not have "
+ "correct prerender script.")
if node.parm("lprerender").eval() != "python":
- self.raise_error(("REMOTE_PUBLISH node prerender script "
- "type not set to 'python'"))
+ self.raise_error("REMOTE_PUBLISH node prerender script "
+ "type not set to 'python'")
@classmethod
def repair(cls, context):
@@ -48,5 +48,4 @@ class ValidateRemotePublishOutNode(pyblish.api.ContextPlugin):
lib.create_remote_publish_node(force=True)
def raise_error(self, message):
- self.log.error(message)
- raise PublishValidationError(message, title=self.label)
+ raise PublishValidationError(message)
diff --git a/openpype/hosts/houdini/plugins/publish/validate_review_colorspace.py b/openpype/hosts/houdini/plugins/publish/validate_review_colorspace.py
new file mode 100644
index 0000000000..03ecd1b052
--- /dev/null
+++ b/openpype/hosts/houdini/plugins/publish/validate_review_colorspace.py
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+import pyblish.api
+from openpype.pipeline import (
+ PublishValidationError,
+ OptionalPyblishPluginMixin
+)
+from openpype.pipeline.publish import RepairAction
+from openpype.hosts.houdini.api.action import SelectROPAction
+
+import os
+import hou
+
+
+class SetDefaultViewSpaceAction(RepairAction):
+ label = "Set default view colorspace"
+ icon = "mdi.monitor"
+
+
+class ValidateReviewColorspace(pyblish.api.InstancePlugin,
+ OptionalPyblishPluginMixin):
+ """Validate Review Colorspace parameters.
+
+ It checks if 'OCIO Colorspace' parameter was set to valid value.
+ """
+
+ order = pyblish.api.ValidatorOrder + 0.1
+ families = ["review"]
+ hosts = ["houdini"]
+ label = "Validate Review Colorspace"
+ actions = [SetDefaultViewSpaceAction, SelectROPAction]
+
+ optional = True
+
+ def process(self, instance):
+
+ if not self.is_active(instance.data):
+ return
+
+ if os.getenv("OCIO") is None:
+ self.log.debug(
+ "Using Houdini's Default Color Management, "
+ " skipping check.."
+ )
+ return
+
+ rop_node = hou.node(instance.data["instance_node"])
+ if rop_node.evalParm("colorcorrect") != 2:
+ # any colorspace settings other than default requires
+ # 'Color Correct' parm to be set to 'OpenColorIO'
+ raise PublishValidationError(
+ "'Color Correction' parm on '{}' ROP must be set to"
+ " 'OpenColorIO'".format(rop_node.path())
+ )
+
+ if rop_node.evalParm("ociocolorspace") not in \
+ hou.Color.ocio_spaces():
+
+ raise PublishValidationError(
+ "Invalid value: Colorspace name doesn't exist.\n"
+ "Check 'OCIO Colorspace' parameter on '{}' ROP"
+ .format(rop_node.path())
+ )
+
+ @classmethod
+ def repair(cls, instance):
+ """Set Default View Space Action.
+
+ It is a helper action more than a repair action,
+ used to set colorspace on opengl node to the default view.
+ """
+ from openpype.hosts.houdini.api.colorspace import get_default_display_view_colorspace # noqa
+
+ rop_node = hou.node(instance.data["instance_node"])
+
+ if rop_node.evalParm("colorcorrect") != 2:
+ rop_node.setParms({"colorcorrect": 2})
+ cls.log.debug(
+ "'Color Correction' parm on '{}' has been set to"
+ " 'OpenColorIO'".format(rop_node.path())
+ )
+
+ # Get default view colorspace name
+ default_view_space = get_default_display_view_colorspace()
+
+ rop_node.setParms({"ociocolorspace": default_view_space})
+ cls.log.info(
+ "'OCIO Colorspace' parm on '{}' has been set to "
+ "the default view color space '{}'"
+ .format(rop_node, default_view_space)
+ )
diff --git a/openpype/hosts/houdini/plugins/publish/validate_usd_render_product_names.py b/openpype/hosts/houdini/plugins/publish/validate_usd_render_product_names.py
index 02c44ab94e..1daa96f2b9 100644
--- a/openpype/hosts/houdini/plugins/publish/validate_usd_render_product_names.py
+++ b/openpype/hosts/houdini/plugins/publish/validate_usd_render_product_names.py
@@ -24,7 +24,7 @@ class ValidateUSDRenderProductNames(pyblish.api.InstancePlugin):
if not os.path.isabs(filepath):
invalid.append(
- "Output file path is not " "absolute path: %s" % filepath
+ "Output file path is not absolute path: %s" % filepath
)
if invalid:
diff --git a/openpype/hosts/houdini/startup/MainMenuCommon.xml b/openpype/hosts/houdini/startup/MainMenuCommon.xml
index 47a4653d5d..5818a117eb 100644
--- a/openpype/hosts/houdini/startup/MainMenuCommon.xml
+++ b/openpype/hosts/houdini/startup/MainMenuCommon.xml
@@ -2,7 +2,19 @@