Referencia de plugins
El archivo
Sección titulada «El archivo»Un plugin es un solo archivo .js.
Su manifiesto es un comentario que abre el archivo, y es todo lo que hay hasta el primer terminador de comentario.
Yonto lee el manifiesto como texto y nunca ejecuta el archivo para conocerlo, por eso un permiso vive ahí y no en una exportación.
El archivo es un módulo ES y puede usar await en el nivel superior.
No debe leer yonto mientras se está evaluando, así que léelo dentro de una función.
El manifiesto
Sección titulada «El manifiesto»| Clave | |
|---|---|
kind |
"content-source". |
id |
De 2 a 32 caracteres: letras minúsculas, dígitos y guiones, empezando por una letra o un dígito. Instalar un plugin con el id de uno integrado lo sustituye, y al quitarlo vuelve el integrado. |
name, version |
Lo que ve el usuario. version son tres números, 1.2.3, y una compilación nueva necesita una versión nueva. |
contractVersion |
La parte más reciente de la interfaz de plugins que usa el plugin. lint la calcula: declarar menos se rechaza y declarar más genera un aviso, porque rechazaría apps que podrían ejecutar el plugin. |
provides |
"source" es un plugin que es una sola fuente, e instalarlo cambia a ella. "source-type" es un tipo de fuente que el usuario configura y aparece en Añadir fuente; necesita un configSchema. |
allowedHosts |
Los hosts a los que puede acceder el plugin, como hosts simples. Una entrada *. coincide con los subdominios. lint rechaza una entrada que no pueda ser un host. |
configSchema |
Los campos del formulario de Añadir fuente, más abajo. |
description, probeQuery |
Una frase para el cuadro de diálogo de instalación y una palabra que doctor busca. |
lint rechaza una clave que no conoce, y el mensaje nombra la clave conocida más parecida.
Una televisión ignora una clave que no conoce, así que un plugin escrito para una interfaz posterior se instala igualmente.
Ajustes
Sección titulada «Ajustes»Cada entrada de configSchema tiene un id, un label y un type, y puede ser required o tener un default.
Los tipos son text, secret, url, choice y bool.
- Cada respuesta llega en
yonto.configcomo una cadena, sin espacios en los extremos. - Un campo que se deja vacío no existe, así que
yonto.config.x || FALLBACKes la forma de leer uno. - Un
booles la cadena'true'o'false', y'false'se evalúa como verdadera, así que compárala:yonto.config.x === 'true'. - Un campo
requiredque se deja vacío se rechaza antes de la primera llamada, nombrando el campo. - Un
choiceenumera susoptions. lintrechaza un pluginprovides: "source"con un campo obligatorio y sindefault, porque una fuente que no pregunta nada no tiene pregunta.
El host de un campo url que rellena el usuario se permite además de allowedHosts, y así es como un plugin para un servidor propio del usuario puede dejar allowedHosts vacío.
hostsFromConfig desactiva la lista por completo.
Es para plugins cuya única función es leer direcciones que aporta el usuario, como un catálogo XPTV, y un plugin que lo declara le pide al usuario que confirme antes.
No lo uses para evitar escribir un host.
Las funciones
Sección titulada «Las funciones»Cuatro son obligatorias, y lint rechaza un plugin que no las tenga.
| Función | Se llama con | Responde |
|---|---|---|
getCategories() |
nada | [{ id, name }], las pestañas de Inicio. |
getMediaList(categoryId, { page, filters, cursor }) |
el id de la categoría y luego un objeto de opciones: page cuenta desde 1, filters asigna a cada id de filtro el id de la opción elegida y cursor solo está si entregaste uno |
Una lista de resúmenes, o { items, nextCursor } cuando la fuente pagina con cursor. Una lista vacía termina el listado. |
getMediaDetail(id) |
un id que entregaste |
Un detalle: el resumen más synopsis, genres y playbackOptions. |
search(query) |
la consulta, tal cual | Una lista de resúmenes. |
Un resumen es { id, title } y puede tener type, posterUrl, backdropUrl, year y rating, todos cadenas.
type es una cadena simple, y uno no reconocido recurre a un valor seguro por defecto, así que escribe "movie" y no "MOVIE".
Una opción de reproducción es { label, stream: { url, mimeType, headers } }.
En lugar de stream puede llevar pan, un enlace compartido de una unidad en la nube que la app canjea, o track, una cadena que se te devolverá en getStream.
Estas son opcionales.
doctor dice cuáles tiene un plugin.
| Función | Para qué sirve |
|---|---|
getFilters(categoryId) |
Grupos de filtros para una categoría. No exportes una que devuelva []; una fuente sin filtros simplemente no los tiene. |
getRecommendations() |
Los títulos destacados en Inicio. |
checkHealth() |
Una línea de estado para Ajustes. Sin ella, una fuente está sana si getCategories responde. |
getImageHeaders() |
Cabeceras que se envían con las peticiones de carátulas. |
getSubSources() |
Cuando una fuente son varias bibliotecas, un selector. |
getStream(token) |
Canjear un track. |
Una fuente que no puede buscar sigue exportando search, y lanza yonto.error.unavailable(reason).
El global yonto
Sección titulada «El global yonto»Un plugin llega al host a través de yonto y declara el contrato 21: ninguna app ejecuta un plugin que declare menos.
yonto.config |
Los ajustes del usuario, un objeto de cadenas. |
yonto.fetch(url, { method, headers, body, encoding, redirect }) |
Se resuelve con { status, url, headers, body, bodyBase64 } para cualquier estado. Solo se rechaza cuando no hay respuesta, con un code: REQUEST_INVALID, HOST_NOT_ALLOWED, REDIRECT_REFUSED, TIMEOUT, RESPONSE_TOO_LARGE o REQUEST_FAILED. |
yonto.text.decode(…) |
Decodifica bytes que no son UTF-8, como GBK, ya que no existe TextDecoder. |
yonto.html.load(markup), yonto.xml.load(markup) |
El $ de cheerio sobre una página o un documento XML. |
yonto.store.get / set / remove / clear |
Una caché guardada para esta fuente. set(key, value, ttlSeconds). |
yonto.now() |
El reloj. Úsalo, y nunca Date.now(), para medir el tiempo transcurrido. |
yonto.sleep(ms) |
No existe setTimeout. |
yonto.log(message) |
Una línea de registro. |
yonto.partial(reason) |
Indica que la respuesta que vas a devolver está incompleta, como una búsqueda que llegó a tres de cuatro sitios. La app muestra la frase debajo del resultado. |
yonto.installId(), yonto.subSource() |
Qué instalación es esta y la biblioteca que eligió el usuario. |
yonto.crypto, yonto.encoding |
md5, sha1, sha256, hmacSha256, aesCbcDecrypt y conversiones de base64 y hexadecimal, con cadenas de entrada y de salida. |
yonto.cryptoJs(), yonto.jsEncrypt() |
La biblioteca CryptoJS o JSEncrypt completa, para un sitio que necesita más. Tardan en arrancar y no generan aleatoriedad segura, así que nunca crees una clave con ellas. |
Las peticiones siguen las reglas de WHATWG Fetch.
Un GET con cuerpo, credenciales en la URL y cabeceras que pertenecen al host, como Host o Content-Length, se rechazan antes de enviar nada.
Cookie, Origin y Referer sí puedes definirlos.
Errores
Sección titulada «Errores»Lanza uno de estos y la app muestra la pantalla correcta:
| Significa | |
|---|---|
yonto.error.notFound(message) |
El título no existe. |
yonto.error.unauthenticated(message) |
El servidor rechazó las credenciales que dio el usuario. |
yonto.error.misconfigured(reason) |
Un campo de tu propio formulario está en blanco o es incorrecto, y ningún reintento ayudaría. |
yonto.error.unreachable(reason) |
El servidor no respondió. La app puede dejarlo descansar. |
yonto.error.unavailable(reason) |
Cualquier otra cosa que haya salido mal. |
reason y message se muestran al usuario tal cual, en el idioma del sitio que lees, porque nada los traduce.
Nombra la parte que falló y no la fuente, porque el titular de la app ya dice No se puede acceder a <source>.
Que sea una sola frase.
Pon un estado o un código en yonto.log, y nunca una URL, una clave ni lo que buscó el usuario.
Un error lanzado con un code desconocido, o sin código, es un METHOD_THREW, que se muestra como un fallo del plugin.
Límites
Sección titulada «Límites»- El JavaScript propio de una llamada puede ejecutarse 20 segundos en total; el tiempo de espera en
yonto.fetchoyonto.sleepno cuenta. Pasados 20 segundos, ya no puede empezar ninguna llamada nueva ayonto.fetch,yonto.sleepoyonto.store, y una llamada que nunca termina se corta a los 85 segundos. Una fuente ejecuta una llamada a la vez, así que una llamada lenta hace esperar a todas las que van detrás. Haz la petición en la llamada que necesita los datos y guarda en caché lo que puedas. - El cuerpo de una respuesta es de 16 MB como máximo, y menos si no es texto plano.
Lee
bodyBase64en la llamada que hizo la petición. - La caché guarda 256 claves, cada una de hasta 1 MiB de JSON, por fuente.
Una escritura que lo supere se rechaza con
STORE_REFUSED, así que captúralo y sigue. - Un archivo de plugin es de 1 MiB como máximo para instalarse, y la descarga tiene 30 segundos.
- Un plugin no puede leer un archivo, abrir un socket, ejecutar un temporizador ni acceder a un host del que no se informó al usuario.
- El suelo de direcciones privadas rechaza las direcciones de loopback,
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16, de enlace local y*.localpara todos los plugins, diga lo que diga su manifiesto. La única excepción es una dirección que una persona escribió en un campourl.
La definición exacta
Sección titulada «La definición exacta»Esta página es la versión corta.
El texto completo de la interfaz, con las razones y todos los casos límite, es contracts/content-source-http.md en kangzj/yonto-plugins, y sus esquemas son los archivos que hay a su lado.