插件参考
插件是一个 .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 全局对象
Section titled “yonto 全局对象”插件通过 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 里,包括背后的原因和各种边界情况,模式文件就在它旁边。