API 参考
本文档详细说明爬虫源需要实现的接口规范。
接口概览
爬虫源按需实现以下 5 个接口方法(并非全部必选)。例如推送类脚本只需实现 detail 和 play。运行器调用时传入 method、params 和 context,脚本内各 handler 的推荐签名为 (params, context)。
| 方法 | 说明 | params 主要字段 | 返回值 |
|---|---|---|---|
home | 获取首页数据 | {} | 分类列表和推荐视频 |
category | 获取分类数据 | {categoryId, page, filters?} | 分页视频列表 |
detail | 获取视频详情 | {videoId} | 视频详情信息 |
search | 搜索视频 | {keyword, page?, quick?} | 搜索结果列表 |
play | 获取播放地址 | {playId, flag?} | 播放地址信息 |
所有接口的入参均由运行器传入两部分:params(本次调用的业务参数,见上表)和 context(请求上下文,见下文「请求上下文 context」)。调用端标识等从 context.from 获取,不再通过 params 传递。
请求上下文 context
除 params 外,运行器会传入 context(由 Runner 通过 stdin 注入),脚本应使用 handler 的第二个参数接收。context 各字段的详细说明见 爬虫开发介绍 - 请求上下文 context。
// context 类型
interface RequestContext {
baseURL?: string; // 当前请求的基础 URL(如 OmniBox 服务地址)
headers?: Record<string, string>; // 客户端请求头(User-Agent、Cookie 等)
sourceId?: string; // 当前爬虫源 ID
from?: string; // 调用端:web(默认)| tvbox | uz | catvod | emby;未传时默认为 web
}- baseURL:可用于拼装绝对链接(如图片、播放地址)。
- headers:客户端请求头,可按需透传到第三方 API。
- sourceId:当前爬虫源 ID,调用
getSourceCategoryData、addPlayHistory等 API 时由 SDK 从 context 读取。 - from:调用端标识,未传时默认为
web;可按端做差异化(例如网页端隐藏某线路、TV 端只返回直链等)。
home - 获取首页数据
获取首页的分类列表和推荐视频列表。
入参
- params:
{}(无业务参数) - context:请求上下文,见 请求上下文 context。可从
context.from、context.baseURL等按需使用。
返回值
{
class: Array<{
type_id: string; // 分类ID
type_name: string; // 分类名称
}>;
list: Array<VodItem>; // 推荐视频列表
filters?: { // 可选:筛选条件
[categoryId: string]: Array<{
key: string; // 筛选器key
name: string; // 筛选器名称
init: string; // 默认值
value: Array<{
name: string; // 选项名称
value: string; // 选项值
}>;
}>;
};
banner?: Array<BannerItem>; // 可选:跑马灯Banner数据
}示例
async function home(params, context) {
// context 含 baseURL、headers、sourceId、from,可按需使用
const from = context?.from; // 如 "web"(默认)| "tvbox" | "emby"
return {
class: [
{ type_id: "1", type_name: "电影" },
{ type_id: "2", type_name: "电视剧" },
],
list: [
{
vod_id: "1",
link: "https://movie.douban.com/subject/1",
vod_name: "示例视频",
vod_pic: "https://example.com/pic.jpg",
type_id: "1",
type_name: "电影",
vod_remarks: "HD",
vod_year: "2024",
vod_douban_score: "8.5",
vod_subtitle: "2024 / 中国大陆 / 剧情",
search: 0, // 可选,UZ 专用:1=点击时执行搜索
},
],
banner: [
{
title: "热门电影",
subtitle: "2024年度最佳",
backgroundImage: "https://example.com/banner-bg.jpg",
genre: "剧情",
actors: "张三, 李四",
description: "这是一部精彩的电影",
},
],
};
}category - 获取分类数据
获取指定分类的分页视频列表。
入参
- params:本次调用的业务参数。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| categoryId | string | 是 | 分类 ID |
| page | number | 是 | 页码,默认 1 |
| filters | object | 否 | 筛选条件,如 type、area、year、sort 等 |
示例:{ "categoryId": "1", "page": 1, "filters": { "type": "电影", "area": "中国大陆", "year": "2024", "sort": "1" } }
- context:请求上下文,见 请求上下文 context。
返回值
{
page: number; // 当前页码
pagecount: number; // 总页数
total: number; // 总记录数
list: Array<VodItem>; // 视频列表
}示例
async function category(params, context) {
const categoryId = params.categoryId || "1";
const page = params.page || 1;
const filters = params.filters || {};
// context.baseURL、context.from 等可按需使用
return {
page: page,
pagecount: 10,
total: 100,
list: [
{
vod_id: `${categoryId}_${page}_1`,
link: `https://movie.douban.com/subject/${categoryId}_${page}_1`,
vod_name: "分类视频",
vod_pic: "https://example.com/pic.jpg",
type_id: categoryId,
type_name: "电影",
vod_remarks: "HD",
vod_year: "2024",
vod_douban_score: "8.5",
search: 0, // 可选,UZ 专用:1=点击时执行搜索,0=否
},
],
};
}detail - 获取视频详情
获取视频的详细信息,包括简介、播放地址等。
入参
- params:本次调用的业务参数。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| videoId | string | 是 | 视频 ID |
示例:{ "videoId": "123" }
- context:请求上下文,见 请求上下文 context。调用端标识(如 web、tvbox、emby)从 context.from 获取,可按端做差异化(例如网页端隐藏某线路、TV 端只返回直链等)。
返回值
{
list: Array<{
vod_id: string;
vod_name: string;
vod_pic: string;
vod_content?: string; // 视频简介
vod_director?: string; // 导演
vod_actor?: string; // 演员
vod_area?: string; // 地区
vod_year?: string; // 年份
vod_remarks?: string; // 备注
vod_douban_score?: string; // 豆瓣评分
type_name?: string; // 分类名称
vod_play_sources?: PlaySource[]; // 结构化播放源列表
}>;
}
// 播放源结构
interface PlaySource {
name: string; // 线路名称
episodes: Episode[]; // 剧集列表
}
// 剧集信息
interface Episode {
name: string; // 剧集名称
playId: string; // 播放ID
size?: number; // 视频大小(字节,可选)
// TMDB剧集信息(可选,从刮削元数据中获取)
episodeName?: string; // TMDB剧集名称
episodeOverview?: string; // TMDB剧集简介
episodeAirDate?: string; // TMDB剧集首播日期
episodeStillPath?: string; // TMDB剧集剧照路径
episodeVoteAverage?: number; // TMDB剧集评分
episodeRuntime?: number; // TMDB剧集时长(分钟)
}播放源格式说明
使用 vod_play_sources 数组,每个元素包含:
name: 线路名称episodes: 剧集数组,每个剧集包含名称、播放ID、大小和 TMDB 信息(可选)
从刮削元数据获取TMDB信息
爬虫脚本可以通过 OmniBox.getScrapeMetadata() 获取刮削元数据,然后匹配 videoMappings 来填充剧集的TMDB信息:
const metadata = await OmniBox.getScrapeMetadata(resourceId);
const videoMappings = metadata.videoMappings || [];
// 在构建剧集时,匹配映射关系并填充TMDB信息
for (const file of videoFiles) {
const formattedFileId = `${shareURL}|${file.fid}`;
const mapping = videoMappings.find((m) => m.fileId === formattedFileId);
const episode = {
name: fileName,
playId: formattedFileId,
size: file.size,
};
// 如果找到映射关系,填充TMDB信息
if (mapping) {
episode.episodeName = mapping.episodeName;
episode.episodeOverview = mapping.episodeOverview;
episode.episodeAirDate = mapping.episodeAirDate;
episode.episodeStillPath = mapping.episodeStillPath;
episode.episodeVoteAverage = mapping.episodeVoteAverage;
episode.episodeRuntime = mapping.episodeRuntime;
}
}示例
async function detail(params, context) {
const videoId = params.videoId;
const from = context?.from || "web"; // 调用端:web | tvbox | uz | catvod | emby
// 获取刮削元数据(可选)
let metadata = null;
try {
metadata = await OmniBox.getScrapeMetadata(videoId);
} catch (error) {
OmniBox.log("warn", `获取元数据失败: ${error.message}`);
}
const videoMappings = metadata?.videoMappings || [];
// 构建结构化播放源
const playSources = [
{
name: "线路1",
episodes: [
{
name: "第1集",
playId: "ep1_id",
size: 1073741824, // 1GB in bytes
// 如果匹配到TMDB映射,填充TMDB信息
episodeName: "离乡",
episodeOverview: "少年王林,家境贫困...",
episodeAirDate: "2023-09-25",
episodeStillPath: "/gYVT7ybhpQswGrrGU3WBXKnwW5Q.jpg",
episodeVoteAverage: 9.7,
episodeRuntime: 25,
},
{
name: "第2集",
playId: "ep2_id",
size: 1073741824,
},
],
},
{
name: "线路2",
episodes: [
{
name: "第1集",
playId: "ep1_alt_id",
size: 1073741824,
},
],
},
];
return {
list: [
{
vod_id: videoId,
link: `https://movie.douban.com/subject/${videoId}`,
vod_name: "示例视频详情",
vod_pic: "https://example.com/pic.jpg",
vod_content: "视频简介内容",
vod_year: "2024",
vod_remarks: "HD",
vod_douban_score: "8.5",
type_name: "电影",
vod_play_sources: playSources,
},
],
};
}search - 搜索视频
根据关键词搜索视频。
入参
- params:本次调用的业务参数。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| keyword | string | 是 | 搜索关键词 |
| page | number | 否 | 页码,默认 1 |
| quick | boolean | 否 | 是否为快速搜索(可选) |
示例:{ "keyword": "关键词", "page": 1, "quick": false }
- context:请求上下文,见 请求上下文 context。
返回值
{
page: number; // 当前页码
pagecount: number; // 总页数
total: number; // 总记录数
list: Array<VodItem>; // 搜索结果列表
}示例
async function search(params, context) {
const keyword = params.keyword || "";
const page = params.page || 1;
// context.from、context.sourceId 等可按需使用
if (!keyword) {
return {
page: 1,
pagecount: 0,
total: 0,
list: [],
};
}
return {
page: page,
pagecount: 5,
total: 50,
list: [
{
vod_id: `search_${keyword}_1`,
link: `https://movie.douban.com/subject/search_${keyword}_1`,
vod_name: `搜索结果: ${keyword}`,
vod_pic: "https://example.com/pic.jpg",
type_id: "1",
type_name: "电影",
vod_remarks: "HD",
vod_year: "2024",
vod_douban_score: "8.5",
search: 1, // 可选,UZ 专用:1=点击该条时执行搜索
},
],
};
}play - 获取播放地址
获取视频的播放地址。可同时返回弹幕列表(danmaku)和解析标识(parse)。
入参
- params:本次调用的业务参数。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| playId | string | 是 | 播放地址 ID(如剧集 playId) |
| flag | string | 否 | 播放源标识,默认 "play" |
示例:{ "playId": "ep1_123", "flag": "play" }
- context:请求上下文,见 请求上下文 context。调用端标识从 context.from 获取,可按端做差异化(例如网页端过滤某线路、TV 端只返回直链等)。
返回值
推荐格式:urls 数组(含弹幕与 parse)
{
urls: Array<{ name: string; url: string }>; // 播放地址列表,每项为画质/线路名与地址
flag: string; // 播放源标识
header?: Record<string, string>; // HTTP 请求头(可选)
danmaku?: Array<{ name: string; url: string }>; // 弹幕列表(可选),见下文
parse?: number; // 0=直链不需解析,1=需客户端嗅探解析(仅 ok影视 app 有效)
}弹幕 danmaku(可选)
返回弹幕时,客户端可加载对应弹幕文件并叠加到播放器。结构为数组,每项包含:
| 字段 | 类型 | 说明 |
|---|---|---|
name | string | 弹幕名称(如剧名+集数,用于展示) |
url | string | 弹幕文件或接口 URL(如 XML 弹幕接口) |
示例:根据当前集数匹配第三方弹幕 API,返回一条或多条弹幕源。
// 示例:返回一条弹幕
playResponse.danmaku = [{ name: "某番剧 - 第1集", url: "https://danmu.example.com/api/v2/comment/12345?format=xml" }];参考实现见 backend/static/templates/js/site_spider.js 中的 play 与 matchDanmu。
parse 字段(可选)
| 值 | 含义 |
|---|---|
0 | 播放地址为直链(如 .m3u8、.mp4),无需解析 |
1 | 需要客户端嗅探解析(如从解析页/跳转页中提取真实播放地址) |
说明:parse === 1 时的“客户端嗅探解析”仅对 ok影视 app 有效,其他客户端(网页、TVBox、UZ 等)会忽略该字段,按直链处理。
其他兼容格式
- 格式 1:单地址
url: string,可带header、danmaku、parse。 - 格式 2:
url: string[](名称与地址交替)。 - 格式 3:
url: { values: Array<{name, url}>, position: number }。
示例
async function play(params, context) {
const playId = params.playId;
const flag = params.flag || "play";
const from = context?.from || "web"; // 调用端:web | tvbox | uz | catvod | emby
// 推荐:使用 urls 数组格式,可同时返回弹幕与 parse
let urls = [
{ name: "4K", url: `https://example.com/video/${playId}_4k.m3u8` },
{ name: "1080P", url: `https://example.com/video/${playId}_1080p.m3u8` },
];
if (from === "web") {
urls = urls.filter((item) => item.name !== "RAW");
}
const playResponse = {
urls,
flag,
header: { "User-Agent": "Mozilla/5.0", Referer: "https://example.com/" },
parse: /\.(m3u8|mp4)$/.test(playId) ? 0 : 1, // 直链为 0,否则为 1(仅 ok影视 app 会做嗅探解析)
};
// 可选:附加弹幕(若脚本有弹幕匹配逻辑)
const danmakuList = await matchDanmu(videoName, episodeNum); // 自定义函数
if (danmakuList.length > 0) {
playResponse.danmaku = danmakuList; // [{ name: "剧名 - 第1集", url: "https://..." }, ...]
}
return playResponse;
}仅返回单一直链时也可使用简化格式:
return {
url: `https://example.com/video/${playId}.m3u8`,
flag: flag,
header: { Referer: "https://example.com/" },
parse: 0, // 直链,无需解析
danmaku: [{ name: "弹幕", url: "https://danmu.example.com/api/comment/123?format=xml" }],
};数据模型
VodItem - 视频项
用于 home、category、detail、search 等接口返回的列表项。除下列字段外,detail 返回的单条还可包含 vod_content、vod_director、vod_actor、vod_play_sources 等,见 detail 返回值。
{
vod_id: string; // 视频ID(必填)
link: string; // 豆瓣详情链接(必填),格式:https://movie.douban.com/subject/{vod_id}
vod_name: string; // 视频名称(必填)
vod_pic: string; // 封面图片URL(必填)
type_id: string; // 分类ID(必填)
type_name: string; // 分类名称(必填)
vod_remarks?: string; // 备注(可选)
vod_year?: string; // 年份(可选)
vod_douban_score?: string; // 豆瓣评分(可选)
vod_subtitle?: string; // 副标题(可选)
vod_tag?: string; // 特殊标记(可选)。当为 "folder" 时表示该条目是“目录/文件夹”,客户端点击会进入该目录(以 vod_id 作为下一层 categoryId)而不是跳转播放页
search?: number; // UZ 播放器专用:点击该影片时是否执行搜索,1=是,0=否(可选)
}BannerItem - Banner项(首页轮播)
用于网页端首页显示的跑马灯轮播图
{
title: string; // 标题(必填)
subtitle?: string; // 副标题(可选)
backgroundImage: string; // 背景图 URL(必填)
genre?: string; // 类型/标签(可选)
actors?: string; // 演员信息(可选)
description?: string; // 描述(可选)
}