diff --git a/client/ayon_premiere/api/extension.zxp b/client/ayon_premiere/api/extension.zxp index f146f10..e8dca55 100644 Binary files a/client/ayon_premiere/api/extension.zxp and b/client/ayon_premiere/api/extension.zxp differ diff --git a/client/ayon_premiere/api/extension/CSXS/manifest.xml b/client/ayon_premiere/api/extension/CSXS/manifest.xml index 368416a..78c99da 100644 --- a/client/ayon_premiere/api/extension/CSXS/manifest.xml +++ b/client/ayon_premiere/api/extension/CSXS/manifest.xml @@ -5,7 +5,7 @@ ExtensionBundleName="io.ynput.PPRO.panel" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - + diff --git a/client/ayon_premiere/hooks/pre_launch_install_ayon_extension.py b/client/ayon_premiere/hooks/pre_launch_install_ayon_extension.py new file mode 100644 index 0000000..cdbfc18 --- /dev/null +++ b/client/ayon_premiere/hooks/pre_launch_install_ayon_extension.py @@ -0,0 +1,153 @@ +import os +import platform +from zipfile import ZipFile +import xml.etree.ElementTree as ET +from shutil import rmtree + + +from ayon_premiere import PREMIERE_ADDON_ROOT +from ayon_applications import PreLaunchHook, LaunchTypes + + +class InstallAyonExtensionToPremiere(PreLaunchHook): + """ + Automatically 'installs' the AYON Premiere extension. + + Checks if Premiere already has the extension in the relevant folder, + will try to create that folder and unzip the extension if not. + """ + + app_groups = {"premiere"} + + order = 1 + launch_types = {LaunchTypes.local} + + def execute(self): + try: + settings = self.data["project_settings"][self.host_name] + if not settings["hooks"]["InstallAyonExtensionToPremiere"][ + "enabled" + ]: + return + self.inner_execute() + + except Exception: + self.log.warning( + "Processing of {} crashed.".format(self.__class__.__name__), + exc_info=True, + ) + + def inner_execute(self): + self.log.info("Installing AYON Premiere extension.") + + # Windows only for now. + if not platform.system().lower() == "windows": + self.log.info("Non Windows platform. Cancelling..") + return + + target_path = os.path.join( + os.environ["appdata"], r"Adobe\CEP\extensions\io.ynput.PPRO.panel" + ) + + extension_path = os.path.join( + PREMIERE_ADDON_ROOT, + r"api\extension.zxp", + ) + + # Extension already installed, compare the versions + if os.path.exists(target_path): + self.log.info( + f"The extension already exists at: {target_path}. " + f"Comparing versions.." + ) + if not self._compare_extension_versions( + target_path, extension_path + ): + return + + try: + self.log.debug(f"Creating directory: {target_path}") + os.makedirs(target_path, exist_ok=True) + + with ZipFile(extension_path, "r") as archive: + archive.extractall(path=target_path) + self.log.info("Successfully installed AYON extension") + + except OSError as error: + self.log.warning(f"OS error has occured: {error}") + + except PermissionError as error: + self.log.warning(f"Permissions error has occured: {error}") + + except Exception as error: + self.log.warning(f"An unexpected error occured: {error}") + + def _compare_extension_versions( + self, target_path: str, extension_path: str + ) -> bool: + try: + # opens the existing extension manifest to get the Version attr + with open(f"{target_path}/CSXS/manifest.xml", "rb") as xml_file: + installed_version = ( + ET.parse(xml_file) + .getroot() + .attrib.get("ExtensionBundleVersion") + ) + self.log.debug( + f"Current extension version found: {installed_version}" + ) + + if not installed_version: + self.log.warning( + "Unable to resolve the currently installed extension " + "version. Cancelling.." + ) + return False + + # opens the .zxp manifest to get the Version attribute. + with ZipFile(extension_path, "r") as archive: + xml_file = archive.open("CSXS/manifest.xml") + new_version = ( + ET.parse(xml_file) + .getroot() + .attrib.get("ExtensionBundleVersion") + ) + if not new_version: + self.log.warning( + "Unable to resolve the new extension version. " + "Cancelling.." + ) + self.log.debug(f"New extension version found: {new_version}") + + # compare the two versions, a simple == is enough since + # we don't care if the version increments or decrements + # if they match nothing happens. + if installed_version == new_version: + self.log.info("Versions matched. Cancelling..") + return False + + # remove the existing addon + self.log.info( + "Version mismatch found. Removing old extensions.." + ) + rmtree(target_path) + return True + + except PermissionError as error: + self.log.warning( + "Permissions error has occurred while comparing " + f"versions: {error}" + ) + return False + + except OSError as error: + self.log.warning( + f"OS error has occured while comparing versions: {error}" + ) + return False + + except Exception as error: + self.log.warning( + f"An unexpected error occured when comparing version: {error}" + ) + return False diff --git a/server/__init__.py b/server/__init__.py index ce4b0fb..c0500e1 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -2,11 +2,11 @@ from ayon_server.addons import BaseServerAddon -from .settings import MySettings, DEFAULT_VALUES +from .settings import PremiereSettings, DEFAULT_VALUES class MyAddon(BaseServerAddon): - settings_model: Type[MySettings] = MySettings + settings_model: Type[PremiereSettings] = PremiereSettings async def get_default_settings(self): settings_model_cls = self.get_settings_model() diff --git a/server/settings.py b/server/settings.py index fcb6ab5..d7e58e8 100644 --- a/server/settings.py +++ b/server/settings.py @@ -1,7 +1,27 @@ -from ayon_server.settings import BaseSettingsModel +from ayon_server.settings import BaseSettingsModel, SettingsField -DEFAULT_VALUES = {} +class HookOptionalModel(BaseSettingsModel): + enabled: bool = SettingsField(False, title="Enabled") -class MySettings(BaseSettingsModel): - pass + +class HooksModel(BaseSettingsModel): + InstallAyonExtensionToPremiere: HookOptionalModel = SettingsField( + default_factory=HookOptionalModel, + title="Install AYON Extension", + ) + + +class PremiereSettings(BaseSettingsModel): + hooks: HooksModel = SettingsField( + default_factory=HooksModel, title="Hooks" + ) + + +DEFAULT_VALUES = { + "hooks": { + "InstallAyonExtensionToPremiere": { + "enabled": False, + } + } +}