### Bash Script for AYON Addon Development Setup Source: https://context7.com/ynput/ayon-example-addon/llms.txt A bash script outlining the steps to clone and set up the AYON example addon for local development. It involves navigating to the AYON server's addon directory, creating a new directory for the addon, and then cloning the addon's repository into a 'dev' subfolder within that directory. This structure facilitates local modifications and testing. ```bash # Clone addon to AYON server addons directory cd /server/addons/ mkdir example cd example git clone https://github.com/ynput/ayon-example-addon dev # Directory structure: # addons/example/ # ├── 1.0.0/ # Production version ``` -------------------------------- ### Dockerfile for AYON Service Deployment Source: https://context7.com/ynput/ayon-example-addon/llms.txt A Dockerfile used for building a containerized AYON background service. It utilizes a Python 3.11 Alpine image, installs Poetry for dependency management, copies project files, installs dependencies using Poetry, and copies the service code. It also includes a volume for development purposes and sets the entry point to run the service. ```dockerfile # services/Dockerfile FROM python:3.11-alpine3.18 # Install Poetry RUN pip install poetry # Copy dependency definitions COPY pyproject.toml poetry.lock ./ RUN poetry config virtualenvs.create false \ && poetry install --no-interaction --no-ansi # Copy service code COPY service /service/service # Volume for development (mount host code) VOLUME /service/service # Entry point CMD ["python", "-m", "service"] ``` -------------------------------- ### Build and Run Microservice with Make Source: https://context7.com/ynput/ayon-example-addon/llms.txt Builds the Docker image for the microservice and then runs it in development mode with live reload. These commands are executed from the 'services' directory. ```bash cd services make build # Build Docker image: ynput/ayon-example-service make dev # Run with live reload and host network ``` -------------------------------- ### View Microservice Logs with Docker Source: https://context7.com/ynput/ayon-example-addon/llms.txt Follows the logs of the running AYON microservice container. This command is essential for debugging and monitoring the microservice's activity. ```bash docker logs -f ayon-example-service ``` -------------------------------- ### Poetry Configuration for AYON Example Service Source: https://context7.com/ynput/ayon-example-addon/llms.txt This TOML file configures the project using Poetry, specifying the package name, version, and a brief description for the AYON example background service. It lists Python 3.11 as the required version and includes dependencies such as 'nxtools' for logging utilities and 'ayon-python-api' for interacting with the AYON API. ```toml # services/pyproject.toml [tool.poetry] name = "ayon-example-service" version = "2.0.0" description = "AYON example background service" [tool.poetry.dependencies] python = "^3.11" nxtools = "^1.6" # Logging utilities ayon-python-api = "1.0.1" # AYON API client ``` -------------------------------- ### Build Production Package with Python Source: https://context7.com/ynput/ayon-example-addon/llms.txt Builds a production-ready package for the AYON addon. This command is executed from the 'dev' directory and outputs the package to a specified directory. ```bash cd dev python create_package.py -o /tmp/packages ``` -------------------------------- ### Clone Ayon Example Addon Repository (Bash) Source: https://github.com/ynput/ayon-example-addon/blob/develop/README.md This snippet demonstrates how to clone the Ayon example addon repository into the addons directory for development purposes. It involves creating a new directory for the addon and then cloning the rez-addon repository into a 'dev' subfolder. ```bash cd addons/ mkdir example cd example git clone https://github.com/ynput/ayon-example-rez-addon dev ``` -------------------------------- ### Implement REST API Endpoint for Random Folder (Python) Source: https://context7.com/ynput/ayon-example-addon/llms.txt Defines a GET endpoint '/addons/example/get-random-folder/{project_name}' that retrieves a random folder from the project database. It includes user authorization, permission filtering, and handles potential database or entity not found errors. The response is tailored to the user's permission level. ```Python # server/__init__.py from ayon_server.addons import BaseServerAddon from ayon_server.api.dependencies import CurrentUser, ProjectName from ayon_server.entities import FolderEntity from ayon_server.lib.postgres import Postgres from ayon_server.exceptions import NotFoundException class ExampleAddon(BaseServerAddon): def initialize(self): self.add_endpoint( "get-random-folder/{project_name}", self.get_random_folder, method="GET", ) async def get_random_folder( self, user: CurrentUser, project_name: ProjectName, ): """Return a random folder from the database""" settings = await self.get_project_settings(project_name) # Query random folder by type try: result = await Postgres.fetch( f""" SELECT id FROM project_{project_name}.folders WHERE folder_type = $1 ORDER BY RANDOM() LIMIT 1 """, settings.folder_type, ) except Postgres.UndefinedTableError: raise NotFoundException(f"Project {project_name} not found") try: folder_id = result[0]["id"] except IndexError: raise NotFoundException("No folder found") # Load entity and enforce read access folder = await FolderEntity.load(project_name, folder_id) await folder.ensure_read_access(user) # Return payload respecting user permission level return folder.as_user(user) # Usage via HTTP: # GET /addons/example/get-random-folder/my_project # Authorization: Bearer # Response: {"id": "...", "name": "...", "folderType": "...", ...} ``` -------------------------------- ### Define Addon Package Metadata (Python) Source: https://context7.com/ynput/ayon-example-addon/llms.txt Specifies the addon's identity, version, and associated microservices. This file acts as a single source of truth for addon configuration and deployment. ```Python # package.py name = "example" title = "Example" version = "2.1.0" services = { "ExampleService": {"image": "ynput/ayon-example-service"}, } plugin_for = ["ayon_server"] build_command = "" ``` -------------------------------- ### REST API: Get Random Folder Source: https://context7.com/ynput/ayon-example-addon/llms.txt Retrieves a random folder from the project database, applying user access control and permission filtering. This endpoint demonstrates how to define custom RESTful APIs within an AYON addon. ```APIDOC ## GET /addons/example/get-random-folder/{project_name} ### Description Returns a random folder from the project database with proper user access control and permission filtering. ### Method GET ### Endpoint /addons/example/get-random-folder/{project_name} ### Parameters #### Path Parameters - **project_name** (string) - Required - The name of the project to retrieve a folder from. #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response (200) - **id** (string) - The unique identifier of the folder. - **name** (string) - The name of the folder. - **folderType** (string) - The type of the folder. - ... (other folder entity fields) #### Response Example ```json { "id": "a1b2c3d4-e5f6-7890-1234-567890abcdef", "name": "Scene001", "folderType": "Scene", "path": "/Projects/MyProject/Scenes/Scene001", "status": "Approved", "ownerId": "user-id-123", "createdAt": "2023-10-27T10:00:00Z", "updatedAt": "2023-10-27T11:00:00Z" } ``` ``` -------------------------------- ### Python Site Settings Model Source: https://context7.com/ynput/ayon-example-addon/llms.txt Defines a Python settings model for site-specific configurations that are stored locally on an AYON server site. It includes settings like chair orientation with an enum resolver and floor material. ```python # server/site_settings.py from ayon_server.settings import BaseSettingsModel, SettingsField class ExampleSiteSettings(BaseSettingsModel): """Site-specific configuration""" chair_orientation: str = SettingsField( "north", title="Chair orientation", enum_resolver=lambda: ["north", "south", "east", "west"], ) floor_material: str = SettingsField( "wood", title="Floor material", ) # Linked to addon: class ExampleAddon(BaseServerAddon): site_settings_model = ExampleSiteSettings # Accessed via: # site_settings = await addon.get_site_settings() ``` -------------------------------- ### Extract Client Code for Development with Python Source: https://context7.com/ynput/ayon-example-addon/llms.txt Extracts only the client-side code for development purposes. This command is useful for testing or debugging frontend components without building the full package. ```bash python create_package.py --only-client -o ./client_extracted ``` -------------------------------- ### Subscribe to Entity Events and Cache Settings (Python) Source: https://context7.com/ynput/ayon-example-addon/llms.txt Demonstrates subscribing to 'entity.task.status_changed' events and executing custom logic. It also includes a method to cache frequently accessed studio settings for performance and updates this cache when settings change. ```Python # server/__init__.py from ayon_server.events import EventModel, EventStream from nxtools import logging class ExampleAddon(BaseServerAddon): def initialize(self): # Subscribe to task status change events EventStream.subscribe( "entity.task.status_changed", self.on_task_status_changed ) async def get_cached_setting(self) -> str: """Cache frequently accessed settings for performance""" if not hasattr(self, "_cached_setting") or self._cached_setting is None: studio_settings = await self.get_studio_settings() self._cached_setting = studio_settings.grouped_settings.favorite_color return self._cached_setting async def on_settings_changed( self, old_settings: ExampleSettings, new_settings: ExampleSettings, **kwargs, ) -> None: """Update cache when settings change""" new_favorite_color = new_settings.grouped_settings.favorite_color logging.debug(f"New favorite color is {new_favorite_color}") self._cached_setting = new_favorite_color async def on_task_status_changed(self, event: EventModel): """React to task status changes""" favorite_color = await self.get_cached_setting() logging.debug(f"Example addon says, that {event.description}") logging.debug(f"Admin's favorite color is {favorite_color}") # Events are automatically dispatched by AYON server ``` -------------------------------- ### Python Main Settings Model with Diverse Field Types Source: https://context7.com/ynput/ayon-example-addon/llms.txt The primary Python settings model for the AYON example addon, showcasing a wide array of field types including string input, static and dynamic enums, numeric input with validation, textareas, lists of strings, lists of objects, nested models, color fields, and hidden fields. ```python class ExampleSettings(BaseSettingsModel): """Main settings model with all field types""" # String input string_input: str = SettingsField( "default value", title="String input", placeholder="Enter text...", section="Basic Inputs", ) # Enum (static) enum_input: Literal["option1", "option2", "option3"] = SettingsField( "option1", title="Enum input", enum_resolver=lambda: ["option1", "option2", "option3"], section="Basic Inputs", ) # Enum (dynamic from database) project_enum: str = SettingsField( "", title="Select project", enum_resolver=async_enum_resolver, section="Dynamic Enums", ) # Enum (with custom labels) labeled_enum: str = SettingsField( "value0", title="Labeled enum", enum_resolver=enum_resolver, section="Dynamic Enums", ) # Numeric input with validation numeric_input: int = SettingsField( 42, title="Numeric input", gt=0, le=1000, section="Numeric Inputs", ) # Textarea textarea_input: str = SettingsField( "", title="Textarea", widget="textarea", section="Text Inputs", ) # List of strings (multiselect) list_of_strings: list[str] = SettingsField( default_factory=list, title="List of strings", section="Collections", ) # List of objects with validation list_of_objects: list[CompactListSubmodel] = SettingsField( default_factory=list, title="List of objects", section="Collections", ) @validator("list_of_objects") def validate_unique_names(cls, value): """Ensure all names in list are unique""" ensure_unique_names(value) return value # Nested model grouped_settings: GroupedSettings = SettingsField( default_factory=GroupedSettings, title="Grouped settings", section="Advanced", ) # Color fields colors: Colors = SettingsField( default_factory=Colors, title="Colors", section="Advanced", ) # Hidden field (only visible in specific scope) hidden_field: str = SettingsField( "", title="Hidden field", scope=["studio"], # Only visible at studio level section="Advanced", ) # Folder type enum (built-in) folder_type: str = SettingsField( "Folder", title="Folder type", enum_resolver=folder_types_enum, section="Built-in Enums", ) # Settings accessed via: # studio_settings = await addon.get_studio_settings() # project_settings = await addon.get_project_settings("my_project") ``` -------------------------------- ### Python Script for Creating Deployable AYON Addon Packages Source: https://context7.com/ynput/ayon-example-addon/llms.txt A Python script designed to automate the creation of deployable AYON addon packages. It handles copying server code, building and copying frontend assets, zipping client code, and packaging static assets. The script supports options for specifying output directory, skipping zip creation, and extracting only client code. Dependencies include 'zipfile' and 'pathlib'. ```python import zipfile from pathlib import Path def create_server_package( output_dir: Path, addon_name: str, addon_version: str, keep_sources: bool = False, skip_zip: bool = False, ) -> Path: """Create deployable addon package""" package_dir = output_dir / addon_name / addon_version package_dir.mkdir(parents=True, exist_ok=True) # Copy server Python code copy_server_content(addon_name, package_dir) # Build and copy frontend if exists if Path("frontend/package.json").exists(): copy_frontend_content(package_dir) # Zip client code to private/ if Path("client").exists(): zip_client_side(addon_name, addon_version, package_dir) # Copy public/private static assets for dir_name in ["public", "private"]: if Path(dir_name).exists(): safe_copy_tree(Path(dir_name), package_dir / dir_name) # Create final zip package if not skip_zip: zip_path = output_dir / f"{addon_name}-{addon_version}.zip" with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: for file in package_dir.rglob("*"): if file.is_file(): zf.write(file, file.relative_to(package_dir)) if not keep_sources: shutil.rmtree(package_dir) return zip_path return package_dir # Usage: # python create_package.py # Default to ./package/ # python create_package.py -o /server/addons # Custom output # python create_package.py --skip-zip # Keep unzipped # python create_package.py --only-client -o ./dist # Extract client only ``` -------------------------------- ### Python Settings Model for Grouped Settings Source: https://context7.com/ynput/ayon-example-addon/llms.txt Defines a Python settings model intended to be displayed as a collapsed group in the UI. It uses layout and group flags to control its presentation and contains fields for name, quest, and favorite color. ```python class GroupedSettings(BaseSettingsModel): """Settings displayed in collapsed group""" _layout = "expanded" _isGroup = True name: str = SettingsField("", title="Your name") quest: str = SettingsField("", title="Your quest") favorite_color: str = SettingsField("blue", title="Your favorite color") ``` -------------------------------- ### Python Settings Model for Color Fields Source: https://context7.com/ynput/ayon-example-addon/llms.txt A Python settings model defining color fields using predefined types for RGB hex and RGBA float values. It includes default values and customizable titles for each color setting. ```python class Colors(BaseSettingsModel): """Color field examples (default: blue)""" rgb_hex: ColorRGB_hex = SettingsField("#0000ff", title="RGB Hex") rgba_float: ColorRGBA_float = SettingsField((0, 0, 1, 1), title="RGBA Float") ``` -------------------------------- ### Execute AYON Actions on Server (Python) Source: https://context7.com/ynput/ayon-example-addon/llms.txt Handles the execution of actions triggered from the AYON web interface. It differentiates between server-side actions and client-side launcher actions based on the action identifier. For server-side actions, it retrieves entity information and returns a server response. For launcher actions, it prepares arguments for client execution. ```python # server/__init__.py from ayon_server.actions import ActionExecutor, ExecuteResponseModel from ayon_server.helpers.get_entity_class import get_entity_class from ayon_server.exceptions import NotImplementedException class ExampleAddon(BaseServerAddon): async def get_simple_actions( self, project_name: str | None = None, variant: str = "production", ) -> list[SimpleActionManifest]: """Return list of actions to display in UI""" return EXAMPLE_SIMPLE_ACTIONS async def execute_action( self, executor: ActionExecutor, ) -> ExecuteResponseModel: """Execute action based on identifier""" if executor.identifier.startswith("example-"): # Server-side action execution context = executor.context entity_type = context.entity_type entity_id = context.entity_ids[0] entity_class = get_entity_class(entity_type) entity = await entity_class.load(context.project_name, entity_id) return await executor.get_server_action_response( message=f"{executor.identifier} performed on {entity_type} {entity.name}" ) elif executor.identifier.startswith("launch-"): # Launcher action (executed on client) return await executor.get_launcher_action_response( args=["--project", context.project_name, "--task", entity_id] ) raise NotImplementedException(f"Not implemented action: {executor.identifier}") # User clicks action in browser -> POST to /api/actions/execute # Request: {"identifier": "example-folder-action-1", "context": {...}} # Response: {"success": true, "message": "Action completed"} ``` -------------------------------- ### Event Subscription: Task Status Change Source: https://context7.com/ynput/ayon-example-addon/llms.txt Subscribes to 'entity.task.status_changed' events and logs custom messages along with the user's favorite color setting. This showcases how to integrate with AYON's event system. ```APIDOC ## Event Subscription: Task Status Change ### Description Subscribes to server events, specifically when a task's status changes. It then logs information about the event and a studio-level setting. ### Method Event Subscription (Internal) ### Endpoint Not applicable (triggered by server events) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response (Internal) Logs are generated on the server console. #### Response Example ``` INFO: Example addon says, that Task "CharacterRig" in project "MyProject" has status changed from "InProgress" to "Done". DEBUG: Admin's favorite color is Blue ``` ``` -------------------------------- ### Python Settings Model for Compact List Submodel with Validation Source: https://context7.com/ynput/ayon-example-addon/llms.txt A Python settings model for items within a list that should have a compact layout. It includes a 'name' field with a validator to ensure it's lowercase alphanumeric and a 'value' field with range constraints. ```python class CompactListSubmodel(BaseSettingsModel): """List item with compact layout""" _layout = "compact" name: str = SettingsField("", title="Name") value: int = SettingsField(0, title="Value", ge=0, le=100) @validator("name") def validate_name(cls, value): """Normalize name to lowercase alphanumeric""" return normalize_name(value) ``` -------------------------------- ### Define AYON Settings Model with Field Types (Python) Source: https://context7.com/ynput/ayon-example-addon/llms.txt Defines a comprehensive settings model for an AYON addon, demonstrating various field types and validators available through Pydantic and AYON's settings utilities. It includes dynamic enum resolvers for fetching data from the database, showcasing advanced configuration capabilities. ```python # server/settings.py from pydantic import validator from ayon_server.settings import BaseSettingsModel, SettingsField, ensure_unique_names from ayon_server.settings.enum import folder_types_enum, anatomy_presets_enum from ayon_server.types import ColorRGB_hex, ColorRGBA_float from ayon_server.lib.postgres import Postgres # Dynamic enum resolver (async) async def async_enum_resolver() -> list[str]: """Return list of project names from database""" return [row["name"] async for row in Postgres.iterate("SELECT name FROM projects")] ``` -------------------------------- ### Python Service for AYON Event Processing Source: https://context7.com/ynput/ayon-example-addon/llms.txt Implements a background service that subscribes to AYON events, specifically folder status changes filtered for 'Approved'. It enrolls an event job, updates its status to 'in_progress', simulates work, and then updates the status to 'finished' with a descriptive message. This service requires the 'ayon-python-api' library. ```python import socket import sys import time import ayon_api SENDER = f"example-service-{socket.gethostname()}" def process_event(): """Enroll and process folder approval events""" # Subscribe to folder status changes filtered for "Approved" job = ayon_api.enroll_event_job( source_topic="entity.folder.status_changed", target_topic="example.approval_handler", sender=SENDER, description="Approved folder detected. Thinking…", max_retries=3, events_filter={ "conditions": [ { "key": "payload/newValue", "value": "Approved", } ] } ) if not job: time.sleep(5) # No pending events return # Get source event details src_job = ayon_api.get_event(job["dependsOn"]) ayon_project_name = src_job["project"] # Update status: in_progress ayon_api.update_event( job["id"], sender=SENDER, status="in_progress", project_name=ayon_project_name, description="Stand by. I am pretending to do something…", ) # Simulate work time.sleep(2) # Update status: finished with notification ayon_api.update_event( job["id"], sender=SENDER, status="finished", project_name=ayon_project_name, description=f"Good job {src_job['user']}! Your folder has been approved.", ) def main(): """Continuous event processing loop""" while True: process_event() if __name__ == "__main__": try: ayon_api.init_service() connected = True except Exception: sys.exit(1) main() # Service configuration (services/.env): # AYON_SERVER_URL=http://localhost:5000 # AYON_API_KEY=veryinsecurapikey # Deployment (services/Makefile): # make build # Build Docker image # make dev # Run with live reload ``` -------------------------------- ### Define Browser Actions for AYON Entities (Python) Source: https://context7.com/ynput/ayon-example-addon/llms.txt Defines various UI actions that appear in the AYON web interface for different entity types, such as folder context menus and application launchers. It utilizes SimpleActionManifest to configure identifiers, labels, categories, icons, entity types, and multi-selection allowances. This code is intended for server-side definition of client-executable actions. ```python # server/actions.py from ayon_server.actions import SimpleActionManifest EXAMPLE_SIMPLE_ACTIONS = [ # Folder context menu action SimpleActionManifest( identifier="example-folder-action-1", label="Example folder action 1", category="server", order=100, icon={"type": "material-symbols", "name": "folder"}, entity_type="folder", entity_subtypes=None, allow_multiselection=False, ), # Admin action with delete icon SimpleActionManifest( identifier="example-folder-action-2", label="Example folder action 2", category="admin", order=100, icon={"type": "material-symbols", "name": "delete"}, entity_type="folder", entity_subtypes=None, allow_multiselection=False, ), # Application launcher for Nuke (task-specific) SimpleActionManifest( identifier="launch-nuke", label="Launch Nuke", category="application", order=100, icon={"type": "url", "url": "{addon_url}/public/icons/nuke.png"}, entity_type="task", entity_subtypes=["Compositing", "Roto", "Matchmove"], allow_multiselection=True, ), # Maya launcher for multiple task types SimpleActionManifest( identifier="launch-maya", label="Launch Maya", category="application", order=100, icon={"type": "url", "url": "{addon_url}/public/icons/maya.png"}, entity_type="task", entity_subtypes=["FX", "Modeling", "Lighting", "Animation", "Rigging", "Lookdev"], allow_multiselection=False, ) ] ``` -------------------------------- ### Python Enum Resolver with Custom Labels Source: https://context7.com/ynput/ayon-example-addon/llms.txt Defines a Python function to resolve enum values and their corresponding custom labels for use in dropdown menus within the AYON settings UI. This is useful for dynamically generating options with user-friendly text. ```python def enum_resolver() -> list[dict[str, str]]: """Return value/label dicts for dropdown""" return [{"value": f"value{i}", "label": f"Label {i}"} for i in range(10)] ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.