Skip to content

pre_launch_open_panel

OpenPanelOnFirstLaunch

Bases: PreLaunchHook

Deploy a JSX to AE's Scripts/Startup folder to auto-open the panel.

The deployed JSX polls until CEP registers the AYON menu command, then waits to see if the workspace restores the panel automatically. If it does not within 5 seconds, it calls executeCommand to open it.

Source code in client/ayon_aftereffects/hooks/pre_launch_open_panel.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
class OpenPanelOnFirstLaunch(PreLaunchHook):
    """Deploy a JSX to AE's Scripts/Startup folder to auto-open the panel.

    The deployed JSX polls until CEP registers the AYON menu command,
    then waits to see if the workspace restores the panel automatically.
    If it does not within 5 seconds, it calls executeCommand to open it.
    """

    app_groups = {"aftereffects"}
    order = 15
    launch_types = {LaunchTypes.local}

    def execute(self):
        # The panel (JavaScript) cannot read AYON settings directly, so the
        # core "Version Up Workfile" menu setting is passed through an env var
        # that the panel reads via ExtendScript '$.getenv'.
        version_up_enabled = bool(
            self.data["project_settings"]
            .get("core", {}).get("tools", {})
            .get("ayon_menu", {}).get("version_up_current_workfile")
        )
        self.launch_context.env["AYON_VERSION_UP_WORKFILE"] = (
            "true" if version_up_enabled else "false"
        )

        try:
            settings = self.data["project_settings"]["aftereffects"]
            enabled = bool(settings["auto_open_panel"])
            self.launch_context.env["AYON_AUTO_OPEN_PANEL"] = (
                "true" if enabled else "false"
            )
            if enabled:
                self._inner_execute()
        except Exception:
            self.log.warning(
                "Could not deploy panel startup script.",
                exc_info=True,
            )

    def _inner_execute(self):
        # launch_args[0] is still the AE executable at order=15;
        # AEPrelaunchHook (order=20) pops it later.
        exe_path = str(self.launch_context.launch_args[0])
        startup_dirs = self._get_startup_dirs(exe_path)
        script_name = "open_ayon_panel.jsx"

        source = (
            Path(AFTEREFFECTS_ADDON_ROOT) /
            "api" /
            "extension" /
            "jsx" /
            script_name
        )

        script_content = source.read_text(encoding="utf-8")
        for startup_dir in startup_dirs:
            startup_dir.mkdir(parents=True, exist_ok=True)
            target = startup_dir / script_name
            target.write_text(script_content, encoding="utf-8")
            self.log.info("Panel startup script deployed to: %s", target)

    def _get_startup_dirs(self, exe_path: str) -> "list[Path]":
        """Return user-level Scripts/Startup paths for the targeted AE.

        Extracts the release year from the executable path (e.g. 2025),
        converts it to the internal major version (25), then globs the
        user-level Adobe preferences directory for matching version
        folders (e.g. 25.6).  Returns an empty list when no matching
        folder exists yet (first-ever AE launch before prefs are
        created) or on unsupported platforms.
        """
        match = re.search(r"After Effects (\d{4})", exe_path)
        if not match:
            return []

        major = int(match.group(1)) - 2000  # 2025 → 25

        system = platform.system().lower()
        if system == "darwin":
            base = (
                Path.home() / 'Library/Preferences/Adobe/After Effects'
            )
        elif system == "windows":
            appdata = os.environ.get("APPDATA", "")
            if not appdata:
                return []
            base = Path(appdata) / 'Adobe/After Effects'
        else:
            return []

        matching = list(base.glob(f"{major}.*"))
        return [d / "Scripts/Startup" for d in matching]