API 参考
本文档详细说明爬虫源需要实现的接口规范。
接口概览
所有爬虫源必须实现以下 5 个接口方法:
| 方法 | 说明 | 参数 | 返回值 |
|---|---|---|---|
home | 获取首页数据 | {} | 分类列表和推荐视频 |
category | 获取分类数据 | {categoryId, page, filters?} | 分页视频列表 |
detail | 获取视频详情 | {videoId} | 视频详情信息 |
search | 搜索视频 | {keyword, page?} | 搜索结果列表 |
play | 获取播放地址 | {playId, flag?} | 播放地址信息 |
home - 获取首页数据
获取首页的分类列表和推荐视频列表。
参数
json
{}返回值
typescript
{
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数据
}示例
javascript
async function home(params) {
return {
class: [
{ type_id: "1", type_name: "电影" },
{ type_id: "2", type_name: "电视剧" },
],
list: [
{
vod_id: "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 / 中国大陆 / 剧情",
},
],
banner: [
{
title: "热门电影",
subtitle: "2024年度最佳",
backgroundImage: "https://example.com/banner-bg.jpg",
genre: "剧情",
actors: "张三, 李四",
description: "这是一部精彩的电影",
},
],
};
}category - 获取分类数据
获取指定分类的分页视频列表。
参数
json
{
"categoryId": "1", // 分类ID(必填)
"page": 1, // 页码(必填,默认1)
"filters": { // 筛选条件(可选)
"type": "电影",
"area": "中国大陆",
"year": "2024",
"sort": "1"
}
}返回值
typescript
{
page: number; // 当前页码
pagecount: number; // 总页数
total: number; // 总记录数
list: Array<VodItem>; // 视频列表
}示例
javascript
async function category(params) {
const categoryId = params.categoryId || "1";
const page = params.page || 1;
const filters = params.filters || {};
return {
page: page,
pagecount: 10,
total: 100,
list: [
{
vod_id: `${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",
},
],
};
}detail - 获取视频详情
获取视频的详细信息,包括简介、播放地址等。
参数
json
{
"videoId": "123", // 视频ID(必填)
"source": "web" // 来源标识(可选,默认为空)。可选值:web(网页端)、app(应用端)、tvbox(TVBox客户端)等
}返回值
typescript
{
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[]; // 结构化播放源列表(推荐使用)
vod_play_from?: string; // 废弃,保留用于兼容性检查
vod_play_url?: string; // 废弃,保留用于兼容性检查
}>;
}
// 播放源结构
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信息
旧格式(废弃):vod_play_from 和 vod_play_url 使用字符串拼接:
vod_play_from: 使用$$$分隔不同线路vod_play_url: 使用$$$分隔不同线路,#分隔同一线路的不同集数,$分隔集数名称和地址
从刮削元数据获取TMDB信息
爬虫脚本可以通过 OmniBox.getScrapeMetadata() 获取刮削元数据,然后匹配 videoMappings 来填充剧集的TMDB信息:
javascript
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;
}
}示例
javascript
async function detail(params) {
const videoId = params.videoId;
// 获取刮削元数据(可选)
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,
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 - 搜索视频
根据关键词搜索视频。
参数
json
{
"keyword": "关键词", // 搜索关键词(必填)
"page": 1, // 页码(可选,默认1)
"quick": false // 快速搜索标识(可选)
}返回值
typescript
{
page: number; // 当前页码
pagecount: number; // 总页数
total: number; // 总记录数
list: Array<VodItem>; // 搜索结果列表
}示例
javascript
async function search(params) {
const keyword = params.keyword || "";
const page = params.page || 1;
if (!keyword) {
return {
page: 1,
pagecount: 0,
total: 0,
list: [],
};
}
return {
page: page,
pagecount: 5,
total: 50,
list: [
{
vod_id: `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",
},
],
};
}play - 获取播放地址
获取视频的播放地址。
参数
json
{
"playId": "ep1_123", // 播放地址ID(必填)
"flag": "play", // 播放源标识(可选,默认"play")
"source": "web" // 来源标识(可选,默认为空)。可选值:web(网页端)、app(应用端)、tvbox(TVBox客户端)等
}返回值
支持三种格式:
格式1:字符串(单个播放地址)
typescript
{
url: string; // 播放地址
flag: string; // 播放源标识
header?: Record<string, string>; // HTTP请求头(可选)
danmaku?: Array<{ // 弹幕列表(可选)
name?: string;
url: string;
}>;
parse?: number; // 是否需要解析(0=不需要,1=需要)
}格式2:数组(多线路)
typescript
{
url: Array<string>; // 每两个元素为一对:名称、地址
flag: string;
header?: Record<string, string>;
parse?: number;
}格式3:对象(多线路,带默认位置)
typescript
{
url: {
values: Array<{
name: string;
url: string;
}>;
position: number; // 默认播放位置(索引)
};
flag: string;
header?: Record<string, string>;
parse?: number;
}示例
javascript
async function play(params) {
const playId = params.playId;
const flag = params.flag || "play";
const source = params.source || ""; // 来源标识(web/app/tvbox等)
// 根据来源参数过滤播放地址(示例)
// 例如:网页端过滤掉RAW画质
let urlList = [
{ name: "RAW", url: `https://example.com/video/${playId}_raw.m3u8` },
{ name: "4K", url: `https://example.com/video/${playId}_4k.m3u8` },
{ name: "1080P", url: `https://example.com/video/${playId}_1080p.m3u8` },
];
// 如果来源是网页端,过滤掉RAW画质
if (source === "web") {
urlList = urlList.filter(item => item.name !== "RAW");
}
// 格式1:字符串(单个播放地址)
// return {
// url: `https://example.com/video/${playId}.m3u8`,
// flag: flag,
// header: {
// "User-Agent": "Mozilla/5.0",
// "Referer": "https://example.com/",
// },
// parse: 0,
// };
// 格式2:数组
// return {
// url: [
// "4K",
// `https://example.com/video/${playId}_4k.m3u8`,
// "1080P",
// `https://example.com/video/${playId}_1080p.m3u8`,
// ],
// flag: flag,
// header: {},
// parse: 0,
// };
// 格式3:对象
// return {
// url: {
// values: [
// { name: "4K", url: `https://example.com/video/${playId}_4k.m3u8` },
// { name: "1080P", url: `https://example.com/video/${playId}_1080p.m3u8` },
// ],
// position: 0,
// },
// flag: flag,
// header: {},
// parse: 0,
// };
}数据模型
VodItem - 视频项
typescript
{
vod_id: string; // 视频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; // 副标题(可选)
}BannerItem - Banner项
typescript
{
id: string; // Banner项ID(必填)
title: string; // 标题(必填)
subtitle?: string; // 副标题(可选)
imageUrl: string; // 图片URL(必填)
backgroundImage: string; // 背景图片URL(必填)
genre?: string; // 类型/标签(可选)
actors?: string; // 演员信息(可选)
description?: string; // 描述(可选)
tags?: string; // 标签(可选)
type?: string; // 类型(可选)
isAd?: boolean; // 是否为广告(可选,默认false)
adTitle?: string; // 广告标题(可选)
adButton?: string; // 广告按钮文字(可选)
adUrl?: string; // 广告链接(可选)
}错误处理
所有接口方法都应该包含错误处理:
javascript
async function home(params) {
try {
// 业务逻辑
return result;
} catch (error) {
await OmniBox.log("error", `获取首页数据失败: ${error.message}`);
return {
class: [],
list: [],
};
}
}下一步
- 快速开始 - 了解如何创建第一个爬虫源
- JavaScript SDK - 查看 JavaScript SDK 文档
- Python SDK - 查看 Python SDK 文档
- 示例代码 - 查看更多实际示例
