What's new for plugin authors¶
What changed in Kino that matters when you write a plugin, by app version. Every number is in the contract and the reference files.
Kino 0.9.53: kino.meta and kino.tmdb¶
No new apiVersion: it is still 7, and nothing on this list makes you change your plugin. Both new
calls exist only from Kino 0.9.53, so feature-detect them (typeof kino.meta === "function",
typeof kino.tmdb === "function"); node sdk/validate.mjs warns when your code calls one without that check.
kino.meta(query): ask Kino what it knows about a title ({ type, ids: { imdb, tmdb, tvdb, kitsu, mal, anilist }, lang }) and get synopsis, year, poster, backdrop, logo, genres, runtime, episodes with their ids, the cross-reference of every id, ratings and cast, ornull. Kino answers from its own TMDB lookup, AniList for anime, and the person's othermetaplugins, merged the way its info page merges them; your plugin never touches a TMDB key, and it is never asked on its own behalf. 30 calls a minute, 8 s at most, cached 30 minutes. ThekinoAPI.kino.tmdb(path, params): TMDB's read-only v3 API with no key in your plugin. Kino's own key goes first, behind Kino's TMDB cache (on disk, shared with its own screens, a copy up to 7 days old when TMDB is down) and limits of its own (20 calls per 10 s per plugin, 60 per 10 s for all plugins), so plugins cannot spend Kino's quota. Only when Kino's key fails (TMDB answers 401/403/429 for it, or one of those limits is spent) does the same request go again with the person's key: the one they type in Ajustes ("Tu llave de TMDB", optional, synced between their devices), else the one they configured in an installed Stremio addon, once they agree. A key your plugin keeps in its own settings is never used. Kino adds the key; your code never sees any, and TMDB needs no entry in yourhosts.no_tmdb_key(with Kino's sentence for the person ine.userMessage: "Agrega tu llave de TMDB en Ajustes, o instala un addon de TMDB de Stremio configurado con tu llave.") is left for a Kino build without a key of its own and a person without one. Allowlisted read paths only, 40 calls per 10 s per plugin whichever key, cached 10 minutes. A TMDB-based catalog no longer needs its own key setting (keep it only as a fallback for older Kino). ThekinoAPI, a complete example.- The Node kit runs both:
KINO_META_FIXTURE,KINO_TMDB_KEY(or"tmdbKey"insdk/config.json) andKINO_TMDB_FIXTURE. Test it locally.
Kino 0.9.51: apiVersion 7¶
apiVersion 7 = Kino 0.9.51. The contract (contract.json) now says maxApiVersion 7 and
kino.apiVersion reports 7. A manifest with "apiVersion": 7 is refused by Kino 0.9.50 and older ("Este
plugin necesita una versión más nueva de Kino"), so declare 7 only if you use tracking or segments.
Nothing else on this list makes you change your plugin.
tracking(apiVersion 7): your plugin exportstrack(event)and Kino tells it which movie or episode plays on that device, from any source:start,progress,stopandwatched, with the episode's own ids and the show's apart. Approved in red ("Le contará a … qué ves y cuándo lo terminas"), with a "Enviar lo que veo" switch in your Ajustes tab; events wait in a queue that survives offline and a closed app (ordered retries, 200 per plugin, 7 days). Return{ skipped: true }for an event your service has no use for. Telling a tracker what the person watches.segments(apiVersion 7): your plugin exportssegments(query)and tells Kino where any movie's or episode's intro and credits are; the "Saltar intro" and "Saltar outro" buttons and auto-skip use them on phone and TV. No red approval. Where the intro and credits are.- Subtitles for the exact file:
subtitles()getsfile: { hash?, size?, name? }, what Kino knows of the playing file (its OpenSubtitles hash, its size, its name, or a release-style one built from the title), to rank the exact release first. A Stremio addon gets it as thevideoHash,videoSizeandfilenameextras. No newapiVersion. Subtitles for any title, Stremio addons. metawith a logo, ratings and cast: ametaanswer may carrylogo(shown instead of the name on the info page),ratings(up to 6, from IMDb, Rotten Tomatoes, Letterboxd…) andcast(up to 20); an AIOMetadata-style Stremio addon gives them through itslogo,imdbRatingandapp_extras.cast. No newapiVersion: older versions ignore them. Describing other titles.stremio:///detail/…links open the title in Kino (Seenr's "Open in Stremio" and the like) instead of being ignored. Stremio addons.-
"Tu servidor" 1.5.0, the reference plugin, now shows everything a server of one's own can use up to apiVersion 7:
tracking,segments,subtitleswithfile,metawith logo, ratings and cast, and the apiVersion 6 features (section, categories, request signing, copies, the full settings form, aresolve: trueplaylist,liveSearchand channel paging). Example plugins. -
Nuvio compatibility v2: Kino converts many more Nuvio scrapers. Multi-file scrapers (siblings read from the same repository, at most 16 files and 1 MiB), a Node subset (
path,url,util,events,querystring,timers,bufferandhttp/https/undicioverkino.fetch;setInterval,queueMicrotask), each scraper'sonSettingsas a settings form synced across devices, and every playable copy offered as a labelled alternative in the Servidor menu (embed pages last). A Kodi-styleurl|User-Agent=…address becomes headers. Peer-to-peer and debrid scrapers are refused ("No compatible"). None of it is for your own plugin: it is what a Nuvio scraper may rely on. Nuvio scrapers. - Settings form: a
section'shintmay be up to 300 characters and wraps (sectionHintMaxCharsincontract.json). Builds before 0.9.51 refuse one over 80, so keep it short if your plugin must install on them. The settings form. - A
%in an error's text no longer crashes the app. Up to 0.9.50, an error from your plugin whose text carried a%(a percent-encoded URL such as?q=Inception%20sin a "fetch failed") could kill Kino outright. Now an error's text can be anything; if your plugin must run on 0.9.50 and older, keep encoded addresses out of its error messages. Errors your code can catch. - Recomendados gains Stremio utility addons (subtitles such as OpenSubtitles v3 and Subtis, catalogs such as Cinemeta, TMDB and IMDb) and free, legal channels (Pluto TV, Radios). The list is no longer published on npm: Kino reads it from this repository on GitHub, then from archive.org, then from jsDelivr's copy of the same GitHub file. An addon that plays video is still never recommended. Stremio addons.
- A Stremio addon whose description denies torrents is no longer hidden ("no incluye streams, torrents ni contenido P2P"); the id and name stay strict. What Kino refuses.
- Your plugin's card no longer wears a "Kino" badge (it read as made by Kino); Stremio addons and Nuvio scrapers keep theirs.
Kino 0.9.50: apiVersion 6¶
apiVersion 6 = Kino 0.9.50. The contract (contract.json) now says maxApiVersion 6 and
kino.apiVersion reports 6. A manifest with "apiVersion": 6 is refused by Kino 0.9.49 and older
("Este plugin necesita una versión más nueva de Kino"), so declare 6 only if you use something on this
list. Everything a plugin can now change in how Kino shows it is gathered on
Customize your plugin.
- Larger and typed sealed secrets: up to 8,192 bytes, and typed cipher keys (
use: "cipher-key") that also work fordes-ede3. The manifest. migrate: move to your plugin what the person had saved and Kino can no longer open. Moving saved titles.- Request-signed streams:
signing: "request",signContext, thesignexport,resolve(ref, { retry })andalternateHosts. Signing every request. - The settings form: the
section,statusandactiontypes (up to 16, on top of the 12 valued ones),settingsStatus,actionwithclearSettings,validateSettings, the plugin's own tab in Ajustes and syncing across devices. The settings form. debugandtelemetry(trueor"verbose"),kino.log.report, the Registro page, theKinoPlugin/<id>andKinoPlaylogcat tags, and playback metrics. Logs and telemetry.- Modo debug in every plugin: every installed plugin (yours, a generated Stremio addon, a Nuvio
scraper) has a "Modo debug" switch in its Ajustes tab, with no work on your side: on, its failures show
on screen and its Registro can be copied or shared, so a person can send you a screenshot or their
Registro.
"debug": truenow only makes the switch on by default; without it the switch starts off. The person's choice survives updates and syncs to their other devices. Modo debug. - Early rejections are caught: a
throwin anasyncfunction before its firstawaitis caught by the caller'stry/catch(or.catch(),Promise.all), as in Node; only a rejection nobody ever handles still fails the call. Keep awaiting first if your plugin must run on 0.9.49 and older. The rejection trap. section,categoriesandtheme: a section of your own, a group in Categorías and your colors. Section, categories and colors.scopedSearch: answer the search inside a "Ver más" page yourself. The contract.kino.error(code, message, { userMessage }): your own sentence for the person, with safety rules and attributed to your plugin. The contract.adult: trueentries behind the person's 18+ code (they used to be dropped). 18+ content.- Channels in your Home rows (
kind: "live"inhome; they used to be dropped). Live channels. - Key pairs in
kino.crypto:generateKeyPair,sign,verify,importKey,deriveSharedSecret. The kino API. - The hidden browser:
"browser": true(approved in red, "Puede abrir páginas web ocultas para encontrar el video") andkino.browser.capture, which opens an embed in a hidden in-app WebView inside aresolvethe person started and returns the video requests it made, held so their tokens stay fresh, with the headers and cookies to play them. All its traffic goes through a proxy with a per-capture credential and vetted, pinned IPs; the home network never; one page at a time; cookies and storage wiped. A page that asks for a human ends withblocked: Kino never solves a captcha. An approved plugin'sresolvegets 75 s. Also, with"browser": "pages"(its own red line),kino.browser.page, which reads a page's HTML through the same hidden browser when the site's automatic check passes by itself (never fromcategories; the top document must stay on your hosts, every redirect hop checked, or the read endsblocked). Hidden browser. Stream.labeland labelled lazy copies: name each copy ("Latino · Servidor 1") for the player's new Servidor menu, and list copies as{ label, ref }that Kino resolves throughresolve(ref)only when the person picks one, the automatic fallback reaches it (at most 20 s each) or a download's copy choice probes it. A failed pick returns to the copy that was playing. Labelled and lazy copies.meta: describe titles other sources listed (synopsis, images, episodes) when TMDB and AniList have nothing. Describing other titles.
No new apiVersion (for any plugin):
alternativeson aStream: up to 8 copies of the same video; Kino moves to the next when one cannot play on the device. The contract.- Play on the TV from the phone, new chapters of followed series and "Para ti" work for titles of any plugin. What people see.
- Updates: a check when the app starts (at most every 12 h), a badge for pending ones, and a failed call with a pending update says so. Publishing.
- A signed plugin at two repositories counts as the same plugin with the same
idand key. Signed plugins. subtitlesexport: answer the player's "Buscar subtítulos en línea" for any title Kino knows by IMDb or TMDB id, alongside your videos or as a subtitle provider ("capabilities": ["subtitles"]alone). Subtitles for any title.Stream.skip: where this file's opening and ending are, for "Saltar intro" / "Saltar outro"; yours win over AniSkip, a hand correction wins over yours. The contract.- The manifest's
categories(movies,series,anime,live,radio,subtitles,utilities,adult): the plugin marketplace's category chips. The manifest. - Settings form:
settingsStatus()is asked again after every action, sorefresh: trueis no longer needed. The settings form. - Logs: only a plugin that declares
telemetrysendskino.loglines with a failure, recommended or not; for now there is no switch to turn it off. Logs and telemetry. - "De la comunidad" is its own tab of the Plugins screen. Publishing.
- Stremio subtitle addons (OpenSubtitles v3, translators such as GTSubs) install as subtitle providers; machine translations show as "Español (traducido)". Nothing for you to write. Stremio addons.
- Reserved ids:
live,local,unknown,plugin,own,subtitle-keys,subtitle-prefs(the list changed: older versions reserve a few more, so if one says "El id … está reservado por Kino", pick another). - Claims and takedowns of community plugins:
community-blocklist.json. - Sending to the TV: every HLS goes through the phone; a file without
headersgoes direct and, if the TV fails it, through the phone. Sending to the TV. genreon a Home row, a live category or a playlist (peliculas,series,anime,infantil,documentales,deportes,noticias,musica,entretenimiento,otros): Categorías groups the browsable rows of every plugin by it, and En vivo filters by it across providers. Optional; without it Kino guesses from the title. The contract.streamHeaderson a playlist: theUser-AgentorRefererthe player sends for every channel of the list, kept apart from the list's own downloadheaders. Live channels.- A failing synchronous
kino.*call is catchable (a fullkino.storage, akino.cryptoerror): yourtry/catchgets an ordinaryError; Kino 0.9.49 ended the whole call there. Errors your code can catch. - After a Kino upgrade, plugin updates that wait for approval are installed once, from the plugin's own address, with a one-time "Se actualizaron tus plugins" notice listing what each may do now. Publishing.
- The settings form, documented whole: every field type, attribute and default, with a complete example. The settings form.
(Kino builds before genre and streamHeaders ignore them; the exact version that first shipped each
was not checked.)
Also in this version (documented earlier on this page as "next version"):
- Install from a manifest URL. People can paste the
httpsURL of akino-plugin.jsonon any public server, not only a GitHubowner/repo. Such aurl:install readsentryandiconnext to the manifest, cannot use sealedsecrets, always counts as unsigned, and never appears in "De la comunidad" (discovery still uses the GitHub topic). Akino-plugin.jsonURL on GitHub, raw.githubusercontent.com or jsDelivr (cdn.jsdelivr.net/gh/owner/repo@<exact ref>/…;@latestis the default branch, a version range is refused) becomesowner/repoas before. Installing from a manifest URL, Publishing. liveSearch, a new optional export for live channels (no newapiVersion: it stays 3 withchannels). Kino asks it from En vivo's search while some of your channels were never listed; it returns channels like aliveChannelspage, at most 100 kept, from 2 typed characters, 15 s. Live channels.- Big live catalogs keep paging.
liveChannelsgets 10 pages at first, then 5 more each time the person scrolls near the end, up to 10,000 channels (200 pages) per category. Older versions stop at 10 pages. Test withnode sdk/run.mjs . live search <query>. - M3U lists may be UTF-8, Latin-1 or UTF-16;
#EXTINFattributes may be single-quoted or bare; a list over 20 MB or a guide over 50 MB is cut at its last whole line instead of refused. Live channels. - Stremio addons can be installed by people from the same field (Kino generates the plugin; nothing
for you to write). Their
resolve, like a converted Nuvio scraper's, gets 75 s. What is supported, what is refused (torrents and P2P, always) and how to make an addon work well: Stremio addons. dituis no longer a reserved pluginid(older versions still refuse it, so avoid it).
Kino 0.9.46 to 0.9.49¶
- A leading
./inentryandiconis accepted. Kino drops it and installs the plugin. Kino 0.9.45 and older still refuse it (El campo "entry" debe ser una ruta relativa a un archivo .js), so keep writing"plugin.js"and"icon.png". The kit'svalidate.mjsrefuses"./plugin.js"for that reason. Details. - Your logs help when a recommended plugin fails. For a plugin in Kino's recommended catalog, a
failed call's last 30
kino.loglines (scrubbed, 2 KB) go with the error report asplugin_log. Log steps and statuses, never what the person typed or a secret.kino.log. - A live channel never shows a download button, even in a plugin that declares
download.
About the version of each item
These items are in the builds released as 0.9.46 to 0.9.49; the exact version in which each one first appeared was not checked.
Kino 0.9.45¶
Contract (contract.json, maxApiVersion 5):
- Signed plugins,
apiVersion5. Optional author signature inkino-plugin.json(node sdk/seal.mjs --keygen,--sign;validate.mjschecks it), key pinned at first install, "Firmado por su autor" on the consent sheet, "Firmado" badge, the author key in the details. It needs Kino 0.9.45+; older apps refuse anapiVersion5 manifest. Earlier drafts of these docs said 0.9.46: it shipped in 0.9.45. Signed plugins. - No maximum number of
hosts. The old limit of 20 is gone (only the manifest's 16 KB bounds it). Kino 0.9.44 and older still refuse more than 20, and the kit warns about it. The manifest. kino.apiVersionreports 5.
Behavior you may notice (no manifest change):
- Sending to the TV (Chromecast and DLNA) works for plugin titles. Direct for mp4/webm and HLS
with no
headers; through the phone when you setheaders; never for DRM, DASH, progressive MPEG-TS or a format nothing identifies. Sending to the TV. - Plugins follow the person across their devices (phone and TV): installs, switches, approvals, settings and passwords (encrypted) sync, and the other device installs your plugin from the same address. Plugins on other devices.
- Install addresses can also be a
raw.githubusercontent.com/.../kino-plugin.json(ormanifest.json, for Nuvio) URL, or agithub.com/.../blob/...one; a ref that only comes from a pasted URL is not a pin. Index, Nuvio scrapers. - HLS downloads:
EXT-X-DISCONTINUITYis kept as is unless the format changes at it; a bad key or an empty segment ends as "Este video no se puede descargar"; a retry resumes only with the same content. Downloads stay declarative:"download"incapabilities, nothing to export. Downloads. - M3U channel headers:
#EXTHTTP,url|User-Agent=...and#KODIPROPheaders are read; onlyUser-Agent,Referer,OriginandCookieare kept. Live channels. kino.fetch: refused redirect hops count toward the 60-request limit, at most 6 fetches in flight, at most 3 host questions per call, IPv6 forms of private addresses refused, no device proxy. A plugin converted from a Nuvio scraper gets 250 requests and a 75 sresolve. Limits.
About the version of each item
The contract file states the version only for signed plugins and the host limit (0.9.45). The other items above are in the build that was released as 0.9.45; the exact version in which each one first appeared was not checked.
Already there before 0.9.45¶
streamHosts: "any" (apiVersion 4), liveStreamHosts: "any" (apiVersion 3 plus the channels
capability), fetchHosts: "any" (written by Kino into converted Nuvio scrapers only, never for your
plugin), and the question Kino asks the person the first time a stream uses a host you did not
declare. They are documented in the manifest,
live channels and the contract.