Merge branch 'develop' into enhancement/AY-5539_define-creators-per-task
|
|
@ -8,7 +8,6 @@ import inspect
|
|||
import logging
|
||||
import threading
|
||||
import collections
|
||||
|
||||
from uuid import uuid4
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
|
@ -50,10 +49,15 @@ IGNORED_MODULES_IN_AYON = set()
|
|||
# When addon was moved from ayon-core codebase
|
||||
# - this is used to log the missing addon
|
||||
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),
|
||||
|
|
@ -61,7 +65,9 @@ MOVED_ADDON_MILESTONE_VERSIONS = {
|
|||
"maya": VersionInfo(0, 2, 0),
|
||||
"nuke": VersionInfo(0, 2, 0),
|
||||
"resolve": VersionInfo(0, 2, 0),
|
||||
"royalrender": VersionInfo(0, 2, 0),
|
||||
"substancepainter": VersionInfo(0, 2, 0),
|
||||
"houdini": VersionInfo(0, 3, 0),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -550,6 +556,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
|
||||
|
||||
|
|
@ -580,6 +589,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.
|
||||
|
||||
|
|
@ -695,6 +724,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.
|
||||
|
||||
|
|
@ -871,10 +924,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
|
||||
|
|
@ -886,6 +935,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:
|
||||
|
|
@ -1174,39 +1230,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
|
|
@ -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,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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
Adobe webserver
|
||||
---------------
|
||||
Aiohttp (Asyncio) based websocket server used for communication with host
|
||||
applications, currently only for Adobe (but could be used for any non python
|
||||
DCC which has websocket client).
|
||||
|
||||
This webserver is started in spawned Python process that opens DCC during
|
||||
its launch, waits for connection from DCC and handles communication going
|
||||
forward. Server is closed before Python process is killed.
|
||||
|
||||
(Different from `ayon_core/modules/webserver` as that one is running in Tray,
|
||||
this one is running in spawn Python process.)
|
||||
|
|
@ -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
|
|
@ -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,12 +79,12 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|||
|
||||
exclude = [
|
||||
"client/ayon_core/hosts/unreal/integration/*",
|
||||
"client/ayon_core/hosts/aftereffects/api/extension/js/libs/*",
|
||||
"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"
|
||||
"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/*"
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -147,6 +147,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,
|
||||
|
|
@ -297,6 +306,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"
|
||||
|
|
@ -316,6 +329,9 @@ class GlobalToolsModel(BaseSettingsModel):
|
|||
|
||||
|
||||
DEFAULT_TOOLS_VALUES = {
|
||||
"ayon_menu": {
|
||||
"version_up_current_workfile": False
|
||||
},
|
||||
"creator": {
|
||||
"product_types_smart_select": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from .version import __version__
|
||||
from .addon import (
|
||||
AFTEREFFECTS_ADDON_ROOT,
|
||||
AfterEffectsAddon,
|
||||
|
|
@ -6,6 +7,8 @@ from .addon import (
|
|||
|
||||
|
||||
__all__ = (
|
||||
"__version__",
|
||||
|
||||
"AFTEREFFECTS_ADDON_ROOT",
|
||||
"AfterEffectsAddon",
|
||||
"get_launch_script_path",
|
||||
|
|
@ -2,11 +2,14 @@ import os
|
|||
|
||||
from ayon_core.addon import AYONAddon, IHostAddon
|
||||
|
||||
from .version import __version__
|
||||
|
||||
AFTEREFFECTS_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class AfterEffectsAddon(AYONAddon, IHostAddon):
|
||||
name = "aftereffects"
|
||||
version = __version__
|
||||
host_name = "aftereffects"
|
||||
|
||||
def add_implementation_envs(self, env, _app):
|
||||
|
|
@ -25,7 +25,7 @@ download [Anastasiy’s Extension Manager](https://install.anastasiy.com/)
|
|||
The easiest way to get the server and After Effects launch is with:
|
||||
|
||||
```
|
||||
python -c ^"import ayon_core.hosts.photoshop;ayon_core.hosts.aftereffects.launch(""c:\Program Files\Adobe\Adobe After Effects 2020\Support Files\AfterFX.exe"")^"
|
||||
python -c ^"import ayon_core.hosts.photoshop;ayon_aftereffects.launch(""c:\Program Files\Adobe\Adobe After Effects 2020\Support Files\AfterFX.exe"")^"
|
||||
```
|
||||
|
||||
`avalon.aftereffects.launch` launches the application and server, and also closes the server when After Effects exists.
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
|
@ -18,8 +18,8 @@ from ayon_core.lib import Logger, is_in_tests
|
|||
from ayon_core.pipeline import install_host
|
||||
from ayon_core.addon import AddonsManager
|
||||
from ayon_core.tools.utils import host_tools, get_ayon_qt_app
|
||||
from ayon_core.tools.adobe_webserver.app import WebServerTool
|
||||
|
||||
from .webserver import WebServerTool
|
||||
from .ws_stub import get_stub
|
||||
from .lib import set_settings
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ def main(*subprocess_args):
|
|||
"""Main entrypoint to AE launching, called from pre hook."""
|
||||
sys.excepthook = safe_excepthook
|
||||
|
||||
from ayon_core.hosts.aftereffects.api import AfterEffectsHost
|
||||
from ayon_aftereffects.api import AfterEffectsHost
|
||||
|
||||
host = AfterEffectsHost()
|
||||
install_host(host)
|
||||
|
|
@ -355,7 +355,7 @@ class AfterEffectsRoute(WebSocketRoute):
|
|||
return "nothing"
|
||||
|
||||
def create_placeholder_route(self):
|
||||
from ayon_core.hosts.aftereffects.api.workfile_template_builder import \
|
||||
from ayon_aftereffects.api.workfile_template_builder import \
|
||||
create_placeholder
|
||||
partial_method = functools.partial(create_placeholder)
|
||||
|
||||
|
|
@ -365,7 +365,7 @@ class AfterEffectsRoute(WebSocketRoute):
|
|||
return "nothing"
|
||||
|
||||
def update_placeholder_route(self):
|
||||
from ayon_core.hosts.aftereffects.api.workfile_template_builder import \
|
||||
from ayon_aftereffects.api.workfile_template_builder import \
|
||||
update_placeholder
|
||||
partial_method = functools.partial(update_placeholder)
|
||||
|
||||
|
|
@ -375,7 +375,7 @@ class AfterEffectsRoute(WebSocketRoute):
|
|||
return "nothing"
|
||||
|
||||
def build_workfile_template_route(self):
|
||||
from ayon_core.hosts.aftereffects.api.workfile_template_builder import \
|
||||
from ayon_aftereffects.api.workfile_template_builder import \
|
||||
build_workfile_template
|
||||
partial_method = functools.partial(build_workfile_template)
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ workfile or others.
|
|||
import os
|
||||
import sys
|
||||
|
||||
from ayon_core.hosts.aftereffects.api.launch_logic import main as host_main
|
||||
from ayon_aftereffects.api.launch_logic import main as host_main
|
||||
|
||||
# Get current file to locate start point of sys.argv
|
||||
CURRENT_FILE = os.path.abspath(__file__)
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
|
@ -14,8 +14,6 @@ from ayon_core.pipeline import (
|
|||
AYON_INSTANCE_ID,
|
||||
)
|
||||
from ayon_core.pipeline.load import any_outdated_containers
|
||||
import ayon_core.hosts.aftereffects
|
||||
|
||||
from ayon_core.host import (
|
||||
HostBase,
|
||||
IWorkfileHost,
|
||||
|
|
@ -23,6 +21,7 @@ from ayon_core.host import (
|
|||
IPublishHost
|
||||
)
|
||||
from ayon_core.tools.utils import get_ayon_qt_app
|
||||
from ayon_aftereffects import AFTEREFFECTS_ADDON_ROOT
|
||||
|
||||
from .launch_logic import get_stub
|
||||
from .ws_stub import ConnectionNotEstablishedYet
|
||||
|
|
@ -30,10 +29,7 @@ from .ws_stub import ConnectionNotEstablishedYet
|
|||
log = Logger.get_logger(__name__)
|
||||
|
||||
|
||||
HOST_DIR = os.path.dirname(
|
||||
os.path.abspath(ayon_core.hosts.aftereffects.__file__)
|
||||
)
|
||||
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
|
||||
PLUGINS_DIR = os.path.join(AFTEREFFECTS_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")
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
"""This Webserver tool is python 3 specific.
|
||||
"""Webserver for communication with AfterEffects.
|
||||
|
||||
Don't import directly to avalon.tools or implementation of Python 2 hosts
|
||||
would break.
|
||||
Aiohttp (Asyncio) based websocket server used for communication with host
|
||||
application.
|
||||
|
||||
This webserver is started in spawned Python process that opens DCC during
|
||||
its launch, waits for connection from DCC and handles communication going
|
||||
forward. Server is closed before Python process is killed.
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
|
|
@ -12,9 +16,7 @@ import socket
|
|||
|
||||
from aiohttp import web
|
||||
|
||||
from wsrpc_aiohttp import (
|
||||
WSRPCClient
|
||||
)
|
||||
from wsrpc_aiohttp import WSRPCClient
|
||||
|
||||
from ayon_core.pipeline import get_global_context
|
||||
|
||||
|
|
@ -12,7 +12,7 @@ from ayon_core.pipeline.workfile.workfile_template_builder import (
|
|||
PlaceholderPlugin,
|
||||
PlaceholderItem
|
||||
)
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
PLACEHOLDER_SET = "PLACEHOLDERS_SET"
|
||||
PLACEHOLDER_ID = "openpype.placeholder"
|
||||
|
|
@ -8,7 +8,8 @@ import logging
|
|||
import attr
|
||||
|
||||
from wsrpc_aiohttp import WebSocketAsync
|
||||
from ayon_core.tools.adobe_webserver.app import WebServerTool
|
||||
|
||||
from .webserver import WebServerTool
|
||||
|
||||
|
||||
class ConnectionNotEstablishedYet(Exception):
|
||||
|
|
@ -7,7 +7,7 @@ from ayon_core.lib import (
|
|||
is_using_ayon_console,
|
||||
)
|
||||
from ayon_applications import PreLaunchHook, LaunchTypes
|
||||
from ayon_core.hosts.aftereffects import get_launch_script_path
|
||||
from ayon_aftereffects import get_launch_script_path
|
||||
|
||||
|
||||
def get_launch_kwargs(kwargs):
|
||||
|
|
@ -2,16 +2,16 @@ import re
|
|||
|
||||
from ayon_core import resources
|
||||
from ayon_core.lib import BoolDef, UISeparatorDef
|
||||
from ayon_core.hosts.aftereffects import api
|
||||
from ayon_core.pipeline import (
|
||||
Creator,
|
||||
CreatedInstance,
|
||||
CreatorError
|
||||
)
|
||||
from ayon_core.hosts.aftereffects.api.pipeline import cache_and_get_instances
|
||||
from ayon_core.hosts.aftereffects.api.lib import set_settings
|
||||
from ayon_core.lib import prepare_template_data
|
||||
from ayon_core.pipeline.create import PRODUCT_NAME_ALLOWED_SYMBOLS
|
||||
from ayon_aftereffects import api
|
||||
from ayon_aftereffects.api.pipeline import cache_and_get_instances
|
||||
from ayon_aftereffects.api.lib import set_settings
|
||||
|
||||
|
||||
class RenderCreator(Creator):
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import ayon_api
|
||||
|
||||
import ayon_core.hosts.aftereffects.api as api
|
||||
from ayon_core.pipeline import (
|
||||
AutoCreator,
|
||||
CreatedInstance
|
||||
)
|
||||
from ayon_core.hosts.aftereffects.api.pipeline import cache_and_get_instances
|
||||
from ayon_aftereffects import api
|
||||
from ayon_aftereffects.api.pipeline import cache_and_get_instances
|
||||
|
||||
|
||||
class AEWorkfileCreator(AutoCreator):
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import re
|
||||
|
||||
from ayon_core.pipeline import get_representation_path
|
||||
from ayon_core.hosts.aftereffects import api
|
||||
|
||||
from ayon_core.hosts.aftereffects.api.lib import (
|
||||
from ayon_aftereffects import api
|
||||
from ayon_aftereffects.api.lib import (
|
||||
get_background_layers,
|
||||
get_unique_layer_name,
|
||||
)
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import re
|
||||
|
||||
from ayon_core.pipeline import get_representation_path
|
||||
from ayon_core.hosts.aftereffects import api
|
||||
from ayon_core.hosts.aftereffects.api.lib import get_unique_layer_name
|
||||
from ayon_aftereffects import api
|
||||
from ayon_aftereffects.api.lib import get_unique_layer_name
|
||||
|
||||
|
||||
class FileLoader(api.AfterEffectsLoader):
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import pyblish.api
|
||||
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
|
||||
class AddPublishHighlight(pyblish.api.InstancePlugin):
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
"""Close AE after publish. For Webpublishing only."""
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
|
||||
class CloseAE(pyblish.api.ContextPlugin):
|
||||
|
|
@ -2,7 +2,7 @@ import os
|
|||
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
|
||||
class CollectAudio(pyblish.api.ContextPlugin):
|
||||
|
|
@ -2,7 +2,7 @@ import os
|
|||
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
|
||||
class CollectCurrentFile(pyblish.api.ContextPlugin):
|
||||
|
|
@ -2,7 +2,7 @@ import os
|
|||
import re
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.hosts.aftereffects.api import (
|
||||
from ayon_aftereffects.api import (
|
||||
get_stub,
|
||||
get_extension_manifest_path
|
||||
)
|
||||
|
|
@ -6,8 +6,8 @@ import pyblish.api
|
|||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.pipeline.publish import RenderInstance
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
@attr.s
|
||||
class AERenderInstance(RenderInstance):
|
||||
|
|
@ -14,6 +14,7 @@ class CollectReview(pyblish.api.ContextPlugin):
|
|||
label = "Collect Review"
|
||||
hosts = ["aftereffects"]
|
||||
order = pyblish.api.CollectorOrder + 0.1
|
||||
settings_category = "aftereffects"
|
||||
|
||||
def process(self, context):
|
||||
for instance in context:
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import os
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
class ExtractLocalRender(publish.Extractor):
|
||||
"""Render RenderQueue locally."""
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import pyblish.api
|
||||
|
||||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
|
||||
class ExtractSaveScene(pyblish.api.ContextPlugin):
|
||||
|
|
@ -2,7 +2,7 @@ import pyblish.api
|
|||
from ayon_core.lib import version_up
|
||||
from ayon_core.pipeline.publish import get_errored_plugins_from_context
|
||||
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
|
||||
class IncrementWorkfile(pyblish.api.InstancePlugin):
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from ayon_core.pipeline import publish
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
|
||||
class RemovePublishHighlight(publish.Extractor):
|
||||
|
|
@ -9,7 +9,7 @@ import pyblish.api
|
|||
from ayon_core.pipeline import (
|
||||
PublishXmlValidationError
|
||||
)
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
|
||||
class ValidateFootageItems(pyblish.api.InstancePlugin):
|
||||
|
|
@ -5,7 +5,7 @@ from ayon_core.pipeline.publish import (
|
|||
ValidateContentsOrder,
|
||||
PublishXmlValidationError,
|
||||
)
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
from ayon_aftereffects.api import get_stub
|
||||
|
||||
|
||||
class ValidateInstanceFolderRepair(pyblish.api.Action):
|
||||
|
|
@ -13,7 +13,7 @@ from ayon_core.pipeline import (
|
|||
PublishXmlValidationError,
|
||||
OptionalPyblishPluginMixin
|
||||
)
|
||||
from ayon_core.hosts.aftereffects.api import get_folder_settings
|
||||
from ayon_aftereffects.api import get_folder_settings
|
||||
|
||||
|
||||
class ValidateSceneSettings(OptionalPyblishPluginMixin,
|
||||
|
|
@ -60,6 +60,7 @@ class ValidateSceneSettings(OptionalPyblishPluginMixin,
|
|||
label = "Validate Scene Settings"
|
||||
families = ["render.farm", "render.local", "render"]
|
||||
hosts = ["aftereffects"]
|
||||
settings_category = "aftereffects"
|
||||
optional = True
|
||||
|
||||
skip_timelines_check = [".*"] # * >> skip for all
|
||||
|
|
@ -2,9 +2,11 @@ from ayon_core.pipeline.workfile.workfile_template_builder import (
|
|||
CreatePlaceholderItem,
|
||||
PlaceholderCreateMixin
|
||||
)
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
from ayon_core.hosts.aftereffects.api.lib import set_settings
|
||||
import ayon_core.hosts.aftereffects.api.workfile_template_builder as wtb
|
||||
from ayon_aftereffects.api import (
|
||||
get_stub,
|
||||
workfile_template_builder as wtb,
|
||||
)
|
||||
from ayon_aftereffects.api.lib import set_settings
|
||||
|
||||
|
||||
class AEPlaceholderCreatePlugin(wtb.AEPlaceholderPlugin,
|
||||
|
|
@ -2,8 +2,10 @@ from ayon_core.pipeline.workfile.workfile_template_builder import (
|
|||
LoadPlaceholderItem,
|
||||
PlaceholderLoadMixin
|
||||
)
|
||||
from ayon_core.hosts.aftereffects.api import get_stub
|
||||
import ayon_core.hosts.aftereffects.api.workfile_template_builder as wtb
|
||||
from ayon_aftereffects.api import (
|
||||
get_stub,
|
||||
workfile_template_builder as wtb,
|
||||
)
|
||||
|
||||
|
||||
class AEPlaceholderLoadPlugin(wtb.AEPlaceholderPlugin, PlaceholderLoadMixin):
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring AYON addon 'aftereffects' version."""
|
||||
__version__ = "0.2.0"
|
||||
6
server_addon/aftereffects/client/pyproject.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[project]
|
||||
name="aftereffects"
|
||||
description="AYON AfterEffects addon."
|
||||
|
||||
[ayon.runtimeDependencies]
|
||||
wsrpc_aiohttp = "^3.1.1" # websocket server
|
||||
|
|
@ -1,3 +1,10 @@
|
|||
name = "aftereffects"
|
||||
title = "AfterEffects"
|
||||
version = "0.1.4"
|
||||
version = "0.2.0"
|
||||
|
||||
client_dir = "ayon_aftereffects"
|
||||
|
||||
ayon_required_addons = {
|
||||
"core": ">0.3.2",
|
||||
}
|
||||
ayon_compatible_addons = {}
|
||||
|
|
|
|||