mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into enhancement/OP-7072_Validate-Loaded-Plugins
This commit is contained in:
commit
c05bb7bab4
43 changed files with 898 additions and 123 deletions
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -35,6 +35,8 @@ body:
|
|||
label: Version
|
||||
description: What version are you running? Look to OpenPype Tray
|
||||
options:
|
||||
- 3.17.6-nightly.1
|
||||
- 3.17.5
|
||||
- 3.17.5-nightly.3
|
||||
- 3.17.5-nightly.2
|
||||
- 3.17.5-nightly.1
|
||||
|
|
@ -133,8 +135,6 @@ body:
|
|||
- 3.15.1
|
||||
- 3.15.1-nightly.6
|
||||
- 3.15.1-nightly.5
|
||||
- 3.15.1-nightly.4
|
||||
- 3.15.1-nightly.3
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
|
|
|||
457
CHANGELOG.md
457
CHANGELOG.md
|
|
@ -1,6 +1,463 @@
|
|||
# Changelog
|
||||
|
||||
|
||||
## [3.17.5](https://github.com/ynput/OpenPype/tree/3.17.5)
|
||||
|
||||
|
||||
[Full Changelog](https://github.com/ynput/OpenPype/compare/3.17.4...3.17.5)
|
||||
|
||||
### **🆕 New features**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Fusion: Add USD loader <a href="https://github.com/ynput/OpenPype/pull/4896">#4896</a></summary>
|
||||
|
||||
Add an OpenPype managed USD loader (`uLoader`) for Fusion.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Fusion: Resolution validator <a href="https://github.com/ynput/OpenPype/pull/5325">#5325</a></summary>
|
||||
|
||||
Added a resolution validator.The code is from my old PR (https://github.com/ynput/OpenPype/pull/4921) that I closed because the PR also contained a frame range validator that no longer is needed.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Context Selection tool: Refactor Context tool (for AYON) <a href="https://github.com/ynput/OpenPype/pull/5766">#5766</a></summary>
|
||||
|
||||
Context selection tool has AYON variant.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>AYON: Use AYON username for user in template data <a href="https://github.com/ynput/OpenPype/pull/5842">#5842</a></summary>
|
||||
|
||||
Use ayon username for template data in AYON mode.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Testing: app_group flag <a href="https://github.com/ynput/OpenPype/pull/5869">#5869</a></summary>
|
||||
|
||||
`app_group` command flag. This is for changing which flavour of the host to launch. In the case of Maya, you can launch Maya and MayaPy, but it can be used for the Nuke family as well.Split from #5644
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **🚀 Enhancements**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Enhancement: Fusion fix saver creation + minor Blender/Fusion logging tweaks <a href="https://github.com/ynput/OpenPype/pull/5558">#5558</a></summary>
|
||||
|
||||
- Blender change logs to `debug` level in preparation for new publisher artist facing reports (note that it currently still uses the old publisher)
|
||||
- Fusion: Create Saver fix redeclaration of default_variants
|
||||
- Fusion: Fix saver being created in incorrect state without saving directly after create
|
||||
- Fusion: Allow reset frame range on render family
|
||||
- Fusion: Tweak logging level for artist-facing report
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Resolve: load clip to timeline at set time <a href="https://github.com/ynput/OpenPype/pull/5665">#5665</a></summary>
|
||||
|
||||
It is possible to load clip to correct place on timeline.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Nuke: Optional Deadline workfile dependency. <a href="https://github.com/ynput/OpenPype/pull/5732">#5732</a></summary>
|
||||
|
||||
Adds option to add the workfile as dependency for the Deadline job.Think it used to have something like this, but it disappeared. Usecase is for remote workflow where the Nuke script needs to be synced before the job can start.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Enhancement/houdini rearrange ayon houdini settings files <a href="https://github.com/ynput/OpenPype/pull/5748">#5748</a></summary>
|
||||
|
||||
Rearranging Houdini Settings to be more readable, easier to edit, update settings (include all families/product types)This PR is mainly for Ayon Settings to have more organized files. For Openpype, I'll make sure that each Houdini setting in Ayon has an equivalent in Openpype.
|
||||
- [x] update Ayon settings, fix typos and remove deprecated settings.
|
||||
- [x] Sync with Openpype
|
||||
- [x] Test in Openpype
|
||||
- [x] Test in Ayon
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Chore: updating create ayon addon script <a href="https://github.com/ynput/OpenPype/pull/5822">#5822</a></summary>
|
||||
|
||||
Adding developers environment options.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Max: Implement Validator for Properties/Attributes Value Check <a href="https://github.com/ynput/OpenPype/pull/5824">#5824</a></summary>
|
||||
|
||||
Add optional validator which can check if the property attributes are valid in Max
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Nuke: Remove unused 'get_render_path' function <a href="https://github.com/ynput/OpenPype/pull/5826">#5826</a></summary>
|
||||
|
||||
Remove unused function `get_render_path` from nuke integration.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Chore: Limit current context template data function <a href="https://github.com/ynput/OpenPype/pull/5845">#5845</a></summary>
|
||||
|
||||
Current implementation of `get_current_context_template_data` does return the same values as base template data function `get_template_data`.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Max: Make sure Collect Render not ignoring instance asset <a href="https://github.com/ynput/OpenPype/pull/5847">#5847</a></summary>
|
||||
|
||||
- Make sure Collect Render is not always using asset from context.
|
||||
- Make sure Scene version being collected
|
||||
- Clean up unnecessary uses of code in the collector.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Ftrack: Events are not processed if project is not available in OpenPype <a href="https://github.com/ynput/OpenPype/pull/5853">#5853</a></summary>
|
||||
|
||||
Events that happened on project which is not in OpenPype is not processed.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Nuke: Add Nuke 11.0 as default setting <a href="https://github.com/ynput/OpenPype/pull/5855">#5855</a></summary>
|
||||
|
||||
Found I needed Nuke 11.0 in the default settings to help with unit testing.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>TVPaint: Code cleanup <a href="https://github.com/ynput/OpenPype/pull/5857">#5857</a></summary>
|
||||
|
||||
Removed unused import. Use `AYON` label in ayon mode. Removed unused data in publish context `"previous_context"`.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>AYON settings: Use correct label for follow workfile version <a href="https://github.com/ynput/OpenPype/pull/5874">#5874</a></summary>
|
||||
|
||||
Follow workfile version label was marked as Collect Anatomy Instance Data label.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **🐛 Bug fixes**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Nuke: Fix workfile template builder so representations get loaded next to each other <a href="https://github.com/ynput/OpenPype/pull/5061">#5061</a></summary>
|
||||
|
||||
Refactor when the cleanup of the placeholder happens for the cases where multiple representations are loaded by a single placeholder.The existing code didn't take into account the case where a template placeholder can load multiple representations so it was trying to do the cleanup of the placeholder node and the re-arrangement of the imported nodes too early. I assume this was designed only for the cases where a single representation can load multiple nodes.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Nuke: Dont update node name on update <a href="https://github.com/ynput/OpenPype/pull/5704">#5704</a></summary>
|
||||
|
||||
When updating `Image` containers the code is trying to set the name of the node. This results in a warning message from Nuke shown below;Suggesting to not change the node name when updating.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>UIDefLabel can be unique <a href="https://github.com/ynput/OpenPype/pull/5827">#5827</a></summary>
|
||||
|
||||
`UILabelDef` have implemented comparison and uniqueness.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>AYON: Skip kitsu module when creating ayon addons <a href="https://github.com/ynput/OpenPype/pull/5828">#5828</a></summary>
|
||||
|
||||
Create AYON packages is skipping kitsu module in creation of modules/addons and kitsu module is not loaded from modules on start. The addon already has it's repository https://github.com/ynput/ayon-kitsu.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Bugfix: Collect Rendered Files only collecting first instance <a href="https://github.com/ynput/OpenPype/pull/5832">#5832</a></summary>
|
||||
|
||||
Collect all instances from the metadata file - don't return on first instance iteration.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Houdini: set frame range for the created composite ROP <a href="https://github.com/ynput/OpenPype/pull/5833">#5833</a></summary>
|
||||
|
||||
Quick bug fix for created composite ROP, set its frame range to the frame range of the playbar.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Fix registering launcher actions from OpenPypeModules <a href="https://github.com/ynput/OpenPype/pull/5843">#5843</a></summary>
|
||||
|
||||
Fix typo `actions_dir` -> `path` to fix register launcher actions fromm OpenPypeModule
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Bugfix in houdini shelves manager and beautify settings <a href="https://github.com/ynput/OpenPype/pull/5844">#5844</a></summary>
|
||||
|
||||
This PR fixes the problem in this PR https://github.com/ynput/OpenPype/issues/5457 by using the right function to load a pre-made houdini `.shelf` fileAlso, it beautifies houdini shelves settings to provide better guidance for users which helps with other issue https://github.com/ynput/OpenPype/issues/5458 , Rather adding default shelf and set names, I'll educate users how to use the tool correctly.Users now are able to select between the two options.| OpenPype | Ayon || -- | -- || | |
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Blender: Fix missing Grease Pencils in review <a href="https://github.com/ynput/OpenPype/pull/5848">#5848</a></summary>
|
||||
|
||||
Fix Grease Pencil missing in review when isolating objects.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Blender: Fix Render Settings in Ayon <a href="https://github.com/ynput/OpenPype/pull/5849">#5849</a></summary>
|
||||
|
||||
Fix Render Settings in Ayon for Blender.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Bugfix: houdini tab menu working as expected <a href="https://github.com/ynput/OpenPype/pull/5850">#5850</a></summary>
|
||||
|
||||
This PR:Tab menu name changes to Ayon when using ayon get_network_categories is checked in all creator plugins. | Product | Network Category | | -- | -- | | Alembic camera | rop, obj | | Arnold Ass | rop | | Arnold ROP | rop | | Bgeo | rop, sop | | composite sequence | cop2, rop | | hda | obj | | Karma ROP | rop | | Mantra ROP | rop | | ABC | rop, sop | | RS proxy | rop, sop| | RS ROP | rop | | Review | rop | | Static mesh | rop, obj, sop | | USD | lop, rop | | USD Render | rop | | VDB | rop, obj, sop | | V Ray | rop |
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Bigfix: Houdini skip frame_range_validator if node has no 'trange' parameter <a href="https://github.com/ynput/OpenPype/pull/5851">#5851</a></summary>
|
||||
|
||||
I faced a bug when publishing HDA instance as it has no `trange` parameter. As this PR title says : skip frame_range_validator if node has no 'trange' parameter
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Bugfix: houdini image sequence loading and missing frames <a href="https://github.com/ynput/OpenPype/pull/5852">#5852</a></summary>
|
||||
|
||||
I made this PR in to fix issues mentioned here https://github.com/ynput/OpenPype/pull/5833#issuecomment-1789207727in short:
|
||||
- image load doesn't work
|
||||
- publisher only publish one frame
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Nuke: loaders' containers updating as nodes <a href="https://github.com/ynput/OpenPype/pull/5854">#5854</a></summary>
|
||||
|
||||
Nuke loaded containers are updating correctly even they have been duplicating of originally loaded nodes. This had previously been removed duplicated nodes.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>deadline: settings are not blocking extension input <a href="https://github.com/ynput/OpenPype/pull/5864">#5864</a></summary>
|
||||
|
||||
Settings are not blocking user input.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Blender: Fix loading of blend layouts <a href="https://github.com/ynput/OpenPype/pull/5866">#5866</a></summary>
|
||||
|
||||
Fix a problem with loading blend layouts.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>AYON: Launcher refresh issues <a href="https://github.com/ynput/OpenPype/pull/5867">#5867</a></summary>
|
||||
|
||||
Fixed refresh of projects issue in launcher tool. And renamed Qt models to contain `Qt` in their name (it was really hard to find out where were used). It is not possible to click on disabled item in launcher's projects view.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Fix the Wrong key words for tycache workfile template settings in AYON <a href="https://github.com/ynput/OpenPype/pull/5870">#5870</a></summary>
|
||||
|
||||
Fix the wrong key words for the tycache workfile template settings in AYON(i.e. Instead of families, product_types should be used)
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>AYON tools: Handle empty icon definition <a href="https://github.com/ynput/OpenPype/pull/5876">#5876</a></summary>
|
||||
|
||||
Ignore if passed icon definition is `None`.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **🔀 Refactored code**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Houdini: Remove on instance toggled callback <a href="https://github.com/ynput/OpenPype/pull/5860">#5860</a></summary>
|
||||
|
||||
Remove on instance toggled callback which isn't relevant to the new publisher
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Chore: Remove unused `instanceToggled` callbacks <a href="https://github.com/ynput/OpenPype/pull/5862">#5862</a></summary>
|
||||
|
||||
The `instanceToggled` callbacks should be irrelevant for new publisher.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.17.4](https://github.com/ynput/OpenPype/tree/3.17.4)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -282,6 +282,9 @@ def run(script):
|
|||
"--app_variant",
|
||||
help="Provide specific app variant for test, empty for latest",
|
||||
default=None)
|
||||
@click.option("--app_group",
|
||||
help="Provide specific app group for test, empty for default",
|
||||
default=None)
|
||||
@click.option("-t",
|
||||
"--timeout",
|
||||
help="Provide specific timeout value for test case",
|
||||
|
|
@ -294,11 +297,11 @@ def run(script):
|
|||
help="MongoDB for testing.",
|
||||
default=None)
|
||||
def runtests(folder, mark, pyargs, test_data_folder, persist, app_variant,
|
||||
timeout, setup_only, mongo_url):
|
||||
timeout, setup_only, mongo_url, app_group):
|
||||
"""Run all automatic tests after proper initialization via start.py"""
|
||||
PypeCommands().run_tests(folder, mark, pyargs, test_data_folder,
|
||||
persist, app_variant, timeout, setup_only,
|
||||
mongo_url)
|
||||
mongo_url, app_group)
|
||||
|
||||
|
||||
@main.command(help="DEPRECATED - run sync server")
|
||||
|
|
|
|||
|
|
@ -28,16 +28,22 @@ class ExtractBlend(publish.Extractor):
|
|||
for obj in instance:
|
||||
data_blocks.add(obj)
|
||||
# Pack used images in the blend files.
|
||||
if obj.type == 'MESH':
|
||||
for material_slot in obj.material_slots:
|
||||
mat = material_slot.material
|
||||
if mat and mat.use_nodes:
|
||||
tree = mat.node_tree
|
||||
if tree.type == 'SHADER':
|
||||
for node in tree.nodes:
|
||||
if node.bl_idname == 'ShaderNodeTexImage':
|
||||
if node.image:
|
||||
node.image.pack()
|
||||
if obj.type != 'MESH':
|
||||
continue
|
||||
for material_slot in obj.material_slots:
|
||||
mat = material_slot.material
|
||||
if not(mat and mat.use_nodes):
|
||||
continue
|
||||
tree = mat.node_tree
|
||||
if tree.type != 'SHADER':
|
||||
continue
|
||||
for node in tree.nodes:
|
||||
if node.bl_idname != 'ShaderNodeTexImage':
|
||||
continue
|
||||
# Check if image is not packed already
|
||||
# and pack it if not.
|
||||
if node.image and node.image.packed_file is None:
|
||||
node.image.pack()
|
||||
|
||||
bpy.data.libraries.write(filepath, data_blocks)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Class for handling Render Settings."""
|
||||
from maya import cmds # noqa
|
||||
import maya.mel as mel
|
||||
import six
|
||||
import sys
|
||||
|
||||
|
|
@ -63,6 +61,10 @@ class RenderSettings(object):
|
|||
|
||||
def set_default_renderer_settings(self, renderer=None):
|
||||
"""Set basic settings based on renderer."""
|
||||
# Not all hosts can import this module.
|
||||
from maya import cmds
|
||||
import maya.mel as mel
|
||||
|
||||
if not renderer:
|
||||
renderer = cmds.getAttr(
|
||||
'defaultRenderGlobals.currentRenderer').lower()
|
||||
|
|
|
|||
|
|
@ -771,7 +771,8 @@ class ReferenceLoader(Loader):
|
|||
"ma": "mayaAscii",
|
||||
"mb": "mayaBinary",
|
||||
"abc": "Alembic",
|
||||
"fbx": "FBX"
|
||||
"fbx": "FBX",
|
||||
"usd": "USD Import"
|
||||
}.get(representation["name"])
|
||||
|
||||
assert file_type, "Unsupported representation: %s" % representation
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import os
|
||||
import difflib
|
||||
import contextlib
|
||||
|
||||
from maya import cmds
|
||||
import qargparse
|
||||
|
||||
from openpype.settings import get_project_settings
|
||||
import openpype.hosts.maya.api.plugin
|
||||
|
|
@ -128,6 +130,12 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
|
|||
if not attach_to_root:
|
||||
group_name = namespace
|
||||
|
||||
kwargs = {}
|
||||
if "file_options" in options:
|
||||
kwargs["options"] = options["file_options"]
|
||||
if "file_type" in options:
|
||||
kwargs["type"] = options["file_type"]
|
||||
|
||||
path = self.filepath_from_context(context)
|
||||
with maintained_selection():
|
||||
cmds.loadPlugin("AbcImport.mll", quiet=True)
|
||||
|
|
@ -139,7 +147,8 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
|
|||
reference=True,
|
||||
returnNewNodes=True,
|
||||
groupReference=attach_to_root,
|
||||
groupName=group_name)
|
||||
groupName=group_name,
|
||||
**kwargs)
|
||||
|
||||
shapes = cmds.ls(nodes, shapes=True, long=True)
|
||||
|
||||
|
|
@ -251,3 +260,92 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
|
|||
else:
|
||||
self.log.warning("This version of Maya does not support locking of"
|
||||
" transforms of cameras.")
|
||||
|
||||
|
||||
class MayaUSDReferenceLoader(ReferenceLoader):
|
||||
"""Reference USD file to native Maya nodes using MayaUSDImport reference"""
|
||||
|
||||
families = ["usd"]
|
||||
representations = ["usd"]
|
||||
extensions = {"usd", "usda", "usdc"}
|
||||
|
||||
options = ReferenceLoader.options + [
|
||||
qargparse.Boolean(
|
||||
"readAnimData",
|
||||
label="Load anim data",
|
||||
default=True,
|
||||
help="Load animation data from USD file"
|
||||
),
|
||||
qargparse.Boolean(
|
||||
"useAsAnimationCache",
|
||||
label="Use as animation cache",
|
||||
default=True,
|
||||
help=(
|
||||
"Imports geometry prims with time-sampled point data using a "
|
||||
"point-based deformer that references the imported "
|
||||
"USD file.\n"
|
||||
"This provides better import and playback performance when "
|
||||
"importing time-sampled geometry from USD, and should "
|
||||
"reduce the weight of the resulting Maya scene."
|
||||
)
|
||||
),
|
||||
qargparse.Boolean(
|
||||
"importInstances",
|
||||
label="Import instances",
|
||||
default=True,
|
||||
help=(
|
||||
"Import USD instanced geometries as Maya instanced shapes. "
|
||||
"Will flatten the scene otherwise."
|
||||
)
|
||||
),
|
||||
qargparse.String(
|
||||
"primPath",
|
||||
label="Prim Path",
|
||||
default="/",
|
||||
help=(
|
||||
"Name of the USD scope where traversing will begin.\n"
|
||||
"The prim at the specified primPath (including the prim) will "
|
||||
"be imported.\n"
|
||||
"Specifying the pseudo-root (/) means you want "
|
||||
"to import everything in the file.\n"
|
||||
"If the passed prim path is empty, it will first try to "
|
||||
"import the defaultPrim for the rootLayer if it exists.\n"
|
||||
"Otherwise, it will behave as if the pseudo-root was passed "
|
||||
"in."
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
file_type = "USD Import"
|
||||
|
||||
def process_reference(self, context, name, namespace, options):
|
||||
cmds.loadPlugin("mayaUsdPlugin", quiet=True)
|
||||
|
||||
def bool_option(key, default):
|
||||
# Shorthand for getting optional boolean file option from options
|
||||
value = int(bool(options.get(key, default)))
|
||||
return "{}={}".format(key, value)
|
||||
|
||||
def string_option(key, default):
|
||||
# Shorthand for getting optional string file option from options
|
||||
value = str(options.get(key, default))
|
||||
return "{}={}".format(key, value)
|
||||
|
||||
options["file_options"] = ";".join([
|
||||
string_option("primPath", default="/"),
|
||||
bool_option("importInstances", default=True),
|
||||
bool_option("useAsAnimationCache", default=True),
|
||||
bool_option("readAnimData", default=True),
|
||||
# TODO: Expose more parameters
|
||||
# "preferredMaterial=none",
|
||||
# "importRelativeTextures=Automatic",
|
||||
# "useCustomFrameRange=0",
|
||||
# "startTime=0",
|
||||
# "endTime=0",
|
||||
# "importUSDZTextures=0"
|
||||
])
|
||||
options["file_type"] = self.file_type
|
||||
|
||||
return super(MayaUSDReferenceLoader, self).process_reference(
|
||||
context, name, namespace, options
|
||||
)
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class _ModuleClass(object):
|
|||
if attr_name in self.__attributes__:
|
||||
self.log.warning(
|
||||
"Duplicated name \"{}\" in {}. Overriding.".format(
|
||||
self.name, attr_name
|
||||
attr_name, self.name
|
||||
)
|
||||
)
|
||||
self.__attributes__[attr_name] = value
|
||||
|
|
@ -408,6 +408,10 @@ def _load_ayon_addons(openpype_modules, modules_key, log):
|
|||
addon_name = addon_info["name"]
|
||||
addon_version = addon_info["version"]
|
||||
|
||||
# OpenPype addon does not have any addon object
|
||||
if addon_name == "openpype":
|
||||
continue
|
||||
|
||||
dev_addon_info = dev_addons_info.get(addon_name, {})
|
||||
use_dev_path = dev_addon_info.get("enabled", False)
|
||||
|
||||
|
|
@ -438,7 +442,7 @@ def _load_ayon_addons(openpype_modules, modules_key, log):
|
|||
# Ignore of files is implemented to be able to run code from code
|
||||
# where usually is more files than just the addon
|
||||
# Ignore start and setup scripts
|
||||
if name in ("setup.py", "start.py"):
|
||||
if name in ("setup.py", "start.py", "__pycache__"):
|
||||
continue
|
||||
|
||||
path = os.path.join(addon_dir, name)
|
||||
|
|
@ -454,7 +458,15 @@ def _load_ayon_addons(openpype_modules, modules_key, log):
|
|||
|
||||
try:
|
||||
mod = __import__(basename, fromlist=("",))
|
||||
imported_modules.append(mod)
|
||||
for attr_name in dir(mod):
|
||||
attr = getattr(mod, attr_name)
|
||||
if (
|
||||
inspect.isclass(attr)
|
||||
and issubclass(attr, OpenPypeModule)
|
||||
):
|
||||
imported_modules.append(mod)
|
||||
break
|
||||
|
||||
except BaseException:
|
||||
log.warning(
|
||||
"Failed to import \"{}\"".format(basename),
|
||||
|
|
@ -467,19 +479,26 @@ def _load_ayon_addons(openpype_modules, modules_key, log):
|
|||
))
|
||||
continue
|
||||
|
||||
if len(imported_modules) == 1:
|
||||
mod = imported_modules[0]
|
||||
addon_alias = getattr(mod, "V3_ALIAS", None)
|
||||
if not addon_alias:
|
||||
addon_alias = addon_name
|
||||
v3_addons_to_skip.append(addon_alias)
|
||||
new_import_str = "{}.{}".format(modules_key, addon_alias)
|
||||
if len(imported_modules) > 1:
|
||||
log.warning((
|
||||
"Skipping addon '{}'."
|
||||
" Multiple modules were found ({}) in dir {}."
|
||||
).format(
|
||||
addon_name,
|
||||
", ".join([m.__name__ for m in imported_modules]),
|
||||
addon_dir,
|
||||
))
|
||||
continue
|
||||
|
||||
sys.modules[new_import_str] = mod
|
||||
setattr(openpype_modules, addon_alias, mod)
|
||||
mod = imported_modules[0]
|
||||
addon_alias = getattr(mod, "V3_ALIAS", None)
|
||||
if not addon_alias:
|
||||
addon_alias = addon_name
|
||||
v3_addons_to_skip.append(addon_alias)
|
||||
new_import_str = "{}.{}".format(modules_key, addon_alias)
|
||||
|
||||
else:
|
||||
log.info("More then one module was imported")
|
||||
sys.modules[new_import_str] = mod
|
||||
setattr(openpype_modules, addon_alias, mod)
|
||||
|
||||
return v3_addons_to_skip
|
||||
|
||||
|
|
@ -997,7 +1016,18 @@ class ModulesManager:
|
|||
continue
|
||||
|
||||
method = getattr(module, method_name)
|
||||
paths = method(*args, **kwargs)
|
||||
try:
|
||||
paths = method(*args, **kwargs)
|
||||
except Exception:
|
||||
self.log.warning(
|
||||
(
|
||||
"Failed to get plugin paths from module"
|
||||
" '{}' using '{}'."
|
||||
).format(module.__class__.__name__, method_name),
|
||||
exc_info=True
|
||||
)
|
||||
continue
|
||||
|
||||
if paths:
|
||||
# Convert to list if value is not list
|
||||
if not isinstance(paths, (list, tuple, set)):
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ This is resolving index of server lists stored in `deadlineServers` instance
|
|||
attribute or using default server if that attribute doesn't exists.
|
||||
|
||||
"""
|
||||
from maya import cmds
|
||||
|
||||
import pyblish.api
|
||||
from openpype.pipeline.publish import KnownPublishError
|
||||
|
||||
|
|
@ -44,7 +42,8 @@ class CollectDeadlineServerFromInstance(pyblish.api.InstancePlugin):
|
|||
str: Selected Deadline Webservice URL.
|
||||
|
||||
"""
|
||||
|
||||
# Not all hosts can import this module.
|
||||
from maya import cmds
|
||||
deadline_settings = (
|
||||
render_instance.context.data
|
||||
["system_settings"]
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ import getpass
|
|||
import attr
|
||||
from datetime import datetime
|
||||
|
||||
import bpy
|
||||
|
||||
from openpype.lib import is_running_from_build
|
||||
from openpype.pipeline import legacy_io
|
||||
from openpype.pipeline.farm.tools import iter_expected_files
|
||||
|
|
@ -142,6 +140,9 @@ class BlenderSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
|
|||
return job_info
|
||||
|
||||
def get_plugin_info(self):
|
||||
# Not all hosts can import this module.
|
||||
import bpy
|
||||
|
||||
plugin_info = BlenderPluginInfo(
|
||||
SceneFile=self.scene_path,
|
||||
Version=bpy.app.version_string,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import json
|
|||
from datetime import datetime
|
||||
|
||||
import requests
|
||||
import hou
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
|
@ -31,6 +30,8 @@ class HoudiniSubmitPublishDeadline(pyblish.api.ContextPlugin):
|
|||
targets = ["deadline"]
|
||||
|
||||
def process(self, context):
|
||||
# Not all hosts can import this module.
|
||||
import hou
|
||||
|
||||
# Ensure no errors so far
|
||||
assert all(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import hou
|
||||
|
||||
import os
|
||||
import attr
|
||||
import getpass
|
||||
from datetime import datetime
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from openpype.pipeline import legacy_io
|
||||
|
|
@ -119,6 +118,8 @@ class HoudiniSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
|
|||
return job_info
|
||||
|
||||
def get_plugin_info(self):
|
||||
# Not all hosts can import this module.
|
||||
import hou
|
||||
|
||||
instance = self._instance
|
||||
context = instance.context
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import os
|
||||
import getpass
|
||||
import copy
|
||||
|
||||
import attr
|
||||
|
||||
from openpype.lib import (
|
||||
TextDef,
|
||||
BoolDef,
|
||||
|
|
@ -15,11 +15,6 @@ from openpype.pipeline import (
|
|||
from openpype.pipeline.publish.lib import (
|
||||
replace_with_published_scene_path
|
||||
)
|
||||
from openpype.hosts.max.api.lib import (
|
||||
get_current_renderer,
|
||||
get_multipass_setting
|
||||
)
|
||||
from openpype.hosts.max.api.lib_rendersettings import RenderSettings
|
||||
from openpype_modules.deadline import abstract_submit_deadline
|
||||
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
|
||||
from openpype.lib import is_running_from_build
|
||||
|
|
@ -191,6 +186,13 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
|||
self.submit(self.assemble_payload(job_info, plugin_info))
|
||||
|
||||
def _use_published_name(self, data, project_settings):
|
||||
# Not all hosts can import these modules.
|
||||
from openpype.hosts.max.api.lib import (
|
||||
get_current_renderer,
|
||||
get_multipass_setting
|
||||
)
|
||||
from openpype.hosts.max.api.lib_rendersettings import RenderSettings
|
||||
|
||||
instance = self._instance
|
||||
job_info = copy.deepcopy(self.job_info)
|
||||
plugin_info = copy.deepcopy(self.plugin_info)
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ from collections import OrderedDict
|
|||
|
||||
import attr
|
||||
|
||||
from maya import cmds
|
||||
|
||||
from openpype.pipeline import (
|
||||
legacy_io,
|
||||
OpenPypePyblishPluginMixin
|
||||
|
|
@ -246,6 +244,8 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
|||
return job_info
|
||||
|
||||
def get_plugin_info(self):
|
||||
# Not all hosts can import this module.
|
||||
from maya import cmds
|
||||
|
||||
instance = self._instance
|
||||
context = instance.context
|
||||
|
|
@ -288,7 +288,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
|||
return plugin_payload
|
||||
|
||||
def process_submission(self):
|
||||
|
||||
from maya import cmds
|
||||
instance = self._instance
|
||||
|
||||
filepath = self.scene_path # publish if `use_publish` else workfile
|
||||
|
|
@ -675,7 +675,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
|||
str
|
||||
|
||||
"""
|
||||
|
||||
from maya import cmds
|
||||
# "vrayscene/<Scene>/<Scene>_<Layer>/<Layer>"
|
||||
vray_settings = cmds.ls(type="VRaySettingsNode")
|
||||
node = vray_settings[0]
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ import os
|
|||
import attr
|
||||
from datetime import datetime
|
||||
|
||||
from maya import cmds
|
||||
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.pipeline import legacy_io, PublishXmlValidationError
|
||||
from openpype.tests.lib import is_in_tests
|
||||
|
|
@ -127,7 +125,8 @@ class MayaSubmitRemotePublishDeadline(
|
|||
job_info.EnvironmentKeyValue[key] = value
|
||||
|
||||
def get_plugin_info(self):
|
||||
|
||||
# Not all hosts can import this module.
|
||||
from maya import cmds
|
||||
scene = self._instance.context.data["currentFile"]
|
||||
|
||||
plugin_info = MayaPluginInfo()
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ from datetime import datetime
|
|||
import requests
|
||||
import pyblish.api
|
||||
|
||||
import nuke
|
||||
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.pipeline import legacy_io
|
||||
from openpype.pipeline.publish import (
|
||||
|
|
@ -498,6 +496,9 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin,
|
|||
Returning:
|
||||
list: captured groups list
|
||||
"""
|
||||
# Not all hosts can import this module.
|
||||
import nuke
|
||||
|
||||
captured_groups = []
|
||||
for lg_name, list_node_class in self.limit_groups.items():
|
||||
for node_class in list_node_class:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import collections
|
||||
|
||||
from openpype.client import get_project
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
|
|
@ -73,8 +76,21 @@ class FirstVersionStatus(BaseEvent):
|
|||
if not self.task_status_map:
|
||||
return
|
||||
|
||||
entities_info = self.filter_event_ents(event)
|
||||
if not entities_info:
|
||||
filtered_entities_info = self.filter_entities_info(event)
|
||||
if not filtered_entities_info:
|
||||
return
|
||||
|
||||
for project_id, entities_info in filtered_entities_info.items():
|
||||
self.process_by_project(session, event, project_id, entities_info)
|
||||
|
||||
def process_by_project(self, session, event, project_id, entities_info):
|
||||
project_name = self.get_project_name_from_event(
|
||||
session, event, project_id
|
||||
)
|
||||
if get_project(project_name) is None:
|
||||
self.log.debug(
|
||||
f"Project '{project_name}' not found in OpenPype. Skipping"
|
||||
)
|
||||
return
|
||||
|
||||
entity_ids = []
|
||||
|
|
@ -154,18 +170,18 @@ class FirstVersionStatus(BaseEvent):
|
|||
exc_info=True
|
||||
)
|
||||
|
||||
def filter_event_ents(self, event):
|
||||
filtered_ents = []
|
||||
for entity in event["data"].get("entities", []):
|
||||
def filter_entities_info(self, event):
|
||||
filtered_entities_info = collections.defaultdict(list)
|
||||
for entity_info in event["data"].get("entities", []):
|
||||
# Care only about add actions
|
||||
if entity.get("action") != "add":
|
||||
if entity_info.get("action") != "add":
|
||||
continue
|
||||
|
||||
# Filter AssetVersions
|
||||
if entity["entityType"] != "assetversion":
|
||||
if entity_info["entityType"] != "assetversion":
|
||||
continue
|
||||
|
||||
entity_changes = entity.get("changes") or {}
|
||||
entity_changes = entity_info.get("changes") or {}
|
||||
|
||||
# Check if version of Asset Version is `1`
|
||||
version_num = entity_changes.get("version", {}).get("new")
|
||||
|
|
@ -177,9 +193,18 @@ class FirstVersionStatus(BaseEvent):
|
|||
if not task_id:
|
||||
continue
|
||||
|
||||
filtered_ents.append(entity)
|
||||
project_id = None
|
||||
for parent_item in reversed(entity_info["parents"]):
|
||||
if parent_item["entityType"] == "show":
|
||||
project_id = parent_item["entityId"]
|
||||
break
|
||||
|
||||
return filtered_ents
|
||||
if project_id is None:
|
||||
continue
|
||||
|
||||
filtered_entities_info[project_id].append(entity_info)
|
||||
|
||||
return filtered_entities_info
|
||||
|
||||
|
||||
def register(session):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import collections
|
||||
|
||||
from openpype.client import get_project
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
|
|
@ -99,6 +101,10 @@ class NextTaskUpdate(BaseEvent):
|
|||
project_name = self.get_project_name_from_event(
|
||||
session, event, project_id
|
||||
)
|
||||
if get_project(project_name) is None:
|
||||
self.log.debug("Project not found in OpenPype. Skipping")
|
||||
return
|
||||
|
||||
# Load settings
|
||||
project_settings = self.get_project_settings_from_event(
|
||||
event, project_name
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import copy
|
|||
from typing import Any
|
||||
|
||||
import ftrack_api
|
||||
|
||||
from openpype.client import get_project
|
||||
from openpype_modules.ftrack.lib import (
|
||||
BaseEvent,
|
||||
query_custom_attributes,
|
||||
|
|
@ -139,6 +141,10 @@ class PushHierValuesToNonHierEvent(BaseEvent):
|
|||
project_name: str = self.get_project_name_from_event(
|
||||
session, event, project_id
|
||||
)
|
||||
if get_project(project_name) is None:
|
||||
self.log.debug("Project not found in OpenPype. Skipping")
|
||||
return set(), set()
|
||||
|
||||
# Load settings
|
||||
project_settings: dict[str, Any] = (
|
||||
self.get_project_settings_from_event(event, project_name)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import collections
|
||||
|
||||
from openpype.client import get_project
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
|
|
@ -60,6 +62,10 @@ class TaskStatusToParent(BaseEvent):
|
|||
project_name = self.get_project_name_from_event(
|
||||
session, event, project_id
|
||||
)
|
||||
if get_project(project_name) is None:
|
||||
self.log.debug("Project not found in OpenPype. Skipping")
|
||||
return
|
||||
|
||||
# Load settings
|
||||
project_settings = self.get_project_settings_from_event(
|
||||
event, project_name
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import collections
|
||||
|
||||
from openpype.client import get_project
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
|
|
@ -102,6 +104,10 @@ class TaskToVersionStatus(BaseEvent):
|
|||
project_name = self.get_project_name_from_event(
|
||||
session, event, project_id
|
||||
)
|
||||
if get_project(project_name) is None:
|
||||
self.log.debug("Project not found in OpenPype. Skipping")
|
||||
return
|
||||
|
||||
# Load settings
|
||||
project_settings = self.get_project_settings_from_event(
|
||||
event, project_name
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import collections
|
||||
|
||||
from openpype.client import get_project
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
|
|
@ -22,6 +24,10 @@ class ThumbnailEvents(BaseEvent):
|
|||
project_name = self.get_project_name_from_event(
|
||||
session, event, project_id
|
||||
)
|
||||
if get_project(project_name) is None:
|
||||
self.log.debug("Project not found in OpenPype. Skipping")
|
||||
return
|
||||
|
||||
# Load settings
|
||||
project_settings = self.get_project_settings_from_event(
|
||||
event, project_name
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from openpype.client import get_project
|
||||
from openpype_modules.ftrack.lib import BaseEvent
|
||||
|
||||
|
||||
|
|
@ -50,6 +51,10 @@ class VersionToTaskStatus(BaseEvent):
|
|||
project_name = self.get_project_name_from_event(
|
||||
session, event, project_id
|
||||
)
|
||||
if get_project(project_name) is None:
|
||||
self.log.debug("Project not found in OpenPype. Skipping")
|
||||
return
|
||||
|
||||
# Load settings
|
||||
project_settings = self.get_project_settings_from_event(
|
||||
event, project_name
|
||||
|
|
|
|||
|
|
@ -5,15 +5,9 @@ Requires:
|
|||
masterLayer -> instance data attribute
|
||||
otioClipRange -> instance data attribute
|
||||
"""
|
||||
# import os
|
||||
import opentimelineio as otio
|
||||
import pyblish.api
|
||||
from pprint import pformat
|
||||
from openpype.pipeline.editorial import (
|
||||
get_media_range_with_retimes,
|
||||
otio_range_to_frame_range,
|
||||
otio_range_with_handles
|
||||
)
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class CollectOtioFrameRanges(pyblish.api.InstancePlugin):
|
||||
|
|
@ -27,6 +21,14 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin):
|
|||
hosts = ["resolve", "hiero", "flame", "traypublisher"]
|
||||
|
||||
def process(self, instance):
|
||||
# Not all hosts can import these modules.
|
||||
import opentimelineio as otio
|
||||
from openpype.pipeline.editorial import (
|
||||
get_media_range_with_retimes,
|
||||
otio_range_to_frame_range,
|
||||
otio_range_with_handles
|
||||
)
|
||||
|
||||
# get basic variables
|
||||
otio_clip = instance.data["otioClip"]
|
||||
workfile_start = instance.data["workfileFrameStart"]
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ Provides:
|
|||
instance -> families (adding ["review", "ftrack"])
|
||||
"""
|
||||
|
||||
import opentimelineio as otio
|
||||
import pyblish.api
|
||||
from pprint import pformat
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class CollectOtioReview(pyblish.api.InstancePlugin):
|
||||
"""Get matching otio track from defined review layer"""
|
||||
|
|
@ -25,6 +25,9 @@ class CollectOtioReview(pyblish.api.InstancePlugin):
|
|||
hosts = ["resolve", "hiero", "flame"]
|
||||
|
||||
def process(self, instance):
|
||||
# Not all hosts can import this module.
|
||||
import opentimelineio as otio
|
||||
|
||||
# get basic variables
|
||||
otio_review_clips = []
|
||||
otio_timeline = instance.context.data["otioTimeline"]
|
||||
|
|
|
|||
|
|
@ -6,18 +6,15 @@ Provides:
|
|||
instance -> otioReviewClips
|
||||
"""
|
||||
import os
|
||||
|
||||
import clique
|
||||
import opentimelineio as otio
|
||||
import pyblish.api
|
||||
from openpype.pipeline.editorial import (
|
||||
get_media_range_with_retimes,
|
||||
range_from_frames,
|
||||
make_sequence_collection
|
||||
)
|
||||
|
||||
from openpype.pipeline.publish import (
|
||||
get_publish_template_name
|
||||
)
|
||||
|
||||
|
||||
class CollectOtioSubsetResources(pyblish.api.InstancePlugin):
|
||||
"""Get Resources for a subset version"""
|
||||
|
||||
|
|
@ -26,8 +23,14 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin):
|
|||
families = ["clip"]
|
||||
hosts = ["resolve", "hiero", "flame"]
|
||||
|
||||
|
||||
def process(self, instance):
|
||||
# Not all hosts can import these modules.
|
||||
import opentimelineio as otio
|
||||
from openpype.pipeline.editorial import (
|
||||
get_media_range_with_retimes,
|
||||
range_from_frames,
|
||||
make_sequence_collection
|
||||
)
|
||||
|
||||
if "audio" in instance.data["family"]:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import os
|
||||
import tempfile
|
||||
|
||||
import pyblish
|
||||
|
||||
from openpype.lib import (
|
||||
get_ffmpeg_tool_args,
|
||||
run_subprocess
|
||||
)
|
||||
import tempfile
|
||||
import opentimelineio as otio
|
||||
|
||||
|
||||
class ExtractOtioAudioTracks(pyblish.api.ContextPlugin):
|
||||
|
|
@ -155,6 +156,9 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin):
|
|||
Returns:
|
||||
list: list of audio clip dictionaries
|
||||
"""
|
||||
# Not all hosts can import this module.
|
||||
import opentimelineio as otio
|
||||
|
||||
output = []
|
||||
# go trough all audio tracks
|
||||
for otio_track in otio_timeline.tracks:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
|
||||
import pyblish.api
|
||||
import opentimelineio as otio
|
||||
|
||||
from openpype.pipeline import publish
|
||||
|
||||
|
|
@ -16,6 +16,9 @@ class ExtractOTIOFile(publish.Extractor):
|
|||
hosts = ["resolve", "hiero", "traypublisher"]
|
||||
|
||||
def process(self, instance):
|
||||
# Not all hosts can import this module.
|
||||
import opentimelineio as otio
|
||||
|
||||
if not instance.context.data.get("otioTimeline"):
|
||||
return
|
||||
# create representation data
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ Provides:
|
|||
"""
|
||||
|
||||
import os
|
||||
|
||||
import clique
|
||||
import opentimelineio as otio
|
||||
from pyblish import api
|
||||
|
||||
from openpype.lib import (
|
||||
|
|
@ -24,13 +24,6 @@ from openpype.lib import (
|
|||
run_subprocess,
|
||||
)
|
||||
from openpype.pipeline import publish
|
||||
from openpype.pipeline.editorial import (
|
||||
otio_range_to_frame_range,
|
||||
trim_media_range,
|
||||
range_from_frames,
|
||||
frames_to_seconds,
|
||||
make_sequence_collection
|
||||
)
|
||||
|
||||
|
||||
class ExtractOTIOReview(publish.Extractor):
|
||||
|
|
@ -62,6 +55,13 @@ class ExtractOTIOReview(publish.Extractor):
|
|||
output_ext = ".jpg"
|
||||
|
||||
def process(self, instance):
|
||||
# Not all hosts can import these modules.
|
||||
import opentimelineio as otio
|
||||
from openpype.pipeline.editorial import (
|
||||
otio_range_to_frame_range,
|
||||
make_sequence_collection
|
||||
)
|
||||
|
||||
# TODO: convert resulting image sequence to mp4
|
||||
|
||||
# get otio clip and other time info from instance clip
|
||||
|
|
@ -281,6 +281,12 @@ class ExtractOTIOReview(publish.Extractor):
|
|||
Returns:
|
||||
otio.time.TimeRange: trimmed available range
|
||||
"""
|
||||
# Not all hosts can import these modules.
|
||||
from openpype.pipeline.editorial import (
|
||||
trim_media_range,
|
||||
range_from_frames
|
||||
)
|
||||
|
||||
avl_start = int(avl_range.start_time.value)
|
||||
src_start = int(avl_start + start)
|
||||
avl_durtation = int(avl_range.duration.value)
|
||||
|
|
@ -338,6 +344,8 @@ class ExtractOTIOReview(publish.Extractor):
|
|||
Returns:
|
||||
otio.time.TimeRange: trimmed available range
|
||||
"""
|
||||
# Not all hosts can import this module.
|
||||
from openpype.pipeline.editorial import frames_to_seconds
|
||||
|
||||
# create path and frame start to destination
|
||||
output_path, out_frame_start = self._get_ffmpeg_output()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ from openpype.lib import (
|
|||
run_subprocess,
|
||||
)
|
||||
from openpype.pipeline import publish
|
||||
from openpype.pipeline.editorial import frames_to_seconds
|
||||
|
||||
|
||||
class ExtractOTIOTrimmingVideo(publish.Extractor):
|
||||
|
|
@ -75,6 +74,8 @@ class ExtractOTIOTrimmingVideo(publish.Extractor):
|
|||
otio_range (opentime.TimeRange): range to trim to
|
||||
|
||||
"""
|
||||
# Not all hosts can import this module.
|
||||
from openpype.pipeline.editorial import frames_to_seconds
|
||||
|
||||
# create path to destination
|
||||
output_path = self._get_ffmpeg_output(input_file_path)
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ class PypeCommands:
|
|||
|
||||
def run_tests(self, folder, mark, pyargs,
|
||||
test_data_folder, persist, app_variant, timeout, setup_only,
|
||||
mongo_url):
|
||||
mongo_url, app_group):
|
||||
"""
|
||||
Runs tests from 'folder'
|
||||
|
||||
|
|
@ -260,6 +260,9 @@ class PypeCommands:
|
|||
if persist:
|
||||
args.extend(["--persist", persist])
|
||||
|
||||
if app_group:
|
||||
args.extend(["--app_group", app_group])
|
||||
|
||||
if app_variant:
|
||||
args.extend(["--app_variant", app_variant])
|
||||
|
||||
|
|
|
|||
|
|
@ -641,13 +641,14 @@ def _convert_3dsmax_project_settings(ayon_settings, output):
|
|||
ayon_max["PointCloud"]["attribute"] = new_point_cloud_attribute
|
||||
# --- Publish (START) ---
|
||||
ayon_publish = ayon_max["publish"]
|
||||
try:
|
||||
attributes = json.loads(
|
||||
ayon_publish["ValidateAttributes"]["attributes"]
|
||||
)
|
||||
except ValueError:
|
||||
attributes = {}
|
||||
ayon_publish["ValidateAttributes"]["attributes"] = attributes
|
||||
if "ValidateAttributes" in ayon_publish:
|
||||
try:
|
||||
attributes = json.loads(
|
||||
ayon_publish["ValidateAttributes"]["attributes"]
|
||||
)
|
||||
except ValueError:
|
||||
attributes = {}
|
||||
ayon_publish["ValidateAttributes"]["attributes"] = attributes
|
||||
|
||||
if "ValidateLoadedPlugin" in ayon_publish:
|
||||
loaded_plugin = (
|
||||
|
|
@ -1027,10 +1028,14 @@ def _convert_traypublisher_project_settings(ayon_settings, output):
|
|||
item["family"] = item.pop("product_type")
|
||||
|
||||
shot_add_tasks = ayon_editorial_simple["shot_add_tasks"]
|
||||
|
||||
# TODO: backward compatibility and remove in future
|
||||
if isinstance(shot_add_tasks, dict):
|
||||
shot_add_tasks = []
|
||||
|
||||
# aggregate shot_add_tasks items
|
||||
new_shot_add_tasks = {
|
||||
item["name"]: item["task_type"]
|
||||
item["name"]: {"type": item["task_type"]}
|
||||
for item in shot_add_tasks
|
||||
}
|
||||
ayon_editorial_simple["shot_add_tasks"] = new_shot_add_tasks
|
||||
|
|
@ -1452,7 +1457,10 @@ class _AyonSettingsCache:
|
|||
def _use_bundles(cls):
|
||||
if _AyonSettingsCache.use_bundles is None:
|
||||
major, minor, _, _, _ = ayon_api.get_server_version_tuple()
|
||||
_AyonSettingsCache.use_bundles = major == 0 and minor >= 3
|
||||
use_bundles = True
|
||||
if (major, minor) < (0, 3):
|
||||
use_bundles = False
|
||||
_AyonSettingsCache.use_bundles = use_bundles
|
||||
return _AyonSettingsCache.use_bundles
|
||||
|
||||
@classmethod
|
||||
|
|
@ -1483,7 +1491,7 @@ class _AyonSettingsCache:
|
|||
bundles = ayon_api.get_bundles()
|
||||
user = ayon_api.get_user()
|
||||
username = user["name"]
|
||||
for bundle in bundles:
|
||||
for bundle in bundles["bundles"]:
|
||||
if (
|
||||
bundle.get("isDev")
|
||||
and bundle.get("activeUser") == username
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ class _IconsCache:
|
|||
|
||||
@classmethod
|
||||
def get_icon(cls, icon_def):
|
||||
if not icon_def:
|
||||
return None
|
||||
icon_type = icon_def["type"]
|
||||
cache_key = cls._get_cache_key(icon_def)
|
||||
cache = cls._cache.get(cache_key)
|
||||
|
|
|
|||
|
|
@ -579,6 +579,10 @@ class AssetsField(BaseClickableFrame):
|
|||
"""Change to asset names set with last `set_selected_items` call."""
|
||||
self.set_selected_items(self._origin_value)
|
||||
|
||||
def confirm_value(self):
|
||||
self._origin_value = copy.deepcopy(self._selected_items)
|
||||
self._has_value_changed = False
|
||||
|
||||
|
||||
class TasksComboboxProxy(QtCore.QSortFilterProxyModel):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
@ -785,6 +789,15 @@ class TasksCombobox(QtWidgets.QComboBox):
|
|||
|
||||
self._set_is_valid(is_valid)
|
||||
|
||||
def confirm_value(self, asset_names):
|
||||
new_task_name = self._selected_items[0]
|
||||
self._origin_value = [
|
||||
(asset_name, new_task_name)
|
||||
for asset_name in asset_names
|
||||
]
|
||||
self._origin_selection = copy.deepcopy(self._selected_items)
|
||||
self._has_value_changed = False
|
||||
|
||||
def set_selected_items(self, asset_task_combinations=None):
|
||||
"""Set items for selected instances.
|
||||
|
||||
|
|
@ -919,6 +932,10 @@ class VariantInputWidget(PlaceholderLineEdit):
|
|||
"""Change text of multiselection."""
|
||||
self._multiselection_text = text
|
||||
|
||||
def confirm_value(self):
|
||||
self._origin_value = copy.deepcopy(self._current_value)
|
||||
self._has_value_changed = False
|
||||
|
||||
def _set_is_valid(self, valid):
|
||||
if valid == self._is_valid:
|
||||
return
|
||||
|
|
@ -1110,6 +1127,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget):
|
|||
btns_layout = QtWidgets.QHBoxLayout()
|
||||
btns_layout.setContentsMargins(0, 0, 0, 0)
|
||||
btns_layout.addStretch(1)
|
||||
btns_layout.setSpacing(5)
|
||||
btns_layout.addWidget(submit_btn)
|
||||
btns_layout.addWidget(cancel_btn)
|
||||
|
||||
|
|
@ -1160,6 +1178,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget):
|
|||
|
||||
subset_names = set()
|
||||
invalid_tasks = False
|
||||
asset_names = []
|
||||
for instance in self._current_instances:
|
||||
new_variant_value = instance.get("variant")
|
||||
new_asset_name = instance.get("asset")
|
||||
|
|
@ -1173,6 +1192,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget):
|
|||
if task_name is not None:
|
||||
new_task_name = task_name
|
||||
|
||||
asset_names.append(new_asset_name)
|
||||
try:
|
||||
new_subset_name = self._controller.get_subset_name(
|
||||
instance.creator_identifier,
|
||||
|
|
@ -1210,6 +1230,15 @@ class GlobalAttrsWidget(QtWidgets.QWidget):
|
|||
self._set_btns_enabled(False)
|
||||
self._set_btns_visible(invalid_tasks)
|
||||
|
||||
if variant_value is not None:
|
||||
self.variant_input.confirm_value()
|
||||
|
||||
if asset_name is not None:
|
||||
self.asset_value_widget.confirm_value()
|
||||
|
||||
if task_name is not None:
|
||||
self.task_value_widget.confirm_value(asset_names)
|
||||
|
||||
self.instance_context_changed.emit()
|
||||
|
||||
def _on_cancel(self):
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from openpype.tools.utils import (
|
|||
MessageOverlayObject,
|
||||
PixmapLabel,
|
||||
)
|
||||
from openpype.tools.utils.lib import center_window
|
||||
|
||||
from .constants import ResetKeySequence
|
||||
from .publish_report_viewer import PublishReportViewerWidget
|
||||
|
|
@ -529,6 +530,7 @@ class PublisherWindow(QtWidgets.QDialog):
|
|||
def _on_first_show(self):
|
||||
self.resize(self.default_width, self.default_height)
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
center_window(self)
|
||||
self._reset_on_show = self._reset_on_first_show
|
||||
|
||||
def _on_show_timer(self):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring Pype version."""
|
||||
__version__ = "3.17.5-nightly.3"
|
||||
__version__ = "3.17.6-nightly.1"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "OpenPype"
|
||||
version = "3.17.4" # OpenPype
|
||||
version = "3.17.5" # OpenPype
|
||||
description = "Open VFX and Animation pipeline with support."
|
||||
authors = ["OpenPype Team <info@openpype.io>"]
|
||||
license = "MIT License"
|
||||
|
|
|
|||
|
|
@ -5,19 +5,17 @@ from ayon_server.settings import BaseSettingsModel, task_types_enum
|
|||
|
||||
class ClipNameTokenizerItem(BaseSettingsModel):
|
||||
_layout = "expanded"
|
||||
# TODO was 'dict-modifiable', is list of dicts now, must be fixed in code
|
||||
name: str = Field("#TODO", title="Tokenizer name")
|
||||
name: str = Field("", title="Tokenizer name")
|
||||
regex: str = Field("", title="Tokenizer regex")
|
||||
|
||||
|
||||
class ShotAddTasksItem(BaseSettingsModel):
|
||||
_layout = "expanded"
|
||||
# TODO was 'dict-modifiable', is list of dicts now, must be fixed in code
|
||||
name: str = Field('', title="Key")
|
||||
task_type: list[str] = Field(
|
||||
task_type: str = Field(
|
||||
title="Task type",
|
||||
default_factory=list,
|
||||
enum_resolver=task_types_enum)
|
||||
enum_resolver=task_types_enum
|
||||
)
|
||||
|
||||
|
||||
class ShotRenameSubmodel(BaseSettingsModel):
|
||||
|
|
@ -54,7 +52,7 @@ class TokenToParentConvertorItem(BaseSettingsModel):
|
|||
)
|
||||
|
||||
|
||||
class ShotHierchySubmodel(BaseSettingsModel):
|
||||
class ShotHierarchySubmodel(BaseSettingsModel):
|
||||
enabled: bool = True
|
||||
parents_path: str = Field(
|
||||
"",
|
||||
|
|
@ -102,9 +100,9 @@ class EditorialSimpleCreatorPlugin(BaseSettingsModel):
|
|||
title="Shot Rename",
|
||||
default_factory=ShotRenameSubmodel
|
||||
)
|
||||
shot_hierarchy: ShotHierchySubmodel = Field(
|
||||
shot_hierarchy: ShotHierarchySubmodel = Field(
|
||||
title="Shot Hierarchy",
|
||||
default_factory=ShotHierchySubmodel
|
||||
default_factory=ShotHierarchySubmodel
|
||||
)
|
||||
shot_add_tasks: list[ShotAddTasksItem] = Field(
|
||||
title="Add tasks to shot",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring addon version."""
|
||||
__version__ = "0.1.2"
|
||||
__version__ = "0.1.3"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ def pytest_addoption(parser):
|
|||
help="True - keep test_db, test_openpype, outputted test files"
|
||||
)
|
||||
|
||||
parser.addoption(
|
||||
"--app_group", action="store", default=None,
|
||||
help="Keep empty to use default application or explicit"
|
||||
)
|
||||
|
||||
parser.addoption(
|
||||
"--app_variant", action="store", default=None,
|
||||
help="Keep empty to locate latest installed variant or explicit"
|
||||
|
|
@ -45,6 +50,11 @@ def persist(request):
|
|||
return request.config.getoption("--persist")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def app_group(request):
|
||||
return request.config.getoption("--app_group")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def app_variant(request):
|
||||
return request.config.getoption("--app_variant")
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class MayaHostFixtures(HostFixtures):
|
|||
yield dest_path
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def startup_scripts(self, monkeypatch_session):
|
||||
def startup_scripts(self, monkeypatch_session, download_test_data):
|
||||
"""Points Maya to userSetup file from input data"""
|
||||
startup_path = os.path.join(
|
||||
os.path.dirname(__file__), "input", "startup"
|
||||
|
|
@ -44,6 +44,11 @@ class MayaHostFixtures(HostFixtures):
|
|||
"{}{}{}".format(startup_path, os.pathsep, original_pythonpath)
|
||||
)
|
||||
|
||||
monkeypatch_session.setenv(
|
||||
"MAYA_CMD_FILE_OUTPUT",
|
||||
os.path.join(download_test_data, "output.log")
|
||||
)
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def skip_compare_folders(self):
|
||||
yield []
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import re
|
||||
import os
|
||||
|
||||
from tests.lib.assert_classes import DBAssert
|
||||
from tests.integration.hosts.maya.lib import MayaLocalPublishTestClass
|
||||
|
||||
|
|
@ -35,6 +38,32 @@ class TestPublishInMaya(MayaLocalPublishTestClass):
|
|||
|
||||
TIMEOUT = 120 # publish timeout
|
||||
|
||||
def test_publish(
|
||||
self,
|
||||
dbcon,
|
||||
publish_finished,
|
||||
download_test_data
|
||||
):
|
||||
"""Testing Pyblish and Python logs within Maya."""
|
||||
|
||||
# All maya output via MAYA_CMD_FILE_OUTPUT env var during test run
|
||||
logging_path = os.path.join(download_test_data, "output.log")
|
||||
with open(logging_path, "r") as f:
|
||||
logging_output = f.read()
|
||||
|
||||
print(("-" * 50) + "LOGGING" + ("-" * 50))
|
||||
print(logging_output)
|
||||
|
||||
# Check for pyblish errors.
|
||||
error_regex = r"pyblish \(ERROR\)((.|\n)*?)((pyblish \())"
|
||||
matches = re.findall(error_regex, logging_output)
|
||||
assert not matches, matches[0][0]
|
||||
|
||||
# Check for python errors.
|
||||
error_regex = r"// Error((.|\n)*)"
|
||||
matches = re.findall(error_regex, logging_output)
|
||||
assert not matches, matches[0][0]
|
||||
|
||||
def test_db_asserts(self, dbcon, publish_finished):
|
||||
"""Host and input data dependent expected results in DB."""
|
||||
print("test_db_asserts")
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import glob
|
|||
import platform
|
||||
import requests
|
||||
import re
|
||||
import time
|
||||
|
||||
from tests.lib.db_handler import DBHandler
|
||||
from tests.lib.file_handler import RemoteFileHandler
|
||||
|
|
@ -248,19 +249,22 @@ class PublishTest(ModuleUnitTest):
|
|||
SETUP_ONLY = False
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def app_name(self, app_variant):
|
||||
def app_name(self, app_variant, app_group):
|
||||
"""Returns calculated value for ApplicationManager. Eg.(nuke/12-2)"""
|
||||
from openpype.lib import ApplicationManager
|
||||
app_variant = app_variant or self.APP_VARIANT
|
||||
app_group = app_group or self.APP_GROUP
|
||||
|
||||
application_manager = ApplicationManager()
|
||||
if not app_variant:
|
||||
variant = (
|
||||
application_manager.find_latest_available_variant_for_group(
|
||||
self.APP_GROUP))
|
||||
app_group
|
||||
)
|
||||
)
|
||||
app_variant = variant.name
|
||||
|
||||
yield "{}/{}".format(self.APP_GROUP, app_variant)
|
||||
yield "{}/{}".format(app_group, app_variant)
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def app_args(self, download_test_data):
|
||||
|
|
@ -331,7 +335,7 @@ class PublishTest(ModuleUnitTest):
|
|||
print("Creating only setup for test, not launching app")
|
||||
yield False
|
||||
return
|
||||
import time
|
||||
|
||||
time_start = time.time()
|
||||
timeout = timeout or self.TIMEOUT
|
||||
timeout = float(timeout)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue