mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into enhancement/AY-4909_Move-AfterEffects-client-code
This commit is contained in:
commit
175833de3c
607 changed files with 1703 additions and 1177 deletions
|
|
@ -8,7 +8,6 @@ import inspect
|
|||
import logging
|
||||
import threading
|
||||
import collections
|
||||
|
||||
from uuid import uuid4
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
|
@ -52,9 +51,13 @@ IGNORED_MODULES_IN_AYON = set()
|
|||
MOVED_ADDON_MILESTONE_VERSIONS = {
|
||||
"aftereffects": VersionInfo(0, 2, 0),
|
||||
"applications": VersionInfo(0, 2, 0),
|
||||
"blender": VersionInfo(0, 2, 0),
|
||||
"celaction": VersionInfo(0, 2, 0),
|
||||
"clockify": VersionInfo(0, 2, 0),
|
||||
"flame": VersionInfo(0, 2, 0),
|
||||
"fusion": VersionInfo(0, 2, 0),
|
||||
"harmony": VersionInfo(0, 2, 0),
|
||||
"hiero": VersionInfo(0, 2, 0),
|
||||
"max": VersionInfo(0, 2, 0),
|
||||
"photoshop": VersionInfo(0, 2, 0),
|
||||
"traypublisher": VersionInfo(0, 2, 0),
|
||||
|
|
@ -63,6 +66,7 @@ MOVED_ADDON_MILESTONE_VERSIONS = {
|
|||
"nuke": VersionInfo(0, 2, 0),
|
||||
"resolve": VersionInfo(0, 2, 0),
|
||||
"substancepainter": VersionInfo(0, 2, 0),
|
||||
"houdini": VersionInfo(0, 3, 0),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -551,6 +555,9 @@ class AYONAddon(object):
|
|||
enabled = True
|
||||
_id = None
|
||||
|
||||
# Temporary variable for 'version' property
|
||||
_missing_version_warned = False
|
||||
|
||||
def __init__(self, manager, settings):
|
||||
self.manager = manager
|
||||
|
||||
|
|
@ -581,6 +588,26 @@ class AYONAddon(object):
|
|||
|
||||
pass
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
"""Addon version.
|
||||
|
||||
Todo:
|
||||
Should be abstract property (required). Introduced in
|
||||
ayon-core 0.3.3 .
|
||||
|
||||
Returns:
|
||||
str: Addon version as semver compatible string.
|
||||
|
||||
"""
|
||||
if not self.__class__._missing_version_warned:
|
||||
self.__class__._missing_version_warned = True
|
||||
print(
|
||||
f"DEV WARNING: Addon '{self.name}' does not have"
|
||||
f" defined version."
|
||||
)
|
||||
return "0.0.0"
|
||||
|
||||
def initialize(self, settings):
|
||||
"""Initialization of addon attributes.
|
||||
|
||||
|
|
@ -696,6 +723,30 @@ class OpenPypeAddOn(OpenPypeModule):
|
|||
enabled = True
|
||||
|
||||
|
||||
class _AddonReportInfo:
|
||||
def __init__(
|
||||
self, class_name, name, version, report_value_by_label
|
||||
):
|
||||
self.class_name = class_name
|
||||
self.name = name
|
||||
self.version = version
|
||||
self.report_value_by_label = report_value_by_label
|
||||
|
||||
@classmethod
|
||||
def from_addon(cls, addon, report):
|
||||
class_name = addon.__class__.__name__
|
||||
report_value_by_label = {
|
||||
label: reported.get(class_name)
|
||||
for label, reported in report.items()
|
||||
}
|
||||
return cls(
|
||||
addon.__class__.__name__,
|
||||
addon.name,
|
||||
addon.version,
|
||||
report_value_by_label
|
||||
)
|
||||
|
||||
|
||||
class AddonsManager:
|
||||
"""Manager of addons that helps to load and prepare them to work.
|
||||
|
||||
|
|
@ -872,10 +923,6 @@ class AddonsManager:
|
|||
name_alias = getattr(addon, "openpype_alias", None)
|
||||
if name_alias:
|
||||
aliased_names.append((name_alias, addon))
|
||||
enabled_str = "X"
|
||||
if not addon.enabled:
|
||||
enabled_str = " "
|
||||
self.log.debug("[{}] {}".format(enabled_str, name))
|
||||
|
||||
now = time.time()
|
||||
report[addon.__class__.__name__] = now - prev_start_time
|
||||
|
|
@ -887,6 +934,13 @@ class AddonsManager:
|
|||
exc_info=True
|
||||
)
|
||||
|
||||
for addon_name in sorted(self._addons_by_name.keys()):
|
||||
addon = self._addons_by_name[addon_name]
|
||||
enabled_str = "X" if addon.enabled else " "
|
||||
self.log.debug(
|
||||
f"[{enabled_str}] {addon.name} ({addon.version})"
|
||||
)
|
||||
|
||||
for item in aliased_names:
|
||||
name_alias, addon = item
|
||||
if name_alias not in self._addons_by_name:
|
||||
|
|
@ -1175,39 +1229,55 @@ class AddonsManager:
|
|||
available_col_names |= set(addon_names.keys())
|
||||
|
||||
# Prepare ordered dictionary for columns
|
||||
cols = collections.OrderedDict()
|
||||
# Add addon names to first columnt
|
||||
cols["Addon name"] = list(sorted(
|
||||
addon.__class__.__name__
|
||||
addons_info = [
|
||||
_AddonReportInfo.from_addon(addon, self._report)
|
||||
for addon in self.addons
|
||||
if addon.__class__.__name__ in available_col_names
|
||||
))
|
||||
]
|
||||
addons_info.sort(key=lambda x: x.name)
|
||||
|
||||
addon_name_rows = [
|
||||
addon_info.name
|
||||
for addon_info in addons_info
|
||||
]
|
||||
addon_version_rows = [
|
||||
addon_info.version
|
||||
for addon_info in addons_info
|
||||
]
|
||||
|
||||
# Add total key (as last addon)
|
||||
cols["Addon name"].append(self._report_total_key)
|
||||
addon_name_rows.append(self._report_total_key)
|
||||
addon_version_rows.append(f"({len(addons_info)})")
|
||||
|
||||
cols = collections.OrderedDict()
|
||||
# Add addon names to first columnt
|
||||
cols["Addon name"] = addon_name_rows
|
||||
cols["Version"] = addon_version_rows
|
||||
|
||||
# Add columns from report
|
||||
total_by_addon = {
|
||||
row: 0
|
||||
for row in addon_name_rows
|
||||
}
|
||||
for label in self._report.keys():
|
||||
cols[label] = []
|
||||
|
||||
total_addon_times = {}
|
||||
for addon_name in cols["Addon name"]:
|
||||
total_addon_times[addon_name] = 0
|
||||
|
||||
for label, reported in self._report.items():
|
||||
for addon_name in cols["Addon name"]:
|
||||
col_time = reported.get(addon_name)
|
||||
if col_time is None:
|
||||
cols[label].append("N/A")
|
||||
rows = []
|
||||
col_total = 0
|
||||
for addon_info in addons_info:
|
||||
value = addon_info.report_value_by_label.get(label)
|
||||
if value is None:
|
||||
rows.append("N/A")
|
||||
continue
|
||||
cols[label].append("{:.3f}".format(col_time))
|
||||
total_addon_times[addon_name] += col_time
|
||||
|
||||
rows.append("{:.3f}".format(value))
|
||||
total_by_addon[addon_info.name] += value
|
||||
col_total += value
|
||||
total_by_addon[self._report_total_key] += col_total
|
||||
rows.append("{:.3f}".format(col_total))
|
||||
cols[label] = rows
|
||||
# Add to also total column that should sum the row
|
||||
cols[self._report_total_key] = []
|
||||
for addon_name in cols["Addon name"]:
|
||||
cols[self._report_total_key].append(
|
||||
"{:.3f}".format(total_addon_times[addon_name])
|
||||
)
|
||||
cols[self._report_total_key] = [
|
||||
"{:.3f}".format(total_by_addon[addon_name])
|
||||
for addon_name in cols["Addon name"]
|
||||
]
|
||||
|
||||
# Prepare column widths and total row count
|
||||
# - column width is by
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
from .addon import BlenderAddon
|
||||
|
||||
|
||||
__all__ = (
|
||||
"BlenderAddon",
|
||||
)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
from .addon import (
|
||||
HIERO_ROOT_DIR,
|
||||
HieroAddon,
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"HIERO_ROOT_DIR",
|
||||
"HieroAddon",
|
||||
)
|
||||
|
|
@ -7,6 +7,8 @@ import six
|
|||
from ayon_core.lib import Logger
|
||||
from ayon_core.modules import AYONAddon, IPluginPaths
|
||||
|
||||
from .version import __version__
|
||||
|
||||
|
||||
class DeadlineWebserviceError(Exception):
|
||||
"""
|
||||
|
|
@ -16,6 +18,7 @@ class DeadlineWebserviceError(Exception):
|
|||
|
||||
class DeadlineModule(AYONAddon, IPluginPaths):
|
||||
name = "deadline"
|
||||
version = __version__
|
||||
|
||||
def initialize(self, studio_settings):
|
||||
# This module is always enabled
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class FusionSubmitDeadline(
|
|||
else:
|
||||
context.data[key] = True
|
||||
|
||||
from ayon_core.hosts.fusion.api.lib import get_frame_path
|
||||
from ayon_fusion.api.lib import get_frame_path
|
||||
|
||||
deadline_url = instance.data["deadline"]["url"]
|
||||
assert deadline_url, "Requires Deadline Webservice URL"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
from .version import __version__
|
||||
from .addon import JobQueueAddon
|
||||
|
||||
|
||||
__all__ = (
|
||||
"__version__",
|
||||
|
||||
"JobQueueAddon",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -44,9 +44,12 @@ import platform
|
|||
from ayon_core.addon import AYONAddon, click_wrap
|
||||
from ayon_core.settings import get_studio_settings
|
||||
|
||||
from .version import __version__
|
||||
|
||||
|
||||
class JobQueueAddon(AYONAddon):
|
||||
name = "job_queue"
|
||||
version = __version__
|
||||
|
||||
def initialize(self, studio_settings):
|
||||
addon_settings = studio_settings.get(self.name) or {}
|
||||
|
|
|
|||
1
client/ayon_core/modules/job_queue/version.py
Normal file
1
client/ayon_core/modules/job_queue/version.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
__version__ = "1.0.0"
|
||||
|
|
@ -7,6 +7,7 @@ from ayon_core.addon import AYONAddon, ITrayAction
|
|||
class LauncherAction(AYONAddon, ITrayAction):
|
||||
label = "Launcher"
|
||||
name = "launcher_tool"
|
||||
version = "1.0.0"
|
||||
|
||||
def initialize(self, settings):
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from ayon_core.addon import AYONAddon, ITrayAddon
|
|||
|
||||
class LoaderAddon(AYONAddon, ITrayAddon):
|
||||
name = "loader_tool"
|
||||
version = "1.0.0"
|
||||
|
||||
def initialize(self, settings):
|
||||
# Tray attributes
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from ayon_core.addon import AYONAddon, ITrayAction
|
|||
class PythonInterpreterAction(AYONAddon, ITrayAction):
|
||||
label = "Console"
|
||||
name = "python_interpreter"
|
||||
version = "1.0.0"
|
||||
admin_action = True
|
||||
|
||||
def initialize(self, settings):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
from .version import __version__
|
||||
from .addon import RoyalRenderAddon
|
||||
|
||||
|
||||
__all__ = (
|
||||
"__version__",
|
||||
|
||||
"RoyalRenderAddon",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,10 +4,13 @@ import os
|
|||
|
||||
from ayon_core.addon import AYONAddon, IPluginPaths
|
||||
|
||||
from .version import __version__
|
||||
|
||||
|
||||
class RoyalRenderAddon(AYONAddon, IPluginPaths):
|
||||
"""Class providing basic Royal Render implementation logic."""
|
||||
name = "royalrender"
|
||||
version = __version__
|
||||
|
||||
# _rr_api = None
|
||||
# @property
|
||||
|
|
|
|||
1
client/ayon_core/modules/royalrender/version.py
Normal file
1
client/ayon_core/modules/royalrender/version.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
__version__ = "0.1.1"
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
from .version import __version__
|
||||
from .timers_manager import (
|
||||
TimersManager
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"__version__",
|
||||
|
||||
"TimersManager",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from ayon_core.addon import (
|
|||
)
|
||||
from ayon_core.lib.events import register_event_callback
|
||||
|
||||
from .version import __version__
|
||||
from .exceptions import InvalidContextError
|
||||
|
||||
TIMER_MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
|
@ -96,6 +97,7 @@ class TimersManager(
|
|||
See `ExampleTimersManagerConnector`.
|
||||
"""
|
||||
name = "timers_manager"
|
||||
version = __version__
|
||||
label = "Timers Service"
|
||||
|
||||
_required_methods = (
|
||||
|
|
|
|||
1
client/ayon_core/modules/timers_manager/version.py
Normal file
1
client/ayon_core/modules/timers_manager/version.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
__version__ = "0.1.1"
|
||||
|
|
@ -1,8 +1,13 @@
|
|||
from .version import __version__
|
||||
from .structures import HostMsgAction
|
||||
from .webserver_module import (
|
||||
WebServerAddon
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"__version__",
|
||||
|
||||
"HostMsgAction",
|
||||
"WebServerAddon",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,22 +9,18 @@ from qtpy import QtWidgets
|
|||
from ayon_core.addon import ITrayService
|
||||
from ayon_core.tools.stdout_broker.window import ConsoleDialog
|
||||
|
||||
from .structures import HostMsgAction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Host listener icon type
|
||||
class IconType:
|
||||
IDLE = "idle"
|
||||
RUNNING = "running"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
class MsgAction:
|
||||
CONNECTING = "connecting"
|
||||
INITIALIZED = "initialized"
|
||||
ADD = "add"
|
||||
CLOSE = "close"
|
||||
|
||||
|
||||
class HostListener:
|
||||
def __init__(self, webserver, module):
|
||||
self._window_per_id = {}
|
||||
|
|
@ -96,22 +92,22 @@ class HostListener:
|
|||
if msg.type == aiohttp.WSMsgType.TEXT:
|
||||
host_name, action, text = self._parse_message(msg)
|
||||
|
||||
if action == MsgAction.CONNECTING:
|
||||
if action == HostMsgAction.CONNECTING:
|
||||
self._action_per_id[host_name] = None
|
||||
# must be sent to main thread, or action wont trigger
|
||||
self.module.execute_in_main_thread(
|
||||
lambda: self._host_is_connecting(host_name, text))
|
||||
elif action == MsgAction.CLOSE:
|
||||
elif action == HostMsgAction.CLOSE:
|
||||
# clean close
|
||||
self._close(host_name)
|
||||
await ws.close()
|
||||
elif action == MsgAction.INITIALIZED:
|
||||
elif action == HostMsgAction.INITIALIZED:
|
||||
self.module.execute_in_main_thread(
|
||||
# must be queued as _host_is_connecting might not
|
||||
# be triggered/finished yet
|
||||
lambda: self._set_host_icon(host_name,
|
||||
IconType.RUNNING))
|
||||
elif action == MsgAction.ADD:
|
||||
elif action == HostMsgAction.ADD:
|
||||
self.module.execute_in_main_thread(
|
||||
lambda: self._add_text(host_name, text))
|
||||
elif msg.type == aiohttp.WSMsgType.ERROR:
|
||||
|
|
|
|||
6
client/ayon_core/modules/webserver/structures.py
Normal file
6
client/ayon_core/modules/webserver/structures.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Host listener message actions
|
||||
class HostMsgAction:
|
||||
CONNECTING = "connecting"
|
||||
INITIALIZED = "initialized"
|
||||
ADD = "add"
|
||||
CLOSE = "close"
|
||||
1
client/ayon_core/modules/webserver/version.py
Normal file
1
client/ayon_core/modules/webserver/version.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
__version__ = "1.0.0"
|
||||
|
|
@ -26,9 +26,12 @@ import socket
|
|||
from ayon_core import resources
|
||||
from ayon_core.addon import AYONAddon, ITrayService
|
||||
|
||||
from .version import __version__
|
||||
|
||||
|
||||
class WebServerAddon(AYONAddon, ITrayService):
|
||||
name = "webserver"
|
||||
version = __version__
|
||||
label = "WebServer"
|
||||
|
||||
webserver_url_env = "AYON_WEBSERVER_URL"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,12 @@ from pyblish.lib import MessageHandler
|
|||
|
||||
from ayon_core import AYON_CORE_ROOT
|
||||
from ayon_core.host import HostBase
|
||||
from ayon_core.lib import is_in_tests, initialize_ayon_connection, emit_event
|
||||
from ayon_core.lib import (
|
||||
is_in_tests,
|
||||
initialize_ayon_connection,
|
||||
emit_event,
|
||||
version_up
|
||||
)
|
||||
from ayon_core.addon import load_addons, AddonsManager
|
||||
from ayon_core.settings import get_project_settings
|
||||
|
||||
|
|
@ -21,6 +26,8 @@ from .template_data import get_template_data_with_names
|
|||
from .workfile import (
|
||||
get_workdir,
|
||||
get_custom_workfile_template_by_string_context,
|
||||
get_workfile_template_key_from_context,
|
||||
get_last_workfile
|
||||
)
|
||||
from . import (
|
||||
register_loader_plugin_path,
|
||||
|
|
@ -579,3 +586,48 @@ def get_process_id():
|
|||
if _process_id is None:
|
||||
_process_id = str(uuid.uuid4())
|
||||
return _process_id
|
||||
|
||||
|
||||
def version_up_current_workfile():
|
||||
"""Function to increment and save workfile
|
||||
"""
|
||||
host = registered_host()
|
||||
if not host.has_unsaved_changes():
|
||||
print("No unsaved changes, skipping file save..")
|
||||
return
|
||||
|
||||
project_name = get_current_project_name()
|
||||
folder_path = get_current_folder_path()
|
||||
task_name = get_current_task_name()
|
||||
host_name = get_current_host_name()
|
||||
|
||||
template_key = get_workfile_template_key_from_context(
|
||||
project_name,
|
||||
folder_path,
|
||||
task_name,
|
||||
host_name,
|
||||
)
|
||||
anatomy = Anatomy(project_name)
|
||||
|
||||
data = get_template_data_with_names(
|
||||
project_name, folder_path, task_name, host_name
|
||||
)
|
||||
data["root"] = anatomy.roots
|
||||
|
||||
work_template = anatomy.get_template_item("work", template_key)
|
||||
|
||||
# Define saving file extension
|
||||
extensions = host.get_workfile_extensions()
|
||||
current_file = host.get_current_workfile()
|
||||
if current_file:
|
||||
extensions = [os.path.splitext(current_file)[-1]]
|
||||
|
||||
work_root = work_template["directory"].format_strict(data)
|
||||
file_template = work_template["file"].template
|
||||
last_workfile_path = get_last_workfile(
|
||||
work_root, file_template, data, extensions, True
|
||||
)
|
||||
new_workfile_path = version_up(last_workfile_path)
|
||||
if os.path.exists(new_workfile_path):
|
||||
new_workfile_path = version_up(new_workfile_path)
|
||||
host.save_workfile(new_workfile_path)
|
||||
|
|
|
|||
|
|
@ -336,17 +336,16 @@ def get_plugin_settings(plugin, project_settings, log, category=None):
|
|||
settings_category = getattr(plugin, "settings_category", None)
|
||||
if settings_category:
|
||||
try:
|
||||
return (
|
||||
project_settings
|
||||
[settings_category]
|
||||
["publish"]
|
||||
[plugin.__name__]
|
||||
)
|
||||
category_settings = project_settings[settings_category]
|
||||
except KeyError:
|
||||
log.warning((
|
||||
"Couldn't find plugin '{}' settings"
|
||||
" under settings category '{}'"
|
||||
).format(plugin.__name__, settings_category))
|
||||
"Couldn't find settings category '{}' in project settings"
|
||||
).format(settings_category))
|
||||
return {}
|
||||
|
||||
try:
|
||||
return category_settings["publish"][plugin.__name__]
|
||||
except KeyError:
|
||||
return {}
|
||||
|
||||
# Use project settings based on a category name
|
||||
|
|
|
|||
|
|
@ -172,12 +172,30 @@ class VersionItem:
|
|||
def __gt__(self, other):
|
||||
if not isinstance(other, VersionItem):
|
||||
return False
|
||||
if (
|
||||
other.version == self.version
|
||||
and self.is_hero
|
||||
):
|
||||
# Make sure hero versions are positive
|
||||
version = abs(self.version)
|
||||
other_version = abs(other.version)
|
||||
# Hero version is greater than non-hero
|
||||
if version == other_version:
|
||||
return self.is_hero
|
||||
return version > other_version
|
||||
|
||||
def __lt__(self, other):
|
||||
if not isinstance(other, VersionItem):
|
||||
return True
|
||||
return other.version < self.version
|
||||
# Make sure hero versions are positive
|
||||
version = abs(self.version)
|
||||
other_version = abs(other.version)
|
||||
# Non-hero version is lesser than hero
|
||||
if version == other_version:
|
||||
return not self.is_hero
|
||||
return version < other_version
|
||||
|
||||
def __ge__(self, other):
|
||||
return self.__eq__(other) or self.__gt__(other)
|
||||
|
||||
def __le__(self, other):
|
||||
return self.__eq__(other) or self.__lt__(other)
|
||||
|
||||
def to_data(self):
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -348,10 +348,18 @@ class LoaderController(BackendLoaderController, FrontendLoaderController):
|
|||
return set()
|
||||
|
||||
if not self._loaded_products_cache.is_valid:
|
||||
if isinstance(self._host, ILoadHost):
|
||||
containers = self._host.get_containers()
|
||||
else:
|
||||
containers = self._host.ls()
|
||||
try:
|
||||
if isinstance(self._host, ILoadHost):
|
||||
containers = self._host.get_containers()
|
||||
else:
|
||||
containers = self._host.ls()
|
||||
|
||||
except BaseException:
|
||||
self.log.error(
|
||||
"Failed to collect loaded products.", exc_info=True
|
||||
)
|
||||
containers = []
|
||||
|
||||
repre_ids = set()
|
||||
for container in containers:
|
||||
repre_id = container.get("representation")
|
||||
|
|
|
|||
|
|
@ -321,6 +321,8 @@ class LoaderFoldersWidget(QtWidgets.QWidget):
|
|||
"""
|
||||
|
||||
self._folders_proxy_model.setFilterFixedString(name)
|
||||
if name:
|
||||
self._folders_view.expandAll()
|
||||
|
||||
def set_merged_products_selection(self, items):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -199,7 +199,9 @@ class ProductsModel(QtGui.QStandardItemModel):
|
|||
product_item = self._product_items_by_id.get(product_id)
|
||||
if product_item is None:
|
||||
return None
|
||||
return list(product_item.version_items.values())
|
||||
product_items = list(product_item.version_items.values())
|
||||
product_items.sort(reverse=True)
|
||||
return product_items
|
||||
|
||||
if role == QtCore.Qt.EditRole:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
from .broker import StdOutBroker
|
||||
|
||||
__all__ = (
|
||||
"StdOutBroker",
|
||||
)
|
||||
|
|
@ -1,173 +1,12 @@
|
|||
import os
|
||||
import sys
|
||||
import threading
|
||||
import collections
|
||||
import websocket
|
||||
import json
|
||||
from datetime import datetime
|
||||
import warnings
|
||||
from .broker import StdOutBroker
|
||||
|
||||
from ayon_core.lib import Logger
|
||||
from openpype_modules.webserver.host_console_listener import MsgAction
|
||||
warnings.warn(
|
||||
(
|
||||
"Import of 'StdOutBroker' from 'ayon_core.tools.stdout_broker.app'"
|
||||
" is deprecated. Please use 'ayon_core.tools.stdout_broker' instead."
|
||||
),
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
log = Logger.get_logger(__name__)
|
||||
|
||||
|
||||
class StdOutBroker:
|
||||
"""
|
||||
Application showing console in Services tray for non python hosts
|
||||
instead of cmd window.
|
||||
"""
|
||||
MAX_LINES = 10000
|
||||
TIMER_TIMEOUT = 0.200
|
||||
|
||||
def __init__(self, host_name):
|
||||
self.host_name = host_name
|
||||
self.webserver_client = None
|
||||
|
||||
self.original_stdout_write = None
|
||||
self.original_stderr_write = None
|
||||
self.log_queue = collections.deque()
|
||||
|
||||
date_str = datetime.now().strftime("%d%m%Y%H%M%S")
|
||||
self.host_id = "{}_{}".format(self.host_name, date_str)
|
||||
|
||||
self._std_available = False
|
||||
self._is_running = False
|
||||
self._catch_std_outputs()
|
||||
|
||||
self._timer = None
|
||||
|
||||
@property
|
||||
def send_to_tray(self):
|
||||
"""Checks if connected to tray and have access to logs."""
|
||||
return self.webserver_client and self._std_available
|
||||
|
||||
def start(self):
|
||||
"""Start app, create and start timer"""
|
||||
if not self._std_available or self._is_running:
|
||||
return
|
||||
self._is_running = True
|
||||
self._create_timer()
|
||||
self._connect_to_tray()
|
||||
|
||||
def stop(self):
|
||||
"""Disconnect from Tray, process last logs"""
|
||||
if not self._is_running:
|
||||
return
|
||||
self._is_running = False
|
||||
self._process_queue()
|
||||
self._disconnect_from_tray()
|
||||
|
||||
def host_connected(self):
|
||||
"""Send to Tray console that host is ready - icon change. """
|
||||
log.info("Host {} connected".format(self.host_id))
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": MsgAction.INITIALIZED,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
self._send(payload)
|
||||
|
||||
def _create_timer(self):
|
||||
timer = threading.Timer(self.TIMER_TIMEOUT, self._timer_callback)
|
||||
timer.start()
|
||||
self._timer = timer
|
||||
|
||||
def _timer_callback(self):
|
||||
if not self._is_running:
|
||||
return
|
||||
self._process_queue()
|
||||
self._create_timer()
|
||||
|
||||
def _connect_to_tray(self):
|
||||
"""Connect to Tray webserver to pass console output. """
|
||||
if not self._std_available: # not content to log
|
||||
return
|
||||
ws = websocket.WebSocket()
|
||||
webserver_url = os.environ.get("AYON_WEBSERVER_URL")
|
||||
|
||||
if not webserver_url:
|
||||
print("Unknown webserver url, cannot connect to pass log")
|
||||
return
|
||||
|
||||
webserver_url = webserver_url.replace("http", "ws")
|
||||
ws.connect("{}/ws/host_listener".format(webserver_url))
|
||||
self.webserver_client = ws
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": MsgAction.CONNECTING,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
self._send(payload)
|
||||
|
||||
def _disconnect_from_tray(self):
|
||||
"""Send to Tray that host is closing - remove from Services. """
|
||||
print("Host {} closing".format(self.host_name))
|
||||
if not self.webserver_client:
|
||||
return
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": MsgAction.CLOSE,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
|
||||
self._send(payload)
|
||||
self.webserver_client.close()
|
||||
|
||||
def _catch_std_outputs(self):
|
||||
"""Redirects standard out and error to own functions"""
|
||||
if sys.stdout:
|
||||
self.original_stdout_write = sys.stdout.write
|
||||
sys.stdout.write = self._my_stdout_write
|
||||
self._std_available = True
|
||||
|
||||
if sys.stderr:
|
||||
self.original_stderr_write = sys.stderr.write
|
||||
sys.stderr.write = self._my_stderr_write
|
||||
self._std_available = True
|
||||
|
||||
def _my_stdout_write(self, text):
|
||||
"""Appends outputted text to queue, keep writing to original stdout"""
|
||||
if self.original_stdout_write is not None:
|
||||
self.original_stdout_write(text)
|
||||
if self.send_to_tray:
|
||||
self.log_queue.append(text)
|
||||
|
||||
def _my_stderr_write(self, text):
|
||||
"""Appends outputted text to queue, keep writing to original stderr"""
|
||||
if self.original_stderr_write is not None:
|
||||
self.original_stderr_write(text)
|
||||
if self.send_to_tray:
|
||||
self.log_queue.append(text)
|
||||
|
||||
def _process_queue(self):
|
||||
"""Sends lines and purges queue"""
|
||||
if not self.send_to_tray:
|
||||
return
|
||||
|
||||
lines = tuple(self.log_queue)
|
||||
self.log_queue.clear()
|
||||
if lines:
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": MsgAction.ADD,
|
||||
"text": "\n".join(lines)
|
||||
}
|
||||
|
||||
self._send(payload)
|
||||
|
||||
def _send(self, payload):
|
||||
"""Worker method to send to existing websocket connection."""
|
||||
if not self.send_to_tray:
|
||||
return
|
||||
|
||||
try:
|
||||
self.webserver_client.send(json.dumps(payload))
|
||||
except ConnectionResetError: # Tray closed
|
||||
self._connect_to_tray()
|
||||
__all__ = ("StdOutBroker", )
|
||||
|
|
|
|||
174
client/ayon_core/tools/stdout_broker/broker.py
Normal file
174
client/ayon_core/tools/stdout_broker/broker.py
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
import os
|
||||
import sys
|
||||
import threading
|
||||
import collections
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
import websocket
|
||||
|
||||
from ayon_core.lib import Logger
|
||||
from ayon_core.modules.webserver import HostMsgAction
|
||||
|
||||
log = Logger.get_logger(__name__)
|
||||
|
||||
|
||||
class StdOutBroker:
|
||||
"""
|
||||
Application showing console in Services tray for non python hosts
|
||||
instead of cmd window.
|
||||
"""
|
||||
MAX_LINES = 10000
|
||||
TIMER_TIMEOUT = 0.200
|
||||
|
||||
def __init__(self, host_name):
|
||||
self.host_name = host_name
|
||||
self.webserver_client = None
|
||||
|
||||
self.original_stdout_write = None
|
||||
self.original_stderr_write = None
|
||||
self.log_queue = collections.deque()
|
||||
|
||||
date_str = datetime.now().strftime("%d%m%Y%H%M%S")
|
||||
self.host_id = "{}_{}".format(self.host_name, date_str)
|
||||
|
||||
self._std_available = False
|
||||
self._is_running = False
|
||||
self._catch_std_outputs()
|
||||
|
||||
self._timer = None
|
||||
|
||||
@property
|
||||
def send_to_tray(self):
|
||||
"""Checks if connected to tray and have access to logs."""
|
||||
return self.webserver_client and self._std_available
|
||||
|
||||
def start(self):
|
||||
"""Start app, create and start timer"""
|
||||
if not self._std_available or self._is_running:
|
||||
return
|
||||
self._is_running = True
|
||||
self._create_timer()
|
||||
self._connect_to_tray()
|
||||
|
||||
def stop(self):
|
||||
"""Disconnect from Tray, process last logs"""
|
||||
if not self._is_running:
|
||||
return
|
||||
self._is_running = False
|
||||
self._process_queue()
|
||||
self._disconnect_from_tray()
|
||||
|
||||
def host_connected(self):
|
||||
"""Send to Tray console that host is ready - icon change. """
|
||||
log.info("Host {} connected".format(self.host_id))
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": HostMsgAction.INITIALIZED,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
self._send(payload)
|
||||
|
||||
def _create_timer(self):
|
||||
timer = threading.Timer(self.TIMER_TIMEOUT, self._timer_callback)
|
||||
timer.start()
|
||||
self._timer = timer
|
||||
|
||||
def _timer_callback(self):
|
||||
if not self._is_running:
|
||||
return
|
||||
self._process_queue()
|
||||
self._create_timer()
|
||||
|
||||
def _connect_to_tray(self):
|
||||
"""Connect to Tray webserver to pass console output. """
|
||||
if not self._std_available: # not content to log
|
||||
return
|
||||
ws = websocket.WebSocket()
|
||||
webserver_url = os.environ.get("AYON_WEBSERVER_URL")
|
||||
|
||||
if not webserver_url:
|
||||
print("Unknown webserver url, cannot connect to pass log")
|
||||
return
|
||||
|
||||
webserver_url = webserver_url.replace("http", "ws")
|
||||
ws.connect("{}/ws/host_listener".format(webserver_url))
|
||||
self.webserver_client = ws
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": HostMsgAction.CONNECTING,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
self._send(payload)
|
||||
|
||||
def _disconnect_from_tray(self):
|
||||
"""Send to Tray that host is closing - remove from Services. """
|
||||
print("Host {} closing".format(self.host_name))
|
||||
if not self.webserver_client:
|
||||
return
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": HostMsgAction.CLOSE,
|
||||
"text": "Integration with {}".format(
|
||||
str.capitalize(self.host_name))
|
||||
}
|
||||
|
||||
self._send(payload)
|
||||
self.webserver_client.close()
|
||||
|
||||
def _catch_std_outputs(self):
|
||||
"""Redirects standard out and error to own functions"""
|
||||
if sys.stdout:
|
||||
self.original_stdout_write = sys.stdout.write
|
||||
sys.stdout.write = self._my_stdout_write
|
||||
self._std_available = True
|
||||
|
||||
if sys.stderr:
|
||||
self.original_stderr_write = sys.stderr.write
|
||||
sys.stderr.write = self._my_stderr_write
|
||||
self._std_available = True
|
||||
|
||||
def _my_stdout_write(self, text):
|
||||
"""Appends outputted text to queue, keep writing to original stdout"""
|
||||
if self.original_stdout_write is not None:
|
||||
self.original_stdout_write(text)
|
||||
if self.send_to_tray:
|
||||
self.log_queue.append(text)
|
||||
|
||||
def _my_stderr_write(self, text):
|
||||
"""Appends outputted text to queue, keep writing to original stderr"""
|
||||
if self.original_stderr_write is not None:
|
||||
self.original_stderr_write(text)
|
||||
if self.send_to_tray:
|
||||
self.log_queue.append(text)
|
||||
|
||||
def _process_queue(self):
|
||||
"""Sends lines and purges queue"""
|
||||
if not self.send_to_tray:
|
||||
return
|
||||
|
||||
lines = tuple(self.log_queue)
|
||||
self.log_queue.clear()
|
||||
if lines:
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": HostMsgAction.ADD,
|
||||
"text": "\n".join(lines)
|
||||
}
|
||||
|
||||
self._send(payload)
|
||||
|
||||
def _send(self, payload):
|
||||
"""Worker method to send to existing websocket connection."""
|
||||
if not self.send_to_tray:
|
||||
return
|
||||
|
||||
try:
|
||||
self.webserver_client.send(json.dumps(payload))
|
||||
except ConnectionResetError: # Tray closed
|
||||
self._connect_to_tray()
|
||||
|
|
@ -447,8 +447,10 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
|||
|
||||
def initialize_addons(self):
|
||||
self._initializing_addons = True
|
||||
self.tray_man.initialize_addons()
|
||||
self._initializing_addons = False
|
||||
try:
|
||||
self.tray_man.initialize_addons()
|
||||
finally:
|
||||
self._initializing_addons = False
|
||||
|
||||
def _click_timer_timeout(self):
|
||||
self._click_timer.stop()
|
||||
|
|
|
|||
|
|
@ -370,6 +370,8 @@ class FoldersWidget(QtWidgets.QWidget):
|
|||
"""
|
||||
|
||||
self._folders_proxy_model.setFilterFixedString(name)
|
||||
if name:
|
||||
self._folders_view.expandAll()
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh folders model.
|
||||
|
|
|
|||
|
|
@ -79,11 +79,12 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|||
|
||||
exclude = [
|
||||
"client/ayon_core/scripts/slates/__init__.py",
|
||||
"client/ayon_core/modules/click_wrap.py",
|
||||
"client/ayon_core/hosts/unreal/integration/*",
|
||||
"client/ayon_core/hosts/hiero/api/startup/*",
|
||||
"client/ayon_core/modules/deadline/repository/custom/plugins/CelAction/*",
|
||||
"client/ayon_core/modules/deadline/repository/custom/plugins/HarmonyAYON/*",
|
||||
"client/ayon_core/modules/click_wrap.py",
|
||||
"client/ayon_core/scripts/slates/__init__.py",
|
||||
"server_addon/hiero/client/ayon_hiero/api/startup/*",
|
||||
"server_addon/aftereffects/client/ayon_aftereffects/api/extension/js/libs/*"
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -798,7 +798,7 @@ class PublishPuginsModel(BaseSettingsModel):
|
|||
)
|
||||
ValidateOutdatedContainers: PluginStateByHostModel = SettingsField(
|
||||
default_factory=PluginStateByHostModel,
|
||||
title="Validate Containers"
|
||||
title="Validate Outdated Containers"
|
||||
)
|
||||
ValidateIntent: ValidateIntentModel = SettingsField(
|
||||
default_factory=ValidateIntentModel,
|
||||
|
|
|
|||
|
|
@ -118,6 +118,15 @@ class WorkfilesLockProfile(BaseSettingsModel):
|
|||
enabled: bool = SettingsField(True, title="Enabled")
|
||||
|
||||
|
||||
class AYONMenuModel(BaseSettingsModel):
|
||||
_layout = "expanded"
|
||||
version_up_current_workfile: bool = SettingsField(
|
||||
False,
|
||||
title="Version Up Workfile",
|
||||
description="Add 'Version Up Workfile' to AYON menu"
|
||||
)
|
||||
|
||||
|
||||
class WorkfilesToolModel(BaseSettingsModel):
|
||||
workfile_template_profiles: list[WorkfileTemplateProfile] = SettingsField(
|
||||
default_factory=list,
|
||||
|
|
@ -268,6 +277,10 @@ class PublishToolModel(BaseSettingsModel):
|
|||
|
||||
|
||||
class GlobalToolsModel(BaseSettingsModel):
|
||||
ayon_menu: AYONMenuModel = SettingsField(
|
||||
default_factory=AYONMenuModel,
|
||||
title="AYON Menu"
|
||||
)
|
||||
creator: CreatorToolModel = SettingsField(
|
||||
default_factory=CreatorToolModel,
|
||||
title="Creator"
|
||||
|
|
@ -287,6 +300,9 @@ class GlobalToolsModel(BaseSettingsModel):
|
|||
|
||||
|
||||
DEFAULT_TOOLS_VALUES = {
|
||||
"ayon_menu": {
|
||||
"version_up_current_workfile": False
|
||||
},
|
||||
"creator": {
|
||||
"product_types_smart_select": [
|
||||
{
|
||||
|
|
|
|||
13
server_addon/blender/client/ayon_blender/__init__.py
Normal file
13
server_addon/blender/client/ayon_blender/__init__.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
from .version import __version__
|
||||
from .addon import (
|
||||
BlenderAddon,
|
||||
BLENDER_ADDON_ROOT,
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"__version__",
|
||||
|
||||
"BlenderAddon",
|
||||
"BLENDER_ADDON_ROOT",
|
||||
)
|
||||
|
|
@ -1,18 +1,21 @@
|
|||
import os
|
||||
from ayon_core.addon import AYONAddon, IHostAddon
|
||||
|
||||
BLENDER_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
from .version import __version__
|
||||
|
||||
BLENDER_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class BlenderAddon(AYONAddon, IHostAddon):
|
||||
name = "blender"
|
||||
version = __version__
|
||||
host_name = "blender"
|
||||
|
||||
def add_implementation_envs(self, env, _app):
|
||||
"""Modify environments to contain all required for implementation."""
|
||||
# Prepare path to implementation script
|
||||
implementation_user_script_path = os.path.join(
|
||||
BLENDER_ROOT_DIR,
|
||||
BLENDER_ADDON_ROOT,
|
||||
"blender_addon"
|
||||
)
|
||||
|
||||
|
|
@ -61,7 +64,7 @@ class BlenderAddon(AYONAddon, IHostAddon):
|
|||
if app.host_name != self.host_name:
|
||||
return []
|
||||
return [
|
||||
os.path.join(BLENDER_ROOT_DIR, "hooks")
|
||||
os.path.join(BLENDER_ADDON_ROOT, "hooks")
|
||||
]
|
||||
|
||||
def get_workfile_extensions(self):
|
||||
|
|
@ -15,7 +15,6 @@ from .pipeline import (
|
|||
|
||||
from .plugin import (
|
||||
Creator,
|
||||
Loader,
|
||||
)
|
||||
|
||||
from .workio import (
|
||||
|
|
@ -51,7 +50,6 @@ __all__ = [
|
|||
"BlenderHost",
|
||||
|
||||
"Creator",
|
||||
"Loader",
|
||||
|
||||
# Workfiles API
|
||||
"open_file",
|
||||
|
Before Width: | Height: | Size: 632 B After Width: | Height: | Size: 632 B |
|
|
@ -305,7 +305,7 @@ class LaunchCreator(LaunchQtApp):
|
|||
|
||||
|
||||
class LaunchLoader(LaunchQtApp):
|
||||
"""Launch Avalon Loader."""
|
||||
"""Launch AYON Loader."""
|
||||
|
||||
bl_idname = "wm.avalon_loader"
|
||||
bl_label = "Load..."
|
||||
|
|
@ -5,9 +5,6 @@ from typing import Callable, Dict, Iterator, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from . import lib
|
||||
from . import ops
|
||||
|
||||
import pyblish.api
|
||||
import ayon_api
|
||||
|
||||
|
|
@ -33,8 +30,12 @@ from ayon_core.lib import (
|
|||
register_event_callback,
|
||||
emit_event
|
||||
)
|
||||
import ayon_core.hosts.blender
|
||||
from ayon_core.settings import get_project_settings
|
||||
from ayon_blender import BLENDER_ADDON_ROOT
|
||||
|
||||
from . import lib
|
||||
from . import ops
|
||||
|
||||
from .workio import (
|
||||
open_file,
|
||||
save_file,
|
||||
|
|
@ -44,9 +45,7 @@ from .workio import (
|
|||
work_root,
|
||||
)
|
||||
|
||||
|
||||
HOST_DIR = os.path.dirname(os.path.abspath(ayon_core.hosts.blender.__file__))
|
||||
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
|
||||
PLUGINS_DIR = os.path.join(BLENDER_ADDON_ROOT, "plugins")
|
||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
|
||||
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
|
||||
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
|
||||
|
|
@ -4,6 +4,7 @@ import itertools
|
|||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import pyblish.api
|
||||
import bpy
|
||||
|
||||
from ayon_core.pipeline import (
|
||||
|
|
@ -13,6 +14,7 @@ from ayon_core.pipeline import (
|
|||
AVALON_INSTANCE_ID,
|
||||
AYON_INSTANCE_ID,
|
||||
)
|
||||
from ayon_core.pipeline.publish import Extractor
|
||||
from ayon_core.lib import BoolDef
|
||||
|
||||
from .pipeline import (
|
||||
|
|
@ -161,10 +163,23 @@ def deselect_all():
|
|||
bpy.context.view_layer.objects.active = active
|
||||
|
||||
|
||||
class BaseCreator(Creator):
|
||||
class BlenderInstancePlugin(pyblish.api.InstancePlugin):
|
||||
settings_category = "blender"
|
||||
|
||||
|
||||
class BlenderContextPlugin(pyblish.api.ContextPlugin):
|
||||
settings_category = "blender"
|
||||
|
||||
|
||||
class BlenderExtractor(Extractor):
|
||||
settings_category = "blender"
|
||||
|
||||
|
||||
class BlenderCreator(Creator):
|
||||
"""Base class for Blender Creator plug-ins."""
|
||||
defaults = ['Main']
|
||||
|
||||
settings_category = "blender"
|
||||
create_as_asset_group = False
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -265,7 +280,7 @@ class BaseCreator(Creator):
|
|||
return instance_node
|
||||
|
||||
def collect_instances(self):
|
||||
"""Override abstract method from BaseCreator.
|
||||
"""Override abstract method from BlenderCreator.
|
||||
Collect existing instances related to this creator plugin."""
|
||||
|
||||
# Cache instances in shared data
|
||||
|
|
@ -292,7 +307,7 @@ class BaseCreator(Creator):
|
|||
self._add_instance_to_context(instance)
|
||||
|
||||
def update_instances(self, update_list):
|
||||
"""Override abstract method from BaseCreator.
|
||||
"""Override abstract method from BlenderCreator.
|
||||
Store changes of existing instances so they can be recollected.
|
||||
|
||||
Args:
|
||||
|
|
@ -376,13 +391,7 @@ class BaseCreator(Creator):
|
|||
]
|
||||
|
||||
|
||||
class Loader(LoaderPlugin):
|
||||
"""Base class for Loader plug-ins."""
|
||||
|
||||
hosts = ["blender"]
|
||||
|
||||
|
||||
class AssetLoader(LoaderPlugin):
|
||||
class BlenderLoader(LoaderPlugin):
|
||||
"""A basic AssetLoader for Blender
|
||||
|
||||
This will implement the basic logic for linking/appending assets
|
||||
|
|
@ -392,6 +401,7 @@ class AssetLoader(LoaderPlugin):
|
|||
it's different for different types (e.g. model, rig, animation,
|
||||
etc.).
|
||||
"""
|
||||
settings_category = "blender"
|
||||
|
||||
@staticmethod
|
||||
def _get_instance_empty(instance_name: str, nodes: List) -> Optional[bpy.types.Object]:
|
||||
|
|
@ -496,7 +506,7 @@ class AssetLoader(LoaderPlugin):
|
|||
# Only containerise if it's not already a collection from a .blend file.
|
||||
# representation = context["representation"]["name"]
|
||||
# if representation != "blend":
|
||||
# from ayon_core.hosts.blender.api.pipeline import containerise
|
||||
# from ayon_blender.api.pipeline import containerise
|
||||
# return containerise(
|
||||
# name=name,
|
||||
# namespace=namespace,
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from ayon_core.pipeline import install_host
|
||||
from ayon_core.hosts.blender.api import BlenderHost
|
||||
from ayon_blender.api import BlenderHost
|
||||
|
||||
|
||||
def register():
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Converter for legacy Houdini products."""
|
||||
from ayon_core.pipeline.create.creator_plugins import ProductConvertorPlugin
|
||||
from ayon_core.hosts.blender.api.lib import imprint
|
||||
from ayon_blender.api.lib import imprint
|
||||
|
||||
|
||||
class BlenderLegacyConvertor(ProductConvertorPlugin):
|
||||
|
|
@ -42,7 +42,7 @@ class BlenderLegacyConvertor(ProductConvertorPlugin):
|
|||
parameter on them.
|
||||
|
||||
This is using cached entries done in
|
||||
:py:meth:`~BaseCreator.cache_instance_data()`
|
||||
:py:meth:`~BlenderCreator.cache_instance_data()`
|
||||
|
||||
"""
|
||||
self.legacy_instances = self.collection_shared_data.get(
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import lib, plugin
|
||||
from ayon_blender.api import lib, plugin
|
||||
|
||||
|
||||
class CreateAction(plugin.BaseCreator):
|
||||
class CreateAction(plugin.BlenderCreator):
|
||||
"""Action output for character rigs."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.action"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
"""Create an animation asset."""
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateAnimation(plugin.BaseCreator):
|
||||
class CreateAnimation(plugin.BlenderCreator):
|
||||
"""Animation output for character rigs."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.animation"
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateBlendScene(plugin.BaseCreator):
|
||||
class CreateBlendScene(plugin.BlenderCreator):
|
||||
"""Generic group of assets."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.blendscene"
|
||||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_core.hosts.blender.api.pipeline import AVALON_INSTANCES
|
||||
from ayon_blender.api import plugin, lib
|
||||
from ayon_blender.api.pipeline import AVALON_INSTANCES
|
||||
|
||||
|
||||
class CreateCamera(plugin.BaseCreator):
|
||||
class CreateCamera(plugin.BlenderCreator):
|
||||
"""Polygonal static geometry."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.camera"
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateLayout(plugin.BaseCreator):
|
||||
class CreateLayout(plugin.BlenderCreator):
|
||||
"""Layout output for character rigs."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.layout"
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateModel(plugin.BaseCreator):
|
||||
class CreateModel(plugin.BlenderCreator):
|
||||
"""Polygonal static geometry."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.model"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
"""Create a pointcache asset."""
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreatePointcache(plugin.BaseCreator):
|
||||
class CreatePointcache(plugin.BlenderCreator):
|
||||
"""Polygonal static geometry."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.pointcache"
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
import bpy
|
||||
|
||||
from ayon_core.lib import version_up
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.render_lib import prepare_rendering
|
||||
from ayon_core.hosts.blender.api.workio import save_file
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.render_lib import prepare_rendering
|
||||
from ayon_blender.api.workio import save_file
|
||||
|
||||
|
||||
class CreateRenderlayer(plugin.BaseCreator):
|
||||
class CreateRenderlayer(plugin.BlenderCreator):
|
||||
"""Single baked camera."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.render"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
"""Create review."""
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateReview(plugin.BaseCreator):
|
||||
class CreateReview(plugin.BlenderCreator):
|
||||
"""Single baked camera."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.review"
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateRig(plugin.BaseCreator):
|
||||
class CreateRig(plugin.BlenderCreator):
|
||||
"""Artist-friendly rig with controls to direct motion."""
|
||||
|
||||
identifier = "io.openpype.creators.blender.rig"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
"""Create a USD Export."""
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CreateUSD(plugin.BaseCreator):
|
||||
class CreateUSD(plugin.BlenderCreator):
|
||||
"""Create USD Export"""
|
||||
|
||||
identifier = "io.openpype.creators.blender.usd"
|
||||
|
|
@ -2,14 +2,14 @@ import bpy
|
|||
import ayon_api
|
||||
|
||||
from ayon_core.pipeline import CreatedInstance, AutoCreator
|
||||
from ayon_core.hosts.blender.api.plugin import BaseCreator
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api.plugin import BlenderCreator
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_PROPERTY,
|
||||
AVALON_CONTAINERS
|
||||
)
|
||||
|
||||
|
||||
class CreateWorkfile(BaseCreator, AutoCreator):
|
||||
class CreateWorkfile(BlenderCreator, AutoCreator):
|
||||
"""Workfile auto-creator.
|
||||
|
||||
The workfile instance stores its data on the `AVALON_CONTAINERS` collection
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
def append_workfile(context, fname, do_import):
|
||||
|
|
@ -34,7 +34,7 @@ def append_workfile(context, fname, do_import):
|
|||
collection.children.link(coll)
|
||||
|
||||
|
||||
class AppendBlendLoader(plugin.AssetLoader):
|
||||
class AppendBlendLoader(plugin.BlenderLoader):
|
||||
"""Append workfile in Blender (unmanaged)
|
||||
|
||||
Warning:
|
||||
|
|
@ -59,7 +59,7 @@ class AppendBlendLoader(plugin.AssetLoader):
|
|||
return
|
||||
|
||||
|
||||
class ImportBlendLoader(plugin.AssetLoader):
|
||||
class ImportBlendLoader(plugin.BlenderLoader):
|
||||
"""Import workfile in the current Blender scene (unmanaged)
|
||||
|
||||
Warning:
|
||||
|
|
@ -7,8 +7,8 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
from ayon_core.pipeline import get_representation_path
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import (
|
||||
containerise_existing,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
|
@ -16,7 +16,7 @@ from ayon_core.hosts.blender.api.pipeline import (
|
|||
logger = logging.getLogger("ayon").getChild("blender").getChild("load_action")
|
||||
|
||||
|
||||
class BlendActionLoader(plugin.AssetLoader):
|
||||
class BlendActionLoader(plugin.BlenderLoader):
|
||||
"""Load action from a .blend file.
|
||||
|
||||
Warning:
|
||||
|
|
@ -4,11 +4,11 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import AVALON_PROPERTY
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import AVALON_PROPERTY
|
||||
|
||||
|
||||
class BlendAnimationLoader(plugin.AssetLoader):
|
||||
class BlendAnimationLoader(plugin.BlenderLoader):
|
||||
"""Load animations from a .blend file.
|
||||
|
||||
Warning:
|
||||
|
|
@ -10,14 +10,14 @@ from ayon_core.pipeline import (
|
|||
get_representation_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class AudioLoader(plugin.AssetLoader):
|
||||
class AudioLoader(plugin.BlenderLoader):
|
||||
"""Load audio in Blender."""
|
||||
|
||||
product_types = {"audio"}
|
||||
|
|
@ -9,15 +9,15 @@ from ayon_core.pipeline import (
|
|||
registered_host
|
||||
)
|
||||
from ayon_core.pipeline.create import CreateContext
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.lib import imprint
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.lib import imprint
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class BlendLoader(plugin.AssetLoader):
|
||||
class BlendLoader(plugin.BlenderLoader):
|
||||
"""Load assets from a .blend file."""
|
||||
|
||||
product_types = {"model", "rig", "layout", "camera"}
|
||||
|
|
@ -7,15 +7,15 @@ from ayon_core.pipeline import (
|
|||
get_representation_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.lib import imprint
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.lib import imprint
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class BlendSceneLoader(plugin.AssetLoader):
|
||||
class BlendSceneLoader(plugin.BlenderLoader):
|
||||
"""Load assets from a .blend file."""
|
||||
|
||||
product_types = {"blendScene"}
|
||||
|
|
@ -11,14 +11,14 @@ from ayon_core.pipeline import (
|
|||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class CacheModelLoader(plugin.AssetLoader):
|
||||
class CacheModelLoader(plugin.BlenderLoader):
|
||||
"""Load cache models.
|
||||
|
||||
Stores the imported asset in a collection named after the asset.
|
||||
|
|
@ -10,14 +10,14 @@ from ayon_core.pipeline import (
|
|||
get_representation_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin, lib
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class AbcCameraLoader(plugin.AssetLoader):
|
||||
class AbcCameraLoader(plugin.BlenderLoader):
|
||||
"""Load a camera from Alembic file.
|
||||
|
||||
Stores the imported asset in an empty named after the asset.
|
||||
|
|
@ -10,14 +10,14 @@ from ayon_core.pipeline import (
|
|||
get_representation_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin, lib
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class FbxCameraLoader(plugin.AssetLoader):
|
||||
class FbxCameraLoader(plugin.BlenderLoader):
|
||||
"""Load a camera from FBX.
|
||||
|
||||
Stores the imported asset in an empty named after the asset.
|
||||
|
|
@ -10,14 +10,14 @@ from ayon_core.pipeline import (
|
|||
get_representation_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin, lib
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class FbxModelLoader(plugin.AssetLoader):
|
||||
class FbxModelLoader(plugin.BlenderLoader):
|
||||
"""Load FBX models.
|
||||
|
||||
Stores the imported asset in an empty named after the asset.
|
||||
|
|
@ -15,15 +15,15 @@ from ayon_core.pipeline import (
|
|||
loaders_from_representation,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api.pipeline import (
|
||||
AVALON_INSTANCES,
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class JsonLayoutLoader(plugin.AssetLoader):
|
||||
class JsonLayoutLoader(plugin.BlenderLoader):
|
||||
"""Load layout published from Unreal."""
|
||||
|
||||
product_types = {"layout"}
|
||||
|
|
@ -9,14 +9,14 @@ import json
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import get_representation_path
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import (
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import (
|
||||
containerise_existing,
|
||||
AVALON_PROPERTY
|
||||
)
|
||||
|
||||
|
||||
class BlendLookLoader(plugin.AssetLoader):
|
||||
class BlendLookLoader(plugin.BlenderLoader):
|
||||
"""Load models from a .blend file.
|
||||
|
||||
Because they come from a .blend file we can simply link the collection that
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import pyblish.api
|
||||
from ayon_core.hosts.blender.api import workio
|
||||
from ayon_blender.api import workio, plugin
|
||||
|
||||
|
||||
class CollectBlenderCurrentFile(pyblish.api.ContextPlugin):
|
||||
class CollectBlenderCurrentFile(plugin.BlenderContextPlugin):
|
||||
"""Inject the current working file into context"""
|
||||
|
||||
order = pyblish.api.CollectorOrder - 0.5
|
||||
|
|
@ -3,10 +3,11 @@ import bpy
|
|||
import pyblish.api
|
||||
|
||||
from ayon_core.pipeline.publish import KnownPublishError
|
||||
from ayon_core.hosts.blender.api.pipeline import AVALON_PROPERTY
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import AVALON_PROPERTY
|
||||
|
||||
|
||||
class CollectBlenderInstanceData(pyblish.api.InstancePlugin):
|
||||
class CollectBlenderInstanceData(plugin.BlenderInstancePlugin):
|
||||
"""Validator to verify that the instance is not empty"""
|
||||
|
||||
order = pyblish.api.CollectorOrder
|
||||
|
|
@ -5,12 +5,12 @@ import os
|
|||
import re
|
||||
|
||||
import bpy
|
||||
|
||||
from ayon_core.hosts.blender.api import colorspace
|
||||
import pyblish.api
|
||||
|
||||
from ayon_blender.api import colorspace, plugin
|
||||
|
||||
class CollectBlenderRender(pyblish.api.InstancePlugin):
|
||||
|
||||
class CollectBlenderRender(plugin.BlenderInstancePlugin):
|
||||
"""Gather all publishable render instances."""
|
||||
|
||||
order = pyblish.api.CollectorOrder + 0.01
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class CollectReview(pyblish.api.InstancePlugin):
|
||||
class CollectReview(plugin.BlenderInstancePlugin):
|
||||
"""Collect Review data
|
||||
|
||||
"""
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
from pathlib import Path
|
||||
|
||||
from pyblish.api import InstancePlugin, CollectorOrder
|
||||
from pyblish.api import CollectorOrder
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class CollectWorkfile(InstancePlugin):
|
||||
class CollectWorkfile(plugin.BlenderInstancePlugin):
|
||||
"""Inject workfile data into its instance."""
|
||||
|
||||
order = CollectorOrder
|
||||
|
|
@ -4,10 +4,10 @@ import bpy
|
|||
|
||||
from ayon_core.lib import BoolDef
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractABC(plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin):
|
||||
"""Extract as ABC."""
|
||||
|
||||
label = "Extract ABC"
|
||||
|
|
@ -3,12 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractAnimationABC(
|
||||
publish.Extractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
plugin.BlenderExtractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Extract as ABC."""
|
||||
|
||||
|
|
@ -3,9 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractBlend(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Extract a blend file."""
|
||||
|
||||
label = "Extract Blend"
|
||||
|
|
@ -3,11 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractBlendAnimation(
|
||||
publish.Extractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
plugin.BlenderExtractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Extract a blend file."""
|
||||
|
||||
|
|
@ -3,10 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractCameraABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractCameraABC(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Extract camera as ABC."""
|
||||
|
||||
label = "Extract Camera (ABC)"
|
||||
|
|
@ -3,10 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractCamera(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractCamera(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Extract as the camera as FBX."""
|
||||
|
||||
label = "Extract Camera (FBX)"
|
||||
|
|
@ -3,10 +3,12 @@ import os
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class ExtractFBX(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractFBX(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Extract as FBX."""
|
||||
|
||||
label = "Extract FBX"
|
||||
|
|
@ -6,8 +6,8 @@ import bpy_extras
|
|||
import bpy_extras.anim_utils
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import AVALON_PROPERTY
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import AVALON_PROPERTY
|
||||
|
||||
|
||||
def get_all_parents(obj):
|
||||
|
|
@ -42,8 +42,8 @@ def get_highest_root(objects):
|
|||
|
||||
|
||||
class ExtractAnimationFBX(
|
||||
publish.Extractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
plugin.BlenderExtractor,
|
||||
publish.OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Extract as animation."""
|
||||
|
||||
|
|
@ -8,11 +8,13 @@ import bpy_extras.anim_utils
|
|||
from ayon_api import get_representations
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin
|
||||
from ayon_core.hosts.blender.api.pipeline import AVALON_PROPERTY
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.pipeline import AVALON_PROPERTY
|
||||
|
||||
|
||||
class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractLayout(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Extract a layout."""
|
||||
|
||||
label = "Extract Layout (JSON)"
|
||||
|
|
@ -7,11 +7,13 @@ import pyblish.api
|
|||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import capture
|
||||
from ayon_core.hosts.blender.api.lib import maintained_time
|
||||
from ayon_blender.api import capture, plugin
|
||||
from ayon_blender.api.lib import maintained_time
|
||||
|
||||
|
||||
class ExtractPlayblast(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
class ExtractPlayblast(
|
||||
plugin.BlenderExtractor, publish.OptionalPyblishPluginMixin
|
||||
):
|
||||
"""
|
||||
Extract viewport playblast.
|
||||
|
||||
|
|
@ -3,14 +3,13 @@ import glob
|
|||
import json
|
||||
|
||||
import pyblish.api
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import capture
|
||||
from ayon_core.hosts.blender.api.lib import maintained_time
|
||||
from ayon_blender.api import capture, plugin
|
||||
from ayon_blender.api.lib import maintained_time
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
class ExtractThumbnail(publish.Extractor):
|
||||
class ExtractThumbnail(plugin.BlenderExtractor):
|
||||
"""Extract viewport thumbnail.
|
||||
|
||||
Takes review camera and creates a thumbnail based on viewport
|
||||
|
|
@ -2,11 +2,11 @@ import os
|
|||
|
||||
import bpy
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
from ayon_core.pipeline import KnownPublishError
|
||||
from ayon_blender.api import plugin, lib
|
||||
|
||||
|
||||
class ExtractUSD(publish.Extractor):
|
||||
class ExtractUSD(plugin.BlenderExtractor):
|
||||
"""Extract as USD."""
|
||||
|
||||
label = "Extract USD"
|
||||
|
|
@ -40,7 +40,7 @@ class ExtractUSD(publish.Extractor):
|
|||
root = lib.get_highest_root(objects=instance[:])
|
||||
if not root:
|
||||
instance_node = instance.data["transientData"]["instance_node"]
|
||||
raise publish.KnownPublishError(
|
||||
raise KnownPublishError(
|
||||
f"No root object found in instance: {instance_node.name}"
|
||||
)
|
||||
self.log.debug(f"Exporting using active root: {root.name}")
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import pyblish.api
|
||||
from ayon_core.pipeline.publish import OptionalPyblishPluginMixin
|
||||
from ayon_core.hosts.blender.api.workio import save_file
|
||||
from ayon_blender.api.workio import save_file
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class IncrementWorkfileVersion(
|
||||
pyblish.api.ContextPlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
plugin.BlenderContextPlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Increment current workfile version."""
|
||||
|
||||
|
|
@ -2,11 +2,12 @@ import json
|
|||
|
||||
import pyblish.api
|
||||
from ayon_core.pipeline.publish import OptionalPyblishPluginMixin
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class IntegrateAnimation(
|
||||
pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin,
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Generate a JSON file for animation."""
|
||||
|
||||
|
|
@ -2,9 +2,8 @@ from typing import List
|
|||
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
|
||||
import ayon_core.hosts.blender.api.action
|
||||
import ayon_blender.api.action
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_core.pipeline.publish import (
|
||||
ValidateContentsOrder,
|
||||
PublishValidationError,
|
||||
|
|
@ -12,8 +11,10 @@ from ayon_core.pipeline.publish import (
|
|||
)
|
||||
|
||||
|
||||
class ValidateCameraZeroKeyframe(pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
class ValidateCameraZeroKeyframe(
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Camera must have a keyframe at frame 0.
|
||||
|
||||
Unreal shifts the first keyframe to frame 0. Forcing the camera to have
|
||||
|
|
@ -25,7 +26,7 @@ class ValidateCameraZeroKeyframe(pyblish.api.InstancePlugin,
|
|||
hosts = ["blender"]
|
||||
families = ["camera"]
|
||||
label = "Zero Keyframe"
|
||||
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction]
|
||||
actions = [ayon_blender.api.action.SelectInvalidAction]
|
||||
|
||||
@staticmethod
|
||||
def get_invalid(instance) -> List:
|
||||
|
|
@ -2,18 +2,20 @@ import os
|
|||
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
from ayon_core.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateContentsOrder,
|
||||
PublishValidationError,
|
||||
OptionalPyblishPluginMixin
|
||||
)
|
||||
from ayon_core.hosts.blender.api.render_lib import prepare_rendering
|
||||
from ayon_blender.api import plugin
|
||||
from ayon_blender.api.render_lib import prepare_rendering
|
||||
|
||||
|
||||
class ValidateDeadlinePublish(pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
class ValidateDeadlinePublish(
|
||||
plugin.BlenderInstancePlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Validates Render File Directory is
|
||||
not the same in every submission
|
||||
"""
|
||||
|
|
@ -6,6 +6,7 @@ from ayon_core.pipeline.publish import (
|
|||
OptionalPyblishPluginMixin,
|
||||
PublishValidationError
|
||||
)
|
||||
from ayon_blender.api import plugin
|
||||
|
||||
|
||||
class SaveWorkfileAction(pyblish.api.Action):
|
||||
|
|
@ -18,8 +19,10 @@ class SaveWorkfileAction(pyblish.api.Action):
|
|||
bpy.ops.wm.avalon_workfiles()
|
||||
|
||||
|
||||
class ValidateFileSaved(pyblish.api.ContextPlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
class ValidateFileSaved(
|
||||
plugin.BlenderContextPlugin,
|
||||
OptionalPyblishPluginMixin
|
||||
):
|
||||
"""Validate that the workfile has been saved."""
|
||||
|
||||
order = pyblish.api.ValidatorOrder - 0.01
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue