Ir para o conteúdo principal

Owncast Plugin APIs

O tempo de execução do plugin Owncast expõe um único global, owncast, com as funções host que seu plugin pode chamar. Most methods require the matching permission in your manifest. A call without it never reaches Owncast: the host logs the denial and the call does nothing. What your plugin sees depends on the method. Mutating calls that report an outcome raise an error (moderation, users.register, auth.grantSession, kv.set, videoConfig.write, actions.add, actions.clear, and every sql method), readers return an empty or zero value, and calls that return nothing become silent no-ops. fs.write, fs.delete, and storage.upload are the exceptions: they report failure in their return value rather than raising. A few methods are ambient and need no permission: logging, timers, reading bundled assets, and owncast.config.get.

Plugin APIs require Owncast v0.3.0

Plugins require Owncast 0.3.0 or later.

As chamadas são mostradas para ambos os SDKs, escolha seu idioma com as abas. Veja JavaScript ou Python para configuração. (Os nomes dos métodos JavaScript são camelCase, Python usa snake_case, então sendAction se torna send_action, e assim por diante.)

Logging

owncast.log.info(message), .warning(message), and .error(message)

Write an operator-visible entry to Owncast's server log. Owncast records the calling plugin's slug and the matching info, warning, or error severity. It replaces control characters with spaces so each entry stays on one line, then truncates messages longer than 4 KiB.

owncast.log.info('sync started');
owncast.log.warning('provider response is incomplete');
owncast.log.error('sync failed');

Ambiente: nenhuma permissão necessária. See the paired chat-logger examples for JavaScript and Python.

Chat

Construindo um bot de chat, ferramenta de moderação ou filtro? Comece com Plugins de Chat.

owncast.chat.send(text)

Envie uma mensagem de chat. Enviada como a identidade do bot do seu plugin (nome de exibição de bot.displayName ou name em seu manifesto).

owncast.chat.send('hello chat');

Requer chat.send.

owncast.chat.sendAction(text)

Envie uma mensagem de estilo ação ("/me").

owncast.chat.sendAction('is now live');

Requer chat.send.

owncast.chat.system(body)

Envie uma mensagem de anúncio do servidor. Nenhuma identidade de bot anexada. O corpo é renderizado inline como HTML, então use isso para notificações curtas, atribuídas ao servidor, como "a transmissão começará em 5 minutos". Trate o corpo como uma saída de HTML não confiável: não interpolar a entrada do usuário sem escapar.

Requires chat.send.

owncast.chat.sendTo(clientId, text)

Envie uma mensagem privada para um único cliente conectado.

Requires chat.send.

owncast.chat.replyTo(msg, text)

Sussurre uma resposta de volta para quem enviou uma mensagem de chat. Passe a mensagem de chat de onChatMessage/filterChatMessage (ou um ID de cliente puro). Retorna false se a conexão do remetente for desconhecida (sem ID de cliente), então você pode recorrer a um send público. Açúcar sobre sendTo(clientId, text).

onChatMessage(msg) {
if (!owncast.chat.replyTo(msg, "got it")) {
owncast.chat.send("got it");
}
}

Requer chat.send.

owncast.chat.history(limit?)

Retorne as mensagens de chat mais recentes, cada uma com id, user, body e timestamp. limit padrão é 50.

Requer chat.history.

owncast.chat.clients()

Return the list of currently-connected chat clients, each with id, userId?, displayName?, connectedAt?, userAgent?, ipAddress?, and messageCount. id é o ID de cliente por conexão usado por chat.kick.

Requer chat.history.

owncast.chat.deleteMessage(messageId)

Oculte uma mensagem de chat dos espectadores.

Requires chat.moderate.

owncast.chat.kick(clientId)

Desconecte um cliente de chat.

Requires chat.moderate.

Identidade do chat

Cada plugin tem exatamente uma identidade de chat, o bot que o Owncast fornece quando seu plugin é instalado. O nome de exibição é o bot.displayName do seu manifesto, se definido; caso contrário, é o name, com IsBot: true. Tanto send quanto sendAction postam como essa identidade, através do pipeline normal de chat do Owncast (filtros, limites de taxa, moderação). Plugins não podem postar sob nomes arbitrários ou se passar por usuários reais.

O usuário bot é determinado pelo slug do plugin, para que a identidade sobreviva às edições do manifesto em name ou bot.displayName. Se você precisar de múltiplas personas de chat, envie múltiplos plugins.

Usuários

owncast.users.list() e owncast.users.get(id)

Leia a lista de usuários do chat ou um único registro de usuário.

const users = owncast.users.list();
const alice = owncast.users.get('u-alice');

Requer users.read.

owncast.users.setEnabled(id, enabled, reason?)

Ative ou desative um usuário de chat.

owncast.users.setEnabled('u-spammer', false, 'spam');

Requer users.moderate.

owncast.users.banIP(ip)

Proíba um IP de entrar no chat.

owncast.users.banIP('203.0.113.42');

Requer users.moderate.

owncast.users.register({ authId, displayName?, scopes?, profileUrl?, handle?, public? })

Find or create an authenticated Owncast user for an external identity and return { userId }. Pass the provider's stable authId without adding your plugin slug. The host stores the slug separately as the identity provider, so plugins cannot collide with or spoof each other's users. displayName seeds a new user's name and is optional: omit it, or pass null, and Owncast generates a display name the same way it does for anonymous viewers. Non-empty scopes such as ["MODERATOR"] are applied on each call.

The optional profileUrl, handle, and public fields describe a verified external identity. profileUrl must be empty or an absolute HTTP(S) URL. handle is the provider's verified label, such as a GitHub login or fediverse handle. Set public to true only after the viewer opts into public display. It defaults to false. These profile fields are captured when the identity is first registered. Later calls with the same authId return the existing user but do not change the stored profile fields.

const { userId } = owncast.users.register({
authId: 'github:583231',
displayName: 'octocat',
profileUrl: 'https://github.com/octocat',
handle: 'octocat',
public: false, // Set true only after the viewer opts in.
});

Requer users.register.

Autenticação

Esses alimentam um portão de autenticação de visualizadores. grantSession e endSession só funcionam dentro de um manipulador onHttpRequest, porque o host anexa o cookie da sessão à resposta HTTP em andamento.

owncast.auth.grantSession({ userId, ttl? })

Emita uma sessão assinada para um usuário já registrado (o userId de owncast.users.register). O host cria, assina e anexa o cookie de sessão à resposta atual; seu plugin nunca vê o token, então não pode forjar ou vazar. ttl é uma vida útil opcional em segundos (0/omitido usa o padrão do host de 24 horas).

const { userId } = owncast.users.register({ authId: 'shared', displayName: 'Guest' });
owncast.auth.grantSession({ userId });
return { status: 302, headers: { Location: returnTo } };

Requer auth.gate.

owncast.auth.endSession()

Limpe o cookie da sessão do visualizador atual nesta resposta para desconectá-los. Seu plugin ainda controla o redirecionamento (e pode redirecionar para o logout do próprio provedor).

owncast.auth.endSession();
return { status: 302, headers: { Location: '/' } };

Requer auth.gate.

Armazenamento

owncast.kv.get(key) e owncast.kv.set(key, value)

Armazenamento de chave/valor por plugin, nomeado pelo slug do seu plugin. Os valores são strings.

Para tipos mais ricos, use os auxiliares JSON (getJSON / setJSON, get_json / set_json em Python) em vez de analisar e serializar você mesmo. O getter JSON retorna o fallback quando a chave não está definida ou contém JSON inválido. Plugins não podem ler as chaves uns dos outros.

owncast.kv.set('count', '1');
const n = Number(owncast.kv.get('count') ?? '0');

owncast.kv.setJSON('prefs', { theme: 'dark' });
const prefs = owncast.kv.getJSON('prefs', {});

Requer storage.kv.

owncast.storage.upload(name, data)

Envie um arquivo para a área pública de arquivos do Owncast. JavaScript accepts a Uint8Array or string. Python accepts bytes or str. Raw bytes are preserved, while strings are encoded as UTF-8. JavaScript returns { url } or null. Python returns a dict accessed as result["url"], or None.

Requer storage.upload.

owncast.fs.*

A private, sandboxed filesystem at data/plugin-storage/\<your-slug>/files/. Ao contrário de owncast.storage.upload, estes arquivos permanecem do lado do servidor: nunca são servidos via HTTP. Os caminhos são relativos à raiz do seu sandbox. O host restringe cada caminho ao seu próprio diretório (um plugin não pode ler os arquivos de outro plugin, e ../ ou caminhos absolutos colapsam de volta dentro do sandbox). Os diretórios pai são criados conforme necessário ao escrever.

JavaScriptPythonRetornos
fs.read(path)fs.read(path)Uint8Array / bytes, or null / None if missing
fs.readText(path)fs.read_text(path)UTF-8 string / str, or null / None if missing
fs.write(path, data)fs.write(path, data){ error? }
fs.list(dir)fs.list(dir)nomes de entrada (um diretório ausente está vazio)
fs.delete(path)fs.delete(path){ error? } for a file or empty directory
fs.exists(path)fs.exists(path)booleano

fs.read preserves the original bytes. fs.readText and fs.read_text decode UTF-8. Python replaces malformed byte sequences when decoding. fs.write preserves a JavaScript Uint8Array or Python bytes, and UTF-8 encodes strings. fs.write and fs.delete return {} on success. If the host rejects the operation, they return { error } with the reason.

owncast.fs.write('notes/log.txt', 'hello');
const text = owncast.fs.readText('notes/log.txt');

const data = new Uint8Array([0xff, 0x00, 0x80]);
owncast.fs.write('cache/data.bin', data);
const stored = owncast.fs.read('cache/data.bin');
if (stored) owncast.storage.upload('data.bin', stored);

Requer storage.fs.

owncast.sql.*

One private SQLite database per plugin, at data/plugin-storage/\<your-slug>/db/plugin.db, separate from Owncast's own database and from the storage.fs sandbox. The sandbox is rooted at files/, so db/ is not a path owncast.fs.* refuses but one it cannot express, and the filesystem quota walk covers files/ only, so the two quotas stay independent. Reach for this over storage.kv when you need to sort, filter, or aggregate rather than just remember a value.

MethodReturns
sql.exec(sql, params?){ rowsAffected, lastInsertId }
sql.query(sql, params?)rows as objects keyed by column name
sql.queryRow(sql, params?)the first row object, or null when nothing matched

In Python queryRow is query_row, rows come back as dicts, and query_row returns None when nothing matched. An error throws in JavaScript and raises RuntimeError in Python. Parameters are null/None, booleans, numbers, or strings. Anything else is refused.

owncast.sql.exec(`CREATE TABLE IF NOT EXISTS chatters (
user_id TEXT PRIMARY KEY,
messages INTEGER NOT NULL DEFAULT 0
)`);

owncast.sql.exec(
`INSERT INTO chatters (user_id, messages) VALUES (?, 1)
ON CONFLICT (user_id) DO UPDATE SET messages = messages + 1`,
[msg.user.id],
);

const top = owncast.sql.query(
'SELECT user_id, messages FROM chatters ORDER BY messages DESC LIMIT ?',
[5],
);
const mine = owncast.sql.queryRow('SELECT messages FROM chatters WHERE user_id = ?', [msg.user.id]);

Each exec call runs as one host-owned transaction. A multi-statement batch commits whole or leaves the database untouched, so a schema migration can't half-apply. A plugin cannot leave a transaction open across calls, so there's nothing to clean up either.

query never hands back a silently short result. A query that overruns the row cap or the result budget is an error telling you to add a LIMIT, so write the bound you actually want when a table grows with your audience. queryRow reads a single row, which keeps it cheap on a table query is too big for.

LimitValue
Encoded request64 KiB total JSON
Bound parameters64 per call
Returned column value1 MiB
Encoded query result1 MiB
Rows returned10000
Call duration2 seconds
Database size128 MiB

Ordinary SQL is unaffected: DDL, DML, indexes, views, triggers, ORDER BY, recursive CTEs, subqueries, UNION, and the json1 functions all work. Refused in every host: ATTACH, DETACH, every PRAGMA (reads included), temporary-schema DDL both as keywords (CREATE TEMP TABLE / INDEX / TRIGGER / VIEW) and schema-qualified (CREATE TABLE temp.x), load_extension(), VACUUM and VACUUM INTO, and transaction controls (BEGIN, COMMIT, END, ROLLBACK, SAVEPOINT, and RELEASE). exec already owns the transaction around the whole batch.

Owncat cautions youJavaScript loses precision above 2^53

Parameters and results cross the host boundary as JSON. Python can bind and read exact 64-bit SQLite integers. JavaScript loses unsafe integers before JSON.stringify on writes and during JSON.parse on reads. Store values above Number.MAX_SAFE_INTEGER (2^53 - 1) as TEXT when a JavaScript plugin needs them to remain exact.

Requires storage.sql. For a worked example, the chat-leaderboard plugin covers schema creation in one atomic exec, an ON CONFLICT upsert, a bounded ranked query, and a single-row read, in both JavaScript and Python. It contrasts with message-counter, which keeps the same counts in storage.kv and cannot rank.

Configuração

owncast.config.get(key, fallback?)

Read one of your plugin's manifest-declared config settings. Retorna o valor definido pelo administrador quando presente, caso contrário, o padrão declarado, já analisado para seu tipo declarado. Para uma chave desconhecida (ou uma sem valor), retorna fallback.

const cooldownMs = owncast.config.get('cooldownMs', 2000);

Ambient: no permission required. Prefira isso em vez de construir uma página de configurações sob medida e chave/valor para controles simples. (A chave de configuração é o que você nomeou no manifesto, e não é traduzida por idioma.)

Rede

owncast.http.fetch(url, opts?)

Requisição HTTP de saída síncrona. opts transporta method, headers, e body. O resultado é { status, headers, body }. Somente hosts listados em network.allowedHosts do seu manifesto são acessíveis. Tudo o mais retorna um erro.

const res = owncast.http.fetch('https://api.example.com/status');
if (res.status === 200) {
const data = JSON.parse(res.body);
}

Requer network.fetch e uma entrada correspondente em network.allowedHosts. Use isso para HTTP de saída em vez do cliente HTTP próprio da sua linguagem (em Python, não use requests: ele não será compilado em um plugin).

Veja Referência de Manifest: rede para a sintaxe da lista de permissões.

Eventos de plugin para plugin

owncast.events.emit(eventType, payload)

Emit to a custom hook owned by another plugin. eventType is the fully qualified \<recipient-slug>.\<hook> target. The host dispatches that exact name and does not add the emitter's slug. The receiving plugin declares only its local hook name. See the handlers reference.

owncast.events.emit('announcer.announcement.broadcast', { text: 'We are live' });

Requer events.emit.

Estado de stream e servidor

owncast.stream.current()

The current live stream state: { online, title?, summary?, viewers, startedAt?, latencyLevel? }.

Requires server.read.

owncast.stream.broadcaster()

Inbound encode telemetry for the current connection: { remoteAddr?, codecs?, resolution?, framerate?, bitrates? }. Somente leitura, e valor zero quando nenhuma transmissão está conectada. Para alterar a saída do vídeo, consulte o grupo de configuração de vídeo abaixo.

Requer server.read.

owncast.server.info()

Static server info: { name?, url?, summary?, welcomeMessage?, version? }.

const name = owncast.server.info().name;

Requer server.read.

owncast.server.socials()

The streamer's configured social links, each { platform, url, icon? }.

Requires server.read.

owncast.server.emotes()

Os emotes de chat personalizados do servidor: o mesmo conjunto que o endpoint público /api/emoji fornece, cada um { name, url }. Útil para renderizar ou filtrar emotes :code: do lado do servidor.

Requer server.read.

owncast.server.federation()

Whether fediverse federation is enabled and under what handle: { enabled, username?, isPrivate? }. username is omitted when unset, and isPrivate is present only when true.

Requer server.read.

owncast.server.tags()

As tags configuradas do streamer, como uma lista de strings.

Requer server.read.

Configuração de vídeo e transcoding

owncast.videoConfig.read()

Returns the current VideoConfig.

FieldTypeValues
latencyLevelnumber0 through 4
codecstringConfigured ffmpeg encoder name
autoplaystringoff, always, or sound-only
variantsStreamVariant[]Configured output renditions

codec reads can report a legacy value or an encoder added by a newer host. Writes accept libx264, h264_omx, h264_vaapi, h264_qsv, h264_nvenc, h264_v4l2m2m, or h264_videotoolbox. Hardware codecs require the matching encoder in the host's ffmpeg build.

Autoplay off requires the viewer to press play. always starts automatically and may fall back to muted playback. sound-only starts automatically only when the browser allows sound.

Each StreamVariant has these fields:

FieldTypeDescription
widthnumberScaled output width
heightnumberScaled output height
frameratenumberOutput frames per second
videoBitratenumberVideo bitrate in kbps
cpuUsageLevelnumberProcessing usage from 0 (lowest) through 4 (highest)
isPassthroughbooleanPass video through without transcoding

Audio settings are not exposed to plugins. Owncast preserves the existing audio configuration for each variant a plugin updates.

const cfg = owncast.videoConfig.read();

Requer videoconfig.read.

owncast.videoConfig.write(partial)

Update any of the VideoConfig fields above. Passe um objeto parcial. Somente os campos que você incluir são alterados. As alterações se aplicam na próxima reinicialização da transmissão: o host não reinicia uma transmissão ativa.

owncast.videoConfig.write({ latencyLevel: 2, autoplay: "sound-only" });

Requer videoconfig.write. Isso é de alta confiança. Administradores devem conceder de forma restrita.

Notificações

owncast.notifications.discord(text)

Enviar uma notificação do Discord através do webhook configurado do streamer.

Requer notifications.send.

owncast.notifications.browserPush({ title, body, url? })

Push para navegadores subscritos.

owncast.notifications.browserPush({ title: 'Live now', body: 'Come say hi', url: '/' });

Requer notifications.send.

Enviar uma notificação formatada para fediverse (renderiza como uma postagem para os seguidores).

Requer notifications.send.

Fediverse

owncast.fediverse.post(text)

Faça uma postagem pública no fediverse a partir da conta Owncast.

Returns { url } on success (currently with an empty url: Owncast publishes the note but does not yet return its URL), or null when the host rejects the call.

Requer fediverse.post. Alta confiança: uma postagem no fediverse sai sob o próprio identificador do streamer e não pode ser revogada em silêncio. Administradores devem conceder de forma restrita.

Botões de ação (tempo de execução)

owncast.actions.add(button | buttons[])

Anexe um ou mais botões de ação ao conjunto do manifesto do seu plugin sem recarregar. Cada botão tem os mesmos campos que uma entrada manifest.actions (title, além de url/openExternally ou html em linha). O host valida cada entrada com as mesmas regras que manifest.actions e persiste o resultado para que as adições sobrevivam a um recarregamento. A rejected entry throws an error naming the entry and the rule it broke, and the whole batch is rejected, so nothing is added when any entry is invalid.

owncast.actions.add({ title: 'Donate', url: '/plugins/my-plugin/donate', openExternally: true });

Requer ui.modify.

owncast.actions.clear()

Remove todos os botões de ação adicionados em tempo de execução. As ações declaradas no manifesto permanecem.

Requer ui.modify.

Cobertura completa em UI: Botões de ação.

Push em tempo real (Eventos Enviados pelo Servidor)

owncast.sse.send(channel, event, data)

Empurre um Evento Enviado pelo Servidor a cada navegador conectado ao endpoint /_sse/\<channel> do seu plugin.

  • channel: qual stream enviar. Use "" para o canal padrão.
  • event: o nome do evento que o navegador escuta. Use "" para o evento message padrão.
  • data: carga. Strings são enviadas como estão. Qualquer outra coisa é codificada em JSON para você.
owncast.sse.send('alerts', 'donation', { from: 'alice', amount: 5 });

Fire-and-forget. A chamada retorna imediatamente e nunca bloqueia. Clientes lentos descartam quadros em vez de travar seu plugin.

Requer http.sse.

Cobertura completa em Servindo HTTP: Atualizações em tempo real.

Temporizadores

Programe trabalho diferido e repetido. Os temporizadores são ambientais, não é necessária permissão e são limpos automaticamente quando seu plugin é desativado. (Python: set_timeout, set_interval, clear.)

owncast.timer.setTimeout(fn, ms)

Execute fn uma vez após ms milissegundos. Retorna um id.

owncast.timer.setInterval(fn, ms)

Execute fn repetidamente a cada ms milissegundos. Retorna um id.

owncast.timer.clear(id)

Cancele um timeout ou intervalo pendente pelo id retornado pela chamada.

Ativos empacotados

Leia arquivos que você enviou no diretório assets/ do seu plugin. Ambiental: não é necessária permissão. (Python: read, read_text.)

owncast.assets.read(path) and owncast.assets.readText(path)

Read a file bundled under assets/, relative to that directory. read returns the original bytes as a JavaScript Uint8Array or Python bytes. readText and Python's read_text decode the bytes as UTF-8. Python replaces malformed byte sequences when decoding. Missing files return null in JavaScript or None in Python.

const image = owncast.assets.read('badge.png');
const template = owncast.assets.readText('template.html');

Referência completa da API

Os nomes dos métodos abaixo estão na forma JavaScript (camelCase). Os equivalentes em Python são snake_case (sendActionsend_action, banIPban_ip, videoConfigvideo_config, e assim por diante).

APIPermissão
owncast.log.info / .warning / .errornenhum (ambiental)
owncast.chat.sendchat.send
owncast.chat.sendActionchat.send
owncast.chat.sendTochat.send
owncast.chat.systemchat.send
owncast.chat.replyTochat.send
owncast.chat.historychat.history
owncast.chat.clientschat.history
owncast.chat.deleteMessagechat.moderate
owncast.chat.kickchat.moderate
owncast.users.list / .getusers.read
owncast.users.setEnabled / .banIPusers.moderate
owncast.users.registerusers.register
owncast.auth.grantSession / .endSessionauth.gate
owncast.kv.get / .set / .getJSON / .setJSONstorage.kv
owncast.storage.uploadstorage.upload
owncast.fs.read / .readText / .write / .list / .delete / .existsstorage.fs
owncast.sql.exec / .query / .queryRowstorage.sql
owncast.http.fetchnetwork.fetch
owncast.events.emitevents.emit
owncast.stream.currentserver.read
owncast.stream.broadcasterserver.read
owncast.server.info / .socials / .emotes / .federation / .tagsserver.read
owncast.videoConfig.readvideoconfig.read
owncast.videoConfig.writevideoconfig.write
owncast.notifications.discord / .browserPush / .fediversenotifications.send
owncast.fediverse.postfediverse.post
owncast.actions.add / .clearui.modify
owncast.timer.setTimeout / .setInterval / .clearnenhum (ambiental)
owncast.assets.read / .readTextnenhum (ambiental)
owncast.config.getnone (ambient)
owncast.sse.sendhttp.sse

Improve this page

See something missing or incorrect? Edit this page and improve the documentation for everyone.

Contributors to this documentation
Gabe KangasGabe Kangas
O
Owncast