Passer au contenu principal

Plugin Manifest reference

Chaque plugin a un fichier plugin.manifest.json à sa racine. C'est la source de vérité pour l'identité du plugin, les autorisations dont il a besoin, les destinations réseau qu'il est autorisé à appeler, les pages d'administration qu'il contribue et les boutons d'action qu'il ajoute à l'interface utilisateur du visualiseur.

Plugin manifests require Owncast v0.3.0

Plugins require Owncast 0.3.0 or later.

Le manifeste est ce que l'administrateur examine avant d'installer le plugin. L'hôte l'analyse au moment du chargement et applique chaque déclaration. Rien dans le plugin compilé ne peut accorder une capacité que le manifeste n'a pas demandée.

Owncat informs youDisponible dans chaque SDK

Le manifeste est un JSON simple qui décrit le plugin à l'hôte, indépendamment du langage dans lequel vous avez écrit le code. Pour les détails spécifiques au langage, voir la référence SDK JavaScript ou Python.

Manifeste minimum

{
"api": "1",
"name": "My Plugin",
"version": "0.1.0",
"description": "Short description for admins",
"permissions": []
}

api, name, et version sont requis. Tout le reste est optionnel et seulement nécessaire lorsque vous utilisez la fonctionnalité correspondante.

Champs de premier niveau

ChampTypeRequisDescription
apichaineouiVersion du schéma de manifeste. Actuellement "1".
namechaineouiNom d'affichage lisible par l'homme affiché dans les listes d'administrateurs et sur les cartes de registre. Exemple : "Awesome Echo Bot".
slugchainenonIdentifiant canonique (préfixe d'URL, espace de noms de configuration, nom de fichier). Auto-dérivé de name s'il est omis. Voir ci-dessous.
versionchaineouiVotre version de plugin. SemVer recommended. Informational metadata for admins and the registry: the host doesn't gate loading on it.
descriptionchainenonRésumé d'une phrase que l'administrateur voit dans la liste des plugins et lors de l'installation.
categorychainenonRegistry browse category. See category.
permissionsstring[]nonListe des capacités dont votre plugin a besoin. Voir Permissions.
configobjetnonParamètres configurables par l'administrateur que votre plugin lit au moment de l'exécution. Voir Configuration.
botobjetnonConfiguration de chat-bot. Voir bot.
networkobjetnonListe blanche HTTP sortante, requise lorsque network.fetch est accordé. Voir ci-dessous.
actionsobjet[]nonBoutons d'action à ajouter à l'interface utilisateur du visualiseur. Voir UI : Boutons d'action.
adminobjetnonPages d'administration à ajouter à l'interface utilisateur administrateur d'Owncast. Voir UI : Pages d'administration.
stylesstring[]nonFichiers CSS intégrés dans la page du visualiseur. Voir styles.
scriptsstring[]nonFichiers JavaScript intégrés dans la page du visualiseur. Voir scripts.
extraPageContentobjetnonUn objet déclarant un slug et un fichier HTML optionnel ajouté au bloc de contenu supplémentaire du visualiseur. Voir extraPageContent.
tabsobjectnoViewer-page tabs keyed by stable slug. Voir tabs.

name et slug

name est le nom d'affichage lisible par l'homme. Il peut contenir n'importe quel caractère, y compris des espaces et de la ponctuation, et est ce que les administrateurs voient dans la liste des plugins, ce qui apparaît sur les cartes de navigation du registre, et l'identité par défaut du chat-bot.

slug est l'identifiant canonique. Il contrôle :

  • Le préfixe d'URL du plugin : /plugins/<slug>/...
  • L'espace de noms de configuration (magasin clé-valeur)
  • Le nom de fichier de l'artéfact construit (<slug>.ocpkg)
  • La clé primaire dans le registre des plugins

Les slugs sont des lettres minuscules, des chiffres et des tirets, commençant par une lettre, jusqu'à 64 caractères. Le SDK en dérive un de name automatiquement lorsque slug est omis : les espaces et la ponctuation fusionnent en tirets simples, les lettres en minuscules. "Awesome Echo Bot" devient awesome-echo-bot. Épinglez slug explicitement lorsque l'auto-dérivation n'est pas ce que vous voulez, ou lorsque votre nom d'affichage utilise des caractères en dehors de l'ASCII ("Café Helper" donnerait autrement caf-helper).

Évitez de changer le slug après sa sortie : le renommage semblera être un plugin différent pour les administrateurs, avec un nouveau magasin de configuration. Changer name (uniquement affichage) est sans danger. Cela ne change pas l'identité.

category: registry browse category

An optional label that places your plugin in a browse category on the registry and in the admin UI. The canonical values are chat-bots, chat-filters, moderation, authentication, themes, overlays, notifications, integrations, video, analytics, games, admin-utilities, examples, and other.

The SDK's packaging CLI warns when category isn't one of these, but nothing rejects it: the host and registry tolerate unknown categories, they just won't match any browse filter.

bot : identité de chat-bot

Les plugins qui postent dans le chat (en utilisant owncast.chat.send) apparaissent sous un utilisateur chat-bot. Par défaut, le bot apparaît sous le name d'affichage du plugin. Remplacez cela par bot.displayName :

{
"name": "Stream Sidekick",
"bot": {
"displayName": "Sidekick"
}
}

Dans le chat, le bot poste en tant que "Sidekick" au lieu de "Stream Sidekick". La première fois que le plugin se charge, Owncast provisionne un utilisateur de chat persistant sur la base du slug du plugin (de sorte que l'identité du bot survive aux réinstallations et aux changements de nom d'affichage).

bot.displayName n'est pertinent que pour les plugins qui ont l'autorisation chat.send. Il est ignoré sinon.

config : paramètres configurables par l'administrateur

Déclarez des paramètres typés ici et Owncast rend un formulaire éditable pour eux dans l'interface admin, que votre plugin lit au moment de l'exécution avec owncast.config.get. Chaque entrée a un type (string, number, ou boolean), un default, et une description :

{
"config": {
"greeting": { "type": "string", "default": "welcome!", "description": "First-join message" },
"cooldownMs": { "type": "number", "default": 2000, "description": "Per-user command cooldown" },
"modOnly": { "type": "boolean", "default": false, "description": "Restrict to moderators" }
}
}

Config keys starting with __ are reserved: the host uses that prefix to inject per-instance state into the plugin runtime, and a manifest declaring one is rejected at load.

Couverture complète, y compris comment le formulaire se rend, masquage des identifiants, validation, et où les remplacements sont stockés, dans Configuration.

permissions

Chaque entrée débloque un morceau des API de l'hôte. L'hôte rejette les appels à une méthode dont vous n'avez pas déclaré l'autorisation.

{
"permissions": ["chat.send", "storage.kv", "network.fetch"]
}

Voir la référence des permissions pour la liste complète des identifiants et ce que chacun accorde.

network : liste blanche HTTP sortante

network.fetch est conditionné par une liste blanche explicite de noms d'hôtes. Si vous déclarez network.fetch dans permissions, vous avez également besoin d'un champ network.allowedHosts listant les hôtes que vous allez appeler :

{
"permissions": ["network.fetch"],
"network": {
"allowedHosts": ["api.discord.com", "*.weather.com"]
}
}

Les entrées sont des globes de noms d'hôtes. Des noms nus comme api.discord.com correspondent exactement. * est un segment générique, donc *.weather.com correspond à api.weather.com et data.weather.com mais pas à weather.com lui-même ou evil.com.

Le générique "*" correspond à n'importe quel hôte, mais vous devez l'écrire explicitement :

{
"network": { "allowedHosts": ["*"] }
}

C'est intentionnel. Les administrateurs examinant le manifeste voient la portée qu'ils accordent. La plupart des plugins devraient lister les hôtes spécifiques qu'ils appellent à la place.

L'hôte rejette le chargement si network.fetch est accordé sans une entrée allowedHosts.

actions : boutons d'action

Les boutons d'action sont des entrées cliquables qu'Owncast affiche sous le flux. Tant que votre plugin est activé, l'hôte fusionne ses entrées dans la liste qu'Owncast affiche déjà.

{
"permissions": ["ui.modify", "http.serve"],
"actions": [
{
"title": "Chat Overlay",
"description": "Open the live chat overlay",
"url": "/",
"icon": "/star.png",
"color": "#3b82f6"
},
{
"title": "Issue tracker",
"url": "https://github.com/example/my-plugin/issues",
"openExternally": true
},
{
"title": "About this stream",
"html": "<p>Live every weekday at 8pm UTC.</p>"
}
]
}

Chaque entrée :

ChampTypeNotes
titlestringRequis. L'étiquette du bouton.
urlstringSoit un URL absolu https://... soit un chemin. Mutuellement exclusif avec html.
htmlchaineHTML brut rendu dans une fenêtre modale en ligne. Mutuellement exclusif avec url.
iconchaineURL d'image facultative affichée sur le bouton. Les mêmes règles de chemin que url.
colorchaîneCouleur hex facultative pour l'arrière-plan du bouton.
descriptionchaîneFacultatif. Affiché dans le modal qui s'ouvre pour les actions basées sur des URL.
openExternallybooléenSi true, l'URL s'ouvre dans un nouvel onglet au lieu d'un modal en ligne.

Règles que l'hôte applique au moment du chargement :

  • La permission ui.modify est requise. Sans cela, le manifeste est rejeté.
  • Exactement un de url ou html par entrée.
  • Les URL relatives (et les icônes) commençant par / sont automatiquement préfixées au nom de votre plugin. "/" devient /plugins/my-plugin/. "/star.png" devient /plugins/my-plugin/star.png. Vous évite de coder en dur le nom de votre plugin.
  • Les URL (et icônes) qui se résolvent dans votre espace de noms nécessitent http.serve, puisque vous êtes celui qui les sert.
  • Les URL (et icônes) pointant vers l'espace de noms d'un autre plugin sont rejetées. Attrape les fautes de frappe et empêche un plugin de faire de la publicité pour l'interface utilisateur d'un autre.

Couverture complète dans UI : Boutons d'action.

admin : pages administratives

Plugins can register pages that appear in the Owncast admin UI under Plugins. The pages object is keyed by plugin-relative path glob:

{
"permissions": ["http.serve"],
"admin": {
"pages": {
"/admin": { "title": "Settings", "icon": "gear" }
}
}
}

Chaque entrée a :

PartTypeRemarques
object keychaîneRequired path glob under the plugin's namespace, such as "/admin" or "/admin/*".
titlechaîneRequis. L'étiquette de l'onglet affichée dans l'interface d'administration.
iconchaîneFacultatif. Un nom sémantique court (gear, wrench, user, etc.).

The host derives each page path from its object key. A key of "/admin" maps to /plugins/<your-slug>/admin. Requests matching any key are auth-gated by the host, so unauthenticated requests get a 401 before your plugin code runs.

JSON object order is not significant. Owncast displays admin pages in lexicographic path order. pages must be an object. Do not add a path member to a page value. The host rejects arrays and page values containing the legacy path member.

Couverture complète dans UI : Pages d'administration.

styles : injection CSS

Une liste de fichiers CSS que le plugin contribue à la page du visualiseur. Le contenu de chaque fichier est intégré dans le même bloc <style> qu'Owncast utilise déjà pour le CSS personnalisé de l'administrateur, de sorte que les plugins peuvent créer des thèmes sans que chaque contribution ait besoin de son propre tag <link>.

{
"permissions": ["ui.modify"],
"styles": ["theme.css", "overrides.css"]
}

Les règles de chemin correspondent aux URL des boutons d'action :

  • Les chemins simples comme "theme.css" sont automatiquement préfixés à l'espace de noms de votre plugin.
  • Les chemins à simple barre comme "/theme.css" obtiennent le même traitement.
  • Les chemins entièrement qualifiés /plugins/<your-slug>/... passent à travers.
  • Les chemins dans l'espace de noms d'un autre plugin sont rejetés.
  • Les URL http:// et https:// sont rejetées. Regroupez les ressources externes (polices, images) et référencez-les avec @font-face ou url(...) depuis votre CSS, afin qu'un administrateur examinant le manifeste puisse voir chaque fichier qui atterrira sur sa page.
  • Chaque entrée doit se terminer par .css.

Nécessite uniquement ui.modify (le plugin peint à l'intérieur du chrome d'Owncast). http.serve n'est pas requis : les octets de chaque fichier sont lus à partir de assets/ et intégrés dans customStyles sur /api/config, pas servis à une URL. L'hôte émet un commentaire /* plugin : <your-slug> ... */` devant chaque contribution afin qu'un lecteur puisse attribuer une règle à quel que plugin l'a expédié.

Pour un CSS qui dépend de l'état du plugin, un gestionnaire onPageStyles le renvoie au moment de la demande, sans champ manifeste. Sa sortie s'ajoute à customStyles après ces fichiers statiques.

Couverture complète dans UI : feuilles de style du visualiseur.

scripts : injection JavaScript

Une liste de fichiers JavaScript que le plugin contribue à la page du visualiseur. Le contenu de chaque fichier est ajouté à la même réponse d'où provient déjà le JavaScript personnalisé de l'administrateur (/customjavascript), ainsi les plugins peuvent étendre la page sans que chaque contribution ait besoin de son propre tag <script>.

{
"permissions": ["ui.modify"],
"scripts": ["client.js"]
}

Les règles de chemin et les permissions requises correspondent à styles, appliquées aux fichiers .js (seul ui.modify est requis, et l'hôte lit à partir de assets/ et intègre dans /customjavascript). Enveloppez votre script dans une IIFE pour que les déclarations de niveau supérieur ne se heurtent pas au JavaScript de l'administrateur ou d'autres plugins. L'hôte émet un commentaire // plugin : <your-slug> ... devant chaque contribution et enveloppe chaque contribution dans un try/catch pour qu'une erreur d'exécution d'un plugin ne puisse pas casser les autres.

Pour un JavaScript qui dépend de l'état du plugin, un gestionnaire onPageScripts le renvoie au moment de la demande, sans champ manifeste. Sa sortie s'ajoute à /customjavascript après ces fichiers statiques.

Couverture complète dans UI : scripts du visualiseur.

extraPageContent : bloc HTML

Un objet qui contribue un bloc HTML à la zone de contenu supplémentaire du visualiseur, préfixé au texte de l'administrateur sur /api/config.

{
"permissions": ["ui.modify"],
"extraPageContent": { "slug": "banner", "content": "content.html" }
}
ChampTypeRemarques
slugchaîneRequis uniquement lorsque content est omis (l'hôte le passe à onPageContent). Facultatif sinon. Lettres minuscules, chiffres et tirets, commençant par une lettre.
contentstringFacultatif. Chemin relatif vers un fichier HTML statique dans assets/. Lorsqu'il est présent, les octets de ce fichier sont intégrés directement. Lorsqu'il est omis, l'hôte appelle onPageContent à la place.

Statique (avec content) : l'hôte lit le fichier au moment de la demande et intègre les octets. Les mêmes règles de chemin que styles et scripts, appliquées à une seule entrée .html. Le HTML du plugin contourne le processeur markdown afin que les balises et attributs passent tels quels.

Dynamique (sans content) : implémentez onPageContent({ slug, user? }) dans votre plugin pour renvoyer du HTML au moment de la demande. Utilisez ceci lorsque le contenu doit varier par spectateur ou s'appuyer sur des données en direct (par exemple, des salutations personnalisées ou des statistiques de diffusion actuelles). user est l'identité de chat du spectateur, présente lors de l'authentification.

Nécessite ui.modify. http.serve n'est pas requis car le HTML est intégré dans la réponse de configuration, pas servi en tant qu'URL. Chaque contribution est enveloppée avec un <!-- plugin : <your-slug> ... --> commentaire afin qu'un lecteur puisse attribuer le balisage en retour.

Couverture complète dans UI : Contenu de page supplémentaire.

tabs : onglets de la page du visualiseur

The tabs object contributes tabs to the viewer page's tab row next to the built-in About and Followers tabs. Each object key is the tab's stable slug. Every value requires title, and content is optional.

{
"permissions": ["ui.modify"],
"tabs": {
"music": { "title": "Music", "content": "music.html" },
"schedule": { "title": "Schedule", "content": "schedule.html" }
}
}

Each entry has:

PartRemarques
object keyRequired stable slug. Lettres minuscules, chiffres et tirets, commençant par une lettre. The host passes this key to onTabContent when content is omitted.
titleRequis. L'étiquette affichée sur l'onglet. Doit être unique dans les onglets du plugin.
contentFacultatif. Chemin relatif vers un fichier HTML sous assets/. Les mêmes règles de chemin que extraPageContent (préfixées automatiquement à votre espace de noms, les chemins entre plugins et les URL http(s):// rejetées, doivent se terminer par .html). When omitted, the host calls onTabContent.

Within each plugin, Owncast displays tabs in lexicographic slug order. JSON object order is not significant. Ordering between tabs from different plugins is unspecified. tabs must be an object. Do not add a slug member to a tab value. The host rejects arrays and tab values containing the legacy slug member.

Nécessite ui.modify. http.serve is not required: each static tab's HTML is read from assets/ and inlined into the pluginTabs[] array on /api/config. For a dynamic tab, the host passes the object key to onTabContent as slug and inlines the returned HTML.

Couverture complète dans UI : Onglets de page du visualiseur.

Contrat manifeste-runtime

Lorsque votre plugin se charge, l'hôte analyse le manifeste et demande au runtime de s'enregistrer. It compares the two and rejects the load when:

  • the slugs don't match (slug is the canonical identity on both sides)
  • the runtime uses a permission that wasn't declared in the manifest

version is intentionally not compared. It's informational metadata the host gates nothing on, and the SDK bakes it into the registration from the same manifest at build time anyway.

Vous n'écrivez pas vous-même l'enregistrement : le SDK le génère à partir des gestionnaires que vous définissez (voir votre référence SDK pour savoir comment les gestionnaires sont déclarés dans votre langue). Savoir que ce contrat existe est utile lors du débogage. Une erreur "permission demandée à l'exécution non déclarée dans le manifeste" signifie que vous avez ajouté un gestionnaire qui a besoin d'une permission que vous avez oublié de lister.

Exemple complet

Un manifeste non trivial exerçant la plupart des fonctionnalités :

{
"api": "1",
"name": "Stream Sidekick",
"slug": "stream-sidekick",
"version": "0.2.0",
"description": "Posts to Discord on stream start, shows an overlay, and adds a Donate button.",
"permissions": [
"chat.send",
"chat.filter",
"storage.kv",
"http.serve",
"http.sse",
"network.fetch",
"notifications.send",
"ui.modify"
],
"bot": {
"displayName": "Sidekick"
},
"network": {
"allowedHosts": ["api.discord.com", "*.example.com"]
},
"actions": [
{
"title": "Donate",
"url": "https://example.com/donate",
"openExternally": true
}
],
"admin": {
"pages": {
"/admin": { "title": "Sidekick settings", "icon": "gear" }
}
},
"styles": ["sidekick.css"],
"scripts": ["sidekick.js"],
"extraPageContent": { "slug": "intro", "content": "intro.html" },
"tabs": {
"schedule": { "title": "Schedule", "content": "schedule.html" }
}
}

Improve this page

See something missing or incorrect? Edit the English version of this page or help improve translations.

Contributors to this documentation
Gabe KangasGabe Kangas
G
Gabe Kangas