commit 3600c870cd88e04ceffb4f60cd5ae0290f6e2259 Author: Joseph HENRY Date: Thu May 7 17:13:13 2026 +0200 Initial marketplace with flamenco and kitsu plugins Bundles the flamenco-api and kitsu-api skills as Claude Code plugins under an "adm-tools" marketplace. Co-Authored-By: Claude Opus 4.7 (1M context) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..eb24ca1 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,22 @@ +{ + "name": "adm-tools", + "owner": { + "name": "Autour de Minuit", + "email": "adv.bdl.dev@gmail.com" + }, + "description": "Claude Code plugins for the ADM Blender pipeline and production tracking", + "plugins": [ + { + "name": "flamenco", + "source": "./plugins/flamenco", + "description": "Flamenco render farm tools and references", + "version": "1.0.0" + }, + { + "name": "kitsu", + "source": "./plugins/kitsu", + "description": "Kitsu production tracking tools and references", + "version": "1.0.0" + } + ] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5f8f34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +*.swp +*.swo +__pycache__/ +.venv/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..740e7f3 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# ADM Tools — Claude Code Marketplace + +Claude Code plugins for Autour de Minuit's Blender pipeline and production tracking. + +## Plugins + +- **flamenco** — Flamenco render farm tools. Currently bundles the `flamenco-api` skill (API reference, configuration, worker/job management). +- **kitsu** — Kitsu production tracking tools. Currently bundles the `kitsu-api` skill (Zou REST API and gazu SDK reference). + +## Install + +``` +/plugin marketplace add https://git.autourdeminuit.com/autour_de_minuit/claude-plugins.git +/plugin install flamenco@adm-tools +/plugin install kitsu@adm-tools +``` + +Once installed, the skills auto-trigger on relevant prompts. Namespaced names are `flamenco:flamenco-api` and `kitsu:kitsu-api`. + +## Updating + +After a new version is pushed: + +``` +/plugin marketplace update adm-tools +``` + +## Layout + +``` +.claude-plugin/marketplace.json — marketplace catalog +plugins//.claude-plugin/ — per-plugin manifest +plugins//skills// — bundled skill (SKILL.md + references) +``` + +## Releasing changes + +1. Edit the skill content under `plugins//skills//`. +2. Bump the `version` in both `plugins//.claude-plugin/plugin.json` and the matching entry in `.claude-plugin/marketplace.json`. +3. Commit and push. +4. Tell teammates to run `/plugin marketplace update adm-tools`. diff --git a/plugins/flamenco/.claude-plugin/plugin.json b/plugins/flamenco/.claude-plugin/plugin.json new file mode 100644 index 0000000..4010fed --- /dev/null +++ b/plugins/flamenco/.claude-plugin/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "flamenco", + "description": "Flamenco render farm tools and references", + "version": "1.0.0", + "author": { + "name": "Autour de Minuit" + } +} diff --git a/plugins/flamenco/skills/flamenco-api/SKILL.md b/plugins/flamenco/skills/flamenco-api/SKILL.md new file mode 100644 index 0000000..d4cb4ed --- /dev/null +++ b/plugins/flamenco/skills/flamenco-api/SKILL.md @@ -0,0 +1,189 @@ +--- +name: flamenco-api +description: > + Flamenco render farm manager API reference, configuration, and usage guide. + Use this skill whenever working with Flamenco — querying jobs, workers, tasks, or farm status via + the REST API, writing curl commands or Python scripts that talk to the Flamenco Manager, + configuring the manager or workers, troubleshooting render farm issues, managing worker tags + or sleep schedules, creating custom job types, or understanding how Flamenco integrates with + Blender. Also use when the user mentions render farm, farm status, render workers, job submission, + or Flamenco in any context. Trigger on mentions of flamenco, render farm, render worker, + farm status, job queue, render job, or worker management in the context of rendering. +--- + +# Flamenco Render Farm + +[Flamenco](https://flamenco.blender.org/) is the render farm manager developed by the Blender +Foundation. It consists of a **Manager** (HTTP server, web UI, REST API) and one or more +**Workers** that pull jobs from the Manager and execute them — usually Blender renders, but the +job-type system is generic enough for ffmpeg encodes, file copies, or anything else. + +## Setup before answering + +Flamenco is self-hosted, so the Manager URL is site-specific. Before constructing API calls: + +1. Check the user's environment for a Manager URL (env var like `FLAMENCO_MANAGER_URL`, + project config, or `CLAUDE.md`). The Manager web UI and the REST API share the same host; + `/api/v3/` is the API prefix. +2. If you can't find one, ask the user. Don't invent a URL. + +In examples below, `` is a placeholder for the user's Manager root +(e.g. `https://flamenco.example.com` or `http://localhost:8080`). The API base is +`/api/v3/`. + +Useful endpoints on any Manager: + +- Web UI: `/` +- Swagger UI: `/api/v3/swagger-ui/` +- OpenAPI spec: `/api/v3/openapi3.json` + +The API has no authentication by default — Flamenco assumes a trusted LAN. If the user has +fronted it with a reverse proxy that adds auth, account for that. + +## Quick API examples + +```bash +# Farm status (one of: active, idle, waiting, asleep, inoperative, unknown, starting) +curl -s /api/v3/status + +# Manager version +curl -s /api/v3/version + +# List all workers +curl -s /api/v3/worker-mgt/workers + +# List all jobs +curl -s /api/v3/jobs + +# Get a specific job +curl -s /api/v3/jobs/{job_id} + +# Get tasks for a job +curl -s /api/v3/jobs/{job_id}/tasks + +# Get task details (commands, status, assigned worker) +curl -s /api/v3/tasks/{task_id} + +# Tail a task's log as plain text +curl -s /api/v3/tasks/{task_id}/logtail + +# Query jobs by status (note: jobs query is POST, not GET) +curl -s /api/v3/jobs \ + -X POST -H 'Content-Type: application/json' \ + -d '{"status_in": ["failed"], "order_by": ["updated_at"], "limit": 5}' + +# Change worker status (awake, asleep, offline, restart) — is_lazy waits for current task +curl -X POST /api/v3/worker-mgt/workers/{worker_id}/setstatus \ + -H 'Content-Type: application/json' -d '{"status": "awake", "is_lazy": false}' + +# Cancel / pause / requeue a job +curl -X POST /api/v3/jobs/{job_id}/setstatus \ + -H 'Content-Type: application/json' -d '{"status": "canceled", "reason": "no longer needed"}' +``` + +## Key concepts + +- **Job**: A render request, typically submitted from the Blender add-on. Carries settings + (blend file, frame range, output path, format) and is compiled by the Manager into tasks. +- **Task**: An executable unit within a job (e.g. "render frames 1–10"). Assigned to one worker + at a time. A task contains one or more **commands**. +- **Command**: The lowest-level operation a worker runs (e.g. `blender-render`, `move-directory`, + `frames-to-video`). Defined by the worker code; referenced by job-type scripts. +- **Worker**: A machine that polls the Manager for tasks and executes them. Has a status + (awake, asleep, offline, error, …) and a list of supported task types. +- **Tag**: A grouping label for workers. Jobs can be restricted to a tag so they only run on + matching workers (e.g. a `gpu` tag for GPU-only nodes). +- **Job type**: A JavaScript file in the Manager's `scripts/` directory that defines settings + and the compile function (turns a submitted job into a sequence of tasks). Built-in types + like `simple-blender-render` cover the common cases; custom types add new behaviors. +- **Shaman**: Optional content-addressed file store. When enabled, files are uploaded by + SHA256+size and symlinked into per-job checkout dirs. When disabled, files are referenced + in place on shared storage. + +## Status enums + +- **Job**: `active`, `canceled`, `completed`, `failed`, `paused`, `pause-requested`, `queued`, + `cancel-requested`, `requeueing`, `under-construction` +- **Task**: `active`, `canceled`, `completed`, `failed`, `queued`, `soft-failed`, `paused` +- **Worker**: `starting`, `awake`, `asleep`, `error`, `testing`, `offline`, `restart` +- **Farm**: `active`, `idle`, `waiting`, `asleep`, `inoperative`, `unknown`, `starting` + +## Python SDK + +Flamenco ships an auto-generated Python client (`flamenco-manager` on PyPI, importable as +`flamenco.manager`): + +```python +from flamenco.manager import ApiClient, Configuration +from flamenco.manager.api import JobsApi, WorkerMgtApi, MetaApi + +configuration = Configuration(host="") +api_client = ApiClient(configuration) + +meta_api = MetaApi(api_client) +print(meta_api.get_version()) # FlamencoVersion + +jobs_api = JobsApi(api_client) +jobs = jobs_api.fetch_jobs() +failed = [j for j in jobs.jobs if j.status == "failed"] + +worker_api = WorkerMgtApi(api_client) +for w in worker_api.fetch_workers().workers: + print(f"{w.name}: {w.status}") +``` + +For ad-hoc scripts, plain `requests` against the REST API is often simpler and avoids the SDK +dependency. + +## Submitting a job + +Two endpoints take the same body — `POST /api/v3/jobs/check` validates without inserting, +`POST /api/v3/jobs` actually submits. Always send `submitter_platform` (`linux`, `windows`, +`darwin`, or `manager`) so the Manager can apply path replacements. + +```bash +curl -X POST /api/v3/jobs \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "Test render", + "type": "simple-blender-render", + "priority": 50, + "submitter_platform": "linux", + "settings": { + "blendfile": "/render/scene.blend", + "frames": "1-100", + "chunk_size": 5, + "render_output_path": "/render/output/######", + "format": "PNG" + }, + "metadata": {"project": "demo", "user.name": "artist"} + }' +``` + +The exact `settings` keys depend on the job type. List available types with +`GET /api/v3/jobs/types`, or fetch one with `GET /api/v3/jobs/type/{typeName}` to see its +declared settings. + +## Reference files + +For complete details, read these as needed — they're verbose and only worth loading when +you're actually constructing API calls or troubleshooting: + +- **`references/api-reference.md`**: Every endpoint with parameters, request/response schemas, + status codes, and curl examples. Read this when constructing a specific API call or + understanding a response shape. +- **`references/usage-docs.md`** (optional, may not be present): General Flamenco usage — + shared storage, manager/worker configuration, two-way variable replacement, custom job + types, failure handling, troubleshooting. Read this for setup or non-API questions. + +If a reference file is missing or out of date, the canonical source is +`/api/v3/swagger-ui/` and `/api/v3/openapi3.json` — fetching the +OpenAPI JSON is the fastest way to verify a schema. + +## Notes on the worker-side API + +`/api/v3/worker/*` endpoints (`sign-on`, `sign-off`, `register-worker`, `state`, +`state-changed`, `task`, `task/{id}`, `task/{id}/may-i-run`, `task/{id}/output-produced`) +exist for the worker process to talk back to the Manager. They are not normally called by +end-user scripts; only touch them if you're implementing a custom worker or debugging the +worker protocol. diff --git a/plugins/flamenco/skills/flamenco-api/references/api-reference.md b/plugins/flamenco/skills/flamenco-api/references/api-reference.md new file mode 100644 index 0000000..409e2dc --- /dev/null +++ b/plugins/flamenco/skills/flamenco-api/references/api-reference.md @@ -0,0 +1,1233 @@ +# Flamenco Manager API Reference + +Base URL: `` — replace with the user's Flamenco Manager root +(e.g. `https://flamenco.example.com` or `http://localhost:8080`). + +API prefix: `/api/v3` + +All request/response bodies are JSON unless noted otherwise. The API has no authentication +by default; if the user's deployment is behind an auth proxy, add the appropriate headers. + +## Contents + +- **Meta** — version, status, configuration, setup-assistant helpers +- **Jobs** — submit, query, cancel, set status / priority / tag, blocklist, last-rendered +- **Tasks** — fetch, set status, log info / tail +- **Worker management** — list/fetch/delete workers, status changes, tags, sleep schedules +- **Shaman** — content-addressed file storage (only relevant if Shaman is enabled) +- **Worker-side** — endpoints workers use to talk back to the Manager (rarely called by users) +- **Schemas** — `Job`, `Task`, `Worker`, status enums, etc. + +--- + +## Meta + +### Get Version + +``` +GET /api/v3/version +``` + +Returns the Flamenco Manager version. + +**Response:** `200` - `FlamencoVersion` + +```bash +curl /api/v3/version +``` + +--- + +### Get Farm Status + +``` +GET /api/v3/status +``` + +Returns the overall farm status (active, idle, waiting, asleep, etc.). + +**Response:** `200` - `FarmStatusReport` + +```bash +curl /api/v3/status +``` + +--- + +### Get Configuration + +``` +GET /api/v3/configuration +``` + +Returns Manager configuration (storage location, Shaman enabled, first run flag). + +**Response:** `200` - `ManagerConfiguration` + +```bash +curl /api/v3/configuration +``` + +--- + +### Get Configuration File + +``` +GET /api/v3/configuration/file +``` + +Retrieve the raw configuration file contents. + +**Response:** `200` - JSON object or YAML string (via `Accept` header) + +```bash +curl /api/v3/configuration/file +``` + +--- + +### Update Configuration File + +``` +PUT /api/v3/configuration/file +``` + +Overwrite the configuration file. Does not actively reload. + +**Body:** JSON object (no schema validation) + +**Responses:** `204` success | `default` error + +```bash +curl -X PUT /api/v3/configuration/file \ + -H 'Content-Type: application/json' \ + -d '{"storage": "/render/storage"}' +``` + +--- + +### Get Shared Storage Location + +``` +GET /api/v3/configuration/shared-storage/{audience}/{platform} +``` + +Get the shared storage path adjusted for a specific audience and platform. + +| Parameter | In | Type | Description | +|------------|------|--------|------------------------------------------| +| `audience` | path | string | `workers` or `users` | +| `platform` | path | string | OS platform (`linux`, `windows`, `darwin`) | + +**Response:** `200` - `SharedStorageLocation` + +```bash +curl /api/v3/configuration/shared-storage/users/linux +``` + +--- + +### Get Variables + +``` +GET /api/v3/configuration/variables/{audience}/{platform} +``` + +Get Manager variables (used for two-way path replacement). + +| Parameter | In | Type | Description | +|------------|------|--------|------------------------------------------| +| `audience` | path | string | `workers` or `users` | +| `platform` | path | string | OS platform (`linux`, `windows`, `darwin`) | + +**Response:** `200` - `ManagerVariables` (map of variable name to properties) + +```bash +curl /api/v3/configuration/variables/workers/linux +``` + +--- + +### Find Blender Executable + +``` +GET /api/v3/configuration/check/blender +``` + +Auto-discover Blender executable paths on the Manager host. + +**Response:** `200` - `BlenderPathFindResult` (array of `BlenderPathCheckResult`) + +```bash +curl /api/v3/configuration/check/blender +``` + +--- + +### Check Blender Executable Path + +``` +POST /api/v3/configuration/check/blender +``` + +Validate a specific path or command as a Blender executable. + +**Body:** `PathCheckInput` - `{"path": "/usr/bin/blender"}` + +**Response:** `200` - `BlenderPathCheckResult` + +```bash +curl -X POST /api/v3/configuration/check/blender \ + -H 'Content-Type: application/json' \ + -d '{"path": "/usr/bin/blender"}' +``` + +--- + +### Check Shared Storage Path + +``` +POST /api/v3/configuration/check/shared-storage +``` + +Validate a directory path for use as shared storage. + +**Body:** `PathCheckInput` - `{"path": "/render/storage"}` + +**Response:** `200` - `PathCheckResult` + +```bash +curl -X POST /api/v3/configuration/check/shared-storage \ + -H 'Content-Type: application/json' \ + -d '{"path": "/render/storage"}' +``` + +--- + +### Save Setup Assistant Config + +``` +POST /api/v3/configuration/setup-assistant +``` + +Save initial configuration from the Setup Assistant and restart Manager. + +**Body:** `SetupAssistantConfig` - `{"storageLocation": "...", "blenderExecutable": {...}}` + +**Responses:** `204` success | `default` error + +```bash +curl -X POST /api/v3/configuration/setup-assistant \ + -H 'Content-Type: application/json' \ + -d '{"storageLocation": "/render/storage", "blenderExecutable": {"input": "blender", "path": "/usr/bin/blender", "source": "path_envvar", "is_usable": true, "cause": "Found on PATH"}}' +``` + +--- + +## Jobs + +### List All Jobs + +``` +GET /api/v3/jobs +``` + +Fetch all jobs in the database. + +**Response:** `200` - `JobsQueryResult` (`{"jobs": [Job, ...]}`) + +```bash +curl /api/v3/jobs +``` + +--- + +### Submit a Job + +``` +POST /api/v3/jobs +``` + +Submit a new job for execution. + +**Body:** `SubmittedJob` + +| Field | Type | Required | Description | +|----------------------|--------|----------|----------------------------------------------------| +| `name` | string | yes | Job name | +| `type` | string | yes | Job type (e.g. `simple-blender-render`) | +| `priority` | int | yes | 0-100, default 50 | +| `submitter_platform` | string | yes | `linux`, `windows`, `darwin`, or `manager` | +| `settings` | object | no | Job-type-specific settings | +| `metadata` | object | no | Arbitrary key-value metadata | +| `type_etag` | string | no | Etag from `AvailableJobType` to prevent stale submissions | +| `initial_status` | string | no | Override initial status (default: `queued`) | +| `worker_tag` | string | no | UUID of worker tag to restrict execution | +| `storage` | object | no | Shaman checkout ID info | + +**Responses:** `200` - `Job` | `412` etag mismatch | `default` error + +```bash +curl -X POST /api/v3/jobs \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "My Render", + "type": "simple-blender-render", + "priority": 50, + "submitter_platform": "linux", + "settings": { + "blendfile": "/render/project/scene.blend", + "frames": "1-100", + "chunk_size": 5, + "render_output_path": "/render/project/output/######", + "format": "PNG" + }, + "metadata": { + "project": "MyProject", + "user.name": "artist" + } + }' +``` + +--- + +### Check Job (Dry Run) + +``` +POST /api/v3/jobs/check +``` + +Validate a job definition without creating it. Same body as Submit. + +**Responses:** `204` valid | `412` etag mismatch | `default` error + +```bash +curl -X POST /api/v3/jobs/check \ + -H 'Content-Type: application/json' \ + -d '{"name": "Test", "type": "simple-blender-render", "priority": 50, "submitter_platform": "linux", "settings": {"blendfile": "/render/scene.blend", "frames": "1-10"}}' +``` + +--- + +### Fetch a Job + +``` +GET /api/v3/jobs/{job_id} +``` + +Get full info about a specific job. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `job_id` | path | string | yes | + +**Response:** `200` - `Job` + +```bash +curl /api/v3/jobs/abc123-uuid +``` + +--- + +### Delete a Job + +``` +DELETE /api/v3/jobs/{job_id} +``` + +Request deletion of a job, its tasks, and log files. Actual deletion happens in background. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `job_id` | path | string | yes | + +**Responses:** `204` accepted | `default` error + +```bash +curl -X DELETE /api/v3/jobs/abc123-uuid +``` + +--- + +### What Would Delete Do + +``` +GET /api/v3/jobs/{job_id}/what-would-delete-do +``` + +Preview what will be deleted (e.g. whether Shaman checkout dir is removed). + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `job_id` | path | string | yes | + +**Response:** `200` - `JobDeletionInfo` + +```bash +curl /api/v3/jobs/abc123-uuid/what-would-delete-do +``` + +--- + +### Mass Delete Jobs + +``` +DELETE /api/v3/jobs/mass-delete +``` + +Mark jobs for deletion based on criteria. Deletion happens in background. + +**Body:** `JobMassDeletionSelection` + +| Field | Type | Description | +|--------------------|----------|----------------------------------------------| +| `last_updated_max` | datetime | Delete jobs last updated before this timestamp | + +**Responses:** `204` accepted | `416` no matching jobs | `default` error + +```bash +curl -X DELETE /api/v3/jobs/mass-delete \ + -H 'Content-Type: application/json' \ + -d '{"last_updated_max": "2025-01-01T00:00:00Z"}' +``` + +--- + +### Set Job Status + +``` +POST /api/v3/jobs/{job_id}/setstatus +``` + +Change a job's status (e.g. pause, cancel, requeue). + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `job_id` | path | string | yes | + +**Body:** `JobStatusChange` + +| Field | Type | Required | Description | +|----------|--------|----------|----------------------------| +| `status` | string | yes | New `JobStatus` value | +| `reason` | string | yes | Reason for the change | + +**Responses:** `204` accepted | `422` invalid transition | `default` error + +```bash +curl -X POST /api/v3/jobs/abc123-uuid/setstatus \ + -H 'Content-Type: application/json' \ + -d '{"status": "canceled", "reason": "No longer needed"}' +``` + +--- + +### Set Job Priority + +``` +POST /api/v3/jobs/{job_id}/setpriority +``` + +Change a job's priority. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `job_id` | path | string | yes | + +**Body:** `JobPriorityChange` - `{"priority": 75}` (0-100) + +**Responses:** `204` accepted | `422` invalid priority | `default` error + +```bash +curl -X POST /api/v3/jobs/abc123-uuid/setpriority \ + -H 'Content-Type: application/json' \ + -d '{"priority": 75}' +``` + +--- + +### Set Job Tag + +``` +POST /api/v3/jobs/{job_id}/settag +``` + +Assign or remove a worker tag from a job. Send empty body to remove tag. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `job_id` | path | string | yes | + +**Body:** `JobTagChange` - `{"id": "tag-uuid"}` or `{"name": "TagName"}` + +**Responses:** `204` accepted | `422` invalid tag | `default` error + +```bash +curl -X POST /api/v3/jobs/abc123-uuid/settag \ + -H 'Content-Type: application/json' \ + -d '{"name": "GPU-EEVEE"}' +``` + +--- + +### Fetch Job Tasks + +``` +GET /api/v3/jobs/{job_id}/tasks +``` + +Get a summary list of all tasks belonging to a job. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `job_id` | path | string | yes | + +**Response:** `200` - `JobTasksSummary` (`{"tasks": [TaskSummary, ...]}`) + +```bash +curl /api/v3/jobs/abc123-uuid/tasks +``` + +--- + +### Fetch Job Blocklist + +``` +GET /api/v3/jobs/{job_id}/blocklist +``` + +Get the list of worker/task-type pairs blocked on this job. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `job_id` | path | string | yes | + +**Response:** `200` - `JobBlocklist` (array of `JobBlocklistEntry`) + +```bash +curl /api/v3/jobs/abc123-uuid/blocklist +``` + +--- + +### Remove Job Blocklist Entries + +``` +DELETE /api/v3/jobs/{job_id}/blocklist +``` + +Remove specific worker/task-type pairs from the blocklist. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `job_id` | path | string | yes | + +**Body:** `JobBlocklist` (array of `{"worker_id": "...", "task_type": "..."}`) + +**Responses:** `204` accepted | `default` error + +```bash +curl -X DELETE /api/v3/jobs/abc123-uuid/blocklist \ + -H 'Content-Type: application/json' \ + -d '[{"worker_id": "worker-uuid", "task_type": "blender"}]' +``` + +--- + +### Get Last Rendered Image Info (Job) + +``` +GET /api/v3/jobs/{job_id}/last-rendered +``` + +Get URL info for the last-rendered image of a specific job. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `job_id` | path | string | yes | + +**Responses:** `200` - `JobLastRenderedImageInfo` | `204` no image + +```bash +curl /api/v3/jobs/abc123-uuid/last-rendered +``` + +--- + +### Get Last Rendered Image Info (Global) + +``` +GET /api/v3/jobs/last-rendered +``` + +Get URL info for the global last-rendered image (across all jobs). + +**Responses:** `200` - `JobLastRenderedImageInfo` | `204` no image + +```bash +curl /api/v3/jobs/last-rendered +``` + +--- + +### Get Job Types + +``` +GET /api/v3/jobs/types +``` + +List all available job types and their settings. + +**Response:** `200` - `AvailableJobTypes` (`{"job_types": [AvailableJobType, ...]}`) + +```bash +curl /api/v3/jobs/types +``` + +--- + +### Get Single Job Type + +``` +GET /api/v3/jobs/type/{typeName} +``` + +Get a specific job type and its parameters. + +| Parameter | In | Type | Required | +|------------|------|--------|----------| +| `typeName` | path | string | yes | + +**Response:** `200` - `AvailableJobType` + +```bash +curl /api/v3/jobs/type/simple-blender-render +``` + +--- + +### Fetch a Task + +``` +GET /api/v3/tasks/{task_id} +``` + +Get full details of a single task. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `task_id` | path | string | yes | + +**Response:** `200` - `Task` + +```bash +curl /api/v3/tasks/task-uuid +``` + +--- + +### Set Task Status + +``` +POST /api/v3/tasks/{task_id}/setstatus +``` + +Change a task's status. May affect the parent job status. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `task_id` | path | string | yes | + +**Body:** `TaskStatusChange` + +| Field | Type | Required | Description | +|----------|--------|----------|----------------------------| +| `status` | string | yes | New `TaskStatus` value | +| `reason` | string | yes | Reason for the change | + +**Responses:** `204` accepted | `422` invalid transition | `default` error + +```bash +curl -X POST /api/v3/tasks/task-uuid/setstatus \ + -H 'Content-Type: application/json' \ + -d '{"status": "queued", "reason": "Retry after fix"}' +``` + +--- + +### Get Task Log Info + +``` +GET /api/v3/tasks/{task_id}/log +``` + +Get metadata about the task log (URL, size). + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `task_id` | path | string | yes | + +**Responses:** `200` - `TaskLogInfo` | `204` no log yet + +```bash +curl /api/v3/tasks/task-uuid/log +``` + +--- + +### Get Task Log Tail + +``` +GET /api/v3/tasks/{task_id}/logtail +``` + +Fetch the last few lines of the task's log as plain text. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `task_id` | path | string | yes | + +**Responses:** `200` - `text/plain` | `204` no log yet + +```bash +curl /api/v3/tasks/task-uuid/logtail +``` + +--- + +## Worker Management (worker-mgt) + +### List Workers + +``` +GET /api/v3/worker-mgt/workers +``` + +Get all known workers. + +**Response:** `200` - `WorkerList` (`{"workers": [WorkerSummary, ...]}`) + +```bash +curl /api/v3/worker-mgt/workers +``` + +--- + +### Fetch a Worker + +``` +GET /api/v3/worker-mgt/workers/{worker_id} +``` + +Get detailed info about a specific worker. + +| Parameter | In | Type | Required | +|-------------|------|--------|----------| +| `worker_id` | path | string | yes | + +**Response:** `200` - `Worker` + +```bash +curl /api/v3/worker-mgt/workers/worker-uuid +``` + +--- + +### Delete a Worker + +``` +DELETE /api/v3/worker-mgt/workers/{worker_id} +``` + +Remove a worker. Best used when worker is offline. Assigned tasks will be requeued. + +| Parameter | In | Type | Required | +|-------------|------|--------|----------| +| `worker_id` | path | string | yes | + +**Responses:** `204` deleted | `default` error + +```bash +curl -X DELETE /api/v3/worker-mgt/workers/worker-uuid +``` + +--- + +### Request Worker Status Change + +``` +POST /api/v3/worker-mgt/workers/{worker_id}/setstatus +``` + +Ask a worker to change state (e.g. send to sleep, wake up, restart). + +| Parameter | In | Type | Required | +|-------------|------|--------|----------| +| `worker_id` | path | string | yes | + +**Body:** `WorkerStatusChangeRequest` + +| Field | Type | Required | Description | +|----------|---------|----------|--------------------------------------------------| +| `status` | string | yes | Target `WorkerStatus` value | +| `is_lazy`| boolean | yes | If true, wait for current task to finish first | + +**Responses:** `204` accepted | `default` error + +```bash +curl -X POST /api/v3/worker-mgt/workers/worker-uuid/setstatus \ + -H 'Content-Type: application/json' \ + -d '{"status": "asleep", "is_lazy": true}' +``` + +--- + +### Set Worker Tags + +``` +POST /api/v3/worker-mgt/workers/{worker_id}/settags +``` + +Update which tags a worker belongs to. + +| Parameter | In | Type | Required | +|-------------|------|--------|----------| +| `worker_id` | path | string | yes | + +**Body:** `WorkerTagChangeRequest` - `{"tag_ids": ["tag-uuid-1", "tag-uuid-2"]}` + +**Responses:** `204` accepted | `default` error + +```bash +curl -X POST /api/v3/worker-mgt/workers/worker-uuid/settags \ + -H 'Content-Type: application/json' \ + -d '{"tag_ids": ["tag-uuid-1"]}' +``` + +--- + +### Get Worker Sleep Schedule + +``` +GET /api/v3/worker-mgt/workers/{worker_id}/sleep-schedule +``` + +Fetch the sleep schedule for a worker. + +| Parameter | In | Type | Required | +|-------------|------|--------|----------| +| `worker_id` | path | string | yes | + +**Responses:** `200` - `WorkerSleepSchedule` | `204` no schedule | `default` error + +```bash +curl /api/v3/worker-mgt/workers/worker-uuid/sleep-schedule +``` + +--- + +### Set Worker Sleep Schedule + +``` +POST /api/v3/worker-mgt/workers/{worker_id}/sleep-schedule +``` + +Create or update a worker's sleep schedule. + +| Parameter | In | Type | Required | +|-------------|------|--------|----------| +| `worker_id` | path | string | yes | + +**Body:** `WorkerSleepSchedule` + +| Field | Type | Required | Description | +|----------------|---------|----------|------------------------------------------------------| +| `is_active` | boolean | yes | Whether the schedule is enabled | +| `start_time` | string | yes | Start time in `HH:MM` format | +| `end_time` | string | yes | End time in `HH:MM` format | +| `days_of_week` | string | yes | Space-separated day codes (`mo tu we th fr sa su`) or empty for every day | + +**Responses:** `204` stored | `default` error + +```bash +curl -X POST /api/v3/worker-mgt/workers/worker-uuid/sleep-schedule \ + -H 'Content-Type: application/json' \ + -d '{"is_active": true, "start_time": "09:00", "end_time": "18:00", "days_of_week": "mo tu we th fr"}' +``` + +--- + +### List Worker Tags + +``` +GET /api/v3/worker-mgt/tags +``` + +Get all worker tags. + +**Response:** `200` - `WorkerTagList` (`{"tags": [WorkerTag, ...]}`) + +```bash +curl /api/v3/worker-mgt/tags +``` + +--- + +### Create Worker Tag + +``` +POST /api/v3/worker-mgt/tags +``` + +Create a new worker tag. + +**Body:** `WorkerTag` + +| Field | Type | Required | Description | +|---------------|--------|----------|----------------------------------| +| `name` | string | yes | Tag name | +| `description` | string | no | Tag description | +| `id` | string | no | UUID (auto-generated if omitted) | + +**Response:** `200` - `WorkerTag` (with assigned UUID) + +```bash +curl -X POST /api/v3/worker-mgt/tags \ + -H 'Content-Type: application/json' \ + -d '{"name": "GPU-EEVEE", "description": "Workers with GPU for EEVEE rendering"}' +``` + +--- + +### Fetch Worker Tag + +``` +GET /api/v3/worker-mgt/tag/{tag_id} +``` + +Get a single worker tag. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `tag_id` | path | string | yes | + +**Response:** `200` - `WorkerTag` + +```bash +curl /api/v3/worker-mgt/tag/tag-uuid +``` + +--- + +### Update Worker Tag + +``` +PUT /api/v3/worker-mgt/tag/{tag_id} +``` + +Update an existing worker tag. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `tag_id` | path | string | yes | + +**Body:** `WorkerTag` - `{"name": "NewName", "description": "Updated desc"}` + +**Responses:** `204` stored | `default` error + +```bash +curl -X PUT /api/v3/worker-mgt/tag/tag-uuid \ + -H 'Content-Type: application/json' \ + -d '{"name": "GPU-Cycles", "description": "GPU workers for Cycles"}' +``` + +--- + +### Delete Worker Tag + +``` +DELETE /api/v3/worker-mgt/tag/{tag_id} +``` + +Remove a worker tag. Unassigns all workers from it. + +| Parameter | In | Type | Required | +|-----------|------|--------|----------| +| `tag_id` | path | string | yes | + +**Responses:** `204` removed | `default` error + +```bash +curl -X DELETE /api/v3/worker-mgt/tag/tag-uuid +``` + +--- + +## Shaman (File Transfer) + +Shaman is Flamenco's content-addressed file storage system. Files are uploaded by SHA256+size, then symlinked into job checkout directories. + +### Check File Status + +``` +GET /api/v3/shaman/files/{checksum}/{filesize} +``` + +Check whether a file exists in the Shaman store. + +| Parameter | In | Type | Required | +|------------|------|---------|----------| +| `checksum` | path | string | yes | SHA256 hex digest | +| `filesize` | path | integer | yes | File size in bytes | + +**Response:** `200` - `ShamanSingleFileStatus` (`{"status": "stored"|"uploading"|"unknown"}`) + +```bash +curl /api/v3/shaman/files/35b0491c27b0333d1fb45fc0789a12ca06b1d640d2569780b807de504d7029e0/1424 +``` + +--- + +### Upload File + +``` +POST /api/v3/shaman/files/{checksum}/{filesize} +``` + +Upload a file to the Shaman store. Body is raw binary. + +| Parameter | In | Type | Required | Description | +|------------|--------|---------|----------|--------------------------------| +| `checksum` | path | string | yes | SHA256 hex digest | +| `filesize` | path | integer | yes | File size in bytes | +| `X-Shaman-Can-Defer-Upload` | header | boolean | no | Allow deferring if someone else is uploading | +| `X-Shaman-Original-Filename` | header | string | no | Original filename for logging | + +**Body:** `application/octet-stream` - raw file contents + +**Responses:** `204` accepted | `208` already known | `417` checksum/size mismatch | `425` defer upload + +```bash +curl -X POST /api/v3/shaman/files/35b0491c27b0333d1fb45fc0789a12ca06b1d640d2569780b807de504d7029e0/1424 \ + -H 'Content-Type: application/octet-stream' \ + -H 'X-Shaman-Original-Filename: scene.blend' \ + --data-binary @scene.blend +``` + +--- + +### Check Requirements + +``` +POST /api/v3/shaman/checkout/requirements +``` + +Check which files from a list are missing from the Shaman store. + +**Body:** `ShamanRequirementsRequest` + +```json +{ + "files": [ + {"sha": "35b049...", "size": 1424, "path": "scene.blend"}, + {"sha": "63b72c...", "size": 127, "path": "config.py"} + ] +} +``` + +**Response:** `200` - `ShamanRequirementsResponse` (only unknown/uploading files returned; known files are omitted) + +```bash +curl -X POST /api/v3/shaman/checkout/requirements \ + -H 'Content-Type: application/json' \ + -d '{"files": [{"sha": "35b0491c...", "size": 1424, "path": "scene.blend"}]}' +``` + +--- + +### Create Checkout + +``` +POST /api/v3/shaman/checkout/create +``` + +Create a directory with symlinks to the specified files. All files must already be uploaded. + +**Body:** `ShamanCheckout` + +| Field | Type | Required | Description | +|----------------|--------|----------|---------------------------------------------------------| +| `checkoutPath` | string | yes | Relative path for the checkout (e.g. `project/scene/id`) | +| `files` | array | yes | Array of `ShamanFileSpec` (`{sha, size, path}`) | + +**Responses:** `200` - `ShamanCheckoutResult` | `409` already exists | `424` missing files + +```bash +curl -X POST /api/v3/shaman/checkout/create \ + -H 'Content-Type: application/json' \ + -d '{ + "checkoutPath": "my-project/scene-01/unique-id", + "files": [ + {"sha": "35b0491c...", "size": 1424, "path": "scene.blend"}, + {"sha": "63b72c63...", "size": 127, "path": "config.py"} + ] + }' +``` + +--- + +## Worker-Side Endpoints + +These endpoints under `/api/v3/worker/` are used by the Flamenco Worker process to talk back +to the Manager. Normal user scripts and pipeline tools do **not** call them — only touch them +when implementing a custom worker, or when debugging the worker protocol. They typically +require the worker's bearer token (set during `register-worker`) for authentication. + +| Method | Path | Operation | Purpose | +|--------|------------------------------------------------|------------------------|----------------------------------------------------------------------| +| POST | `/api/v3/worker/register-worker` | `RegisterWorker` | First-time registration; returns a worker ID and secret | +| POST | `/api/v3/worker/sign-on` | `SignOn` | Worker comes online and reports metadata (hostname, task types, OS) | +| POST | `/api/v3/worker/sign-off` | `SignOff` | Worker is going offline | +| GET | `/api/v3/worker/state` | `WorkerState` | Poll for a pending state-change request from the Manager | +| POST | `/api/v3/worker/state-changed` | `WorkerStateChanged` | Acknowledge a state change (or report a worker-local one) | +| POST | `/api/v3/worker/task` | `ScheduleTask` | Ask the Manager for the next task to execute | +| POST | `/api/v3/worker/task/{task_id}` | `TaskUpdate` | Report progress, completion, or failure | +| GET | `/api/v3/worker/task/{task_id}/may-i-run` | `MayWorkerRun` | Check whether the worker is still allowed to run this task | +| POST | `/api/v3/worker/task/{task_id}/output-produced`| `TaskOutputProduced` | Upload the latest rendered frame thumbnail (PNG/JPEG) | + +For the exact request/response schemas, consult `/api/v3/swagger-ui/` — these +endpoints evolve with the worker protocol and are best read from the live OpenAPI spec. + +--- + +## Schemas + +### Job + +Extends `SubmittedJob` with server-assigned fields. + +| Property | Type | Description | +|-----------------------|----------|------------------------------------------| +| `id` | string | UUID | +| `name` | string | Job name | +| `type` | string | Job type identifier | +| `priority` | integer | 0-100 | +| `status` | JobStatus | Current status | +| `activity` | string | Last activity description | +| `created` | datetime | Creation timestamp | +| `updated` | datetime | Last update timestamp | +| `settings` | object | Job-type-specific settings | +| `metadata` | object | Arbitrary key-value pairs | +| `submitter_platform` | string | OS of the submitter | +| `worker_tag` | string | Worker tag UUID (optional) | +| `storage` | object | Shaman checkout info (optional) | +| `steps_completed` | integer | Progress numerator | +| `steps_total` | integer | Progress denominator | +| `delete_requested_at` | datetime | When deletion was requested (optional) | + +### SubmittedJob + +The job definition as sent by the client (see Submit a Job above for fields). + +### Task + +| Property | Type | Description | +|-------------------|--------------|---------------------------------------| +| `id` | string | UUID | +| `name` | string | Task name | +| `job_id` | string | Parent job UUID | +| `index_in_job` | integer | Task index within the job | +| `status` | TaskStatus | Current status | +| `priority` | integer | Task priority | +| `task_type` | string | Type of task (e.g. `blender`, `ffmpeg`) | +| `activity` | string | Current activity description | +| `created` | datetime | Creation timestamp | +| `updated` | datetime | Last update timestamp | +| `last_touched` | datetime | When a worker last worked on this | +| `commands` | [Command] | List of commands to execute | +| `worker` | TaskWorker | Currently assigned worker (optional) | +| `failed_by_workers` | [TaskWorker] | Workers that failed this task | +| `steps_completed` | integer | Progress numerator | +| `steps_total` | integer | Progress denominator | + +### Worker + +Extends `WorkerSummary` with full details. + +| Property | Type | Description | +|------------------------|----------------|-------------------------------------| +| `id` | string | UUID | +| `name` | string | Worker name | +| `status` | WorkerStatus | Current status | +| `version` | string | Flamenco version running | +| `ip_address` | string | IP address | +| `platform` | string | OS (linux, windows, darwin) | +| `supported_task_types` | [string] | Task types this worker can handle | +| `gpus` | [string] | GPU descriptions | +| `last_seen` | datetime | Last contact time | +| `can_restart` | boolean | Whether worker can auto-restart | +| `status_change` | object | Pending status change request | +| `tags` | [WorkerTag] | Assigned tags | +| `task` | WorkerTask | Currently assigned task (optional) | + +### JobStatus + +Enum: `active` | `canceled` | `completed` | `failed` | `paused` | `pause-requested` | `queued` | `cancel-requested` | `requeueing` | `under-construction` + +### TaskStatus + +Enum: `active` | `canceled` | `completed` | `failed` | `queued` | `soft-failed` | `paused` + +### WorkerStatus + +Enum: `starting` | `awake` | `asleep` | `error` | `testing` | `offline` | `restart` + +### WorkerTag + +| Property | Type | Description | +|---------------|--------|--------------------------------------| +| `id` | string | UUID (optional on create) | +| `name` | string | Tag name (required) | +| `description` | string | Tag description (optional) | + +### WorkerSleepSchedule + +| Property | Type | Description | +|----------------|---------|------------------------------------------------------------| +| `is_active` | boolean | Whether the schedule is enabled | +| `start_time` | string | Start time `HH:MM` | +| `end_time` | string | End time `HH:MM` | +| `days_of_week` | string | Space-separated day codes (`mo tu we th fr sa su`) or empty | + +### AvailableJobType + +| Property | Type | Description | +|---------------|------------------------|---------------------------------------| +| `name` | string | Internal identifier | +| `label` | string | Display name | +| `description` | string | Tooltip/description (optional) | +| `etag` | string | Hash (changes when settings change) | +| `settings` | [AvailableJobSetting] | List of configurable settings | + +Each `AvailableJobSetting` has: `key` (string), `type` (`string|int32|float|bool`), `subtype` (optional: `file_path|dir_path|file_name|hashed_file_path`), `default`, `choices` (array), `required` (bool), `editable` (bool), `visible` (`visible|hidden|submission|web`). + +### Command + +| Property | Type | Description | +|--------------------|---------|--------------------------------| +| `name` | string | Command name | +| `parameters` | object | Command parameters | +| `total_step_count` | integer | Expected number of steps | + +### FarmStatus + +Enum: `active` | `idle` | `waiting` | `asleep` | `inoperative` | `unknown` | `starting` + +### FlamencoVersion + +| Property | Type | Description | +|----------------|--------|-------------------------------------| +| `version` | string | Full version string for display | +| `shortversion` | string | Short version (e.g. `3.3-alpha0`) | +| `name` | string | Manager instance name | +| `git` | string | Git version tag | diff --git a/plugins/kitsu/.claude-plugin/plugin.json b/plugins/kitsu/.claude-plugin/plugin.json new file mode 100644 index 0000000..85bc1f9 --- /dev/null +++ b/plugins/kitsu/.claude-plugin/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "kitsu", + "description": "Kitsu production tracking tools and references", + "version": "1.0.0", + "author": { + "name": "Autour de Minuit" + } +} diff --git a/plugins/kitsu/skills/kitsu-api/SKILL.md b/plugins/kitsu/skills/kitsu-api/SKILL.md new file mode 100644 index 0000000..d57c137 --- /dev/null +++ b/plugins/kitsu/skills/kitsu-api/SKILL.md @@ -0,0 +1,381 @@ +--- +name: kitsu-api +description: > + Comprehensive reference for the Kitsu production tracking API (Zou backend) and the gazu Python SDK. + Use this skill whenever working with Kitsu, gazu, CGWire, or production tracking API calls — + whether writing new integrations, debugging API issues, building pipeline tools that talk to Kitsu, + or understanding how the ADM Gadget pipeline interacts with the tracker. Also use when the user + mentions tasks, shots, assets, comments, previews, playlists, or casting in the context of + production tracking. Covers both the REST API (493 endpoints) and the gazu Python wrapper. +--- + +# Kitsu API & gazu SDK Reference + +Kitsu is an open-source production tracking tool for animation/VFX studios, built by CGWire. The backend is called **Zou** and exposes a REST API. **gazu** is the official Python client SDK that wraps this API. + +## Quick Reference + +- **API docs**: https://api-docs.kitsu.cloud/ +- **OpenAPI spec**: https://api-docs.kitsu.cloud/source.json +- **Developer guides**: https://dev.kitsu.cloud/ +- **gazu docs**: https://gazu.cg-wire.com/ +- **Zou source**: https://github.com/cgwire/zou +- **gazu source**: https://github.com/cgwire/gazu + +## Authentication + +### User Login (email/password → JWT) + +```python +import gazu + +gazu.set_host("https://kitsu.mystudio.com/api") +gazu.log_in("user@studio.com", "password") +``` + +Under the hood this POSTs to `/auth/login` with `{"email": "...", "password": "..."}` and receives a JWT access token + refresh token. + +### Bot Authentication (pre-generated token) + +Bots are non-physical users that don't count against subscription seats. Create them in Admin > Bots. Use the token directly: + +```python +gazu.set_host("https://kitsu.mystudio.com/api") +gazu.set_token("bot_jwt_token_here") +``` + +### Token Lifecycle + +- **Access token**: Short-lived JWT, passed as `Authorization: Bearer ` header +- **Refresh token**: Use `GET /auth/refresh-token` to get a new access token +- **Logout**: `GET /auth/logout` +- **Check auth**: `GET /auth/authenticated` (200 = valid, 401 = expired) + +### 2FA Support + +Kitsu supports TOTP, email OTP, FIDO/WebAuthn, and recovery codes. See `/auth/totp`, `/auth/email-otp`, `/auth/fido` endpoints. + +### ADM Pipeline Authentication + +In the ADM Gadget pipeline, credentials come from environment variables set by `adm_env.yml`: +- `TRACKER_URL` — Kitsu instance URL (must end with `/api`) +- `TRACKER_LOGIN` — email +- `TRACKER_PASSWORD` — password +- `TRACKER_PROJECT` — project name to connect to + +The `Kitsu` adapter class (`gadget/src/gadget/resources/trackers/kitsu.py`) wraps gazu and handles connection/reconnection. + +## Core Concepts + +### Entity Hierarchy + +``` +Project +├── Episodes (optional, for TV shows) +│ └── Sequences +│ └── Shots +├── Sequences (for shorts/features) +│ └── Shots +├── Asset Types +│ └── Assets +├── Edits +├── Concepts +└── Scenes +``` + +### Data Model + +Every entity in Kitsu is a dict with at minimum: +- `id` — UUID string +- `name` — display name +- `created_at` / `updated_at` — ISO timestamps +- `data` — custom metadata dict (called `custom_data` in Gadget after normalization) +- `project_id` — parent project UUID + +### Key Relationships + +- **Shot** → belongs to **Sequence** (via `parent_id`) → belongs to **Episode** (optional) +- **Asset** → belongs to **Asset Type** (via `entity_type_id`) +- **Task** → belongs to **Entity** (shot/asset) + **Task Type** + has **Task Status** +- **Comment** → belongs to **Task**, may have **Preview Files** and **Attachments** +- **Casting** → links Assets to Shots (with `nb_occurences`) + +## gazu SDK Patterns + +### Project Operations + +```python +# List/find projects +projects = gazu.project.all_projects() +open_projects = gazu.project.all_open_projects() +project = gazu.project.get_project_by_name("My Project") +project = gazu.project.get_project(project_id) + +# Create project +project = gazu.project.new_project("Name", production_type="short") # short|featurefilm|tvshow + +# Team management +gazu.project.add_person_to_team(project, person) +gazu.project.remove_person_from_team(project, person) +team = gazu.project.get_team(project) +``` + +### Shot Operations + +```python +# Episodes (TV shows only) +episode = gazu.shot.new_episode(project, "E01") +episodes = gazu.shot.all_episodes_for_project(project) + +# Sequences +sequence = gazu.shot.new_sequence(project, "SQ010", episode=episode) +sequences = gazu.shot.all_sequences_for_project(project) + +# Shots +shot = gazu.shot.new_shot(project, sequence, "SH0010", + nb_frames=100, frame_in=101, frame_out=200, + data={"custom_field": "value"}) +shot = gazu.shot.get_shot(shot_id) +shot = gazu.shot.get_shot_by_name(sequence, "SH0010") +shots = gazu.shot.all_shots_for_sequence(sequence) +gazu.shot.update_shot(shot) # after modifying dict fields +gazu.shot.remove_shot(shot_id, force=True) +``` + +### Asset Operations + +```python +# Asset types +asset_type = gazu.asset.new_asset_type("Character") +asset_types = gazu.asset.all_asset_types_for_project(project) + +# Assets +asset = gazu.asset.new_asset(project, asset_type, "Hero", + description="Main character", + extra_data={"variant": "default"}) +asset = gazu.asset.get_asset(asset_id) +asset = gazu.asset.get_asset_by_name(project, "Hero", asset_type) +assets = gazu.asset.all_assets_for_project(project) +gazu.asset.remove_asset(asset_id, force=True) +``` + +### Task Operations + +```python +# Task types & statuses +task_type = gazu.task.get_task_type_by_name("Animation") +wip = gazu.task.get_task_status_by_short_name("wip") +done = gazu.task.get_task_status_by_short_name("done") + +# Create tasks +task = gazu.task.new_task(entity, task_type, task_status=wip, assignees=[person]) + +# Find tasks +tasks = gazu.task.all_tasks_for_shot(shot) +tasks = gazu.task.all_tasks_for_asset(asset) +task = gazu.task.get_task_by_name(entity, task_type) # get_task_by_entity in some versions + +# Assignment +gazu.task.assign_task(task, person) + +# Time tracking +gazu.task.set_time_spent(task, person, "2024-03-18", duration=8*3600) # seconds +gazu.task.add_time_spent(task, person, "2024-03-18", duration=3600) + +# Scheduling +task["start_date"] = "2024-03-01" +task["due_date"] = "2024-03-15" +task["estimation"] = 5 * 8 * 3600 # 5 days in seconds +gazu.task.update_task(task) +``` + +### Comments & Publishing + +```python +# Add comment (also changes task status) +comment = gazu.task.add_comment(task, task_status, + comment="Looking good!", + person=person, + attachments=["/path/to/file.pdf"], + created_at=None) + +# Add preview to comment +preview = gazu.task.add_preview(task, comment, "/path/to/render.mp4") +gazu.task.set_main_preview(preview) # set as entity thumbnail + +# One-step publish shortcut +comment, preview = gazu.task.publish_preview(task, wip, + comment="WIP update", + preview_file_path="/path/to/file.mp4") + +# Read comments +comments = gazu.task.all_comments_for_task(task) +last = gazu.task.get_last_comment_for_task(task) + +# Download previews +gazu.files.download_preview_file(preview, "/path/to/output.mp4") +gazu.files.download_preview_file_thumbnail(preview, "/path/to/thumb.png") +``` + +### Casting (Breakdown) + +```python +# Get casting +casting = gazu.casting.get_shot_casting(shot) # assets in a shot +cast_in = gazu.casting.get_asset_cast_in(asset) # shots using an asset + +# Update casting +gazu.casting.update_shot_casting(project, shot_id, [ + {"asset_id": asset1_id, "nb_occurences": 2}, + {"asset_id": asset2_id, "nb_occurences": 1}, +]) +``` + +### Persons & Teams + +```python +person = gazu.person.new_person("John", "Doe", "john@studio.com", + role="user", departments=[dept]) +persons = gazu.person.all_persons() +person = gazu.person.get_person_by_full_name("John Doe") +gazu.person.add_person_to_department(person, department) +``` + +### Playlists + +```python +playlist = gazu.playlist.new_playlist(project, "Daily Review", + for_client=False, for_entity="shot") +gazu.playlist.add_entity_to_playlist(playlist, entity, preview_file=preview) +``` + +### Event Listeners (Real-time) + +```python +gazu.set_event_host("https://kitsu.mystudio.com") # note: no /api suffix +event_client = gazu.events.init() + +def on_task_status_changed(data): + print(f"Task {data['task_id']} status changed") + +gazu.events.add_listener(event_client, "task:status-changed", on_task_status_changed) +gazu.events.run_client(event_client) # blocks current thread +``` + +Event naming: `entity:action` (e.g., `asset:new`, `shot:update`, `comment:new`, `task:assign`). +Most entities emit `new`, `update`, `delete`. See `references/events.md` for the full list. + +### Caching + +```python +gazu.cache.enable() # enable in-memory cache for all read ops +gazu.cache.disable() +gazu.cache.clear_all() + +# Per-function control +gazu.asset.all_assets.set_expire(120) # seconds +gazu.asset.all_assets.clear_cache() +gazu.asset.all_assets.disable_cache() +``` + +### Low-level Client + +When gazu doesn't wrap an endpoint, use the raw client: + +```python +# GET +data = gazu.client.get("data/projects") +data = gazu.client.fetch_all("tasks", {"project_id": pid, "task_type_id": ttid}) +data = gazu.client.fetch_one("projects", project_id) +data = gazu.client.fetch_first("projects", {"name": "MyProject"}) + +# POST +result = gazu.client.post("data/projects", {"name": "New Project"}) + +# PUT +result = gazu.client.put(f"data/entities/{entity_id}", updated_data) + +# Pagination +page1 = gazu.client.fetch_all("tasks?page=1") # 100 per page +``` + +### Search + +```python +# Full-text search +results = gazu.search.search_entities("bird") +# Returns: {"persons": [...], "assets": [...], "shots": [...]} + +# REST API search +results = gazu.client.post("data/search", { + "query": "hero", + "project_id": project_id, + "limit": 10, + "offset": 0, + "index_names": ["assets"] # filter to specific entity types +}) +``` + +## Roles & Permissions + +| Role | Access Level | +|------|-------------| +| **Admin** (Studio Manager) | Full read/write to all productions and settings | +| **Manager** (Production Manager) | Create assets/shots, manage tasks, post comments — no studio settings | +| **Supervisor** (Department Lead) | Read/write within assigned departments | +| **User** (Artist) | Comment/upload on assigned tasks only | +| **Vendor** | Like User but can only see assigned tasks | +| **Client** | View-only on assigned productions, client playlists only | + +## Custom Actions + +Custom Actions trigger HTTP POST requests from the Kitsu web UI to your endpoint. Created by admins in Admin > Custom Actions. + +Payload sent to your endpoint: +```json +{ + "personid": "uuid", + "personemail": "user@studio.com", + "projectid": "uuid", + "currentpath": "/productions/{id}/assets", + "currentserver": "kitsu.mystudio.com", + "selection": ["task_uuid_1", "task_uuid_2"], + "entitytype": "asset" +} +``` + +## ADM Gadget Integration + +The Gadget pipeline's `Kitsu` adapter (`gadget/src/gadget/resources/trackers/kitsu.py`) wraps gazu with these conventions: + +- **ID normalization**: `get_id()` accepts strings (UUID), dicts (extracts `["id"]`), or Gadget entity objects (uses `.id`) +- **Data normalization**: `_norm_data()` renames `data` → `custom_data` to avoid collision with Python dict usage +- **Task normalization**: `retake_count` → `nb_retakes`, `for_entity` → `for_shots` boolean +- **Comment normalization**: `attachment_files` → `attachments`, adds index-based `id` to checklist items, renames preview `original_name` → `name` +- **Shot creation**: Uses raw `gazu.client.post()` because gazu's `new_shot` didn't support all fields needed + +Key Gadget patterns: +```python +# Gadget entity-level usage +project = Project.from_env() # loads from env vars +shot = project.shots.get(name="sq170_sh0224") +task = shot.tasks.fetch("Anim3D") +task.comments.new(comment="...", preview=["/path/to/img.png"], set_main_preview=True) +``` + +## Detailed References + +For the complete endpoint catalog (all 493 endpoints), see `references/endpoints.md`. +For the full event types list, see `references/events.md`. + +When you need details about a specific endpoint's parameters or response format, fetch the OpenAPI spec: +```bash +curl -s https://api-docs.kitsu.cloud/source.json | python3 -m json.tool +``` + +Or read it programmatically: +```python +import json, urllib.request +spec = json.loads(urllib.request.urlopen("https://api-docs.kitsu.cloud/source.json").read()) +endpoint = spec["paths"]["/data/shots/{shot_id}"] +``` diff --git a/plugins/kitsu/skills/kitsu-api/references/endpoints.md b/plugins/kitsu/skills/kitsu-api/references/endpoints.md new file mode 100644 index 0000000..e619b34 --- /dev/null +++ b/plugins/kitsu/skills/kitsu-api/references/endpoints.md @@ -0,0 +1,742 @@ +# Kitsu API — Complete Endpoint Catalog + +**493 endpoints** across 24 categories. Base URL: `https:///api` + +All endpoints require JWT authentication via `Authorization: Bearer ` header, except `/auth/login` and `/auth/register`. + +## Table of Contents + +1. [Authentication](#authentication) — Login, logout, 2FA, SAML +2. [Projects](#projects) — CRUD, settings, team, stats +3. [Shots](#shots) — Shots, sequences, episodes, scenes +4. [Assets](#assets) — Assets, asset types, instances, sharing +5. [Tasks](#tasks) — Tasks, task types, task statuses, assignments, time tracking +6. [Comments](#comments) — Comments, replies, attachments +7. [Previews](#previews) — Preview files, thumbnails, movies +8. [Files](#files) — Working files, output files, software +9. [Casting/Breakdown](#casting) — Entity casting, asset instances +10. [Persons](#persons) — Users, departments, day-offs, time spents +11. [Playlists](#playlists) — Playlists, build jobs +12. [Events](#events) — Event log, login logs +13. [News](#news) — Activity feed +14. [Notifications](#notifications) — User notifications +15. [Search](#search) — Full-text search, filters +16. [Chat](#chat) — Entity chat, messages +17. [Entities](#entities) — Generic entity operations +18. [Concepts & Edits](#concepts-edits) — Concept art, edit entities +19. [Export/Import](#export-import) — CSV, Kitsu, ShotGrid, OTIO +20. [User Context](#user-context) — Current user's data +21. [Custom Actions](#custom-actions) — Webhook-like triggers +22. [Admin](#admin) — Status, config, organisations, studios +23. [Budgets & Scheduling](#budgets-scheduling) — Budgets, milestones, schedule items +24. [Media Files](#media-files) — Pictures, movies, thumbnails + +--- + +## Authentication + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/auth/login` | Login with email/password, returns JWT | +| GET | `/auth/logout` | Invalidate current token | +| GET | `/auth/authenticated` | Check if token is valid (200/401) | +| GET | `/auth/refresh-token` | Get new access token | +| POST | `/auth/register` | Register new user | +| POST | `/auth/change-password` | Change password (old_password, password, password_2) | +| POST | `/auth/reset-password` | Request password reset email | +| PUT | `/auth/reset-password` | Complete password reset (token, password) | +| PUT/POST/DELETE | `/auth/totp` | Pre-enable/enable/disable TOTP 2FA | +| GET/POST/DELETE | `/auth/email-otp` | Email OTP 2FA management | +| GET/PUT/POST/DELETE | `/auth/fido` | FIDO/WebAuthn device management | +| PUT | `/auth/recovery-codes` | Generate recovery codes | +| GET | `/auth/saml/login` | SAML SSO login redirect | +| POST | `/auth/saml/sso` | SAML SSO callback | + +## Projects + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/projects` | List all / create project | +| GET | `/data/projects/all` | All projects (admin) | +| GET | `/data/projects/open` | Open projects only | +| GET | `/data/projects/news` | News across all projects | +| GET/PUT/DELETE | `/data/projects/{id}` | Get/update/delete project | +| GET/POST | `/data/projects/{id}/team` | Get/add team members | +| DELETE | `/data/projects/{id}/team/{person_id}` | Remove from team | +| GET | `/data/projects/{id}/task-types` | Project task types | +| GET | `/data/projects/{id}/asset-types` | Project asset types | +| GET | `/data/projects/{id}/assets` | Project assets | +| GET | `/data/projects/{id}/comments` | All project comments | +| GET | `/data/projects/{id}/preview-files` | All project previews | +| GET | `/data/projects/{id}/attachment-files` | All project attachments | +| GET | `/data/projects/{id}/output-files` | All project output files | +| GET | `/data/projects/{id}/news` | Project news feed | +| GET | `/data/projects/{id}/news/{news_id}` | Specific news item | +| GET | `/data/projects/{id}/notifications` | Project notifications | +| GET | `/data/projects/{id}/playlists` | Project playlists | +| GET | `/data/projects/{id}/playlists/all` | All playlists including temp | +| POST | `/data/projects/{id}/playlists/temp` | Create temp playlist | +| GET | `/data/projects/{id}/subscriptions` | Project subscriptions | +| GET | `/data/projects/{id}/tasks` | All project tasks | +| GET | `/data/projects/{id}/time-spents` | Project time tracking | +| GET | `/data/projects/{id}/entity-links` | Project entity links | +| DELETE | `/data/projects/{id}/entity-links/{link_id}` | Remove entity link | +| GET | `/data/projects/{id}/build-jobs` | Build jobs for project | +| GET | `/data/projects/{id}/day-offs` | Project day offs | + +### Project Settings + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/data/projects/{id}/settings/asset-types` | Add asset type to project | +| DELETE | `/data/projects/{id}/settings/asset-types/{at_id}` | Remove asset type | +| POST | `/data/projects/{id}/settings/task-types` | Add task type to project | +| DELETE | `/data/projects/{id}/settings/task-types/{tt_id}` | Remove task type | +| GET/POST | `/data/projects/{id}/settings/task-status` | Get/add task statuses | +| DELETE | `/data/projects/{id}/settings/task-status/{ts_id}` | Remove task status | +| GET/POST | `/data/projects/{id}/settings/status-automations` | Status automations | +| DELETE | `/data/projects/{id}/settings/status-automations/{sa_id}` | Remove automation | +| GET/POST | `/data/projects/{id}/settings/preview-background-files` | Preview BG files | +| DELETE | `/data/projects/{id}/settings/preview-background-files/{pbf_id}` | Remove BG | +| POST | `/actions/projects/{id}/set-file-tree` | Set project file tree template | +| POST | `/actions/projects/{id}/delete-tasks` | Batch delete tasks | + +### Project Metadata + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/projects/{id}/metadata-descriptors` | Custom metadata fields | +| POST | `/data/projects/{id}/metadata-descriptors/reorder` | Reorder descriptors | +| GET/PUT/DELETE | `/data/projects/{id}/metadata-descriptors/{desc_id}` | CRUD descriptor | + +### Project Stats & Quotas + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/projects/{id}/episodes/stats` | Episode statistics | +| GET | `/data/projects/{id}/episodes/retake-stats` | Retake statistics | +| GET | `/data/projects/{id}/quotas/{task_type_id}` | Quotas by task type | +| GET | `/data/projects/{id}/quotas/persons/{person_id}` | Personal quotas | +| GET | `/data/projects/{id}/task-types/{tt_id}/time-spents` | Time by task type | + +## Shots + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/shots` | All shots (filterable) | +| GET | `/data/shots/all` | All shots (admin) | +| GET | `/data/shots/with-tasks` | Shots with embedded tasks | +| GET/PUT/DELETE | `/data/shots/{id}` | Get/update/delete shot | +| GET | `/data/shots/{id}/tasks` | Shot's tasks | +| GET | `/data/shots/{id}/task-types` | Shot's task types | +| GET | `/data/shots/{id}/assets` | Assets linked to shot | +| GET | `/data/shots/{id}/asset-types` | Asset types in shot | +| GET | `/data/shots/{id}/preview-files` | Shot preview files | +| GET | `/data/shots/{id}/versions` | Shot versions | +| GET/POST | `/data/shots/{id}/asset-instances` | Shot asset instances | +| DELETE | `/data/shots/{id}/asset-instances/{ai_id}` | Remove instance | +| POST | `/data/projects/{id}/shots` | Create shot in project | + +### Sequences + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/sequences` | All sequences | +| GET | `/data/sequences/with-tasks` | Sequences with tasks | +| GET/DELETE | `/data/sequences/{id}` | Get/delete sequence | +| GET | `/data/sequences/{id}/shots` | Shots in sequence | +| GET | `/data/sequences/{id}/shot-tasks` | All shot tasks in sequence | +| GET | `/data/sequences/{id}/scenes` | Scenes in sequence | +| GET | `/data/sequences/{id}/task-types` | Sequence task types | +| GET | `/data/sequences/{id}/tasks` | Sequence tasks | +| GET/POST | `/data/projects/{id}/sequences` | Project sequences | + +### Episodes + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/episodes` | All episodes | +| GET | `/data/episodes/with-tasks` | Episodes with tasks | +| GET/DELETE | `/data/episodes/{id}` | Get/delete episode | +| GET | `/data/episodes/{id}/sequences` | Episode sequences | +| GET | `/data/episodes/{id}/shots` | Episode shots | +| GET | `/data/episodes/{id}/shot-tasks` | Shot tasks for episode | +| GET | `/data/episodes/{id}/asset-tasks` | Asset tasks for episode | +| GET | `/data/episodes/{id}/edit-tasks` | Edit tasks for episode | +| GET | `/data/episodes/{id}/edits` | Edits for episode | +| GET | `/data/episodes/{id}/task-types` | Episode task types | +| GET | `/data/episodes/{id}/tasks` | Episode tasks | +| GET/POST | `/data/projects/{id}/episodes` | Project episodes | +| GET | `/data/projects/{id}/episodes/casting` | All episode casting | +| GET | `/data/projects/{id}/episodes/{ep_id}/playlists` | Episode playlists | +| GET | `/data/projects/{id}/episodes/{ep_id}/sequences/all/casting` | Casting for all sequences | +| GET | `/data/projects/{id}/episodes/{ep_id}/assets/shared-used` | Shared assets in episode | + +### Scenes + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/scenes/all` | All scenes | +| GET | `/data/scenes/with-tasks` | Scenes with tasks | +| GET/DELETE | `/data/scenes/{id}` | Get/delete scene | +| GET/POST | `/data/scenes/{id}/asset-instances` | Scene asset instances | +| GET | `/data/scenes/{id}/camera-instances` | Camera instances | +| GET/POST | `/data/scenes/{id}/shots` | Scene shots | +| DELETE | `/data/scenes/{id}/shots/{shot_id}` | Remove shot from scene | +| GET | `/data/scenes/{id}/task-types` | Scene task types | +| GET | `/data/scenes/{id}/tasks` | Scene tasks | +| GET/POST | `/data/projects/{id}/scenes` | Project scenes | + +## Assets + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/assets` | All assets (filterable by project_id, asset_type_id) | +| GET | `/data/assets/all` | All assets (admin) | +| GET | `/data/assets/with-tasks` | Assets with embedded tasks | +| GET/DELETE | `/data/assets/{id}` | Get/delete asset (force=true to force) | +| GET | `/data/assets/{id}/tasks` | Asset's tasks | +| GET | `/data/assets/{id}/task-types` | Asset's task types | +| GET | `/data/assets/{id}/assets` | Linked assets | +| GET | `/data/assets/{id}/cast-in` | Shots this asset appears in | +| GET/PUT | `/data/assets/{id}/casting` | Get/set asset casting | +| GET | `/data/assets/{id}/shot-asset-instances` | Instances in shots | +| GET | `/data/assets/{id}/scene-asset-instances` | Instances in scenes | +| GET/POST | `/data/assets/{id}/asset-asset-instances` | Asset instances | +| GET | `/data/asset-types` | All asset types | +| GET | `/data/asset-types/{id}` | Specific asset type | +| GET | `/data/projects/{id}/asset-types` | Project asset types | +| GET | `/data/projects/{id}/asset-types/{at_id}/assets` | Assets of type | +| POST | `/data/projects/{id}/asset-types/{at_id}/assets/new` | Create asset | +| GET | `/data/projects/{id}/asset-types/{at_id}/casting` | Casting by type | +| GET | `/data/projects/{id}/assets` | Project assets | +| GET | `/data/projects/{id}/assets/shared-used` | Shared assets | + +### Asset Sharing + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/actions/assets/share` | Share assets across projects | +| POST | `/actions/projects/{id}/assets/share` | Share project assets | +| POST | `/actions/projects/{id}/asset-types/{at_id}/assets/share` | Share by type | + +## Tasks + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/tasks` | List/create tasks | +| GET | `/data/tasks/open-tasks` | Open tasks | +| GET | `/data/tasks/open-tasks/stats` | Open tasks stats | +| GET/PUT/DELETE | `/data/tasks/{id}` | Get/update/delete task | +| GET | `/data/tasks/{id}/full` | Full task with all relations | +| GET | `/data/tasks/{id}/comments` | Task comments | +| GET/DELETE | `/data/tasks/{id}/comments/{cid}` | Get/delete comment | +| POST | `/data/tasks/{id}/comments/{cid}/reply` | Reply to comment | +| DELETE | `/data/tasks/{id}/comments/{cid}/reply/{rid}` | Delete reply | +| POST | `/data/tasks/{id}/comments/{cid}/ack` | Acknowledge comment | +| DELETE | `/data/tasks/{id}/comments/{cid}/attachments/{aid}` | Delete attachment | +| GET | `/data/tasks/{id}/previews` | Task preview files | +| GET | `/data/tasks/{id}/attachment-files` | Task attachments | +| GET | `/data/tasks/{id}/working-files` | Task working files | +| GET | `/data/tasks/{id}/working-files/last-revisions` | Latest working files | +| POST | `/data/tasks/{id}/working-files/new` | Create working file | +| POST | `/data/tasks/{id}/working-file-path` | Get working file path | + +### Task Actions + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/actions/tasks/{id}/comment` | Add comment to task | +| POST | `/actions/tasks/{id}/batch-comment` | Batch comment | +| POST | `/actions/tasks/batch-comment` | Batch comment (multi-task) | +| PUT | `/actions/tasks/{id}/assign` | Assign task | +| PUT | `/actions/tasks/clear-assignation` | Clear assignments | +| PUT | `/actions/tasks/{id}/set-main-preview` | Set main preview | +| PUT | `/actions/tasks/{id}/to-review` | Send to review | +| POST | `/actions/tasks/{id}/comments/{cid}/add-preview` | Add preview to comment | +| POST | `/actions/tasks/{id}/comments/{cid}/add-attachment` | Add attachment | +| POST/DELETE | `/actions/tasks/{id}/comments/{cid}/preview-files/{pfid}` | Manage previews | +| POST | `/actions/projects/{id}/tasks/comment-many` | Comment many tasks | + +### Task Creation (Bulk) + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/actions/projects/{id}/task-types/{tt_id}/shots/create-tasks` | Create tasks for all shots | +| POST | `/actions/projects/{id}/task-types/{tt_id}/assets/create-tasks` | Create tasks for all assets | +| POST | `/actions/projects/{id}/task-types/{tt_id}/edits/create-tasks` | Create tasks for all edits | +| POST | `/actions/projects/{id}/task-types/{tt_id}/concepts/create-tasks` | Create tasks for concepts | +| DELETE | `/actions/projects/{id}/task-types/{tt_id}/delete-tasks` | Delete all tasks of type | + +### Time Tracking + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/time-spents/` | List/create time spents | +| GET/PUT/DELETE | `/data/time-spents/{id}` | CRUD time spent | +| GET | `/actions/tasks/{id}/time-spents` | Task time spents | +| GET | `/actions/tasks/{id}/time-spents/{date}` | Task time on date | +| POST/DELETE | `/actions/tasks/{id}/time-spents/{date}/persons/{pid}` | Set/delete person time | +| POST | `/actions/tasks/{id}/time-spents/{date}/persons/{pid}/add` | Add time | + +### Task Types & Statuses + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/task-types` | List/create task types | +| GET/PUT/DELETE | `/data/task-types/{id}` | CRUD task type | +| GET/POST | `/data/task-status` | List/create task statuses | +| GET/PUT/DELETE | `/data/task-status/{id}` | CRUD task status | +| POST | `/data/task-type-links` | Link task types | +| POST | `/data/task-status-links` | Link task statuses | +| POST | `/actions/projects/{id}/task-types/{tt_id}/set-shot-nb-frames` | Set frame counts | + +## Comments + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/comments` | List/create comments | +| GET/PUT/DELETE | `/data/comments/{id}` | CRUD comment | + +## Previews + +### Preview Actions + +| Method | Path | Description | +|--------|------|-------------| +| PUT | `/actions/preview-files/{id}/set-main-preview` | Set as main preview | +| PUT | `/actions/preview-files/{id}/update-annotations` | Update annotations | +| PUT | `/actions/preview-files/{id}/update-position` | Update position | +| GET | `/actions/preview-files/{id}/extract-frame` | Extract frame | +| GET | `/actions/preview-files/{id}/extract-tile` | Extract tile | + +### Preview Data + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/preview-files` | List/create preview files | +| GET/PUT/DELETE | `/data/preview-files/{id}` | CRUD preview file | +| GET/POST | `/data/preview-background-files` | Background files | +| GET/PUT/DELETE | `/data/preview-background-files/{id}` | CRUD background file | + +## Files + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/files/{id}` | Get file info | +| GET/POST | `/data/working-files` | List/create working files | +| GET/PUT/DELETE | `/data/working-files/{id}` | CRUD working file | +| GET/POST | `/data/working-files/{id}/file` | Upload/download file | +| PUT | `/actions/working-files/{id}/comment` | Comment on working file | +| PUT | `/actions/working-files/{id}/modified` | Mark as modified | +| GET/POST | `/data/output-files` | List/create output files | +| GET/PUT/DELETE | `/data/output-files/{id}` | CRUD output file | +| GET/POST | `/data/output-types` | List/create output types | +| GET/PUT/DELETE | `/data/output-types/{id}` | CRUD output type | +| GET/POST | `/data/attachment-files` | List/create attachments | +| GET/PUT/DELETE | `/data/attachment-files/{id}` | CRUD attachment | +| GET | `/data/attachment-files/{id}/file/{filename}` | Download attachment | +| GET/POST | `/data/softwares` | List/create software entries | +| GET/PUT/DELETE | `/data/softwares/{id}` | CRUD software | +| GET/POST | `/data/file-status/` | File statuses | +| GET/PUT/DELETE | `/data/file-status/{id}` | CRUD file status | + +### Entity File Operations + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/entities/{id}/working-files` | Entity working files | +| GET | `/data/entities/{id}/output-files` | Entity output files | +| GET | `/data/entities/{id}/output-files/last-revisions` | Latest output revisions | +| POST | `/data/entities/{id}/output-files/new` | Create output file | +| POST | `/data/entities/{id}/output-files/next-revision` | Next revision number | +| POST | `/data/entities/{id}/output-file-path` | Get output file path | +| GET | `/data/entities/{id}/output-types` | Entity output types | +| GET | `/data/entities/{id}/output-types/{ot_id}/output-files` | Files by output type | +| GET | `/data/entities/{id}/preview-files` | Entity preview files | + +### Asset Instance File Operations + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/asset-instances/{ai_id}/output-files` | Instance output files | +| GET | `/data/asset-instances/{ai_id}/entities/{te_id}/output-files/last-revisions` | Latest revisions | +| POST | `/data/asset-instances/{ai_id}/entities/{te_id}/output-files/new` | Create output | +| POST | `/data/asset-instances/{ai_id}/entities/{te_id}/output-files/next-revision` | Next revision | +| POST | `/data/asset-instances/{ai_id}/entities/{te_id}/output-file-path` | Get path | +| GET | `/data/asset-instances/{ai_id}/entities/{te_id}/output-types` | Output types | +| GET | `/data/asset-instances/{ai_id}/entities/{te_id}/output-types/{ot_id}/output-files` | Files | + +## Casting + +| Method | Path | Description | +|--------|------|-------------| +| GET/PUT | `/data/projects/{id}/entities/{eid}/casting` | Entity casting | +| GET | `/data/projects/{id}/asset-types/{at_id}/casting` | Casting by asset type | +| GET | `/data/projects/{id}/sequences/all/casting` | All sequence casting | +| GET | `/data/projects/{id}/sequences/{sid}/casting` | Sequence casting | +| GET/POST | `/data/asset-instances/` | Asset instances | +| GET/PUT/DELETE | `/data/asset-instances/{id}` | CRUD asset instance | + +## Persons + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/persons` | List/create persons | +| GET/PUT/DELETE | `/data/persons/{id}` | CRUD person | +| GET | `/data/persons/{id}/tasks` | Person's tasks | +| GET | `/data/persons/{id}/done-tasks` | Person's completed tasks | +| GET | `/data/persons/{id}/time-spents` | All time spents | +| GET | `/data/persons/{id}/time-spents/{date}` | Time on specific date | +| GET | `/data/persons/{id}/time-spents/day/{y}/{m}/{d}` | Daily time | +| GET | `/data/persons/{id}/time-spents/week/{y}/{w}` | Weekly time | +| GET | `/data/persons/{id}/time-spents/month/{y}/{m}` | Monthly time | +| GET | `/data/persons/{id}/time-spents/month/all/{y}/{m}` | Monthly all | +| GET | `/data/persons/{id}/time-spents/year/{y}` | Yearly time | +| GET | `/data/persons/{id}/related-tasks/{tt_id}` | Related tasks | +| GET | `/data/persons/{id}/quota-shots/day/{y}/{m}/{d}` | Daily quota | +| GET | `/data/persons/{id}/quota-shots/week/{y}/{w}` | Weekly quota | +| GET | `/data/persons/{id}/quota-shots/month/{y}/{m}` | Monthly quota | +| GET/POST | `/data/persons/{id}/desktop-login-logs` | Login logs | + +### Person Aggregate Tables + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/persons/time-spents/day-table/{y}/{m}` | All persons daily | +| GET | `/data/persons/time-spents/week-table/{y}` | All persons weekly | +| GET | `/data/persons/time-spents/month-table/{y}` | All persons monthly | +| GET | `/data/persons/time-spents/year-table/` | All persons yearly | +| GET | `/data/persons/task-dates` | All persons task dates | +| GET | `/data/persons/presence-logs/{month_date}` | Presence logs | + +### Day Offs + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/day-offs/` | List/create day offs | +| GET/PUT/DELETE | `/data/day-offs/{id}` | CRUD day off | +| GET | `/data/persons/{id}/day-offs` | Person day offs | +| GET | `/data/persons/{id}/day-offs/{date}` | Day offs on date | +| GET | `/data/persons/{id}/day-offs/week/{y}/{w}` | Weekly day offs | +| GET | `/data/persons/{id}/day-offs/month/{y}/{m}` | Monthly day offs | +| GET | `/data/persons/{id}/day-offs/year/{y}` | Yearly day offs | +| GET | `/data/persons/day-offs/{y}/{m}` | All persons day offs | + +### Person Actions + +| Method | Path | Description | +|--------|------|-------------| +| PUT | `/actions/persons/{id}/assign` | Assign person | +| POST | `/actions/persons/{id}/change-password` | Change password | +| DELETE | `/actions/persons/{id}/clear-avatar` | Clear avatar | +| POST | `/actions/persons/{id}/departments/add` | Add to department | +| DELETE | `/actions/persons/{id}/departments/{did}` | Remove from dept | +| DELETE | `/actions/persons/{id}/disable-two-factor-authentication` | Disable 2FA | +| GET | `/actions/persons/{id}/invite` | Send invite | + +### Departments + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/departments` | List/create departments | +| GET/PUT/DELETE | `/data/departments/{id}` | CRUD department | +| GET | `/data/departments/hardware-items` | All hardware items | +| GET | `/data/departments/software-licenses` | All software licenses | +| POST | `/data/departments/{id}/hardware-items` | Add hardware | +| GET/DELETE | `/data/departments/{id}/hardware-items/{hid}` | Get/remove | +| POST | `/data/departments/{id}/software-licenses` | Add license | +| GET/DELETE | `/data/departments/{id}/software-licenses/{sid}` | Get/remove | + +## Playlists + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/playlists/` | List/create playlists | +| GET/PUT/DELETE | `/data/playlists/{id}` | CRUD playlist | +| GET | `/data/playlists/entities/{eid}/preview-files` | Entity previews in playlists | +| GET | `/data/playlists/preview-files/running` | Running preview builds | +| GET | `/data/playlists/{id}/build/mp4` | Build MP4 | +| GET | `/data/playlists/{id}/download/zip` | Download ZIP | +| GET/DELETE | `/data/playlists/{id}/jobs/{jid}` | Build job status | +| GET | `/data/playlists/{id}/jobs/{jid}/build/mp4` | Job MP4 | +| POST | `/data/playlists/{id}/notify-clients` | Notify clients | +| POST | `/actions/playlists/{id}/add-entity` | Add entity to playlist | + +## Events + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/events/` | List/create events | +| GET | `/data/events/last` | Recent events (page_size, before, after, only_files) | +| GET | `/data/events/login-logs/last` | Recent login logs | +| GET/PUT/DELETE | `/data/events/{id}` | CRUD event | + +## News + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/news/` | List/create news | +| GET/PUT/DELETE | `/data/news/{id}` | CRUD news | +| GET | `/data/entities/{id}/news` | Entity news | + +## Notifications + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/notifications/` | List/create notifications | +| GET/PUT/DELETE | `/data/notifications/{id}` | CRUD notification | + +## Search + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/data/search` | Full-text search (query, project_id, limit, offset, index_names) | +| GET/POST | `/data/search-filters/` | Saved search filters | +| GET/PUT/DELETE | `/data/search-filters/{id}` | CRUD search filter | +| GET/POST | `/data/search-filter-groups/` | Filter groups | +| GET/PUT/DELETE | `/data/search-filter-groups/{id}` | CRUD filter group | + +## Chat + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/chats/` | List/create chats | +| GET/PUT/DELETE | `/data/chats/{id}` | CRUD chat | +| GET/POST | `/data/chat-messages/` | List/create messages | +| GET/PUT/DELETE | `/data/chat-messages/{id}` | CRUD message | +| GET | `/data/entities/{eid}/chat` | Entity chat | +| GET/POST | `/data/entities/{eid}/chat/messages` | Entity chat messages | +| GET/DELETE | `/data/entities/{eid}/chat/messages/{mid}` | Get/delete message | +| POST/DELETE | `/actions/user/chats/{eid}/join` | Join/leave entity chat | + +## Entities (Generic) + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/entities` | List/create entities | +| GET/PUT/DELETE | `/data/entities/{id}` | CRUD entity | +| POST | `/data/entities/guess_from_path` | Guess entity from file path | +| GET | `/data/entities/{id}/entities-linked/with-tasks` | Linked entities with tasks | +| GET | `/data/entities/{id}/time-spents` | Entity time spents | +| GET | `/data/entities/{id}/task-types/{tt_id}/tasks` | Tasks by type | +| GET/POST | `/data/entity-links/` | Entity links | +| GET/PUT/DELETE | `/data/entity-links/{id}` | CRUD entity link | +| GET/POST | `/data/entity-types` | Entity types | +| GET/PUT/DELETE | `/data/entity-types/{id}` | CRUD entity type | + +## Concepts & Edits + +### Concepts + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/concepts` | All concepts | +| GET | `/data/concepts/with-tasks` | Concepts with tasks | +| GET/DELETE | `/data/concepts/{id}` | Get/delete concept | +| GET | `/data/concepts/{id}/preview-files` | Concept previews | +| GET | `/data/concepts/{id}/task-types` | Concept task types | +| GET | `/data/concepts/{id}/tasks` | Concept tasks | +| GET/POST | `/data/projects/{id}/concepts` | Project concepts | + +### Edits + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/edits` | All edits | +| GET | `/data/edits/all` | All edits (admin) | +| GET | `/data/edits/with-tasks` | Edits with tasks | +| GET/DELETE | `/data/edits/{id}` | Get/delete edit | +| GET | `/data/edits/{id}/preview-files` | Edit previews | +| GET | `/data/edits/{id}/task-types` | Edit task types | +| GET | `/data/edits/{id}/tasks` | Edit tasks | +| GET | `/data/edits/{id}/versions` | Edit versions | +| GET/POST | `/data/projects/{id}/edits` | Project edits | + +## Export/Import + +### CSV Export + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/export/csv/persons.csv` | Export persons | +| GET | `/export/csv/projects.csv` | Export projects | +| GET | `/export/csv/task-types.csv` | Export task types | +| GET | `/export/csv/tasks.csv` | Export tasks | +| GET | `/export/csv/time-spents.csv` | Export time spents | +| GET | `/export/csv/projects/{id}/assets.csv` | Export assets | +| GET | `/export/csv/projects/{id}/shots.csv` | Export shots | +| GET | `/export/csv/projects/{id}/edits.csv` | Export edits | +| GET | `/export/csv/projects/{id}/casting.csv` | Export casting | +| GET | `/export/csv/playlists/{id}` | Export playlist | + +### CSV Import + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/import/csv/persons` | Import persons | +| POST | `/import/csv/projects/{id}/assets` | Import assets | +| POST | `/import/csv/projects/{id}/shots` | Import shots | +| POST | `/import/csv/projects/{id}/edits` | Import edits | +| POST | `/import/csv/projects/{id}/casting` | Import casting | +| POST | `/import/csv/projects/{id}/task-types/{tt_id}/estimations` | Import estimations | +| POST | `/import/csv/projects/{id}/episodes/{ep_id}/task-types/{tt_id}/estimations` | Episode estimations | + +### Kitsu Import + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/import/kitsu/projects` | Import from Kitsu | +| POST | `/import/kitsu/entities` | Import entities | +| POST | `/import/kitsu/tasks` | Import tasks | +| POST | `/import/kitsu/comments` | Import comments | +| POST | `/import/kitsu/entity-links` | Import links | + +### OTIO Import + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/import/otio/projects/{id}` | Import OTIO | +| POST | `/import/otio/projects/{id}/episodes/{ep_id}` | Import OTIO with episode | + +## User Context (Current User) + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/data/user/context` | Current user context | +| GET | `/data/user/tasks` | My tasks | +| GET | `/data/user/tasks-to-check` | Tasks to review | +| GET | `/data/user/done-tasks` | My completed tasks | +| GET | `/data/user/notifications` | My notifications | +| GET/PUT | `/data/user/notifications/{id}` | Get/mark notification | +| POST | `/actions/user/notifications/mark-all-as-read` | Mark all read | +| GET | `/data/user/chats` | My chats | +| GET | `/data/user/time-spents` | My time spents | +| GET | `/data/user/time-spents/{date}` | My time on date | +| GET | `/data/user/day-offs/{date}` | My day offs | +| GET/POST | `/data/user/filters` | My saved filters | +| PUT/DELETE | `/data/user/filters/{id}` | Update/delete filter | +| GET/POST | `/data/user/filter-groups` | My filter groups | +| GET/PUT/DELETE | `/data/user/filter-groups/{id}` | CRUD filter group | +| GET/POST | `/data/user/desktop-login-logs` | Desktop login logs | +| DELETE | `/actions/user/clear-avatar` | Clear my avatar | +| GET | `/data/user/projects/open` | My open projects | +| GET | `/data/user/projects/{id}/asset-types` | My project asset types | +| GET | `/data/user/projects/{id}/asset-types/{at_id}/assets` | My project assets | +| GET | `/data/user/projects/{id}/episodes` | My project episodes | +| GET | `/data/user/projects/{id}/sequences` | My project sequences | +| GET | `/data/user/projects/{id}/task-types/{tt_id}/sequence-subscriptions` | My subscriptions | +| GET | `/data/user/assets/{id}/task-types` | My asset task types | +| GET | `/data/user/assets/{id}/tasks` | My asset tasks | +| GET | `/data/user/shots/{id}/task-types` | My shot task types | +| GET | `/data/user/shots/{id}/tasks` | My shot tasks | +| GET | `/data/user/sequences/{id}/shots` | My sequence shots | +| GET | `/data/user/sequences/{id}/scenes` | My sequence scenes | +| GET | `/data/user/sequences/{id}/task-types` | My sequence task types | +| GET | `/data/user/sequences/{id}/tasks` | My sequence tasks | +| GET | `/data/user/scenes/{id}/task-types` | My scene task types | +| GET | `/data/user/scenes/{id}/tasks` | My scene tasks | +| GET | `/data/user/entities/{sid}/task-types/{tt_id}/subscribed` | Subscription status | +| GET | `/data/user/tasks/{id}/subscribed` | Task subscription status | +| GET | `/data/user/tasks/{id}/time-spents/{date}` | My task time | +| POST | `/actions/user/tasks/{id}/subscribe` | Subscribe to task | +| DELETE | `/actions/user/tasks/{id}/unsubscribe` | Unsubscribe from task | +| POST | `/actions/user/sequences/{sid}/task-types/{tt_id}/subscribe` | Subscribe to sequence | +| DELETE | `/actions/user/sequences/{sid}/task-types/{tt_id}/unsubscribe` | Unsubscribe | + +## Custom Actions + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/custom-actions/` | List/create custom actions | +| GET/PUT/DELETE | `/data/custom-actions/{id}` | CRUD custom action | + +## Admin & System + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/` | API root | +| GET | `/config` | Server configuration | +| GET | `/stats` | Server statistics | +| GET | `/status` | Server status | +| GET | `/status.txt` | Status text | +| GET | `/status/influx` | InfluxDB metrics | +| GET | `/status/resources` | Resource usage | +| GET | `/status/test-event` | Test SSE event | +| GET/POST | `/data/organisations` | Organisations | +| GET/PUT/DELETE | `/data/organisations/{id}` | CRUD organisation | +| GET/POST | `/data/studios` | Studios | +| GET/PUT/DELETE | `/data/studios/{id}` | CRUD studio | +| GET/POST | `/data/project-status` | Project statuses | +| GET/PUT/DELETE | `/data/project-status/{id}` | CRUD project status | +| GET/POST | `/data/status-automations/` | Status automations | +| GET/PUT/DELETE | `/data/status-automations/{id}` | CRUD status automation | +| GET/POST | `/data/plugins` | Plugins | +| GET/PUT/DELETE | `/data/plugins/{id}` | CRUD plugin | +| GET/POST | `/data/subscriptions/` | Subscriptions | +| GET/PUT/DELETE | `/data/subscriptions/{id}` | CRUD subscription | +| GET/POST | `/data/salary-scales` | Salary scales | +| GET/PUT/DELETE | `/data/salary-scales/{id}` | CRUD salary scale | +| GET/POST | `/data/hardware-items` | Hardware items | +| GET/PUT/DELETE | `/data/hardware-items/{id}` | CRUD hardware item | + +## Budgets & Scheduling + +| Method | Path | Description | +|--------|------|-------------| +| GET/POST | `/data/projects/{id}/budgets` | Project budgets | +| GET | `/data/projects/{id}/budgets/time-spents` | Budget time spents | +| GET/PUT/DELETE | `/data/projects/{id}/budgets/{bid}` | CRUD budget | +| GET/POST | `/data/projects/{id}/budgets/{bid}/entries` | Budget entries | +| GET/PUT/DELETE | `/data/projects/{id}/budgets/{bid}/entries/{eid}` | CRUD entry | +| GET/POST | `/data/milestones/` | Milestones | +| GET/PUT/DELETE | `/data/milestones/{id}` | CRUD milestone | +| GET | `/data/projects/{id}/milestones` | Project milestones | +| GET/POST | `/data/schedule-items/` | Schedule items | +| GET/PUT/DELETE | `/data/schedule-items/{id}` | CRUD schedule item | +| GET | `/data/projects/{id}/schedule-items` | Project schedule items | +| GET | `/data/projects/{id}/schedule-items/task-types` | Schedule by task type | +| GET | `/data/projects/{id}/schedule-items/{tt_id}/asset-types` | By asset type | +| GET | `/data/projects/{id}/schedule-items/{tt_id}/episodes` | By episode | +| GET | `/data/projects/{id}/schedule-items/{tt_id}/sequences` | By sequence | +| GET/POST | `/data/production-schedule-versions` | Schedule versions | +| GET/PUT/DELETE | `/data/production-schedule-versions/{id}` | CRUD version | +| GET | `/data/production-schedule-versions/{id}/task-links` | Version task links | +| GET/POST | `/data/production-schedule-version-task-links` | Task links | +| GET/PUT/DELETE | `/data/production-schedule-version-task-links/{id}` | CRUD task link | +| POST | `/actions/production-schedule-versions/{id}/apply-to-production` | Apply schedule | +| POST | `/actions/production-schedule-versions/{id}/set-task-links-from-production` | Sync from prod | +| POST | `/actions/production-schedule-versions/{id}/set-task-links-from-production-schedule-version` | Sync from version | + +## Media Files + +### Pictures + +| Method | Path | Description | +|--------|------|-------------| +| POST | `/pictures/preview-files/{id}` | Upload preview image | +| GET | `/pictures/originals/preview-files/{id}.png` | Original PNG | +| GET | `/pictures/originals/preview-files/{id}.{ext}` | Original (any ext) | +| GET | `/pictures/originals/preview-files/{id}/download` | Download original | +| GET | `/pictures/previews/preview-files/{id}.png` | Preview size | +| GET | `/pictures/thumbnails/preview-files/{id}.png` | Thumbnail | +| GET | `/pictures/thumbnails-square/preview-files/{id}.png` | Square thumbnail | +| GET | `/pictures/thumbnails/attachment-files/{id}.png` | Attachment thumbnail | +| GET/POST | `/pictures/thumbnails/persons/{id}` | Person avatar | +| GET/POST | `/pictures/thumbnails/persons/{id}.png` | Person avatar PNG | +| GET/POST | `/pictures/thumbnails/projects/{id}` | Project thumbnail | +| GET/POST | `/pictures/thumbnails/projects/{id}.png` | Project thumbnail PNG | +| GET/POST | `/pictures/thumbnails/organisations/{id}` | Org thumbnail | +| GET/POST | `/pictures/thumbnails/organisations/{id}.png` | Org thumbnail PNG | +| POST | `/pictures/preview-background-files/{id}` | Upload BG file | +| GET | `/pictures/preview-background-files/{id}.{ext}` | Get BG file | +| GET/POST | `/pictures/thumbnails/preview-background-files/{id}.png` | BG thumbnail | + +### Movies + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/movies/originals/preview-files/{id}.mp4` | Original movie | +| GET | `/movies/originals/preview-files/{id}/download` | Download movie | +| GET | `/movies/low/preview-files/{id}.mp4` | Low-res movie | +| GET | `/movies/tiles/preview-files/{id}.png` | Movie tile strip | diff --git a/plugins/kitsu/skills/kitsu-api/references/events.md b/plugins/kitsu/skills/kitsu-api/references/events.md new file mode 100644 index 0000000..b6f24f0 --- /dev/null +++ b/plugins/kitsu/skills/kitsu-api/references/events.md @@ -0,0 +1,183 @@ +# Kitsu Event Types + +Events are emitted in real-time via Server-Sent Events (SSE) and stored in the event log. + +## Listening to Events + +```python +import gazu + +gazu.set_host("https://kitsu.mystudio.com/api") +gazu.set_event_host("https://kitsu.mystudio.com") # no /api suffix +gazu.log_in("user@studio.com", "password") + +event_client = gazu.events.init() + +def handler(data): + print(data) + +gazu.events.add_listener(event_client, "task:status-changed", handler) +gazu.events.run_client(event_client) # blocks +``` + +Run the listener in a separate thread since `run_client()` blocks. + +## Event Naming Convention + +Pattern: `entity_type:action` + +Entity type is the model name lowercased with spaces replaced by hyphens. + +## Callback Data + +The callback `data` dict always includes: +- The entity ID (e.g., `asset_id`, `shot_id`, `task_id`) +- `project_id` when relevant + +## Complete Event List + +### Assets +- `asset:new` — Asset created +- `asset:update` — Asset updated +- `asset:delete` — Asset deleted +- `asset:new-link` — Asset linked to another entity +- `asset:remove-link` — Asset link removed +- `asset:casting-update` — Asset casting changed +- `asset-type:new` — Asset type created +- `asset-type:update` — Asset type updated +- `asset-type:delete` — Asset type deleted +- `asset-instance:new` — Asset instance created +- `asset-instance:update` — Asset instance updated +- `asset-instance:delete` — Asset instance deleted + +### Shots / Sequences / Episodes / Scenes +- `shot:new` — Shot created +- `shot:update` — Shot updated +- `shot:delete` — Shot deleted +- `shot:casting-update` — Shot casting changed +- `sequence:new` — Sequence created +- `sequence:update` — Sequence updated +- `sequence:delete` — Sequence deleted +- `episode:new` — Episode created +- `episode:update` — Episode updated +- `episode:delete` — Episode deleted +- `scene:new` — Scene created +- `scene:update` — Scene updated +- `scene:delete` — Scene deleted + +### Tasks +- `task:new` — Task created +- `task:update` — Task updated +- `task:delete` — Task deleted +- `task:assign` — Person assigned to task +- `task:unassign` — Person unassigned from task +- `task:status-changed` — Task status changed (via comment) +- `task:to-review` — Task sent for review +- `task-type:new` — Task type created +- `task-type:update` — Task type updated +- `task-type:delete` — Task type deleted +- `task-status:new` — Task status created +- `task-status:update` — Task status updated +- `task-status:delete` — Task status deleted + +### Comments +- `comment:new` — Comment posted +- `comment:update` — Comment edited +- `comment:delete` — Comment removed +- `comment:reply` — Reply added to comment +- `comment:acknowledge` — Comment acknowledged + +### Files +- `preview-file:new` — Preview file uploaded +- `preview-file:update` — Preview file updated +- `working-file:new` — Working file created +- `output-file:new` — Output file created +- `output-file:update` — Output file updated +- `output-file:delete` — Output file deleted + +### Persons +- `person:new` — Person created +- `person:update` — Person updated +- `person:delete` — Person deleted + +### Projects +- `project:new` — Project created +- `project:update` — Project updated +- `project:delete` — Project deleted + +### Edits & Concepts +- `edit:new` — Edit created +- `edit:update` — Edit updated +- `edit:delete` — Edit deleted +- `concept:new` — Concept created +- `concept:update` — Concept updated +- `concept:delete` — Concept deleted + +### Playlists +- `playlist:new` — Playlist created +- `playlist:update` — Playlist updated +- `playlist:delete` — Playlist deleted + +### Notifications +- `notification:new` — Notification created +- `notification:update` — Notification updated +- `notification:delete` — Notification deleted + +### Chat +- `chat:new` — Chat created +- `chat:update` — Chat updated +- `chat:delete` — Chat deleted +- `chat-message:new` — Message sent +- `chat-message:update` — Message edited +- `chat-message:delete` — Message deleted + +### News +- `news:new` — News item created +- `news:update` — News item updated +- `news:delete` — News item deleted + +### Entity Links +- `entity-link:new` — Entity link created +- `entity-link:delete` — Entity link removed + +### Budget +- `budget:new` — Budget created +- `budget:update` — Budget updated +- `budget:delete` — Budget deleted +- `budget-entry:new` — Budget entry created +- `budget-entry:update` — Budget entry updated +- `budget-entry:delete` — Budget entry deleted + +### Schedule +- `schedule-item:new` — Schedule item created +- `schedule-item:update` — Schedule item updated +- `schedule-item:delete` — Schedule item deleted +- `milestone:new` — Milestone created +- `milestone:update` — Milestone updated +- `milestone:delete` — Milestone deleted + +### Time Tracking +- `time-spent:new` — Time entry created +- `time-spent:update` — Time entry updated +- `time-spent:delete` — Time entry deleted + +### Build Jobs +- `build-job:new` — Build job created +- `build-job:update` — Build job updated +- `build-job:delete` — Build job deleted + +## Reading Event History + +```python +# Recent events +events = gazu.client.get("data/events/last?page_size=100") + +# Filter by date +events = gazu.client.get("data/events/last?page_size=100&before=2024-02-01&after=2024-01-01") + +# File events only +events = gazu.client.get("data/events/last?page_size=100&only_files=true") + +# Login logs +logs = gazu.client.get("data/events/login-logs/last") +```