# `plugin.json` – Manifest-Referenz Dieses Dokument beschreibt alle Felder des Plugin-Manifests `plugin.json`. Grundlage sind das JSON-Schema `schemas/plugin/plugin_manifest_v1.schema.json` und der Validator `packages/plugin_sdk/hms_plugin_sdk/manifest.py` (PLAN.md §14.2–14.3). ## Schema-Version und API-Version | Feld | Typ | Pflicht | Wert | | --- | --- | --- | --- | | `schema_version` | integer | ja | `1` (konstant) | | `api_version` | integer | ja | `1` (konstant) | `schema_version` und `api_version` müssen exakt `1` sein. Der Validator lehnt jede andere Version ab (`manifest.py`). ## Pflichtfelder Das Schema verlangt folgende Felder auf oberster Ebene: ```json ["schema_version", "id", "name", "version", "api_version", "kind", "vendor", "entrypoints", "capabilities", "failure_mode"] ``` ### `id` - **Typ:** string - **Pflicht:** ja - **Regeln:** Reverse-DNS-Notation, mindestens 5 Zeichen, mindestens zwei durch `.` getrennte Teile, nur Kleinbuchstaben `a–z`, Ziffern `0–9`, `.`, `_`, `-`. Kein `..` erlaubt. - **Beispiel:** `com.hms.fx.vignette` ### `name` - **Typ:** string, minLength 1 - **Pflicht:** ja - **Beispiel:** `Vignette` ### `version` - **Typ:** string - **Pflicht:** ja - **Regeln:** Semantische Version `X.Y.Z` (genau drei durch `.` getrennte Ganzzahlen). - **Beispiel:** `1.0.0` ### `kind` - **Typ:** enum - **Pflicht:** ja - **Werte:** `source`, `generator`, `filter`, `transition`, `mixer`, `output`, `control`, `automation` (PLAN.md §14.1) - **Beispiel:** `filter` ### `vendor` - **Typ:** string, minLength 1 - **Pflicht:** ja - **Beispiel:** `HMS` ### `entrypoints` - **Typ:** object, minProperties 1 - **Pflicht:** ja - **Beschreibung:** Backend-Entrypoints mit Shader-Pässen. Erlaubte Backend-Schlüssel: `d3d11`, `gl`, `gles`. Jeder Entrypoint ist ein Objekt mit: | Feld | Typ | Pflicht | Beschreibung | | --- | --- | --- | --- | | `type` | string | ja | Entrypoint-Typ, z. B. `hlsl`, `glsl`, `glsl_es`, `hlsl_multipass`, `glsl_multipass`, `glsl_es_multipass` | | `passes` | array, minItems 1 | ja | Liste der Shader-Pässe | Jeder Pass ist ein Objekt mit genau einem Shader-Pfad: - **D3D11:** `pixel_shader` (z. B. `shaders/d3d11/main.hlsl`) - **GL/GLES:** `fragment` (z. B. `shaders/gl/main.frag`) **Beispiel (Einpass, Vignette):** ```json "entrypoints": { "d3d11": { "type": "hlsl", "passes": [{"pixel_shader": "shaders/d3d11/main.hlsl"}] }, "gl": { "type": "glsl", "passes": [{"fragment": "shaders/gl/main.frag"}] }, "gles": { "type": "glsl_es", "passes": [{"fragment": "shaders/gles/main.frag"}] } } ``` **Beispiel (Multipass, Gaussian Blur, PLAN.md §14.3):** ```json "entrypoints": { "d3d11": { "type": "hlsl_multipass", "passes": [ {"pixel_shader": "shaders/d3d11/horizontal.hlsl"}, {"pixel_shader": "shaders/d3d11/vertical.hlsl"} ] }, "gl": { "type": "glsl_multipass", "passes": [ {"fragment": "shaders/gl/horizontal.frag"}, {"fragment": "shaders/gl/vertical.frag"} ] }, "gles": { "type": "glsl_es_multipass", "passes": [ {"fragment": "shaders/gles/horizontal.frag"}, {"fragment": "shaders/gles/vertical.frag"} ] } } ``` Der Validator prüft, dass jeder deklarierte Shader-Pfad relativ und sicher ist (kein absoluter Pfad, kein `..`) und – wenn ein Plugin-Root übergeben wird – tatsächlich existiert (`manifest.py`). ### `capabilities` - **Typ:** object - **Pflicht:** ja - **Pflichtfelder:** `minimum_tier`, `supported_backends` | Feld | Typ | Pflicht | Beschreibung | | --- | --- | --- | --- | | `minimum_tier` | enum | ja | `DESKTOP_FULL`, `DESKTOP_LITE`, `PI_LITE`, `HEADLESS_CONTROL` | | `requires_input_texture` | boolean | nein | `true`, wenn der Effekt eine Eingangs-Textur braucht (Filter) | | `supported_backends` | array, minItems 1 | ja | `d3d11`, `gl`, `gles` | **Beispiel (Vignette):** ```json "capabilities": { "minimum_tier": "PI_LITE", "requires_input_texture": true, "supported_backends": ["d3d11", "gl", "gles"] } ``` ### `failure_mode` - **Typ:** enum - **Pflicht:** ja - **Werte:** `bypass`, `hold`, `black` - **Beschreibung:** Verhalten bei einem Pluginfehler. `bypass` überbrückt den Effekt (PLAN.md §3.5, §14.5). - **Beispiel:** `bypass` ## Optionale Felder ### `parameters` - **Typ:** array - **Pflicht:** nein (im Schema), aber empfohlen - **Beschreibung:** Liste der Plugin-Parameter. Details siehe [parameters-and-dmx.md](parameters-and-dmx.md). Jeder Parameter ist ein Objekt mit: | Feld | Typ | Pflicht | Beschreibung | | --- | --- | --- | --- | | `id` | string, minLength 1 | ja | Eindeutige Parameter-ID | | `label` | string, minLength 1 | ja | Anzeigename | | `type` | enum | ja | `float`, `int`, `enum`, `bool`, `color` | | `default` | beliebig | ja | Standardwert | | `minimum` | number | nein | Untergrenze (für `float`/`int`) | | `maximum` | number | nein | Obergrenze (für `float`/`int`) | | `values` | array | nein | Werte für `enum` | | `dmx_slots` | array, maxItems 8 | nein | DMX-Slot-Belegung (1–8) | | `curve` | string | nein | Kurvenform, z. B. `quadratic` | **Beispiel (Vignette):** ```json "parameters": [ {"id": "amount", "label": "Amount", "type": "float", "minimum": 0, "maximum": 1, "default": 0.5, "dmx_slots": [1]}, {"id": "radius", "label": "Radius", "type": "float", "minimum": 0, "maximum": 2, "default": 0.5, "dmx_slots": [2]}, {"id": "softness", "label": "Softness", "type": "float", "minimum": 0, "maximum": 1, "default": 0.4, "dmx_slots": [3]}, {"id": "roundness", "label": "Roundness", "type": "float", "minimum": 0, "maximum": 1, "default": 0.5, "dmx_slots": [4]}, {"id": "center_x", "label": "Center X", "type": "float", "minimum": 0, "maximum": 1, "default": 0.5, "dmx_slots": [5]}, {"id": "center_y", "label": "Center Y", "type": "float", "minimum": 0, "maximum": 1, "default": 0.5, "dmx_slots": [6]}, {"id": "color", "label": "Color", "type": "color", "default": [0, 0, 0], "dmx_slots": [7]}, {"id": "invert", "label": "Invert", "type": "bool", "default": 0, "dmx_slots": [8]}, {"id": "mix", "label": "Mix", "type": "float", "minimum": 0, "maximum": 1, "default": 1, "dmx_slots": []} ] ``` **Validator-Regeln für Parameter (`manifest.py`):** - `id` muss vorhanden und ein String sein; doppelte IDs sind Fehler. - `type` muss einer von `float`, `int`, `enum`, `bool`, `color` sein. - Für `float` müssen `minimum`, `maximum` und `default` vorhanden sein. - `dmx_slots` muss eine Liste von Ganzzahlen sein. - Die Summe aller `dmx_slots` über alle Parameter darf **8 nicht überschreiten** (PLAN.md §14.7). ### `adaptive_quality` - **Typ:** object - **Pflicht:** nein (im Schema), aber für qualitätsabhängige Effekte empfohlen - **Pflichtfelder:** `default`, `variants` - **Beschreibung:** Adaptive-Quality-Varianten. Details siehe [adaptive-quality.md](adaptive-quality.md). | Feld | Typ | Pflicht | Beschreibung | | --- | --- | --- | --- | | `default` | string | ja | Standard-Variante, z. B. `auto` | | `variants` | array, minItems 1 | ja | Liste der Varianten | | `transition_ms` | number, minimum 0 | nein | Übergangszeit in Millisekunden | | `semantic_parameters_unchanged` | array of string | nein | Parameter, deren Semantik beim Wechsel unverändert bleibt | Jede Variante ist ein Objekt mit: | Feld | Typ | Pflicht | Beschreibung | | --- | --- | --- | --- | | `id` | string | ja | Varianten-ID, z. B. `low`, `medium`, `high` | | `internal_scale` | number, exclusiveMinimum 0, maximum 1 | nein | Interne Auflösungsskala | | `samples` | integer, minimum 1 | nein | Sample-Anzahl | **Beispiel (Vignette):** ```json "adaptive_quality": { "default": "auto", "variants": [ {"id": "low", "internal_scale": 1.0, "samples": 1}, {"id": "medium", "internal_scale": 1.0, "samples": 1}, {"id": "high", "internal_scale": 1.0, "samples": 1} ], "transition_ms": 180, "semantic_parameters_unchanged": ["mix"] } ``` ## Validierung Die Validierung (`manifest.py`) umfasst (PLAN.md §14.5): - Manifest-Schema; - eindeutige Plugin-ID und semantische Version; - API-Kompatibilität; - Pfad- und ZIP-Sicherheit; - erlaubte Dateitypen und Größenlimits; - Shader-Kompilierung; - Capability-Prüfung; - vollständige, vorab kompilierbare Adaptive-Quality-Varianten und unveränderte Parametersemantik; - Lizenzmetadaten; - Hash des Pakets. ## ZIP-Sicherheit (§27.2) Beim Installieren als ZIP gelten zusätzlich (`manifest.py`): - maximal 512 Dateien (`MAX_PLUGIN_FILES`); - maximale entpackte Gesamtgröße 32 MiB (`MAX_TOTAL_UNPACKED`); - maximale Dateigröße 8 MiB (`MAX_FILE_SIZE`); - erlaubte Dateiendungen: `.json`, `.hlsl`, `.frag`, `.vert`, `.glsl`, `.png`, `.md`, `.txt`, `.toml`, `.csv`; - keine absoluten Pfade, kein `..`; - `plugin.json` muss an der Paketwurzel liegen.