Merge branch 'develop' into feature/OP-5871_Max-Review-feature

This commit is contained in:
Kayla Man 2023-05-29 13:07:25 +08:00
commit a9422a1f3b
24 changed files with 198 additions and 86 deletions

View file

@ -35,6 +35,7 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
- 3.15.9-nightly.1
- 3.15.8
- 3.15.8-nightly.3
- 3.15.8-nightly.2
@ -134,7 +135,6 @@ body:
- 3.14.2-nightly.5
- 3.14.2-nightly.4
- 3.14.2-nightly.3
- 3.14.2-nightly.2
validations:
required: true
- type: dropdown

View file

@ -151,6 +151,7 @@ class NukeHost(
def add_nuke_callbacks():
""" Adding all available nuke callbacks
"""
nuke_settings = get_current_project_settings()["nuke"]
workfile_settings = WorkfileSettings()
# Set context settings.
nuke.addOnCreate(
@ -169,7 +170,10 @@ def add_nuke_callbacks():
# # set apply all workfile settings on script load and save
nuke.addOnScriptLoad(WorkfileSettings().set_context_settings)
nuke.addFilenameFilter(dirmap_file_name_filter)
if nuke_settings["nuke-dirmap"]["enabled"]:
log.info("Added Nuke's dirmaping callback ...")
# Add dirmap for file paths.
nuke.addFilenameFilter(dirmap_file_name_filter)
log.info("Added Nuke callbacks ...")

View file

@ -24,6 +24,8 @@ from .lib import (
get_project_manager,
get_current_project,
get_current_timeline,
get_any_timeline,
get_new_timeline,
create_bin,
get_media_pool_item,
create_media_pool_item,
@ -95,6 +97,8 @@ __all__ = [
"get_project_manager",
"get_current_project",
"get_current_timeline",
"get_any_timeline",
"get_new_timeline",
"create_bin",
"get_media_pool_item",
"create_media_pool_item",

View file

@ -15,6 +15,7 @@ log = Logger.get_logger(__name__)
self = sys.modules[__name__]
self.project_manager = None
self.media_storage = None
self.current_project = None
# OpenPype sequential rename variables
self.rename_index = 0
@ -85,22 +86,60 @@ def get_media_storage():
def get_current_project():
# initialize project manager
get_project_manager()
"""Get current project object.
"""
if not self.current_project:
self.current_project = get_project_manager().GetCurrentProject()
return self.project_manager.GetCurrentProject()
return self.current_project
def get_current_timeline(new=False):
# get current project
"""Get current timeline object.
Args:
new (bool)[optional]: [DEPRECATED] if True it will create
new timeline if none exists
Returns:
TODO: will need to reflect future `None`
object: resolve.Timeline
"""
project = get_current_project()
timeline = project.GetCurrentTimeline()
# return current timeline if any
if timeline:
return timeline
# TODO: [deprecated] and will be removed in future
if new:
media_pool = project.GetMediaPool()
new_timeline = media_pool.CreateEmptyTimeline(self.pype_timeline_name)
project.SetCurrentTimeline(new_timeline)
return get_new_timeline()
return project.GetCurrentTimeline()
def get_any_timeline():
"""Get any timeline object.
Returns:
object | None: resolve.Timeline
"""
project = get_current_project()
timeline_count = project.GetTimelineCount()
if timeline_count > 0:
return project.GetTimelineByIndex(1)
def get_new_timeline():
"""Get new timeline object.
Returns:
object: resolve.Timeline
"""
project = get_current_project()
media_pool = project.GetMediaPool()
new_timeline = media_pool.CreateEmptyTimeline(self.pype_timeline_name)
project.SetCurrentTimeline(new_timeline)
return new_timeline
def create_bin(name: str, root: object = None) -> object:
@ -312,7 +351,13 @@ def get_current_timeline_items(
track_type = track_type or "video"
selecting_color = selecting_color or "Chocolate"
project = get_current_project()
timeline = get_current_timeline()
# get timeline anyhow
timeline = (
get_current_timeline() or
get_any_timeline() or
get_new_timeline()
)
selected_clips = []
# get all tracks count filtered by track type

View file

@ -327,7 +327,10 @@ class ClipLoader:
self.active_timeline = options["timeline"]
else:
# create new sequence
self.active_timeline = lib.get_current_timeline(new=True)
self.active_timeline = (
lib.get_current_timeline() or
lib.get_new_timeline()
)
else:
self.active_timeline = lib.get_current_timeline()

View file

@ -19,6 +19,7 @@ from openpype.lib.transcoding import (
IMAGE_EXTENSIONS
)
class LoadClip(plugin.TimelineItemLoader):
"""Load a subset to timeline as clip

View file

@ -0,0 +1,13 @@
#! python3
from openpype.pipeline import install_host
from openpype.hosts.resolve import api as bmdvr
from openpype.hosts.resolve.api.lib import get_current_project
if __name__ == "__main__":
install_host(bmdvr)
project = get_current_project()
timeline_count = project.GetTimelineCount()
print(f"Timeline count: {timeline_count}")
timeline = project.GetTimelineByIndex(timeline_count)
print(f"Timeline name: {timeline.GetName()}")
print(timeline.GetTrackCount("video"))

View file

@ -1,6 +1,6 @@
import os
import shutil
from openpype.lib import Logger
from openpype.lib import Logger, is_running_from_build
RESOLVE_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
@ -41,6 +41,13 @@ def setup(env):
# copy scripts into Resolve's utility scripts dir
for directory, scripts in scripts.items():
for script in scripts:
if (
is_running_from_build() and
script in ["tests", "develop"]
):
# only copy those if started from build
continue
src = os.path.join(directory, script)
dst = os.path.join(util_scripts_dir, script)
log.info("Copying `{}` to `{}`...".format(src, dst))

View file

@ -17,6 +17,8 @@ class CreateUAsset(UnrealAssetCreator):
family = "uasset"
icon = "cube"
extension = ".uasset"
def create(self, subset_name, instance_data, pre_create_data):
if pre_create_data.get("use_selection"):
ar = unreal.AssetRegistryHelpers.get_asset_registry()
@ -37,10 +39,28 @@ class CreateUAsset(UnrealAssetCreator):
f"{Path(obj).name} is not on the disk. Likely it needs to"
"be saved first.")
if Path(sys_path).suffix != ".uasset":
raise CreatorError(f"{Path(sys_path).name} is not a UAsset.")
if Path(sys_path).suffix != self.extension:
raise CreatorError(
f"{Path(sys_path).name} is not a {self.label}.")
super(CreateUAsset, self).create(
subset_name,
instance_data,
pre_create_data)
class CreateUMap(CreateUAsset):
"""Create Level."""
identifier = "io.ayon.creators.unreal.umap"
label = "Level"
family = "uasset"
extension = ".umap"
def create(self, subset_name, instance_data, pre_create_data):
instance_data["families"] = ["umap"]
super(CreateUMap, self).create(
subset_name,
instance_data,
pre_create_data)

View file

@ -21,6 +21,8 @@ class UAssetLoader(plugin.Loader):
icon = "cube"
color = "orange"
extension = "uasset"
def load(self, context, name, namespace, options):
"""Load and containerise representation into Content Browser.
@ -42,26 +44,29 @@ class UAssetLoader(plugin.Loader):
root = "/Game/Ayon/Assets"
asset = context.get('asset').get('name')
suffix = "_CON"
if asset:
asset_name = "{}_{}".format(asset, name)
else:
asset_name = "{}".format(name)
asset_name = f"{asset}_{name}" if asset else f"{name}"
tools = unreal.AssetToolsHelpers().get_asset_tools()
asset_dir, container_name = tools.create_unique_asset_name(
f"{root}/{asset}/{name}", suffix=""
)
container_name += suffix
unique_number = 1
while unreal.EditorAssetLibrary.does_directory_exist(
f"{asset_dir}_{unique_number:02}"
):
unique_number += 1
asset_dir = f"{asset_dir}_{unique_number:02}"
container_name = f"{container_name}_{unique_number:02}{suffix}"
unreal.EditorAssetLibrary.make_directory(asset_dir)
destination_path = asset_dir.replace(
"/Game",
Path(unreal.Paths.project_content_dir()).as_posix(),
1)
"/Game", Path(unreal.Paths.project_content_dir()).as_posix(), 1)
shutil.copy(self.fname, f"{destination_path}/{name}.uasset")
shutil.copy(
self.fname,
f"{destination_path}/{name}_{unique_number:02}.{self.extension}")
# Create Asset Container
unreal_pipeline.create_container(
@ -77,7 +82,7 @@ class UAssetLoader(plugin.Loader):
"loader": str(self.__class__.__name__),
"representation": context["representation"]["_id"],
"parent": context["representation"]["parent"],
"family": context["representation"]["context"]["family"]
"family": context["representation"]["context"]["family"],
}
unreal_pipeline.imprint(f"{asset_dir}/{container_name}", data)
@ -96,10 +101,10 @@ class UAssetLoader(plugin.Loader):
asset_dir = container["namespace"]
name = representation["context"]["subset"]
unique_number = container["container_name"].split("_")[-2]
destination_path = asset_dir.replace(
"/Game",
Path(unreal.Paths.project_content_dir()).as_posix(),
1)
"/Game", Path(unreal.Paths.project_content_dir()).as_posix(), 1)
asset_content = unreal.EditorAssetLibrary.list_assets(
asset_dir, recursive=False, include_folder=True
@ -107,22 +112,24 @@ class UAssetLoader(plugin.Loader):
for asset in asset_content:
obj = ar.get_asset_by_object_path(asset).get_asset()
if not obj.get_class().get_name() == 'AyonAssetContainer':
if obj.get_class().get_name() != "AyonAssetContainer":
unreal.EditorAssetLibrary.delete_asset(asset)
update_filepath = get_representation_path(representation)
shutil.copy(update_filepath, f"{destination_path}/{name}.uasset")
shutil.copy(
update_filepath,
f"{destination_path}/{name}_{unique_number}.{self.extension}")
container_path = "{}/{}".format(container["namespace"],
container["objectName"])
container_path = f'{container["namespace"]}/{container["objectName"]}'
# update metadata
unreal_pipeline.imprint(
container_path,
{
"representation": str(representation["_id"]),
"parent": str(representation["parent"])
})
"parent": str(representation["parent"]),
}
)
asset_content = unreal.EditorAssetLibrary.list_assets(
asset_dir, recursive=True, include_folder=True
@ -143,3 +150,13 @@ class UAssetLoader(plugin.Loader):
if len(asset_content) == 0:
unreal.EditorAssetLibrary.delete_directory(parent_path)
class UMapLoader(UAssetLoader):
"""Load Level."""
families = ["uasset"]
label = "Load Level"
representations = ["umap"]
extension = "umap"

View file

@ -24,7 +24,7 @@ class CollectInstanceMembers(pyblish.api.InstancePlugin):
ar = unreal.AssetRegistryHelpers.get_asset_registry()
inst_path = instance.data.get('instance_path')
inst_name = instance.data.get('objectName')
inst_name = inst_path.split('/')[-1]
pub_instance = ar.get_asset_by_object_path(
f"{inst_path}.{inst_name}").get_asset()

View file

@ -11,16 +11,17 @@ class ExtractUAsset(publish.Extractor):
label = "Extract UAsset"
hosts = ["unreal"]
families = ["uasset"]
families = ["uasset", "umap"]
optional = True
def process(self, instance):
extension = (
"umap" if "umap" in instance.data.get("families") else "uasset")
ar = unreal.AssetRegistryHelpers.get_asset_registry()
self.log.info("Performing extraction..")
staging_dir = self.staging_dir(instance)
filename = "{}.uasset".format(instance.name)
filename = f"{instance.name}.{extension}"
members = instance.data.get("members", [])
@ -36,13 +37,15 @@ class ExtractUAsset(publish.Extractor):
shutil.copy(sys_path, staging_dir)
self.log.info(f"instance.data: {instance.data}")
if "representations" not in instance.data:
instance.data["representations"] = []
representation = {
'name': 'uasset',
'ext': 'uasset',
'files': filename,
"name": extension,
"ext": extension,
"files": filename,
"stagingDir": staging_dir,
}
instance.data["representations"].append(representation)

View file

@ -1,5 +1,3 @@
import os
import requests
from qtpy import QtCore, QtGui, QtWidgets

View file

@ -1,7 +1,9 @@
import os
import json
import appdirs
import requests
from openpype.modules import OpenPypeModule, ITrayModule
@ -110,16 +112,10 @@ class MusterModule(OpenPypeModule, ITrayModule):
self.save_credentials(token)
def save_credentials(self, token):
"""
Save credentials to JSON file
"""
data = {
'token': token
}
"""Save credentials to JSON file."""
file = open(self.cred_path, 'w')
file.write(json.dumps(data))
file.close()
with open(self.cred_path, "w") as f:
json.dump({'token': token}, f)
def show_login(self):
"""

View file

@ -12,7 +12,8 @@ import pyblish.api
from openpype.lib import (
Logger,
import_filepath,
filter_profiles
filter_profiles,
is_func_signature_supported,
)
from openpype.settings import (
get_project_settings,
@ -30,8 +31,6 @@ from .contants import (
TRANSIENT_DIR_TEMPLATE
)
_ARG_PLACEHOLDER = object()
def get_template_name_profiles(
project_name, project_settings=None, logger=None
@ -498,12 +497,26 @@ def filter_pyblish_plugins(plugins):
# iterate over plugins
for plugin in plugins[:]:
# Apply settings to plugins
if hasattr(plugin, "apply_settings"):
apply_settings_func = getattr(plugin, "apply_settings", None)
if apply_settings_func is not None:
# Use classmethod 'apply_settings'
# - can be used to target settings from custom settings place
# - skip default behavior when successful
try:
plugin.apply_settings(project_settings, system_settings)
# Support to pass only project settings
# - make sure that both settings are passed, when can be
# - that covers cases when *args are in method parameters
both_supported = is_func_signature_supported(
apply_settings_func, project_settings, system_settings
)
project_supported = is_func_signature_supported(
apply_settings_func, project_settings
)
if not both_supported and project_supported:
plugin.apply_settings(project_settings)
else:
plugin.apply_settings(project_settings, system_settings)
except Exception:
log.warning(
@ -870,31 +883,24 @@ def add_repre_files_for_cleanup(instance, repre):
instance.context.data["cleanupFullPaths"].append(expected_file)
def get_publish_instance_label(instance, default=_ARG_PLACEHOLDER):
def get_publish_instance_label(instance):
"""Try to get label from pyblish instance.
First are checked 'label' and 'name' keys in instance data. If are not set
a default value is returned. Instance object is converted to string
if default value is not specific.
First are used values in instance data under 'label' and 'name' keys. Then
is used string conversion of instance object -> 'instance._name'.
Todos:
Maybe 'subset' key could be used too.
Args:
instance (pyblish.api.Instance): Pyblish instance.
default (Optional[Any]): Default value to return if any
Returns:
Union[Any]: Instance label or default label.
str: Instance label.
"""
label = (
return (
instance.data.get("label")
or instance.data.get("name")
or str(instance)
)
if label:
return label
if default is _ARG_PLACEHOLDER:
return str(instance)
return default

View file

@ -872,7 +872,6 @@ class WrappedCallbackItem:
self.log.warning("- item is already processed")
return
self.log.debug("Running callback: {}".format(str(self._callback)))
try:
result = self._callback(*self._args, **self._kwargs)
self._result = result

View file

@ -127,8 +127,7 @@ class OverlayMessageWidget(QtWidgets.QFrame):
if timeout:
self._timeout_timer.setInterval(timeout)
if message_type:
set_style_property(self, "type", message_type)
set_style_property(self, "type", message_type)
self._timeout_timer.start()

View file

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

View file

@ -15,16 +15,16 @@ Structure:
- openpype/modules/MODULE_NAME - structure follow directory structure in code base
- fixture - sample data `(MongoDB dumps, test files etc.)`
- `tests.py` - single or more pytest files for MODULE_NAME
- unit - quick unit test
- MODULE_NAME
- unit - quick unit test
- MODULE_NAME
- fixture
- `tests.py`
How to run:
----------
- use Openpype command 'runtests' from command line (`.venv` in ${OPENPYPE_ROOT} must be activated to use configured Python!)
-- `python ${OPENPYPE_ROOT}/start.py runtests`
By default, this command will run all tests in ${OPENPYPE_ROOT}/tests.
Specific location could be provided to this command as an argument, either as absolute path, or relative path to ${OPENPYPE_ROOT}.
@ -41,17 +41,15 @@ In some cases your tests might be so localized, that you don't care about all en
In that case you might add this dummy configuration BEFORE any imports in your test file
```
import os
os.environ["AVALON_MONGO"] = "mongodb://localhost:27017"
os.environ["OPENPYPE_DEBUG"] = "1"
os.environ["OPENPYPE_MONGO"] = "mongodb://localhost:27017"
os.environ["AVALON_DB"] = "avalon"
os.environ["OPENPYPE_DATABASE_NAME"] = "openpype"
os.environ["AVALON_TIMEOUT"] = '3000'
os.environ["OPENPYPE_DEBUG"] = "3"
os.environ["AVALON_CONFIG"] = "pype"
os.environ["AVALON_DB"] = "avalon"
os.environ["AVALON_TIMEOUT"] = "3000"
os.environ["AVALON_ASSET"] = "Asset"
os.environ["AVALON_PROJECT"] = "test_project"
```
(AVALON_ASSET and AVALON_PROJECT values should exist in your environment)
This might be enough to run your test file separately. Do not commit this skeleton though.
Use only when you know what you are doing!
Use only when you know what you are doing!