Skip to content

数据模型 ​

展示型扩展(widget / search)生成 ListItem。服务型扩展基于 MediaRef 工作。页面之间仅传递 ref 和 Open 意图,不传递函数名。

MediaRef ​

ts
{
  source: "tmdb" | "imdb" | "douban" | "url" | "custom";
  id: string;
  mediaType?: "movie" | "tv";
}
字段规则
source标识来源,用于详情和播放解析
id对应 source 下的稳定字符串。例如,tmdb ID 应为 "1399",而不是 "tv.1399"
mediaTypetmdb / imdb / douban 必填

Open 意图 ​

App 将点击操作转换为:

ts
{
  kind: "detail" | "play";
  ref: MediaRef;
  episode?: { season: number; episode: number };
  origin: { packageId: string; extensionId: string; instanceId?: string };
}

图片 ​

图片字段不提供别名:

ts
{
  poster?: string;
  backdrop?: string;
  cover?: string;
  detailPoster?: string;
  stills?: string[];
}
位置回退链
横图backdrop → cover → poster
竖图poster → backdrop → cover
详情海报detailPoster → 竖图链
剧照仅使用 stills
预告封面trailers[].cover → cover → backdrop → poster

对于使用内置详情的平台条目,图片可由平台数据决定。如果扩展仍返回 images,tmdb 的 poster / backdrop 应传递原始路径,例如 /abc.jpg。

ListItem ​

ts
{
  ref: MediaRef;
  title: string;
  subtitle?: string;
  description?: string;
  rating?: number;
  releaseDate?: string;
  genre?: string;
  images?: { poster?: string; backdrop?: string; cover?: string };
  intent?: "detail" | "play";
  episode?: { season: number; episode: number };
}

省略 intent 时默认为 "detail"。episode 决定剧集能否从小组件直接播放,详见 widget。

DetailItem ​

包含 ListItem 的展示字段(不含 intent),并扩展:

ts
{
  images?: {
    poster?: string;
    backdrop?: string;
    cover?: string;
    detailPoster?: string;
    stills?: string[];
  };
  genres?: { id: string; title: string }[];
  people?: { id: string; title: string; avatar?: string; role?: string }[];
  trailers?: { title?: string; url: string; cover?: string }[];
  episodes?: Episode[];
  related?: ListItem[];
  runtimeSeconds?: number;
}

Episode:{ season, episode, title?, ref? }。ref 缺省沿用详情的 ref。

点击 related 条目时同样使用 Open 解析,origin 保持为打开当前详情时的来源。

当 genres / people 包含 id 时,可在生成条目的 widget 支持相应筛选后用于返回筛选结果;当前版本仅用于展示。

Page / WidgetContent ​

ts
type PageToken =
  | { type: "page"; page: number }
  | { type: "cursor"; cursor: string };

type Page<T> = {
  items: T[];
  next?: PageToken;
};

widget 的 load 和 search 的 search 均返回 { items, next? },等同于 Page<ListItem>。省略 next 表示已到末页。空数组表示请求成功但没有数据,不表示失败。

widget 的 load 输入为 { instanceId?, params, family, page? }。instanceId 是该实例的稳定 ID,位于 input 顶层,不属于 params。详见 widget。

search 的输入为 { query, params, page?, instanceId? }。query 由 App 注入,不属于 params。详见 search。

PlaybackSource ​

ts
{
  id: string;
  name: string;
  url: string;
  description?: string;
  headers?: Record<string, string>;
  player?: "system" | "app";
  skipRedirectProbe?: boolean;
}
字段必填规则
id是稳定标识,用于记录用户选中的线路
name是线路名
url是最终播放地址
description否分辨率、编码等
headers否播放请求头,不含内部控制键
player否system 或 app
skipRedirectProbe否为 true 时跳过播放前的 GET 探测,适用于直播或一次性 URL

sources 的输入为 { ref, instanceId?, params?, season?, episode? }。从 widget 实例进入时,instanceId 和 params 对应该实例。空数组表示当前没有可用线路。完整 protocol 见 playback。

字幕 ​

ts
type SubtitleHit = {
  id: string;
  title: string;
  language: string;
};

type SubtitleFile = {
  url: string;
  fileName?: string;
};

SubtitleHit ​

字段必填规则
id是稳定标识。宿主会保存完整的 hit,并在后续调用中传回 file
title是在选择器中显示的名称
language是BCP 47

match 的输入为 { title?, season?, episode?, fileName?, fileSize?, fileHash?, duration?, ref? }。search 的输入为 { query, ref?, season?, episode? }。空数组表示当前没有可用字幕。

SubtitleFile ​

字段必填规则
url是http(s) 或 bundle://,指向单个字幕文件
fileName否保存到本地时使用的文件名,包含扩展名

file 的输入为 { hit }。返回 null 表示无法获取该文件。完整 protocol 见 subtitle。

弹幕 ​

ts
type DanmuMatch = {
  id: string;
  title: string;
  kind?: "movie" | "series";
  episode?: DanmuEpisode;
};

type DanmuEpisode = {
  id: string;
  title: string;
  season?: number;
  episode?: number;
};

type DanmuCue = {
  time: number;
  text: string;
  mode?: "rtl" | "top" | "bottom";
  color?: string;
  id?: string;
};

DanmuMatch ​

字段必填规则
id是稳定标识,用于记录用户选中的匹配项
title是在选择器中显示的名称
kind否movie 或 series
episode否自动匹配已定位到具体剧集时提供。宿主将直接获取弹幕,不再调用 episodes

match 的输入为 { title?, season?, episode?, fileName?, fileSize?, fileHash?, duration?, ref? }。search 的输入为 { query, ref? }。空数组表示当前没有弹幕匹配项。

DanmuEpisode ​

字段必填规则
id是稳定
title是分集名称
season否季
episode否集

episodes 的输入为 { match }。电影类扩展可以不实现此方法。

DanmuCue ​

字段必填规则
time是出现时间,单位为秒
text是弹幕正文
mode否rtl / top / bottom
color否颜色信息,宿主可以忽略
id否去重

comments 的输入为 { match, episode?, segment? }。segment 的单位为秒,范围采用 [start, end)。空数组表示该时间段没有弹幕。完整 protocol 见 danmu。

用户字段 ​

包 settings 与 widget / search 的 params 使用相同的字段结构。type 仅支持以下四种类型,请勿使用 file、date、password 或其他值:

type控件传到 handler 的值备注
text单行文本stringoptions 用作建议值,不构成枚举约束
number数字number
boolean开关true / falsedefault 写 JSON 布尔,不要写成 "true"
select单选string(option.value)必须有 options: { title, value }[]

公共键:name、title、description?、default?、options?、required?、visibleWhen?: { field, in }。

省略 required 或将其设为 false 时,字段为选填项。设为 true 时,填写页会在控件中显示必填提示,包括标签和空文本框的 placeholder。当可见的必填字段没有有效值时,扩展列表会显示提醒。未设置 default、用户未填写或仅输入空白字符,均视为未填写。boolean / number 只要存在值(包括 false / 0)即视为已填写。该校验不会阻止 load / search 调用,空值仍可能进入 input.params。

visibleWhen.in 始终是字符串。boolean 字段用 "true" / "false" 比较。被 visibleWhen 隐藏的必填字段不显示提示,也不计入扩展列表提醒。

当前版本会将未知 type 按 text 渲染,但扩展不应依赖此回退行为。当前界面尚未渲染 text 的建议列表。包 settings 在包详情页填写,并通过 ctx.settings 读取;用法见 开发者指南 · 设置。扩展 params 的用法见 开发者指南 · 参数。