Personaliza tu plugin¶
Todo lo que un plugin puede cambiar de cómo lo muestra Kino, en un solo lugar, cada cosa con un ejemplo
corto y la página que tiene las reglas completas. Casi todo es nuevo en "apiVersion": 6 (Kino 0.9.50):
declara 6 solo si usas algo de eso, porque Kino 0.9.49 y anteriores rechazan un plugin apiVersion 6.
De un vistazo¶
| Qué | Dónde lo ve la persona | Cómo | apiVersion | Reglas |
|---|---|---|---|---|
| Nombre, descripción, autor | la hoja de consentimiento y cada tarjeta de tu plugin | name, description, author |
1 | Manifiesto |
| Ícono | las tarjetas de tu plugin (Ajustes ▸ Plugins, Recomendados, "De la comunidad") | icon: un .png cuadrado, máximo 128 KB |
1 | Manifiesto |
| Color de acento | la pestaña y los chips de tu plugin, tu nombre sobre tus resultados de búsqueda | color: #RRGGBB |
1 | Manifiesto |
| Chips de la tienda | los chips de categoría de Recomendados, "De la comunidad", "Elige tus fuentes" | categories en el manifiesto |
cualquiera | Manifiesto |
| Un formulario de ajustes y su propia pestaña en Ajustes | Ajustes ▸ <tu plugin> | settings (list desde 4) |
1 | Formulario de ajustes |
| Títulos, líneas de estado, botones, revisión antes de guardar | esa misma pestaña | ajustes section, status, action; settingsStatus, action, validateSettings |
6 | Formulario de ajustes |
| Colores | tu sección, tu pestaña de Ajustes, el título de tu grupo en Categorías, el acento del reproductor mientras suena lo tuyo | theme |
6 | Tus colores |
| Una sección propia, con pestañas y un destacado | barra lateral del TV, franja de chips arriba de Inicio en el celular | "section": { "label" } + section({ tab }) |
6 | Una sección propia |
| Mosaicos en Categorías | Categorías, un grupo con tu nombre | categories() (con browse) |
6 | Tus propias categorías |
| Filas de Inicio, "Ver más" y su género | Inicio, Categorías | filas de home() con ref y genre |
1 | Contrato |
| Canales en tus filas de Inicio | Inicio | ítems kind: "live" en home() |
6 | Canales en vivo |
| Buscar dentro de tus "Ver más" | "Buscar en esta categoría" | scopedSearch |
6 | Contrato |
| Entradas +18 | en todas partes, solo con el código +18 desbloqueado | adult: true |
6 | Contenido +18 |
| Nombres de las copias de un video | el menú Servidor del reproductor | Stream.label, alternatives con label |
6 | Copias con etiqueta y perezosas |
| Botones para saltar | "Saltar intro", "Saltar outro" | Stream.skip |
cualquiera | Contrato |
| Botones para saltar en cualquier título, de cualquier fuente | "Saltar intro", "Saltar outro" | segments |
7 | Dónde están la intro y los créditos |
| Nombres de audio y subtítulos | el menú "Audio y subtítulos" del reproductor | audioTracks[].label, subtitles[].lang |
1 | Contrato |
| Tu propia frase en un error | "Mensaje de <tu plugin>: …" | kino.error(code, detalle, { userMessage }) |
6 | Tu propia frase |
| Subtítulos para cualquier título | "Buscar subtítulos en línea" | la función subtitles |
cualquiera | Subtítulos para cualquier título |
| Fichas de títulos de otros plugins | la ficha de un título, donde TMDB no tenía nada; desde Kino 0.9.51 un logo en lugar del nombre, notas de otros sitios y el reparto | meta (logo, ratings, cast) |
6 | Describir otros títulos |
| El interruptor de un servicio de seguimiento y su estado | "Enviar lo que veo" y "No pudo avisar a …" en tu pestaña de Ajustes | tracking |
7 | Contarle a un servicio de seguimiento |
| Canales, logos, números, guía | En vivo, guía de TV, cajón de canales | channels |
3 | Canales en vivo |
La identidad de tu plugin¶
{
"id": "mi-cine", "name": "Mi cine", "version": "1.0.0", "apiVersion": 6, "entry": "plugin.js",
"description": "Cine colombiano y latinoamericano, con subtítulos",
"author": "ana", "homepage": "https://github.com/ana/mi-cine",
"icon": "icon.png",
"color": "#3D5AFE",
"categories": ["movies", "series"],
"hosts": ["api.example.com"],
"capabilities": ["search", "home", "browse", "episodes", "resolve"]
}
- Escribe
nameydescriptionen español: es lo que muestra cada tarjeta.descriptiontiene máximo 300 caracteres,name40. icones una ruta al lado del manifiesto, nunca./icon.png. Un ícono que falta o que pesa demasiado se omite, nunca hace fallar la instalación.colorpinta tu pestaña, tus chips y tu nombre sobre tus resultados de búsqueda.theme(más abajo) va mucho más allá, desde apiVersion 6.categoriessolo etiqueta tu plugin para los chips de la tienda. No es la funcióncategories(), que pone mosaicos dentro de Categorías de Kino.
Tu pestaña de ajustes¶
Todo plugin encendido tiene su propia pestaña en Ajustes, con su interruptor de
Modo debug; uno con settings muestra ahí también su formulario. Nueve tipos de campo (text,
password, url, toggle, select, list, y desde apiVersion 6 section, status, action),
valores por defecto, campos obligatorios, una revisión antes de guardar y botones que corren tu código:
"settings": [
{ "key": "account", "label": "Tu cuenta", "type": "section", "hint": "Opcional" },
{ "key": "email", "label": "Correo", "type": "text" },
{ "key": "password", "label": "Contraseña", "type": "password" },
{ "key": "linked", "label": "Estado", "type": "status" },
{ "key": "logout", "label": "Cerrar sesión", "type": "action", "confirm": "¿Cerrar la sesión?" },
{ "key": "quality", "label": "Calidad", "type": "select", "default": "hd",
"options": [{ "value": "hd", "label": "Alta" }, { "value": "sd", "label": "Ahorro de datos" }] }
]
export async function settingsStatus() {
return { linked: kino.config.get("email") ? "Cuenta vinculada" : "Sin cuenta" };
}
export async function action(key) {
if (key === "logout") return { message: "Sesión cerrada", clearSettings: ["email", "password"] };
return null;
}
Todos los tipos y atributos, validateSettings y un ejemplo completo: Formulario de ajustes.
No hay campos condicionales: todo ajuste se muestra siempre.
Tu sección, tus mosaicos, tus colores¶
"apiVersion": 6,
"section": { "label": "Mi cine" },
"theme": { "accent": "#3D5AFE", "onAccent": "#FFFFFF", "background": "#101820", "surface": "#1A2733", "highlight": "#F7C948" }
export async function section({ tab }) { // tab es null la primera vez
const tabs = [{ id: "pelis", label: "Películas" }, { id: "series", label: "Series" }];
const chosen = tabs.some((t) => t.id === tab) ? tab : "pelis";
return {
tabs, tab: chosen,
hero: { title: "Estreno de la semana", text: "Una película nueva cada viernes.", image: "https://img.example.com/hero.jpg" },
rows: await rowsFor(chosen), // las mismas filas de home()
};
}
export async function categories() { // necesita la capacidad browse
return [
{ id: "comedia", title: "Comedia", ref: "genre:comedia", art: "https://img.example.com/comedia.jpg" },
{ id: "terror", title: "Terror", ref: "genre:terror" },
];
}
Kino revisa que cada color se lea bien cuando lo usa y vuelve al suyo para un color que no pasa
(node sdk/run.mjs . theme muestra los contrastes). Los errores siguen en el rojo de Kino. Las reglas:
Sección, categorías y colores.
Filas de Inicio¶
export async function home() {
return [
{ id: "nuevas", title: "Recién llegadas", ref: "nuevas", genre: "peliculas", items: newest },
{ id: "canales", title: "Canales de Colombia", genre: "noticias", items: [ // apiVersion 6
{ id: "canal-1", ref: "live:1", title: "Canal Uno", kind: "live", poster: "https://img.example.com/c1.png" },
] },
];
}
- Una fila con
ref(y la capacidadbrowse) termina en "Ver más"; conscopedSearchtú respondes la búsqueda dentro de ella. genre(uno depeliculas,series,anime,infantil,documentales,deportes,noticias,musica,entretenimiento,otros) es como Categorías agrupa las filas navegables de todos los plugins. Sin él, Kino lo adivina por el título.- Cada ítem puede llevar
badges(hasta 3 chips como"Latino","4K"),quality,lang,rating,year,genres,overview, unpostery unbackdrop;ids.tmdbdeja que Kino complete su ficha. - Los ítems
kind: "live"se quedan en las filas de Inicio desde apiVersion 6, como tarjetas de canal con la insignia "En vivo". adult: truemantiene una entrada oculta hasta que la persona desbloquee su código +18.- Tus filas van después de las de Kino, bajo el nombre de tu plugin. Reglas: Contrato.
En el reproductor¶
export async function resolve(ref) {
const servers = await listServers(ref); // [{ id, lang, name }]
const first = await resolveServer(ref, servers[0]);
return {
url: first.url,
label: `${servers[0].lang} · ${servers[0].name}`, // "Latino · Servidor 1" en el menú Servidor
alternatives: servers.slice(1, 9).map((s) => ({ label: `${s.lang} · ${s.name}`, ref: `${ref}|${s.id}` })),
audioTracks: first.dubs.map((d) => ({ lang: d.lang, url: d.url, label: d.name })), // "Español (Latinoamérica)"
subtitles: first.subs.map((s) => ({ lang: s.lang, url: s.url })),
durationMs: first.durationMs,
skip: { openingStartMs: 62_000, openingEndMs: 152_000, endingStartMs: 1_290_000 },
};
}
labely las copias perezosas{ label, ref }(apiVersion 6) llenan la sección Servidor del menú "Audio y subtítulos" del reproductor; una copia se resuelve solo cuando la persona la escoge o el cambio automático llega a ella (Copias con etiqueta y perezosas).audioTracks[].labelse muestra tal cual en el menú de audio; sin él, Kino nombra la pista por sulang.skippone "Saltar intro" y "Saltar outro" en pantalla; una corrección que la persona hace a mano gana.- Con un
theme, la barra de progreso, el deslizador y el borde de foco del reproductor toman tuaccentmientras suena lo tuyo.
Tus propias palabras¶
throw kino.error("not_found", "E404", { userMessage: "Este capítulo ya no está disponible." });
La persona lee "Mensaje de Mi cine: Este capítulo ya no está disponible." en lugar de la línea de Kino, solo cuando la frase pasa las reglas de seguridad (en español, máximo 160 caracteres, sin enlaces, sin pedir dinero, credenciales ni datos de contacto). Las líneas de estado y los mensajes de las acciones de tu formulario de ajustes también son palabras tuyas (Formulario de ajustes).
Lo que no puedes cambiar¶
- Las pantallas, las fuentes y la disposición de Kino, el orden de Inicio (tus filas van después de las de Kino) y el fondo y las superficies del reproductor.
- El color de los errores: siempre el rojo de Kino, diga lo que diga tu
theme. - Ajustes condicionales: todo campo se muestra siempre.
- Nada que corra por fuera de tus funciones: ni pantallas propias, ni notificaciones, ni tareas de fondo. Kino te llama; tú respondes con datos.
- Si el código +18 de la persona está desbloqueado: tú solo marcas entradas con
adult: true, Kino decide.