Plugin Manifest reference
Ogni plugin ha un file plugin.manifest.json nella sua radice. Questa è la fonte della verità per l'identità del plugin, i permessi di cui ha bisogno, le destinazioni di rete che può chiamare, le pagine di amministrazione che contribuisce e i pulsanti d'azione che aggiunge all'interfaccia utente del visualizzatore.
Plugins require Owncast 0.3.0 or later.
Il manifesto è ciò che un amministratore esamina prima di installare il plugin. L'host lo analizza al momento del caricamento e applica ogni dichiarazione. Niente nel plugin compilato può concedere una funzionalità che il manifesto non ha richiesto.
Il manifesto è un JSON semplice che descrive il plugin all'host, indipendentemente dal linguaggio in cui hai scritto il codice. Per dettagli specifici sul linguaggio, vedere il riferimento SDK JavaScript o Python.
Manifesto minimo
{
"api": "1",
"name": "My Plugin",
"version": "0.1.0",
"description": "Short description for admins",
"permissions": []
}
api, name e version sono richiesti. Tutto il resto è facoltativo e necessario solo quando utilizzi la funzionalità corrispondente.
Campi di primo livello
| Campo | Tipo | Richiesto | Descrizione |
|---|---|---|---|
api | stringa | sì | Versione dello schema del manifesto. Attualmente "1". |
name | stringa | sì | Nome visualizzabile leggibile dagli esseri umani mostrato nelle liste degli amministratori e nelle schede del registro. Esempio: "Awesome Echo Bot". |
slug | stringa | no | Identificatore canonico (prefisso URL, namespace di configurazione, nome file). Auto-derivato da name se omesso. Vedi sotto. |
version | stringa | sì | La versione del tuo plugin. SemVer recommended. Informational metadata for admins and the registry: the host doesn't gate loading on it. |
description | stringa | no | Sommario in una sola frase che l'amministratore vede nell'elenco dei plugin e durante l'installazione. |
category | stringa | no | Registry browse category. See category. |
permissions | string[] | no | Elenco delle capacità di cui il tuo plugin ha bisogno. Vedi Permessi. |
config | oggetto | no | Impostazioni configurabili dall'amministratore lette dal tuo plugin al runtime. Vedi Configurazione. |
bot | oggetto | no | Configurazione del chatbot. Vedi bot. |
network | oggetto | no | Elenco di autorizzazione HTTP in uscita, richiesto quando network.fetch è concesso. Vedi sotto. |
actions | oggetto[] | no | Pulsanti d'azione da aggiungere all'interfaccia utente del visualizzatore. Vedi UI: Pulsanti d'azione. |
admin | oggetto | no | Pagine di amministrazione da aggiungere all'interfaccia utente amministrativa di Owncast. Vedi UI: Pagine di amministrazione. |
styles | string[] | no | File CSS incorporati nella pagina del visualizzatore. Vedi styles. |
scripts | string[] | no | File JavaScript incorporati nella pagina del visualizzatore. Vedi scripts. |
extraPageContent | oggetto | no | Un oggetto che dichiara uno slug e un file HTML opzionale preceduto al blocco di contenuto extra del visualizzatore. Vedi extraPageContent. |
tabs | object | no | Viewer-page tabs keyed by stable slug. Vedi tabs. |
name e slug
name è il nome visualizzabile leggibile dagli esseri umani. Può contenere qualsiasi carattere, inclusi spazi e punteggiatura, ed è ciò che gli amministratori vedono nell'elenco dei plugin, ciò che appare sulle schede di scorrimento del registro e l'identità predefinita del chatbot.
slug è l'identificatore canonico. Controlla:
- Il prefisso URL del plugin:
/plugins/<slug>/... - Il namespace di configurazione (memoria chiave-valore)
- Il nome del file dell'articolo costruito (
<slug>.ocpkg) - La chiave primaria nel registro dei plugin
Gli slug sono lettere minuscole, cifre e trattini, che iniziano con una lettera, fino a 64 caratteri. L'SDK ne deriva uno automaticamente da name quando slug è omesso: spazi e punteggiatura si fondono in singoli trattini, lettere minuscole. "Awesome Echo Bot" diventa awesome-echo-bot. Fissa slug esplicitamente quando l'auto-derivazione non è ciò che desideri, o quando il tuo nome visualizzabile utilizza caratteri al di fuori dell'ASCII ("Café Helper" altrimenti produrrebbe caf-helper).
Evitare di cambiare lo slug dopo il rilascio: il rinominare apparirà come un plugin diverso per gli amministratori, con un nuovo archivio di configurazione. Cambiando name (solo visualizzazione) è sicuro. Non cambia 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à del chatbot
I plugin che inviano messaggi nella chat (utilizzando owncast.chat.send) appaiono sotto un utente chatbot. Per impostazione predefinita, il bot appare sotto il name visualizzabile del plugin. Sovrascrivi questo con bot.displayName:
{
"name": "Stream Sidekick",
"bot": {
"displayName": "Sidekick"
}
}
In chat, il bot pubblica come "Sidekick" invece di "Stream Sidekick". La prima volta che il plugin viene caricato, Owncast fornisce un utente chat persistente chiave sul slug del plugin (in modo che l'identità del bot sopravviva a reinstallazioni e cambiamenti di nome visualizzabile).
bot.displayName è rilevante solo per i plugin che hanno il permesso chat.send. Altrimenti viene ignorato.
config: impostazioni configurabili dall'amministratore
Dichiara impostazioni tipizzate qui e Owncast genera un modulo modificabile per esse nell'amministratore, che il tuo plugin legge al runtime con owncast.config.get. Ogni voce ha un type (string, number o boolean), un default e una 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.
Copertura completa, inclusa la rendering del modulo, mascheramento delle credenziali, validazione e dove vengono memorizzati i sovrascrittura, in Configurazione.
permissions
Ogni voce sblocca una parte delle API dell'host. L'host rifiuta le chiamate a un metodo il cui permesso non hai dichiarato.
{
"permissions": ["chat.send", "storage.kv", "network.fetch"]
}
Vedi il riferimento ai permessi per l'elenco completo degli identificatori e ciò che ognuno di essi concede.
network: elenco di autorizzazione HTTP in uscita
network.fetch è soggetto a un elenco esplicito di nomi di host. Se dichiari network.fetch in permissions, hai anche bisogno di un campo network.allowedHosts che elenchi gli host che chiamerai:
{
"permissions": ["network.fetch"],
"network": {
"allowedHosts": ["api.discord.com", "*.weather.com"]
}
}
Le voci sono globi di nomi di host. Nomi semplici come api.discord.com corrispondono esattamente. * è un segmento jolly, quindi *.weather.com corrisponde a api.weather.com e data.weather.com ma non a weather.com stesso o evil.com.
Il jolly "*" corrisponde a qualsiasi host, ma devi scriverlo esplicitamente:
{
"network": { "allowedHosts": ["*"] }
}
Questo è intenzionale. Gli amministratori che esaminano il manifesto vedono il campo che stanno concedendo. La maggior parte dei plugin dovrebbe elencare gli host specifici che chiamano.
L'host rifiuta il caricamento se network.fetch è concesso senza un campo allowedHosts presente.
actions: pulsanti d'azione
I pulsanti d'azione sono voci cliccabili che Owncast mostra sotto lo stream. Mentre il tuo plugin è abilitato, l'host unisce le sue voci nell'elenco che Owncast mostra già.
{
"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>"
}
]
}
Ogni voce:
| Campo | Tipo | Note |
|---|---|---|
title | string | Richiesta. L'etichetta del pulsante. |
url | string | O un URL assoluto https://... o un percorso. Mutualmente esclusivo con html. |
html | stringa | HTML grezzo reso in una finestra modale in linea. Mutualmente esclusivo con url. |
icon | stringa | URL immagine opzionale mostrato sul pulsante. Stesse regole di percorso di url. |
color | stringa | Colore esadecimale opzionale per lo sfondo del pulsante. |
descrizione | stringa | Opzionale. Mostrato nel modulo che si apre per azioni basate su URL. |
openExternally | booleano | Se true, l'URL si apre in una nuova scheda anziché in un modulo in linea. |
Regole che l'host applica al momento del caricamento:
- È richiesta l'autorizzazione
ui.modify. Senza di essa, il manifesto viene rifiutato. - Esattamente uno tra
urlohtmlper voce. - Gli URL relativi (e le icone) che iniziano con
/vengono auto-prefissati con lo spazio dei nomi del tuo plugin."/"diventa/plugins/my-plugin/."/star.png"diventa/plugins/my-plugin/star.png. Ti risparmia dall'inserire manualmente il nome del tuo plugin. - Gli URL (e le icone) che si risolvono nel tuo spazio dei nomi richiedono
http.serve, poiché sei tu a servirli. - Gli URL (e le icone) che puntano allo spazio dei nomi di un altro plugin vengono rifiutati. Cattura errori di battitura e impedisce a un plugin di pubblicizzare l'interfaccia utente di un altro.
Copertura completa in UI: Pulsanti di azione.
admin: pagine di amministrazione
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" }
}
}
}
Ogni voce ha:
| Part | Tipo | Note |
|---|---|---|
| object key | stringa | Required path glob under the plugin's namespace, such as "/admin" or "/admin/*". |
title | stringa | Obbligatoria. L'etichetta della scheda mostrata nell'interfaccia di amministrazione. |
icon | stringa | Opzionale. Un nome semantico breve (gear, wrench, user, e così via). |
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.
Copertura completa in UI: Pagine di amministrazione.
styles: Iniezione di CSS
Un elenco di file CSS che il plugin contribuisce alla pagina del visualizzatore. I contenuti di ciascun file sono in linea nello stesso blocco <style> che Owncast utilizza già per il CSS personalizzato dell'amministratore, quindi i plugin possono modificare il tema della pagina senza necessitare di ciascun contributo un proprio tag <link>.
{
"permissions": ["ui.modify"],
"styles": ["theme.css", "overrides.css"]
}
Le regole di percorso corrispondono agli URL dei pulsanti di azione:
- Percorsi semplici come
"theme.css"vengono auto-prefissati con lo spazio del tuo plugin. - Percorsi a singolo slash come
"/theme.css"ricevono lo stesso trattamento. - Percorsi completamente qualificati
/plugins/<your-slug>/...passano attraverso. - Percorsi nello spazio dei nomi di un altro plugin vengono rifiutati.
- Gli URL
http://ehttps://vengono rifiutati. Raggruppa risorse esterne (font, immagini) e riferiscile con@font-faceourl(...)all'interno del tuo CSS, in modo che un amministratore che esamina il manifesto veda ogni file che arriverà nella sua pagina. - Ogni voce deve terminare con
.css.
Richiede solo ui.modify (il plugin si integra nell'interfaccia di Owncast). http.serve non è necessario: i byte di ciascun file vengono letti da assets/ e in linea in customStyles su /api/config, non serviti a un URL. The host emits a /* plugin: <your-slug> ... */ comment in front of each contribution so a reader can attribute a rule back to whichever plugin shipped it.
Per CSS che dipende dallo stato del plugin, un gestore di onPageStyles lo restituisce al momento della richiesta, senza un campo manifesto. Il suo output viene aggiunto a customStyles dopo questi file statici.
Copertura completa in UI: Stili del visualizzatore.
scripts: Iniezione di JavaScript
Un elenco di file JavaScript che il plugin contribuisce alla pagina del visualizzatore. I contenuti di ciascun file vengono aggiunti alla stessa risposta da cui proviene già il JavaScript personalizzato dell'amministratore (/customjavascript), quindi i plugin possono estendere la pagina senza necessitare di ciascun contributo un proprio tag <script>.
{
"permissions": ["ui.modify"],
"scripts": ["client.js"]
}
Le regole di percorso e le autorizzazioni richieste corrispondono a styles, applicate ai file .js (è necessaria solo ui.modify, e l'host legge da assets/ e inietta in /customjavascript). Avvolgi il tuo script in un IIFE in modo che le dichiarazioni di alto livello non collisionino con il JavaScript dell'amministratore o di altri plugin. L'host emette un // plugin: <your-slug> ... commento davanti a ciascun contributo e avvolge ogni contributo in un try/catch in modo che un errore di runtime di un plugin non possa interrompere gli altri.
Per JavaScript che dipende dallo stato del plugin, un gestore di onPageScripts lo restituisce al momento della richiesta, senza un campo manifesto. Il suo output si aggiunge a /customjavascript dopo questi file statici.
Copertura completa in UI: Script del visualizzatore.
extraPageContent: Blocco HTML
Un oggetto che contribuisce con un blocco HTML all'area di contenuto extra del visualizzatore, preceduto sopra il prosa dell'amministratore su /api/config.
{
"permissions": ["ui.modify"],
"extraPageContent": { "slug": "banner", "content": "content.html" }
}
| Campo | Tipo | Note |
|---|---|---|
slug | stringa | Obbligatoria solo quando content è omesso (l'host lo passa a onPageContent). Opzionale altrimenti. Lettere minuscole, cifre e trattini, che iniziano con una lettera. |
content | string | Opzionale. Percorso relativo a un file HTML statico in assets/. Quando presente, i byte di quel file vengono inseriti direttamente. Quando omesso, l'host chiama invece onPageContent. |
Statica (con content): l'host legge il file al momento della richiesta e inserisce i byte. Stesse regole di percorso di styles e scripts, applicate a un'unica voce .html. L'HTML del plugin bypassa il processore Markdown in modo che tag e attributi passino così come sono scritti.
Dynamic (without content): implement onPageContent({ slug, user? }) in your plugin to return HTML at request time. Usa questo quando il contenuto deve variare in base allo spettatore o attingere a dati live (ad esempio, saluti personalizzati o statistiche dello stream attuale). user è l'identità di chat dello spettatore, presente quando autenticato.
Richiede ui.modify. http.serve non è richiesto perché l'HTML è in linea nella risposta di configurazione, non servito come un URL. Each contribution is wrapped with an <!-- plugin: <your-slug> ... --> comment so a reader can attribute the markup back.
Copertura completa in UI: Contenuto di pagina extra.
tabs: schede della pagina del visualizzatore
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:
| Part | Note |
|---|---|
| object key | Required stable slug. Lettere minuscole, cifre, e trattini, partendo con una lettera. The host passes this key to onTabContent when content is omitted. |
title | Obbligatoria. L'etichetta mostrata sulla scheda. Deve essere univoco all'interno delle schede del plugin. |
content | Opzionale. Percorso relativo a un file HTML sotto assets/. Stesse regole di percorso di extraPageContent (auto-prefissare allo spazio dei nomi, percorsi cross-plugin e URL http(s):// rifiutati, deve terminare con .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.
Richiede 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.
Copertura completa in UI: Schede della pagina del visualizzatore.
Contratto manifesto-runtime
Quando il tuo plugin si carica, l'host analizza il manifesto e chiede al runtime di registrarsi. It compares the two and rejects the load when:
- the slugs don't match (
slugis 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.
Non scrivi tu stesso la registrazione: l'SDK la genera dagli handler che definisci (vedi il tuo riferimento SDK per come vengono dichiarati gli handler nella tua lingua). Essere a conoscenza dell'esistenza di questo contratto è utile quando si eseguono debug. Un errore "autorizzazione richiesta a runtime non dichiarata nel manifesto" significa che hai aggiunto un handler che necessita di un'autorizzazione che hai dimenticato di elencare.
Esempio completo
Un manifesto non banale che esercita la maggior parte delle funzionalità:
{
"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 this page and improve the documentation for everyone.
Gabe Kangas