跳转到内容

插件参考

插件是一个 .js 文件。 清单是文件开头的一段注释,到第一个注释结束符为止。 杨桃把清单当文本读取,不会为了读清单去运行文件,所以权限要写在清单里,不能写在某个导出项里。

文件是 ES 模块,顶层可以用 await。 文件求值期间读不到 yonto,要在函数里再读。

键
kind "content-source"。
id 2 到 32 个字符,只能是小写字母、数字和连字符,且以字母或数字开头。安装与内置插件 id 相同的插件,会替换掉内置插件;移除它之后,内置插件会恢复。
name、version 观众能看到这两项。version 由三段数字组成,如 1.2.3,每次新构建都要换新的版本号。
contractVersion 插件用到的接口里,最新的那部分所对应的版本。lint 会算出该填多少:填低了会被拒绝,填高了会给出警告,因为这会让本来能运行该插件的应用装不上它。
provides "source" 表示插件本身就是一个片源,安装后会自动切换到它。"source-type" 表示这是一类片源,由观众自己配置,会出现在添加片源里,并且需要 configSchema。
allowedHosts 插件可以访问的主机,只写主机名。以 *. 开头的条目匹配它的子域名。lint 会拒绝不可能是主机的条目。
configSchema 添加片源表单的字段,见下文。
description、probeQuery 前者是安装对话框里的一句话,后者是 doctor 用来搜索的一个词。

lint 遇到不认识的键会报错,并提示最接近的已知键。 电视遇到不认识的键会直接忽略,所以按较新接口写的插件仍然可以安装。

configSchema 的每一项都有 id、label 和 type,还可以带 required 或 default。 type 可以是 text、secret、url、choice 和 bool。

  • 每个值在 yonto.config 里都是字符串,首尾空白已经去掉。
  • 留空的字段不存在,所以要这样读:yonto.config.x || FALLBACK。
  • bool 的值是字符串 'true' 或 'false',而 'false' 作为字符串是真值,所以要用比较:yonto.config.x === 'true'。
  • required 字段留空时,第一次调用之前就会报错,并指出是哪个字段。
  • choice 需要用 options 列出可选项。
  • lint 会拒绝带必填字段却没有 default 的 provides: "source" 插件,因为这类片源安装时不会向观众提问,必填项没处可填。

观众在 url 字段里填的主机,不在 allowedHosts 里也允许访问,所以面向观众自己服务器的插件可以把 allowedHosts 留空。 hostsFromConfig 会把这份列表整个关掉。 它是给那些专门读取观众所给地址的插件用的,比如 XPTV 目录。 声明了它的插件,安装前会先请观众确认。 别为了省事,用它来代替写主机列表。

下面四个是必需的,缺任何一个,lint 都会拒绝这个插件。

函数 调用时传入 返回
getCategories() 无 [{ id, name }],即首页上方的标签。
getMediaList(categoryId, { page, filters, cursor }) 分类的 id,加一个选项对象:page 从 1 开始,filters 把筛选器 id 映射到所选选项的 id,cursor 只有你发出过游标才会有 摘要列表;片源按游标分页时,返回 { items, nextCursor }。返回空列表表示到头了。
getMediaDetail(id) 你之前返回过的某个 id 详情:在摘要基础上加 synopsis、genres 和 playbackOptions。
search(query) 原样传入的搜索词 摘要列表。

摘要是 { id, title },还可以带 type、posterUrl、backdropUrl、year 和 rating,都是字符串。 type 是普通字符串,不认识的值会回退成默认值,所以要写 "movie",不要写 "MOVIE"。

播放选项是 { label, stream: { url, mimeType, headers } }。 播放选项也可以不带 stream,改带 pan(由应用兑换的网盘分享)或 track(一个字符串,之后会在 getStream 里交还给你)。

下面这些是可选的。 doctor 会列出插件实现了哪些。

函数 用途
getFilters(categoryId) 某个分类的筛选器分组。没有筛选器的片源就不要导出它,不要导出一个只返回 [] 的空函数。
getRecommendations() 首页上的精选标题。
checkHealth() 设置里显示的一行状态。没有它时,只要 getCategories 有响应,就算片源健康。
getImageHeaders() 请求海报时要带的请求头。
getSubSources() 一个片源包含多个片库时,供观众选择的列表。
getStream(token) 用来兑换 track。

不支持搜索的片源也要导出 search,并抛出 yonto.error.unavailable(reason)。

插件通过 yonto 调用宿主,并声明契约 21:声明更低版本的插件,任何应用都不会运行。

yonto.config 观众的设置,值都是字符串的对象。
yonto.fetch(url, { method, headers, body, encoding, redirect }) 不管状态码是什么,都会返回 { status, url, headers, body, bodyBase64 }。只有完全没有响应时才会拒绝,并带一个 code:REQUEST_INVALID、HOST_NOT_ALLOWED、REDIRECT_REFUSED、TIMEOUT、RESPONSE_TOO_LARGE 或 REQUEST_FAILED。
yonto.text.decode(…) 解码非 UTF-8 的字节,比如 GBK。这里没有 TextDecoder,只能用它。
yonto.html.load(markup)、yonto.xml.load(markup) 返回 cheerio 的 $,分别用来解析网页和 XML 文档。
yonto.store.get / set / remove / clear 这个片源自己的缓存。set(key, value, ttlSeconds)。
yonto.now() 当前时间。计时请用它,不要用 Date.now()。
yonto.sleep(ms) 这里没有 setTimeout,要等待就用它。
yonto.log(message) 一行日志。
yonto.partial(reason) 声明接下来返回的结果不完整,比如一次搜索只查到了四个站点中的三个。应用会把这句话显示在结果下方。
yonto.installId()、yonto.subSource() 当前是哪次安装,以及观众选了哪个片库。
yonto.crypto、yonto.encoding md5、sha1、sha256、hmacSha256、aesCbcDecrypt,以及 base64 和十六进制转换,输入输出都是字符串。
yonto.cryptoJs()、yonto.jsEncrypt() 完整的 CryptoJS 或 JSEncrypt 库,给需要更多功能的网站用。它们启动慢,随机数也不安全,所以不要拿它们生成密钥。

请求遵循 WHATWG Fetch 的规则。 带请求体的 GET、网址里带凭据,以及由宿主管理的请求头(如 Host 或 Content-Length),在发出请求之前就会被拒绝。 Cookie、Origin 和 Referer 可以自己设置。

抛出下面任意一个,应用就会显示对应的界面:

含义
yonto.error.notFound(message) 这个标题不存在。
yonto.error.unauthenticated(message) 服务器拒绝了观众提供的凭据。
yonto.error.misconfigured(reason) 你自己表单里的某个字段为空或不对,重试也没用。
yonto.error.unreachable(reason) 服务器没有响应。应用可能会让它先歇一会儿。
yonto.error.unavailable(reason) 其他所有出错的情况。

reason 和 message 会原样显示给观众,不会被翻译,所以请用你所读取网站的语言来写。 说明是哪一部分出了问题,不用写片源的名字,因为应用的标题已经是 无法连接 <source>。 只写一句话。 状态码和错误码写进 yonto.log,别把网址、密钥或观众的搜索内容写进去。

抛出的错误如果 code 未知或没有 code,就按 METHOD_THREW 处理,显示为插件故障。

  • 一次调用自己的 JavaScript 累计最多运行 20 秒;等待 yonto.fetch 或 yonto.sleep 的时间不算在内。 过了 20 秒,就不能再发起新的 yonto.fetch、yonto.sleep 或 yonto.store 调用;一直不结束的调用会在 85 秒时被终止。 一个片源同一时间只运行一个调用,所以一个慢调用会让后面的调用全部排队等着。 数据要在需要它的那次调用里获取,并尽量缓存。
  • 响应体最大 16 MB,非纯文本的响应体上限更低。 bodyBase64 要在发出请求的那次调用里读取。
  • 每个片源最多存 256 个键,每个键的 JSON 最大 1 MiB。 超出后写入会被 STORE_REFUSED 拒绝,所以要捕获这个错误,然后继续往下走。
  • 插件文件安装时最大 1 MiB,下载限时 30 秒。
  • 插件不能读文件、打开套接字、运行定时器,也不能访问没有告知观众的主机。
  • 内网地址底线会拒绝回环地址、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、链路本地地址和 *.local 地址,对所有插件一视同仁,清单怎么写都一样。 唯一的例外是有人在 url 字段里输入的地址。

本页只是简明版。 接口的完整文本在 kangzj/yonto-plugins 的 contracts/content-source-http.md 里,包括背后的原因和各种边界情况,模式文件就在它旁边。