Writing a Kino plugin¶
A Kino plugin is a video source that anyone can publish as a small GitHub repository: one JSON
manifest and one JavaScript file. A person types owner/repo in Kino, sees which sites the plugin
will talk to, accepts, and from then on the plugin is one more source: its results show up in
search and on Home, and its titles open, list episodes, play in Kino's player, keep progress and
appear in "Continuar viendo" and the library like any other title.
You can write, run and test a plugin on your computer with Node before you ever touch the app. This guide has everything you need: the file layout, the manifest, the contract your code must meet, the API Kino gives you, every limit, the quirks of the JavaScript engine, and how to publish.
The complete API demo is kinotvapp/kino-plugin-own-server
("Tu servidor" 1.5.0: every feature up to apiVersion 7 working end to end); kinotvapp/kino-plugin-archive
(Internet Archive) is the simplest starting template. Both carry the sdk/ folder, the Node
kit. Two more files describe the contract for machines (both on the Reference
page): contract.json holds every number and rule the app enforces (the tables in this guide are
generated from it, and the app's tests pin its own constants to it), and kino.d.ts declares the
whole kino API for your editor (/// <reference path="./kino.d.ts" /> at the top of plugin.js).
The 5-minute path¶
- Start from the template. Create your repository from
kinotvapp/kino-plugin-archive -- the
simplest one -- or from kinotvapp/kino-plugin-own-server
if you need settings, a session, downloads or live channels ("Use this template" on either one,
or clone it and copy
sdk/). Do not fork it: Kino's community search leaves forks out (Get found). Or let the kit write a skeleton:node sdk/init.mjs my-plugin --host example.com. - Declare what you need in
kino-plugin.json: anid, thehostsyou will call and thecapabilitiesyou export (The manifest). - Write the functions in
plugin.js:searchand/orhome, andresolveat least (The contract, ThekinoAPI). - Check it the way Kino does:
node sdk/validate.mjs ., thennode sdk/run.mjs . search "algo"(Test it locally). - Publish it as a public repository with the topic
kino-plugin(mandatory: without it Kino cannot find it), and install it in Kino from Ajustes > Plugins by typingowner/repoor pasting the URL of itskino-plugin.json(Publishing). - Get Kino to show it by itself in "De la comunidad": the topic, name and description, and how to check it, in Get listed in Kino.
Using an AI assistant? Give it the ready-made prompt: it reads this whole guide from
llms-full.txt and follows AGENTS.md.
What a plugin is¶
A public GitHub repository, or a folder inside one, with:
kino-plugin.json the manifest (required)
plugin.js the code: a single ES module (required; its name is set by "entry")
icon.png optional, square, at most 128 KB
README.md for humans
Kino runs your code in a sandbox: no filesystem, no timers, no other plugins, no access to the
person's data. The only way out is kino.fetch, which can reach only the hosts your manifest
declares and the person approved on screen, plus the servers the person typed in your plugin's
settings (see The manifest).
Kino loads exactly one JavaScript file, so there is nothing for an import to resolve to. If you
use a build step or a library, bundle everything into that single file -- see
Splitting your code across files for a worked example.
How people install it. In Kino, Ajustes > Plugins, they type the address of your repository:
| They type | Kino reads |
|---|---|
owner/repo |
the repository root, default branch |
owner/repo/sub/dir |
a folder inside the repository |
owner/repo@v1.2.0 |
a branch, tag or commit (the name cannot contain /); also works with a folder |
https://github.com/owner/repo or .../tree/<ref>/<path> |
the same, pasted from the browser |
https://raw.githubusercontent.com/owner/repo/<ref>/<path>/kino-plugin.json (or a github.com/.../blob/<ref>/.../kino-plugin.json, also /raw/) |
the folder that .json file is in, at that ref; any other kind of file is refused |
https://cdn.jsdelivr.net/gh/owner/repo[@<ref>]/<path>/kino-plugin.json (also fastly, gcore, testingcf and quantil.jsdelivr.net) |
the same repository folder, read from GitHub: no @ref or @latest is the default branch; the ref must be an exact branch, tag or commit (@main, @v1.2.0), so a version range (@1, @^1.2, @1.x) is refused |
https://<any public server>/<path>/kino-plugin.json |
a plugin hosted outside GitHub: see Installing from a manifest URL |
The field in Kino says "Escribe usuario/repositorio de GitHub o pega la URL del manifest
(kino-plugin.json)". A ?query or #fragment in a pasted URL is ignored. A ref that only comes from a
pasted URL (tree, blob, raw, raw.githubusercontent.com or jsDelivr's @ref) is not a pin: a
plugin with sealed secrets pasted that way installs from the default branch.
For a repository address, Kino downloads kino-plugin.json, your entry file and the icon from
raw.githubusercontent.com, which is why the repository has to be public. A URL of a repository's
kino-plugin.json (on GitHub, raw.githubusercontent.com or jsDelivr) always becomes that repository's
address, so sealed secrets, signatures and community search keep working for it.
Installing from a manifest URL¶
Your plugin does not have to live on GitHub (new in the Kino version after 0.9.49). People can paste the
https URL of its kino-plugin.json on any public server: your own site, GitHub Pages, a CDN such as
jsDelivr's npm/. Kino stores it as the address url:https://…/kino-plugin.json (scheme and host
lowercased, default port, query and fragment dropped): that URL is the plugin's identity, and it
travels as is to the person's other devices with plugin sync.
entryandiconare read relative to the manifest URL:"entry": "plugin.js"next tohttps://example.com/kino/kino-plugin.jsonishttps://example.com/kino/plugin.js.httpsonly (http://is refused with "Kino solo instala plugins desde direcciones https…"), on a public name: no IP addresses, nolocalhost, no single-label or.local/.lannames, no user:password in the URL, and the file has to be named exactlykino-plugin.json. A name that resolves to a private address, and a redirect offhttpsor to such a host, are refused too.- No sealed secrets: seals are bound to a GitHub repository, so a manifest with
secretsinstalled from a URL is refused ("Este plugin trae datos sellados, y esos solo funcionan si lo instalas desde su repositorio de GitHub…"). Publish such a plugin on GitHub. - Unsigned: a
signatureis bound toowner/repo, so it is not checked and the plugin installs (and shows) as unsigned. - Everything else is the same as a repository install: the consent sheet,
hostsand every approval rule, and updates: Kino re-reads the same URL and applies a higherversion, asking again when it needs more than was approved. The plugin is never listed by community search (that only finds GitHub repositories with thekino-plugintopic).
Stremio addons are a Kino feature for people, not something you write: in the same field a person
can paste the address of a Stremio addon's manifest.json (or a stremio:// link) and Kino generates
a plugin for it. Nothing in this guide changes for your plugin.
The guide, page by page¶
| Page | What is in it |
|---|---|
| A first plugin | Two files that search and play, and how to run them |
| Signed plugins | Sign your plugin with your own key so people know every update is yours (apiVersion 5, Kino 0.9.45+) |
| What's new | What changed for plugin authors, by Kino version |
| The manifest | Every field and rule of kino-plugin.json, settings, streamHosts, sealed secrets, the person's own servers, insecureHttp, downloads |
| The contract | The functions you export, their arguments, what you return, host questions and the broad video permission, and the errors people understand |
The kino API |
fetch, cookies, secret, crypto, sleep, config, HTML, storage, log, rank |
| Live channels | live items, the En vivo tab, M3U/XMLTV playlists, guides, liveStreamHosts, three recipes |
| Customize your plugin | Everything your plugin can change in how Kino shows it, in one table: icon, color, settings, section, categories, colors, Home rows, the Servidor menu, your own sentences |
| The settings form | Every field type and its attributes, section, status and action, clearSettings, validateSettings, a complete example, the own tab and syncing |
| Signing every request | HLS streams signed on every request: signing, sign, retries and alternateHosts (apiVersion 6) |
| Hidden browser | "browser": true / "pages", kino.browser.capture and kino.browser.page: when to use them, the safety model, never a captcha, timeouts and failures (apiVersion 6) |
| Moving saved titles | migrate: move what the person had saved to your plugin (apiVersion 6) |
| Section, categories and colors | section, categories and theme (apiVersion 6) |
| Logs and telemetry | debug, the Registro page, telemetry, kino.log.report, logcat and playback metrics (apiVersion 6) |
| Limits and engine quirks | Every number in one place, how your code lives, what QuickJS lacks, the rejection trap |
| Test it locally | The Node kit: run.mjs, validate.mjs, record and replay, live channels |
| Get listed in Kino | The five steps to show in "De la comunidad", how long it takes and how to check it |
| Publishing | Releases, updates and approvals, and appearing in "De la comunidad" |
| What people see | The consent sheet, host dialogs, player messages, Configurar, statuses, disabling and uninstalling |
| Cookbook | An HTML site with a login, a JSON API with a token, the person's own server, Widevine, plain http |
| Nuvio scrapers | How people install Nuvio scrapers, what the conversion builds, and its limits |
| Stremio addons | How people install a Stremio addon, how each resource maps, what is refused, and its limits |
| Example plugins | The two published examples, and how the reference plugin is built |
| Claims and plugin takedowns | How to ask for a community plugin to leave the index, what Kino does and how to appeal |
| Reference | contract.json and kino.d.ts, to read or download |