Merge remote-tracking branch 'origin/develop' into feature/OP-3130_unreal-5-support

This commit is contained in:
Ondřej Samohel 2022-05-25 17:06:18 +02:00
commit 35225fd866
No known key found for this signature in database
GPG key ID: 02376E18990A97C6
38 changed files with 549 additions and 334 deletions

View file

@ -69,16 +69,14 @@ jobs:
run: |
git config user.email ${{ secrets.CI_EMAIL }}
git config user.name ${{ secrets.CI_USER }}
cd repos/avalon-core
git checkout main
git pull
cd ../..
git add .
git commit -m "[Automated] Bump version"
tag_name="CI/${{ steps.version.outputs.next_tag }}"
echo $tag_name
git tag -a $tag_name -m "nightly build"
- name: Push to protected main branch
uses: CasperWA/push-protected@v2.10.0
with:

View file

@ -1,166 +1,158 @@
# Changelog
## [3.10.0-nightly.2](https://github.com/pypeclub/OpenPype/tree/HEAD)
## [3.10.0-nightly.5](https://github.com/pypeclub/OpenPype/tree/HEAD)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.4...HEAD)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.8...HEAD)
### 📖 Documentation
**🆕 New features**
- Docs: add all-contributors config and initial list [\#3094](https://github.com/pypeclub/OpenPype/pull/3094)
- Nuke docs with videos [\#3052](https://github.com/pypeclub/OpenPype/pull/3052)
- General: OpenPype modules publish plugins are registered in host [\#3180](https://github.com/pypeclub/OpenPype/pull/3180)
- General: Creator plugins from addons can be registered [\#3179](https://github.com/pypeclub/OpenPype/pull/3179)
- Ftrack: Single image reviewable [\#3157](https://github.com/pypeclub/OpenPype/pull/3157)
- Nuke: Expose write attributes to settings [\#3123](https://github.com/pypeclub/OpenPype/pull/3123)
- Hiero: Initial frame publish support [\#3106](https://github.com/pypeclub/OpenPype/pull/3106)
**🚀 Enhancements**
- Standalone publisher: add support for bgeo and vdb [\#3080](https://github.com/pypeclub/OpenPype/pull/3080)
- Update collect\_render.py [\#3055](https://github.com/pypeclub/OpenPype/pull/3055)
- SiteSync: Added compute\_resource\_sync\_sites to sync\_server\_module [\#2983](https://github.com/pypeclub/OpenPype/pull/2983)
- Project Manager: Allow to paste Tasks into multiple assets at the same time [\#3226](https://github.com/pypeclub/OpenPype/pull/3226)
- Project manager: Sped up project load [\#3216](https://github.com/pypeclub/OpenPype/pull/3216)
- Loader UI: Speed issues of loader with sync server [\#3199](https://github.com/pypeclub/OpenPype/pull/3199)
- Maya: added clean\_import option to Import loader [\#3181](https://github.com/pypeclub/OpenPype/pull/3181)
- Maya: add maya 2023 to default applications [\#3167](https://github.com/pypeclub/OpenPype/pull/3167)
- Compressed bgeo publishing in SAP and Houdini loader [\#3153](https://github.com/pypeclub/OpenPype/pull/3153)
- General: Add 'dataclasses' to required python modules [\#3149](https://github.com/pypeclub/OpenPype/pull/3149)
- Hooks: Tweak logging grammar [\#3147](https://github.com/pypeclub/OpenPype/pull/3147)
- Nuke: settings for reformat node in CreateWriteRender node [\#3143](https://github.com/pypeclub/OpenPype/pull/3143)
- Houdini: Add loader for alembic through Alembic Archive node [\#3140](https://github.com/pypeclub/OpenPype/pull/3140)
- Publisher: UI Modifications and fixes [\#3139](https://github.com/pypeclub/OpenPype/pull/3139)
- General: Simplified OP modules/addons import [\#3137](https://github.com/pypeclub/OpenPype/pull/3137)
- Terminal: Tweak coloring of TrayModuleManager logging enabled states [\#3133](https://github.com/pypeclub/OpenPype/pull/3133)
- General: Cleanup some Loader docstrings [\#3131](https://github.com/pypeclub/OpenPype/pull/3131)
- Nuke: render instance with subset name filtered overrides [\#3117](https://github.com/pypeclub/OpenPype/pull/3117)
- Unreal: Layout and Camera update and remove functions reimplemented and improvements [\#3116](https://github.com/pypeclub/OpenPype/pull/3116)
- Settings: Remove environment groups from settings [\#3115](https://github.com/pypeclub/OpenPype/pull/3115)
- TVPaint: Match renderlayer key with other hosts [\#3110](https://github.com/pypeclub/OpenPype/pull/3110)
- Tray publisher: Simple families from settings [\#3105](https://github.com/pypeclub/OpenPype/pull/3105)
**🐛 Bug fixes**
- RoyalRender Control Submission - AVALON\_APP\_NAME default [\#3091](https://github.com/pypeclub/OpenPype/pull/3091)
- Ftrack: Update Create Folders action [\#3089](https://github.com/pypeclub/OpenPype/pull/3089)
- Project Manager: Avoid unnecessary updates of asset documents [\#3083](https://github.com/pypeclub/OpenPype/pull/3083)
- Standalone publisher: Fix plugins install [\#3077](https://github.com/pypeclub/OpenPype/pull/3077)
- General: Extract review sequence is not converted with same names [\#3076](https://github.com/pypeclub/OpenPype/pull/3076)
- Webpublisher: Use variant value [\#3068](https://github.com/pypeclub/OpenPype/pull/3068)
- Nuke: Add aov matching even for remainder and prerender [\#3060](https://github.com/pypeclub/OpenPype/pull/3060)
- Ftrack: Validate that the user exists on ftrack [\#3237](https://github.com/pypeclub/OpenPype/pull/3237)
- TVPaint: Look for more groups than 12 [\#3228](https://github.com/pypeclub/OpenPype/pull/3228)
- Project Manager: Fix persistent editors on project change [\#3218](https://github.com/pypeclub/OpenPype/pull/3218)
- Deadline: instance data overwrite fix [\#3214](https://github.com/pypeclub/OpenPype/pull/3214)
- Ftrack: Push hierarchical attributes action works [\#3210](https://github.com/pypeclub/OpenPype/pull/3210)
- Standalone Publisher: Always create new representation for thumbnail [\#3203](https://github.com/pypeclub/OpenPype/pull/3203)
- Photoshop: skip collector when automatic testing [\#3202](https://github.com/pypeclub/OpenPype/pull/3202)
- Nuke: render/workfile version sync doesn't work on farm [\#3185](https://github.com/pypeclub/OpenPype/pull/3185)
- Ftrack: Review image only if there are no mp4 reviews [\#3183](https://github.com/pypeclub/OpenPype/pull/3183)
- General: Avoid creating multiple thumbnails [\#3176](https://github.com/pypeclub/OpenPype/pull/3176)
- General/Hiero: better clip duration calculation [\#3169](https://github.com/pypeclub/OpenPype/pull/3169)
- General: Oiio conversion for ffmpeg checks for invalid characters [\#3166](https://github.com/pypeclub/OpenPype/pull/3166)
- Fix for attaching render to subset [\#3164](https://github.com/pypeclub/OpenPype/pull/3164)
- Harmony: fixed missing task name in render instance [\#3163](https://github.com/pypeclub/OpenPype/pull/3163)
- Ftrack: Action delete old versions formatting works [\#3152](https://github.com/pypeclub/OpenPype/pull/3152)
- Deadline: fix the output directory [\#3144](https://github.com/pypeclub/OpenPype/pull/3144)
- General: New Session schema [\#3141](https://github.com/pypeclub/OpenPype/pull/3141)
- General: Missing version on headless mode crash properly [\#3136](https://github.com/pypeclub/OpenPype/pull/3136)
- TVPaint: Composite layers in reversed order [\#3135](https://github.com/pypeclub/OpenPype/pull/3135)
- Nuke: fixing default settings for workfile builder loaders [\#3120](https://github.com/pypeclub/OpenPype/pull/3120)
- Nuke: fix anatomy imageio regex default [\#3119](https://github.com/pypeclub/OpenPype/pull/3119)
**🔀 Refactored code**
- General: Move host install [\#3009](https://github.com/pypeclub/OpenPype/pull/3009)
- Avalon repo removed from Jobs workflow [\#3193](https://github.com/pypeclub/OpenPype/pull/3193)
- General: Remove remaining imports from avalon [\#3130](https://github.com/pypeclub/OpenPype/pull/3130)
**Merged pull requests:**
- Nuke: added suspend\_publish knob [\#3078](https://github.com/pypeclub/OpenPype/pull/3078)
- Bump async from 2.6.3 to 2.6.4 in /website [\#3065](https://github.com/pypeclub/OpenPype/pull/3065)
- Maya: added jpg to filter for Image Plane Loader [\#3223](https://github.com/pypeclub/OpenPype/pull/3223)
- Webpublisher: replace space by underscore in subset names [\#3160](https://github.com/pypeclub/OpenPype/pull/3160)
- StandalonePublisher: removed Extract Background plugins [\#3093](https://github.com/pypeclub/OpenPype/pull/3093)
## [3.9.8](https://github.com/pypeclub/OpenPype/tree/3.9.8) (2022-05-19)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.7...3.9.8)
**🚀 Enhancements**
- nuke: generate publishing nodes inside render group node [\#3206](https://github.com/pypeclub/OpenPype/pull/3206)
- Loader UI: Speed issues of loader with sync server [\#3200](https://github.com/pypeclub/OpenPype/pull/3200)
- Backport of fix for attaching renders to subsets [\#3195](https://github.com/pypeclub/OpenPype/pull/3195)
**🐛 Bug fixes**
- Standalone Publisher: Always create new representation for thumbnail [\#3204](https://github.com/pypeclub/OpenPype/pull/3204)
- Nuke: render/workfile version sync doesn't work on farm [\#3184](https://github.com/pypeclub/OpenPype/pull/3184)
- Ftrack: Review image only if there are no mp4 reviews [\#3182](https://github.com/pypeclub/OpenPype/pull/3182)
- Ftrack: Locations deepcopy issue [\#3177](https://github.com/pypeclub/OpenPype/pull/3177)
- Ftrack: Locations deepcopy issue [\#3175](https://github.com/pypeclub/OpenPype/pull/3175)
- General: Avoid creating multiple thumbnails [\#3174](https://github.com/pypeclub/OpenPype/pull/3174)
- General: TemplateResult can be copied [\#3170](https://github.com/pypeclub/OpenPype/pull/3170)
**Merged pull requests:**
- hiero: otio p3 compatibility issue - metadata on effect use update [\#3194](https://github.com/pypeclub/OpenPype/pull/3194)
## [3.9.7](https://github.com/pypeclub/OpenPype/tree/3.9.7) (2022-05-11)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.6...3.9.7)
**🆕 New features**
- Ftrack: Single image reviewable [\#3158](https://github.com/pypeclub/OpenPype/pull/3158)
**🚀 Enhancements**
- Deadline output dir issue to 3.9x [\#3155](https://github.com/pypeclub/OpenPype/pull/3155)
- nuke: removing redundant code from startup [\#3142](https://github.com/pypeclub/OpenPype/pull/3142)
**🐛 Bug fixes**
- Ftrack: Action delete old versions formatting works [\#3154](https://github.com/pypeclub/OpenPype/pull/3154)
- nuke: adding extract thumbnail settings [\#3148](https://github.com/pypeclub/OpenPype/pull/3148)
**Merged pull requests:**
- Webpublisher: replace space by underscore in subset names [\#3159](https://github.com/pypeclub/OpenPype/pull/3159)
## [3.9.6](https://github.com/pypeclub/OpenPype/tree/3.9.6) (2022-05-03)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.5...3.9.6)
**🆕 New features**
- Nuke: render instance with subset name filtered overrides \(3.9.x\) [\#3125](https://github.com/pypeclub/OpenPype/pull/3125)
**🚀 Enhancements**
- TVPaint: Match renderlayer key with other hosts [\#3109](https://github.com/pypeclub/OpenPype/pull/3109)
**🐛 Bug fixes**
- TVPaint: Composite layers in reversed order [\#3134](https://github.com/pypeclub/OpenPype/pull/3134)
- General: Python 3 compatibility in queries [\#3111](https://github.com/pypeclub/OpenPype/pull/3111)
**Merged pull requests:**
- Ftrack: AssetVersion status on publish [\#3114](https://github.com/pypeclub/OpenPype/pull/3114)
- renderman support for 3.9.x [\#3107](https://github.com/pypeclub/OpenPype/pull/3107)
## [3.9.5](https://github.com/pypeclub/OpenPype/tree/3.9.5) (2022-04-25)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.10.0-nightly.2...3.9.5)
## [3.9.4](https://github.com/pypeclub/OpenPype/tree/3.9.4) (2022-04-15)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.9.4-nightly.2...3.9.4)
### 📖 Documentation
- Documentation: more info about Tasks [\#3062](https://github.com/pypeclub/OpenPype/pull/3062)
- Documentation: Python requirements to 3.7.9 [\#3035](https://github.com/pypeclub/OpenPype/pull/3035)
- Website Docs: Remove unused pages [\#2974](https://github.com/pypeclub/OpenPype/pull/2974)
**🆕 New features**
- General: Local overrides for environment variables [\#3045](https://github.com/pypeclub/OpenPype/pull/3045)
**🚀 Enhancements**
- TVPaint: Added init file for worker to triggers missing sound file dialog [\#3053](https://github.com/pypeclub/OpenPype/pull/3053)
- Ftrack: Custom attributes can be filled in slate values [\#3036](https://github.com/pypeclub/OpenPype/pull/3036)
- Resolve environment variable in google drive credential path [\#3008](https://github.com/pypeclub/OpenPype/pull/3008)
**🐛 Bug fixes**
- GitHub: Updated push-protected action in github workflow [\#3064](https://github.com/pypeclub/OpenPype/pull/3064)
- Nuke: Typos in imports from Nuke implementation [\#3061](https://github.com/pypeclub/OpenPype/pull/3061)
- Hotfix: fixing deadline job publishing [\#3059](https://github.com/pypeclub/OpenPype/pull/3059)
- General: Extract Review handle invalid characters for ffmpeg [\#3050](https://github.com/pypeclub/OpenPype/pull/3050)
- Slate Review: Support to keep format on slate concatenation [\#3049](https://github.com/pypeclub/OpenPype/pull/3049)
- Webpublisher: fix processing of workfile [\#3048](https://github.com/pypeclub/OpenPype/pull/3048)
- Ftrack: Integrate ftrack api fix [\#3044](https://github.com/pypeclub/OpenPype/pull/3044)
- Webpublisher - removed wrong hardcoded family [\#3043](https://github.com/pypeclub/OpenPype/pull/3043)
- LibraryLoader: Use current project for asset query in families filter [\#3042](https://github.com/pypeclub/OpenPype/pull/3042)
- SiteSync: Providers ignore that site is disabled [\#3041](https://github.com/pypeclub/OpenPype/pull/3041)
- Unreal: Creator import fixes [\#3040](https://github.com/pypeclub/OpenPype/pull/3040)
- Settings UI: Version column can be extended so version are visible [\#3032](https://github.com/pypeclub/OpenPype/pull/3032)
- SiteSync: fix transitive alternate sites, fix dropdown in Local Settings [\#3018](https://github.com/pypeclub/OpenPype/pull/3018)
**Merged pull requests:**
- Deadline: reworked pools assignment [\#3051](https://github.com/pypeclub/OpenPype/pull/3051)
- Houdini: Avoid ImportError on `hdefereval` when Houdini runs without UI [\#2987](https://github.com/pypeclub/OpenPype/pull/2987)
## [3.9.3](https://github.com/pypeclub/OpenPype/tree/3.9.3) (2022-04-07)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.9.3-nightly.2...3.9.3)
### 📖 Documentation
- Website Docs: Manager Ftrack fix broken links [\#2979](https://github.com/pypeclub/OpenPype/pull/2979)
**🆕 New features**
- Ftrack: Add description integrator [\#3027](https://github.com/pypeclub/OpenPype/pull/3027)
- Publishing textures for Unreal [\#2988](https://github.com/pypeclub/OpenPype/pull/2988)
**🚀 Enhancements**
- Ftrack: Add more options for note text of integrate ftrack note [\#3025](https://github.com/pypeclub/OpenPype/pull/3025)
- Console Interpreter: Changed how console splitter size are reused on show [\#3016](https://github.com/pypeclub/OpenPype/pull/3016)
- Deadline: Use more suitable name for sequence review logic [\#3015](https://github.com/pypeclub/OpenPype/pull/3015)
- General: default workfile subset name for workfile [\#3011](https://github.com/pypeclub/OpenPype/pull/3011)
- Deadline: priority configurable in Maya jobs [\#2995](https://github.com/pypeclub/OpenPype/pull/2995)
**🐛 Bug fixes**
- Deadline: Fixed default value of use sequence for review [\#3033](https://github.com/pypeclub/OpenPype/pull/3033)
- General: Fix validate asset docs plug-in filename and class name [\#3029](https://github.com/pypeclub/OpenPype/pull/3029)
- General: Fix import after movements [\#3028](https://github.com/pypeclub/OpenPype/pull/3028)
- Harmony: Added creating subset name for workfile from template [\#3024](https://github.com/pypeclub/OpenPype/pull/3024)
- AfterEffects: Added creating subset name for workfile from template [\#3023](https://github.com/pypeclub/OpenPype/pull/3023)
- General: Add example addons to ignored [\#3022](https://github.com/pypeclub/OpenPype/pull/3022)
- Maya: Remove missing import [\#3017](https://github.com/pypeclub/OpenPype/pull/3017)
- Ftrack: multiple reviewable componets [\#3012](https://github.com/pypeclub/OpenPype/pull/3012)
- Tray publisher: Fixes after code movement [\#3010](https://github.com/pypeclub/OpenPype/pull/3010)
- Nuke: fixing unicode type detection in effect loaders [\#3002](https://github.com/pypeclub/OpenPype/pull/3002)
- Nuke: removing redundant Ftrack asset when farm publishing [\#2996](https://github.com/pypeclub/OpenPype/pull/2996)
**Merged pull requests:**
- Maya: Allow to select invalid camera contents if no cameras found [\#3030](https://github.com/pypeclub/OpenPype/pull/3030)
- General: adding limitations for pyright [\#2994](https://github.com/pypeclub/OpenPype/pull/2994)
## [3.9.2](https://github.com/pypeclub/OpenPype/tree/3.9.2) (2022-04-04)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.9.2-nightly.4...3.9.2)
### 📖 Documentation
- Documentation: Added mention of adding My Drive as a root [\#2999](https://github.com/pypeclub/OpenPype/pull/2999)
- Docs: Added MongoDB requirements [\#2951](https://github.com/pypeclub/OpenPype/pull/2951)
**🆕 New features**
- nuke: bypass baking [\#2992](https://github.com/pypeclub/OpenPype/pull/2992)
- Maya to Unreal: Static and Skeletal Meshes [\#2978](https://github.com/pypeclub/OpenPype/pull/2978)
**🚀 Enhancements**
- Nuke: add concurrency attr to deadline job [\#3005](https://github.com/pypeclub/OpenPype/pull/3005)
- Photoshop: create image without instance [\#3001](https://github.com/pypeclub/OpenPype/pull/3001)
- TVPaint: Render scene family [\#3000](https://github.com/pypeclub/OpenPype/pull/3000)
- Nuke: ReviewDataMov Read RAW attribute [\#2985](https://github.com/pypeclub/OpenPype/pull/2985)
- General: `METADATA\_KEYS` constant as `frozenset` for optimal immutable lookup [\#2980](https://github.com/pypeclub/OpenPype/pull/2980)
- General: Tools with host filters [\#2975](https://github.com/pypeclub/OpenPype/pull/2975)
- Hero versions: Use custom templates [\#2967](https://github.com/pypeclub/OpenPype/pull/2967)
**🐛 Bug fixes**
- Hosts: Remove path existence checks in 'add\_implementation\_envs' [\#3004](https://github.com/pypeclub/OpenPype/pull/3004)
- Fix - remove doubled dot in workfile created from template [\#2998](https://github.com/pypeclub/OpenPype/pull/2998)
- PS: fix renaming subset incorrectly in PS [\#2991](https://github.com/pypeclub/OpenPype/pull/2991)
- Fix: Disable setuptools auto discovery [\#2990](https://github.com/pypeclub/OpenPype/pull/2990)
- AEL: fix opening existing workfile if no scene opened [\#2989](https://github.com/pypeclub/OpenPype/pull/2989)
- Maya: Don't do hardlinks on windows for look publishing [\#2986](https://github.com/pypeclub/OpenPype/pull/2986)
- Settings UI: Fix version completer on linux [\#2981](https://github.com/pypeclub/OpenPype/pull/2981)
- Photoshop: Fix creation of subset names in PS review and workfile [\#2969](https://github.com/pypeclub/OpenPype/pull/2969)
- Slack: Added default for review\_upload\_limit for Slack [\#2965](https://github.com/pypeclub/OpenPype/pull/2965)
- General: OIIO conversion for ffmeg can handle sequences [\#2958](https://github.com/pypeclub/OpenPype/pull/2958)
- Settings: Conditional dictionary avoid invalid logs [\#2956](https://github.com/pypeclub/OpenPype/pull/2956)
- General: Smaller fixes and typos [\#2950](https://github.com/pypeclub/OpenPype/pull/2950)
**Merged pull requests:**
- Bump paramiko from 2.9.2 to 2.10.1 [\#2973](https://github.com/pypeclub/OpenPype/pull/2973)
- Bump minimist from 1.2.5 to 1.2.6 in /website [\#2954](https://github.com/pypeclub/OpenPype/pull/2954)
- Bump node-forge from 1.2.1 to 1.3.0 in /website [\#2953](https://github.com/pypeclub/OpenPype/pull/2953)
- Maya - added transparency into review creator [\#2952](https://github.com/pypeclub/OpenPype/pull/2952)
## [3.9.1](https://github.com/pypeclub/OpenPype/tree/3.9.1) (2022-03-18)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.9.1-nightly.3...3.9.1)

View file

@ -151,7 +151,7 @@ def create_otio_reference(clip):
padding = media_source.filenamePadding()
file_head = media_source.filenameHead()
is_sequence = not media_source.singleFile()
frame_duration = media_source.duration()
frame_duration = media_source.duration() - 1
fps = utils.get_rate(clip) or self.project_fps
extension = os.path.splitext(path)[-1]

View file

@ -296,6 +296,8 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
continue
if otio_clip.name not in track_item.name():
continue
self.log.debug("__ parent_range: {}".format(parent_range))
self.log.debug("__ timeline_range: {}".format(timeline_range))
if openpype.lib.is_overlapping_otio_ranges(
parent_range, timeline_range, strict=True):

View file

@ -1093,6 +1093,11 @@ class RenderProductsRenderman(ARenderProducts):
if not enabled:
continue
# Skip display types not producing any file output.
# Is there a better way to do it?
if not display_types.get(display["driverNode"]["type"]):
continue
aov_name = name
if aov_name == "rmanDefaultDisplay":
aov_name = "beauty"

View file

@ -1,7 +1,7 @@
"""A module containing generic loader actions that will display in the Loader.
"""
import qargparse
from openpype.pipeline import load
from openpype.hosts.maya.api.lib import (
maintained_selection,
@ -98,6 +98,15 @@ class ImportMayaLoader(load.LoaderPlugin):
icon = "arrow-circle-down"
color = "#775555"
options = [
qargparse.Boolean(
"clean_import",
label="Clean import",
default=False,
help="Should all occurences of cbId be purged?"
)
]
def load(self, context, name=None, namespace=None, data=None):
import maya.cmds as cmds
@ -114,13 +123,22 @@ class ImportMayaLoader(load.LoaderPlugin):
)
with maintained_selection():
cmds.file(self.fname,
i=True,
preserveReferences=True,
namespace=namespace,
returnNewNodes=True,
groupReference=True,
groupName="{}:{}".format(namespace, name))
nodes = cmds.file(self.fname,
i=True,
preserveReferences=True,
namespace=namespace,
returnNewNodes=True,
groupReference=True,
groupName="{}:{}".format(namespace, name))
if data.get("clean_import", False):
remove_attributes = ["cbId"]
for node in nodes:
for attr in remove_attributes:
if cmds.attributeQuery(attr, node=node, exists=True):
full_attr = "{}.{}".format(node, attr)
print("Removing {}".format(full_attr))
cmds.deleteAttr(full_attr)
# We do not containerize imported content, it remains unmanaged
return

View file

@ -83,7 +83,7 @@ class ImagePlaneLoader(load.LoaderPlugin):
families = ["image", "plate", "render"]
label = "Load imagePlane"
representations = ["mov", "exr", "preview", "png"]
representations = ["mov", "exr", "preview", "png", "jpg"]
icon = "image"
color = "orange"

View file

@ -22,10 +22,46 @@ RENDERER_NODE_TYPES = [
# redshift
"RedshiftMeshParameters"
]
SHAPE_ATTRS = set(SHAPE_ATTRS)
def get_pxr_multitexture_file_attrs(node):
attrs = []
for i in range(9):
if cmds.attributeQuery("filename{}".format(i), node):
file = cmds.getAttr("{}.filename{}".format(node, i))
if file:
attrs.append("filename{}".format(i))
return attrs
FILE_NODES = {
"file": "fileTextureName",
"aiImage": "filename",
"RedshiftNormalMap": "text0",
"PxrBump": "filename",
"PxrNormalMap": "filename",
"PxrMultiTexture": get_pxr_multitexture_file_attrs,
"PxrPtexture": "filename",
"PxrTexture": "filename"
}
def get_attributes(dictionary, attr):
# type: (dict, str) -> list
if callable(dictionary[attr]):
val = dictionary[attr]()
else:
val = dictionary.get(attr, [])
if not isinstance(val, list):
return [val]
return val
def get_look_attrs(node):
"""Returns attributes of a node that are important for the look.
@ -51,15 +87,14 @@ def get_look_attrs(node):
if cmds.objectType(node, isAType="shape"):
attrs = cmds.listAttr(node, changedSinceFileOpen=True) or []
for attr in attrs:
if attr in SHAPE_ATTRS:
if attr in SHAPE_ATTRS or \
attr not in SHAPE_ATTRS and attr.startswith('ai'):
result.append(attr)
elif attr.startswith('ai'):
result.append(attr)
return result
def node_uses_image_sequence(node):
def node_uses_image_sequence(node, node_path):
# type: (str) -> bool
"""Return whether file node uses an image sequence or single image.
Determine if a node uses an image sequence or just a single image,
@ -74,12 +109,15 @@ def node_uses_image_sequence(node):
"""
# useFrameExtension indicates an explicit image sequence
node_path = get_file_node_path(node).lower()
# The following tokens imply a sequence
patterns = ["<udim>", "<tile>", "<uvtile>", "u<u>_v<v>", "<frame0"]
patterns = ["<udim>", "<tile>", "<uvtile>",
"u<u>_v<v>", "<frame0", "<f4>"]
try:
use_frame_extension = cmds.getAttr('%s.useFrameExtension' % node)
except ValueError:
use_frame_extension = False
return (cmds.getAttr('%s.useFrameExtension' % node) or
return (use_frame_extension or
any(pattern in node_path for pattern in patterns))
@ -137,14 +175,15 @@ def seq_to_glob(path):
return path
def get_file_node_path(node):
def get_file_node_paths(node):
# type: (str) -> list
"""Get the file path used by a Maya file node.
Args:
node (str): Name of the Maya file node
Returns:
str: the file path in use
list: the file paths in use
"""
# if the path appears to be sequence, use computedFileTextureNamePattern,
@ -163,15 +202,19 @@ def get_file_node_path(node):
"<uvtile>"]
lower = texture_pattern.lower()
if any(pattern in lower for pattern in patterns):
return texture_pattern
return [texture_pattern]
if cmds.nodeType(node) == 'aiImage':
return cmds.getAttr('{0}.filename'.format(node))
if cmds.nodeType(node) == 'RedshiftNormalMap':
return cmds.getAttr('{}.tex0'.format(node))
try:
file_attributes = get_attributes(FILE_NODES, cmds.nodeType(node))
except AttributeError:
file_attributes = "fileTextureName"
# otherwise use fileTextureName
return cmds.getAttr('{0}.fileTextureName'.format(node))
files = []
for file_attr in file_attributes:
if cmds.attributeQuery(file_attr, node=node, exists=True):
files.append(cmds.getAttr("{}.{}".format(node, file_attr)))
return files
def get_file_node_files(node):
@ -185,16 +228,21 @@ def get_file_node_files(node):
list: List of full file paths.
"""
paths = get_file_node_paths(node)
sequences = []
replaces = []
for index, path in enumerate(paths):
if node_uses_image_sequence(node, path):
glob_pattern = seq_to_glob(path)
sequences.extend(glob.glob(glob_pattern))
replaces.append(index)
path = get_file_node_path(node)
path = cmds.workspace(expandName=path)
if node_uses_image_sequence(node):
glob_pattern = seq_to_glob(path)
return glob.glob(glob_pattern)
elif os.path.exists(path):
return [path]
else:
return []
for index in replaces:
paths.pop(index)
paths.extend(sequences)
return [p for p in paths if os.path.exists(p)]
class CollectLook(pyblish.api.InstancePlugin):
@ -238,13 +286,13 @@ class CollectLook(pyblish.api.InstancePlugin):
"for %s" % instance.data['name'])
# Discover related object sets
self.log.info("Gathering sets..")
self.log.info("Gathering sets ...")
sets = self.collect_sets(instance)
# Lookup set (optimization)
instance_lookup = set(cmds.ls(instance, long=True))
self.log.info("Gathering set relations..")
self.log.info("Gathering set relations ...")
# Ensure iteration happen in a list so we can remove keys from the
# dict within the loop
@ -326,7 +374,10 @@ class CollectLook(pyblish.api.InstancePlugin):
"volumeShader",
"displacementShader",
"aiSurfaceShader",
"aiVolumeShader"]
"aiVolumeShader",
"rman__surface",
"rman__displacement"
]
if look_sets:
materials = []
@ -374,9 +425,10 @@ class CollectLook(pyblish.api.InstancePlugin):
or []
)
files = cmds.ls(history, type="file", long=True)
files.extend(cmds.ls(history, type="aiImage", long=True))
files.extend(cmds.ls(history, type="RedshiftNormalMap", long=True))
all_supported_nodes = FILE_NODES.keys()
files = []
for node_type in all_supported_nodes:
files.extend(cmds.ls(history, type=node_type, long=True))
self.log.info("Collected file nodes:\n{}".format(files))
# Collect textures if any file nodes are found
@ -510,27 +562,24 @@ class CollectLook(pyblish.api.InstancePlugin):
Returns:
dict
"""
self.log.debug("processing: {}".format(node))
if cmds.nodeType(node) not in ["file", "aiImage", "RedshiftNormalMap"]:
all_supported_nodes = FILE_NODES.keys()
if cmds.nodeType(node) not in all_supported_nodes:
self.log.error(
"Unsupported file node: {}".format(cmds.nodeType(node)))
raise AssertionError("Unsupported file node")
if cmds.nodeType(node) == 'file':
self.log.debug(" - file node")
attribute = "{}.fileTextureName".format(node)
computed_attribute = "{}.computedFileTextureNamePattern".format(node)
elif cmds.nodeType(node) == 'aiImage':
self.log.debug("aiImage node")
attribute = "{}.filename".format(node)
computed_attribute = attribute
elif cmds.nodeType(node) == 'RedshiftNormalMap':
self.log.debug("RedshiftNormalMap node")
attribute = "{}.tex0".format(node)
computed_attribute = attribute
self.log.debug(" - got {}".format(cmds.nodeType(node)))
attribute = FILE_NODES.get(cmds.nodeType(node))
source = cmds.getAttr("{}.{}".format(
node,
attribute
))
computed_attribute = "{}.{}".format(node, attribute)
if attribute == "fileTextureName":
computed_attribute = node + ".computedFileTextureNamePattern"
source = cmds.getAttr(attribute)
self.log.info(" - file source: {}".format(source))
color_space_attr = "{}.colorSpace".format(node)
try:
@ -567,11 +616,15 @@ class CollectLook(pyblish.api.InstancePlugin):
self.log.info(" - color space: {}".format(color_space))
# Define the resource
return {"node": node,
"attribute": attribute,
"source": source, # required for resources
"files": files,
"color_space": color_space} # required for resources
return {
"node": node,
# here we are passing not only attribute, but with node again
# this should be simplified and changed extractor.
"attribute": "{}.{}".format(node, attribute),
"source": source, # required for resources
"files": files,
"color_space": color_space
} # required for resources
class CollectModelRenderSets(CollectLook):

View file

@ -339,9 +339,15 @@ class CollectMayaRender(pyblish.api.ContextPlugin):
"source": filepath,
"expectedFiles": full_exp_files,
"publishRenderMetadataFolder": common_publish_meta_path,
"resolutionWidth": cmds.getAttr("defaultResolution.width"),
"resolutionHeight": cmds.getAttr("defaultResolution.height"),
"pixelAspect": cmds.getAttr("defaultResolution.pixelAspect"),
"resolutionWidth": lib.get_attr_in_layer(
"defaultResolution.height", layer=layer_name
),
"resolutionHeight": lib.get_attr_in_layer(
"defaultResolution.width", layer=layer_name
),
"pixelAspect": lib.get_attr_in_layer(
"defaultResolution.pixelAspect", layer=layer_name
),
"tileRendering": render_instance.data.get("tileRendering") or False, # noqa: E501
"tilesX": render_instance.data.get("tilesX") or 2,
"tilesY": render_instance.data.get("tilesY") or 2,

View file

@ -124,9 +124,15 @@ class CollectVrayScene(pyblish.api.InstancePlugin):
# Add source to allow tracing back to the scene from
# which was submitted originally
"source": context.data["currentFile"].replace("\\", "/"),
"resolutionWidth": cmds.getAttr("defaultResolution.width"),
"resolutionHeight": cmds.getAttr("defaultResolution.height"),
"pixelAspect": cmds.getAttr("defaultResolution.pixelAspect"),
"resolutionWidth": lib.get_attr_in_layer(
"defaultResolution.height", layer=layer_name
),
"resolutionHeight": lib.get_attr_in_layer(
"defaultResolution.width", layer=layer_name
),
"pixelAspect": lib.get_attr_in_layer(
"defaultResolution.pixelAspect", layer=layer_name
),
"priority": instance.data.get("priority"),
"useMultipleSceneFiles": instance.data.get(
"vraySceneMultipleFiles")

View file

@ -372,10 +372,12 @@ class ExtractLook(openpype.api.Extractor):
if mode == COPY:
transfers.append((source, destination))
self.log.info('copying')
self.log.info('file will be copied {} -> {}'.format(
source, destination))
elif mode == HARDLINK:
hardlinks.append((source, destination))
self.log.info('hardlinking')
self.log.info('file will be hardlinked {} -> {}'.format(
source, destination))
# Store the hashes from hash to destination to include in the
# database

View file

@ -39,6 +39,9 @@ class CollectBatchData(pyblish.api.ContextPlugin):
def process(self, context):
self.log.info("CollectBatchData")
batch_dir = os.environ.get("OPENPYPE_PUBLISH_DATA")
if os.environ.get("IS_TEST"):
self.log.debug("Automatic testing, no batch data, skipping")
return
assert batch_dir, (
"Missing `OPENPYPE_PUBLISH_DATA`")

View file

@ -84,7 +84,7 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin):
"variant": variant,
"family": resolved_family,
"task": task_name,
"layer": layer.name
"layer": layer.clean_name
}
subset = resolved_subset_template.format(

View file

@ -5,6 +5,7 @@ import openpype.api
from openpype.lib import (
get_ffmpeg_tool_path,
get_ffprobe_streams,
path_to_subprocess_arg,
)
@ -37,82 +38,69 @@ class ExtractThumbnailSP(pyblish.api.InstancePlugin):
if not thumbnail_repre:
return
thumbnail_repre.pop("thumbnail")
files = thumbnail_repre.get("files")
if not files:
return
if isinstance(files, list):
files_len = len(files)
file = str(files[0])
first_filename = str(files[0])
else:
files_len = 1
file = files
first_filename = files
staging_dir = None
is_jpeg = False
if file.endswith(".jpeg") or file.endswith(".jpg"):
is_jpeg = True
if is_jpeg and files_len == 1:
# skip if already is single jpeg file
return
# Convert to jpeg if not yet
full_input_path = os.path.join(
thumbnail_repre["stagingDir"], first_filename
)
self.log.info("input {}".format(full_input_path))
with tempfile.NamedTemporaryFile(suffix=".jpg") as tmp:
full_thumbnail_path = tmp.name
elif is_jpeg:
# use first frame as thumbnail if is sequence of jpegs
full_thumbnail_path = os.path.join(
thumbnail_repre["stagingDir"], file
)
self.log.info(
"For thumbnail is used file: {}".format(full_thumbnail_path)
)
self.log.info("output {}".format(full_thumbnail_path))
else:
# Convert to jpeg if not yet
full_input_path = os.path.join(thumbnail_repre["stagingDir"], file)
self.log.info("input {}".format(full_input_path))
instance.context.data["cleanupFullPaths"].append(full_thumbnail_path)
full_thumbnail_path = tempfile.mkstemp(suffix=".jpg")[1]
self.log.info("output {}".format(full_thumbnail_path))
ffmpeg_path = get_ffmpeg_tool_path("ffmpeg")
ffmpeg_path = get_ffmpeg_tool_path("ffmpeg")
ffmpeg_args = self.ffmpeg_args or {}
ffmpeg_args = self.ffmpeg_args or {}
jpeg_items = [
path_to_subprocess_arg(ffmpeg_path),
# override file if already exists
"-y"
]
jpeg_items = [
"\"{}\"".format(ffmpeg_path),
# override file if already exists
"-y"
]
# add input filters from peresets
jpeg_items.extend(ffmpeg_args.get("input") or [])
# input file
jpeg_items.append("-i \"{}\"".format(full_input_path))
# add input filters from peresets
jpeg_items.extend(ffmpeg_args.get("input") or [])
# input file
jpeg_items.extend([
"-i", path_to_subprocess_arg(full_input_path),
# extract only single file
jpeg_items.append("-frames:v 1")
"-frames:v", "1",
# Add black background for transparent images
jpeg_items.append((
"-filter_complex"
" \"color=black,format=rgb24[c]"
"-filter_complex", (
"\"color=black,format=rgb24[c]"
";[c][0]scale2ref[c][i]"
";[c][i]overlay=format=auto:shortest=1,setsar=1\""
))
),
])
jpeg_items.extend(ffmpeg_args.get("output") or [])
jpeg_items.extend(ffmpeg_args.get("output") or [])
# output file
jpeg_items.append("\"{}\"".format(full_thumbnail_path))
# output file
jpeg_items.append(path_to_subprocess_arg(full_thumbnail_path))
subprocess_jpeg = " ".join(jpeg_items)
subprocess_jpeg = " ".join(jpeg_items)
# run subprocess
self.log.debug("Executing: {}".format(subprocess_jpeg))
openpype.api.run_subprocess(
subprocess_jpeg, shell=True, logger=self.log
)
# run subprocess
self.log.debug("Executing: {}".format(subprocess_jpeg))
openpype.api.run_subprocess(
subprocess_jpeg, shell=True, logger=self.log
)
# remove thumbnail key from origin repre
thumbnail_repre.pop("thumbnail")
streams = get_ffprobe_streams(full_thumbnail_path)
width = height = None
for stream in streams:
@ -121,8 +109,7 @@ class ExtractThumbnailSP(pyblish.api.InstancePlugin):
height = stream["height"]
break
filename = os.path.basename(full_thumbnail_path)
staging_dir = staging_dir or os.path.dirname(full_thumbnail_path)
staging_dir, filename = os.path.split(full_thumbnail_path)
# create new thumbnail representation
representation = {
@ -130,15 +117,11 @@ class ExtractThumbnailSP(pyblish.api.InstancePlugin):
'ext': 'jpg',
'files': filename,
"stagingDir": staging_dir,
"tags": ["thumbnail"],
"tags": ["thumbnail", "delete"],
}
if width and height:
representation["width"] = width
representation["height"] = height
# # add Delete tag when temp file was rendered
if not is_jpeg:
representation["tags"].append("delete")
self.log.info(f"New representation {representation}")
instance.data["representations"].append(representation)

View file

@ -14,6 +14,7 @@ from .pipeline import (
class TrayPublishCreator(Creator):
create_allow_context_change = True
host_name = "traypublisher"
def collect_instances(self):
for instance_data in list_instances():

View file

@ -165,12 +165,12 @@ def parse_group_data(data):
if not group_raw:
continue
parts = group_raw.split(" ")
parts = group_raw.split("|")
# Check for length and concatenate 2 last items until length match
# - this happens if name contain spaces
while len(parts) > 6:
last_item = parts.pop(-1)
parts[-1] = " ".join([parts[-1], last_item])
parts[-1] = "|".join([parts[-1], last_item])
clip_id, group_id, red, green, blue, name = parts
group = {
@ -201,11 +201,16 @@ def get_groups_data(communicator=None):
george_script_lines = (
# Variable containing full path to output file
"output_path = \"{}\"".format(output_filepath),
"loop = 1",
"FOR idx = 1 TO 12",
"empty = 0",
# Loop over 100 groups
"FOR idx = 1 TO 100",
# Receive information about groups
"tv_layercolor \"getcolor\" 0 idx",
"tv_writetextfile \"strict\" \"append\" '\"'output_path'\"' result",
"END"
"PARSE result clip_id group_index c_red c_green c_blue group_name",
# Create and add line to output file
"line = clip_id'|'group_index'|'c_red'|'c_green'|'c_blue'|'group_name",
"tv_writetextfile \"strict\" \"append\" '\"'output_path'\"' line",
"END",
)
george_script = "\n".join(george_script_lines)
execute_george_through_file(george_script, communicator)

View file

@ -17,7 +17,7 @@ def otio_range_to_frame_range(otio_range):
start = _ot.to_frames(
otio_range.start_time, otio_range.start_time.rate)
end = start + _ot.to_frames(
otio_range.duration, otio_range.duration.rate) - 1
otio_range.duration, otio_range.duration.rate)
return start, end
@ -254,7 +254,7 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end):
media_in + source_in + offset_in)
media_out_trimmed = (
media_in + source_in + (
((source_range.duration.value - 1) * abs(
(source_range.duration.value * abs(
time_scalar)) + offset_out))
# calculate available handles

View file

@ -702,6 +702,32 @@ class ModulesManager:
).format(expected_keys, " | ".join(msg_items)))
return output
def collect_creator_plugin_paths(self, host_name):
"""Helper to collect creator plugin paths from modules.
Args:
host_name (str): For which host are creators meants.
Returns:
list: List of creator plugin paths.
"""
# Output structure
from openpype_interfaces import IPluginPaths
output = []
for module in self.get_enabled_modules():
# Skip module that do not inherit from `IPluginPaths`
if not isinstance(module, IPluginPaths):
continue
paths = module.get_creator_plugin_paths(host_name)
if paths:
# Convert to list if value is not list
if not isinstance(paths, (list, tuple, set)):
paths = [paths]
output.extend(paths)
return output
def collect_launch_hook_paths(self):
"""Helper to collect hooks from modules inherited ILaunchHookPaths.

View file

@ -466,7 +466,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
if instance_data.get("multipartExr"):
preview = True
new_instance = copy(instance_data)
new_instance = deepcopy(instance_data)
new_instance["subset"] = subset_name
new_instance["subsetGroup"] = group_name
if preview:
@ -883,8 +883,10 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
new_i = copy(i)
new_i["version"] = at.get("version")
new_i["subset"] = at.get("subset")
new_i["family"] = at.get("family")
new_i["append"] = True
new_i["families"].append(at.get("family"))
# don't set subsetGroup if we are attaching
new_i.pop("subsetGroup")
new_instances.append(new_i)
self.log.info(" - {} / v{}".format(
at.get("subset"), at.get("version")))

View file

@ -356,7 +356,7 @@ class PushHierValuesToNonHier(ServerAction):
values_per_entity_id[entity_id][key] = None
values = query_custom_attributes(
session, all_ids_with_parents, hier_attr_ids, True
session, hier_attr_ids, all_ids_with_parents, True
)
for item in values:
entity_id = item["entity_id"]

View file

@ -92,14 +92,18 @@ def check_credentials(username, api_key, ftrack_server=None):
if not ftrack_server or not username or not api_key:
return False
user_exists = False
try:
session = ftrack_api.Session(
server_url=ftrack_server,
api_key=api_key,
api_user=username
)
# Validated that the username actually exists
user = session.query("User where username is \"{}\"".format(username))
user_exists = user is not None
session.close()
except Exception:
return False
return True
pass
return user_exists

View file

@ -14,11 +14,38 @@ class IPluginPaths(OpenPypeInterface):
"publish": ["path/to/publish_plugins"]
}
"""
# TODO validation of an output
@abstractmethod
def get_plugin_paths(self):
pass
def get_creator_plugin_paths(self, host_name):
"""Retreive creator plugin paths.
Give addons ability to add creator plugin paths based on host name.
NOTES:
- Default implementation uses 'get_plugin_paths' and always return
all creator plugins.
- Host name may help to organize plugins by host, but each creator
alsomay have host filtering.
Args:
host_name (str): For which host are the plugins meant.
"""
paths = self.get_plugin_paths()
if not paths or "create" not in paths:
return []
create_paths = paths["create"]
if not create_paths:
return []
if not isinstance(create_paths, (list, tuple, set)):
create_paths = [create_paths]
return create_paths
class ILaunchHookPaths(OpenPypeInterface):
"""Module has launch hook paths to return.

View file

@ -921,12 +921,18 @@ class SyncServerModule(OpenPypeModule, ITrayModule):
if self.enabled:
for project in self.connection.projects(projection={"name": 1}):
project_name = project["name"]
project_settings = self.get_sync_project_setting(project_name)
if project_settings and project_settings.get("enabled"):
if self.is_project_enabled(project_name):
enabled_projects.append(project_name)
return enabled_projects
def is_project_enabled(self, project_name):
if self.enabled:
project_settings = self.get_sync_project_setting(project_name)
if project_settings and project_settings.get("enabled"):
return True
return False
def handle_alternate_site(self, collection, representation, processed_site,
file_id, synced_file_id):
"""

View file

@ -12,7 +12,7 @@ import pyblish.api
from pyblish.lib import MessageHandler
import openpype
from openpype.modules import load_modules
from openpype.modules import load_modules, ModulesManager
from openpype.settings import get_project_settings
from openpype.lib import (
Anatomy,
@ -107,7 +107,7 @@ def install_host(host):
install_openpype_plugins()
def install_openpype_plugins(project_name=None):
def install_openpype_plugins(project_name=None, host_name=None):
# Make sure modules are loaded
load_modules()
@ -116,6 +116,18 @@ def install_openpype_plugins(project_name=None):
pyblish.api.register_discovery_filter(filter_pyblish_plugins)
register_loader_plugin_path(LOAD_PATH)
modules_manager = ModulesManager()
publish_plugin_dirs = modules_manager.collect_plugin_paths()["publish"]
for path in publish_plugin_dirs:
pyblish.api.register_plugin_path(path)
if host_name is None:
host_name = os.environ.get("AVALON_APP")
creator_paths = modules_manager.collect_creator_plugin_paths(host_name)
for creator_path in creator_paths:
register_creator_plugin_path(creator_path)
if project_name is None:
project_name = os.environ.get("AVALON_PROJECT")

View file

@ -749,6 +749,10 @@ class CreateContext:
"""Is host valid for creation."""
return self._host_is_valid
@property
def host_name(self):
return os.environ["AVALON_APP"]
@property
def log(self):
"""Dynamic access to logger."""
@ -861,6 +865,17 @@ class CreateContext:
"Using first and skipping following"
))
continue
# Filter by host name
if (
creator_class.host_name
and creator_class.host_name != self.host_name
):
self.log.info((
"Creator's host name is not supported for current host {}"
).format(creator_class.host_name, self.host_name))
continue
creator = creator_class(
self,
system_settings,

View file

@ -63,6 +63,12 @@ class BaseCreator:
# `openpype.pipeline.attribute_definitions`
instance_attr_defs = []
# Filtering by host name - can be used to be filtered by host name
# - used on all hosts when set to 'None' for Backwards compatibility
# - was added afterwards
# QUESTION make this required?
host_name = None
def __init__(
self, create_context, system_settings, project_settings, headless=False
):

View file

@ -98,4 +98,4 @@
}
}
}
}
}

View file

@ -87,7 +87,8 @@
"camera",
"gizmo",
"source",
"render"
"render",
"write"
]
},
"ValidateInstanceInContext": {

View file

@ -41,6 +41,9 @@
},
{
"render": "render"
},
{
"write": "write"
}
]
}

View file

@ -1,6 +1,7 @@
import copy
import re
import math
import time
from uuid import uuid4
from Qt import QtCore, QtGui
@ -38,6 +39,14 @@ def is_filtering_recursible():
class BaseRepresentationModel(object):
"""Methods for SyncServer useful in multiple models"""
# Cheap & hackish way how to avoid refreshing of whole sync server module
# on each selection change
_last_project = None
_modules_manager = None
_last_project_cache = 0
_last_manager_cache = 0
_max_project_cache_time = 30
_max_manager_cache_time = 60
def reset_sync_server(self, project_name=None):
"""Sets/Resets sync server vars after every change (refresh.)"""
@ -47,28 +56,53 @@ class BaseRepresentationModel(object):
remote_site = remote_provider = None
if not project_name:
project_name = self.dbcon.Session["AVALON_PROJECT"]
project_name = self.dbcon.Session.get("AVALON_PROJECT")
else:
self.dbcon.Session["AVALON_PROJECT"] = project_name
if project_name:
manager = ModulesManager()
sync_server = manager.modules_by_name["sync_server"]
if not project_name:
self.repre_icons = repre_icons
self.sync_server = sync_server
self.active_site = active_site
self.active_provider = active_provider
self.remote_site = remote_site
self.remote_provider = remote_provider
return
if project_name in sync_server.get_enabled_projects():
active_site = sync_server.get_active_site(project_name)
active_provider = sync_server.get_provider_for_site(
project_name, active_site)
if active_site == 'studio': # for studio use explicit icon
active_provider = 'studio'
now_time = time.time()
project_cache_diff = now_time - self._last_project_cache
if project_cache_diff > self._max_project_cache_time:
self._last_project = None
remote_site = sync_server.get_remote_site(project_name)
remote_provider = sync_server.get_provider_for_site(
project_name, remote_site)
if remote_site == 'studio': # for studio use explicit icon
remote_provider = 'studio'
if project_name == self._last_project:
return
repre_icons = lib.get_repre_icons()
self._last_project = project_name
self._last_project_cache = now_time
manager_cache_diff = now_time - self._last_manager_cache
if manager_cache_diff > self._max_manager_cache_time:
self._modules_manager = None
if self._modules_manager is None:
self._modules_manager = ModulesManager()
self._last_manager_cache = now_time
sync_server = self._modules_manager.modules_by_name["sync_server"]
if sync_server.is_project_enabled(project_name):
active_site = sync_server.get_active_site(project_name)
active_provider = sync_server.get_provider_for_site(
project_name, active_site)
if active_site == 'studio': # for studio use explicit icon
active_provider = 'studio'
remote_site = sync_server.get_remote_site(project_name)
remote_provider = sync_server.get_provider_for_site(
project_name, remote_site)
if remote_site == 'studio': # for studio use explicit icon
remote_provider = 'studio'
repre_icons = lib.get_repre_icons()
self.repre_icons = repre_icons
self.sync_server = sync_server

View file

@ -1472,12 +1472,7 @@ class HierarchyModel(QtCore.QAbstractItemModel):
mimedata.setData("application/copy_task", encoded_data)
return mimedata
def paste_mime_data(self, index, mime_data):
if not index.isValid():
return
item_id = index.data(IDENTIFIER_ROLE)
item = self._items_by_id[item_id]
def _paste_mime_data(self, item, mime_data):
if not isinstance(item, (AssetItem, TaskItem)):
return
@ -1511,6 +1506,25 @@ class HierarchyModel(QtCore.QAbstractItemModel):
task_item = TaskItem(task_data, True)
self.add_item(task_item, parent)
def paste(self, indexes, mime_data):
# Get the selected Assets uniquely
items = set()
for index in indexes:
if not index.isValid():
return
item_id = index.data(IDENTIFIER_ROLE)
item = self._items_by_id[item_id]
# Do not copy into the Task Item so get parent Asset instead
if isinstance(item, TaskItem):
item = item.parent()
items.add(item)
for item in items:
self._paste_mime_data(item, mime_data)
class BaseItem:
"""Base item for HierarchyModel.

View file

@ -195,13 +195,13 @@ class HierarchyView(QtWidgets.QTreeView):
for idx, width in widths_by_idx.items():
self.setColumnWidth(idx, width)
def set_project(self, project_name):
def set_project(self, project_name, force=False):
# Trigger helpers first
self._project_doc_cache.set_project(project_name)
self._tools_cache.refresh()
# Trigger update of model after all data for delegates are filled
self._source_model.set_project(project_name)
self._source_model.set_project(project_name, force)
def _on_project_reset(self):
self.header_init()
@ -365,20 +365,24 @@ class HierarchyView(QtWidgets.QTreeView):
event.accept()
def _copy_items(self, indexes=None):
clipboard = QtWidgets.QApplication.clipboard()
try:
if indexes is None:
indexes = self.selectedIndexes()
mime_data = self._source_model.copy_mime_data(indexes)
QtWidgets.QApplication.clipboard().setMimeData(mime_data)
clipboard.setMimeData(mime_data)
self._show_message("Tasks copied")
except ValueError as exc:
# Change clipboard to contain empty data
empty_mime_data = QtCore.QMimeData()
clipboard.setMimeData(empty_mime_data)
self._show_message(str(exc))
def _paste_items(self):
index = self.currentIndex()
mime_data = QtWidgets.QApplication.clipboard().mimeData()
self._source_model.paste_mime_data(index, mime_data)
rows = self.selectionModel().selectedRows()
self._source_model.paste(rows, mime_data)
def _delete_items(self, indexes=None):
if indexes is None:

View file

@ -184,14 +184,14 @@ class ProjectManagerWindow(QtWidgets.QWidget):
self.resize(1200, 600)
self.setStyleSheet(load_stylesheet())
def _set_project(self, project_name=None):
def _set_project(self, project_name=None, force=False):
self._create_folders_btn.setEnabled(project_name is not None)
self._remove_projects_btn.setEnabled(project_name is not None)
self._add_asset_btn.setEnabled(project_name is not None)
self._add_task_btn.setEnabled(project_name is not None)
self._save_btn.setEnabled(project_name is not None)
self._project_proxy_model.set_filter_default(project_name is not None)
self.hierarchy_view.set_project(project_name)
self.hierarchy_view.set_project(project_name, force)
def _current_project(self):
row = self._project_combobox.currentIndex()
@ -229,11 +229,11 @@ class ProjectManagerWindow(QtWidgets.QWidget):
self._project_combobox.setCurrentIndex(row)
selected_project = self._current_project()
self._set_project(selected_project)
self._set_project(selected_project, True)
def _on_project_change(self):
selected_project = self._current_project()
self._set_project(selected_project)
self._set_project(selected_project, False)
def _on_project_refresh(self):
self.refresh_projects()

View file

@ -202,6 +202,7 @@ def cli_publish(data, publish_paths, gui=True):
if os.path.exists(json_data_path):
with open(json_data_path, "r") as f:
result = json.load(f)
os.remove(json_data_path)
log.info(f"Publish result: {result}")

View file

@ -494,8 +494,6 @@ class AssetModel(QtGui.QStandardItemModel):
# Remove cache of removed items
for asset_id in removed_asset_ids:
self._items_by_asset_id.pop(asset_id)
if asset_id in self._items_with_color_by_id:
self._items_with_color_by_id.pop(asset_id)
# Refresh data
# - all items refresh all data except id

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
__version__ = "3.10.0-nightly.2"
__version__ = "3.10.0-nightly.5"

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "OpenPype"
version = "3.10.0-nightly.2" # OpenPype
version = "3.10.0-nightly.5" # OpenPype
description = "Open VFX and Animation pipeline with support."
authors = ["OpenPype Team <info@openpype.io>"]
license = "MIT License"

View file

@ -386,18 +386,6 @@ def set_modules_environments():
modules_manager = ModulesManager()
module_envs = modules_manager.collect_global_environments()
publish_plugin_dirs = modules_manager.collect_plugin_paths()["publish"]
# Set pyblish plugins paths if any module want to register them
if publish_plugin_dirs:
publish_paths_str = os.environ.get("PYBLISHPLUGINPATH") or ""
publish_paths = publish_paths_str.split(os.pathsep)
_publish_paths = {
os.path.normpath(path) for path in publish_paths if path
}
for path in publish_plugin_dirs:
_publish_paths.add(os.path.normpath(path))
module_envs["PYBLISHPLUGINPATH"] = os.pathsep.join(_publish_paths)
# Merge environments with current environments and update values
if module_envs: