Skip to content

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_fromvod_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: [],
    };
  }
}

下一步

基于 MIT 许可发布