Skip to content
hanyd edited this page Nov 14, 2024 · 9 revisions

0. 说明

1. 服务地址

  • 正式环境:https://bsbsb.top/api/**,例如https://bsbsb.top/api/skipSegments

  • 测试环境:https://bsbsb.top/test/api/**,例如https://bsbsb.top/test/api/skipSegments

测试环境和正式环境功能基本相同,可能会多一些测试功能。两个环境的数据是分开的,如果需要开发测试接口请使用测试环境,每天5:00am从正式环境同步数据到测试环境。

2. 通用说明

  • 所有的 API 都是基于 HTTP 协议的 RESTful API。

  • API大量使用了HTTP错误状态码。例如如果请求参数有误,会返回400;获取数据的接口无数据,会返回404;重复提交会返回429;因此看到错误码不一定是调用出错,

  • 原项目文档:https://wiki.sponsor.ajay.app/w/API_Docs

3. 用户ID:公开ID和私人ID

  • 用户ID有两种,公开ID或者私人ID。私人ID类似密码,用于插件的鉴权,不应该公开;公开ID是私人ID的哈希值,可以用于公开展示,排行榜和统计数据中展示的就是公开ID。

  • 私人ID可以自行设置,要求至少为30个字符长度的纯字符串。服务器不会保存任何私人ID,如果你不幸弄丢了私人ID,那就再也没办法找回了。

  • 公开ID是私人ID用SHA256算法哈希5000次的值,应该为64个字符长度。

设置私人ID的位置

设置 -> 备份/恢复 -> 导入/导出您的私人用户ID

私人用户ID

复制公开ID的位置

公开用户ID

4. 参数说明

1. 公用参数

  • 为了区分调用API的来源方,需要在请求头中添加originx-ext-version字段,分别对应来源和版本号。例如目前插件的默认参数是:
{
  "origin": "chrome-extension://eaoelafamejbnggahofapllmfhlhajdd",
  "x-ext-version": "0.5.0"
}

2. 格式

  • GET方法的参数都是以URL参数传递的,POST、DELETE方法的参数基本都是在body中传递的(部分POST接口支持URL参数,但是不建议使用)。所有的返回都是JSON格式。

  • 列表类型的URL参数,例如categories,传递的是带着括号的列表,例如categories=["sponsor","poi_highlight"]

5. 缓存

  • 为了减少服务器压力,GET接口都配置了nginx缓存,如果需要强制刷新缓存,可以在请求头中添加x-skip-cache: 1。一般来说,可以在做完提交操作后自动调用一次强制刷新缓存,以获取最新数据。过于频繁的强制刷新缓存可能会导致服务器压力过大,所以请谨慎使用。

1. 片段

1. 获取片段 - BVID

获取某个视频的片段。可以指定类别、动作类型、强制返回的片段等。默认返回除了"fill"类别之外的所有数据。

GET /api/skipSegments

参数

{
  videoID: string,            // 视频BVID
  category: string,           // 片段类别,默认返回除了"fill"之外的所有类别,可以重复以包含多个类别
  categories: string[],       // 和category二选一
  requiredSegment: string,    // 强制返回的片段 UUID,即使未达到最小投票阈值。可以重复以包含多个片段
  requiredSegments: string[], // 和requiredSegment二选一
  actionType: string,         // 行为类别,默认返回所有类型,不建议设置。可以重复以包含多个类型
  actionTypes: string[],      // 和actionType二选一
}

返回

[{  // 片段对象
  segment: float[],       // 起始和结束时间(秒),例如 [0, 15.23]
  UUID: string,           // 片段UUID
  category: string,       // 片段类别
  actionType: string,     // 片段动作类型
  locked: int,            // 该提交是否锁定
  votes: int,             // 该片段的投票数
  videoDuration: int,     // 提交时的视频时长,用于判断提交是否过期,未知时为 0,要求误差在 ±2 秒内
}]

错误状态码

400: 参数错误

404: 未找到数据

2. 获取片段 - 哈希值

获取视频的片段,提供额外的隐私保护。sha256HashPrefix是BVID的哈希值的前缀,长度4-32字符(建议使用4字符)。这样的访问方式使得服务器无法知道客户端具体在访问哪个视频,因此提高了隐私保护。这也是插件获取片段的方式。

默认返回除了"fill"类别之外的所有数据。

GET /api/skipSegments/:sha256HashPrefix

参数

{
  sha256HashPrefix: string,   // 视频BVID的哈希值前缀
  category: string,           // 片段类别,默认返回除了"fill"之外的所有类别,可以重复以包含多个类别
  categories: string[],       // 和category二选一
  requiredSegment: string,    // 强制返回的片段 UUID,即使未达到最小投票阈值。可以重复以包含多个片段
  requiredSegments: string[], // 和requiredSegment二选一
  actionType: string,         // 行为类别,默认返回所有类型,不建议设置。可以重复以包含多个类型
  actionTypes: string[],      // 和actionType二选一
}

返回

[{  // 视频对象
  videoID: string,    // BVID
  segments: [{        // 片段对象
    segment: float[],       // 起始和结束时间(秒),例如 [0, 15.23]
    UUID: string,           // 片段UUID
    category: string,       // 片段类别
    actionType: string,     // 片段动作类型
    locked: int,            // 该提交是否锁定
    votes: int,             // 该片段的投票数
    videoDuration: int,     // 提交时的视频时长,用于判断提交是否过期,未知时为 0,要求误差在 ±2 秒内
  }]
}]

错误状态码

400: 参数错误

404: 未找到数据

3. 提交片段

提交片段

POST /api/skipSegments

参数

{
  videoID: string,       // 视频BVID
  userID: string,        // 私人用户ID
  userAgent: string,     // 发起请求的客户端和版本,例如 "Chromium/1.0.0"
  videoDuration: float,  // 视频时长,用于判断视频是否经过换源
  segments: [
    { // 片段列表
      segment: float[],   // 起始和结束时间(秒),例如 [0, 15.23]
      category: string,   // 片段类别
      actionType: string  // 片段动作类型
    },
    ...
  ]
}

返回

[   // 片段列表
  {
    UUID: string,      // 新片段的UUID
    category: string,  // 片段类别
    segment: float[],  // start起始和结束时间(秒)
  }
]

错误状态码

400: 参数错误

403: 被自动审核机制拒绝,原因在返回信息中

429: 重复提交太快(触发速率控制)

409: 重复提交

4. 片段投票

对一个片段进行投票,或者投票修改片段的类别。片段提交者和VIP用户可以一票隐藏片段或者修改片段的类别。

POST /api/voteOnSponsorTime

参数

常规投票:

{
  UUID: string,    // 片段UUID
  userID: string,  // 私人用户ID,用于鉴权
  type: int        // 投票类型:0 -> 点踩,1 -> 点赞,20 -> 撤销投票
}

修改类别:

{
  UUID: string,     // 片段UUID
  userID: string,   // 私人用户ID,用于鉴权
  category: string  // 新的类别
}

返回

错误状态码

400: 参数错误

403: 被自动审核机制拒绝,原因在返回信息中

5. 记录跳过片段

跳过一次片段后调用此接口,记录片段的观看次数。

POST /api/viewedVideoSponsorTime

参数

{
  UUID: string   // 片段UUID
}

返回

错误状态码

400: 参数错误

6. 获取片段信息

获取片段的详细信息,最多一次查询10个片段。

GET /api/segmentInfo

参数

{
  // 最多一次查询10个片段
  UUID: string,
  // 或者
  UUIDs: string[]
}

返回

[{ // 片段列表
  videoID: string,       // 视频BVID
  startTime: float,      // 片段开始时间
  endTime: float,        // 片段结束时间
  votes: int,            // 净点赞数
  locked: int,           // 是否锁定
  UUID: string,          // 片段UUID
  userID: string,        // 提交者的公开用户ID
  timeSubmitted: int,    // 提交时间戳
  views: int,            // 跳过次数
  category: string,      // 片段类别
  actionType: string,    // 片段动作类型
  videoDuration: int,    // 视频时长
  hidden: int,           // 是否隐藏,0为未隐藏,1为隐藏,2为被自动审核机制隐藏
  reputation: int,       // 提交者的声誉
  shadowHidden: int,     // 暗藏,0为未暗藏,1为暗藏
  hashedVideoID: string, // 视频ID的哈希值
  userAgent: string,     // 提交者的客户端信息
}]

错误状态码

400: 参数错误

404: 未找到数据

7. 搜索片段

根据特定的过滤条件,查询一个视频上的片段。注意,这个接口不应该用于获取用户需要跳过的片段,因为没有经过匹配和过滤,可能会看到很多低质量片段。获取最佳片段应该使用/api/skipSegments接口。

GET /api/searchSegments

参数

{  videoID: string,

  category: string,
  // 或者
  categories: string[],

  actionType: string,
  // 或者
  actionTypes: string[],

  page: int,  // 分页页码

  // 投票数范围
  minVotes: int,
  maxVotes: int,

  // 跳过数范围
  minViews: int,
  maxViews: int,

  // 默认为true,如果传false,则不显示匹配该种类型的片段
  locked: boolean,
  hidden: boolean,
  ignored: boolean,  // 隐藏或者
}

返回

{
  segmentCount: int,  // 片段总数
  page: int,          // 分页页码
  segments: [{        // 片段列表,最大10个
    UUID: string,
    timeSubmitted: int,
    startTime: int,
    endTime: int,
    category: string,
    actionType: string,
    votes: int,
    views: int,
    locked: int,
    hidden: int,
    shadowHidden: int,
    userID: string,
    description: string,
  }]
}

错误状态码

400: 参数错误

404: 未找到数据

2. 搬运视频

1. 获取绑定视频 - BVID

GET /api/portVideo

参数

{
  videoID: string,  // 视频BVID
}

返回

{
  "bvID": string,     // B站视频BVID
  "ytbID": string,    // YouTube视频ID
  "UUID": string,     // 绑定记录的UUID(不是视频中片段的UUID,是绑定记录本身的UUID)
  "votes": int,       // 绑定记录的投票数
  "locked": int,      // 绑定记录是否锁定
}

错误状态码

400: 参数错误

404: 未找到数据

2. 获取绑定视频 - 哈希

通过BVID的哈希值获取绑定视频记录。

GET /api/portVideo/:sha256HashPrefix

参数

{
  sha256HashPrefix: string,  // 视频BVID的哈希前缀(4-32字符)
}

返回

[  // 绑定记录列表
  {
    "bvID": string,     // B站视频BVID
    "ytbID": string,    // YouTube视频ID
    "UUID": string,     // 绑定记录的UUID(不是视频中片段的UUID,是绑定记录本身的UUID)
    "votes": int,       // 绑定记录的投票数
    "locked": int,      // 绑定记录是否锁定
  }
]

错误状态码

400: 参数错误

404: 未找到数据

3. 提交绑定视频

POST /api/portVideo

参数

{  // 全部必填
  "bvID": string,       // B站视频BVID
  "ytbID": string,      // YouTube视频ID
  "userID": string,     // 私人用户ID
  "biliDuration": int,  // B站视频时长(单位:秒)
}

返回

{ // 刚刚创建的绑定记录
  "bvID": string,     // B站视频BVID
  "ytbID": string,    // YouTube视频ID
  "UUID": string,     // 绑定记录的UUID(不是视频中片段的UUID,是绑定记录本身的UUID)
  "votes": int,       // 绑定记录的投票数
  "locked": int,      // 绑定记录是否锁定
}

错误状态码

500: 服务器无法获取绑定的YouTube视频信息

400: 参数错误

409:重复提交

429:正在提交中

4. 绑定视频投票

通过BVID的哈希值获取绑定视频记录。

POST /api/votePort

参数

{
  "UUID": string,     // 绑定记录的UUID(不是视频中片段的UUID,是绑定记录本身的UUID)
  "bvID": string,     // B站视频BVID
  "userID": string,   // 私人用户ID
  "type": int,        // 投票类型:0 -> 点踩,1 -> 点赞,20 -> 撤销投票
}

返回

错误状态码

400: 参数错误

404: 未找到数据

429:正在提交中

5. 更新绑定视频片段

更新来自绑定视频的片段,已有的片段会更新投票数,新的片段会被添加,被删除的片段会被隐藏。

POST /api/updatePortedSegments

参数

{
  videoID: string,  // 视频BVID
}

返回

错误状态码

400: 参数错误

404: 未找到数据

429:正在提交中

3. 类别相关

1. 获取锁定类别 - BVID

获取是否锁定了某个视频的类别

GET /api/lockCategories

参数

{
  videoID: string,       // 视频BVID
  actionTypes: string[]  // 动作类型
}

返回

{
  categories: string[],  // 锁定的类别
  reason: string,        // 锁定的原因
  actionTypes: string[]  // 动作类型
}

错误状态码

400: 参数错误

404: 未找到锁定数据

2. 获取锁定类别 - 哈希

Get locked categories for a video with extra privacy

sha256HashPrefix is a hash of the YouTube videoID. It should be the first 4 - 32 characters (4 is recommended). This provides extra privacy by potentially finding more than just the video you are looking for. This makes the server not know exactly what video you are looking for.

GET /api/lockCategories/:sha256HashPrefix

参数

{
  prefix: sha256HashPrefix   // BVID哈希值前缀
}

返回

[{ // 锁定类别列表
   videoID: string,      // BVID
   hash: string,         // BVID哈希值
   categories: string[], // 锁定的类别
   reason: string        // 锁定的原因
}]

错误状态码

400: 参数错误

404: 未找到锁定数据

3. 获取锁定原因

GET /api/lockReason

参数

{
  videoID: string,

  category: string,
  // 或
  categories: string[],

  actionTypes: string[]
}

返回

[{
  category: string,  // category [1]
  locked: integer,   // status of lock
  reason: string,    // reason for lock
  userID: string,    // publicID of locking VIP
  userName: string   // username of locking VIP
}]

错误状态码

400: 参数错误

4. 用户信息

1. 获取用户信息

GET /api/userInfo

参数

{
  userID: string // local UserID
  // OR
  publicUserID: string // Public userID

  values: string[] // Optional, Values to get from userInfo
    // default values are
    // ["userID", "userName", "minutesSaved", "segmentCount", "ignoredSegmentCount",
    // "viewCount", "ignoredViewCount", "warnings", "warningReason", "reputation",
    // "vip", "lastSegmentID"]
  // OR
  value: string // Optional, Value to get from userInfo, can be repeated for multiple values
}

返回

{
  userID: string, // Public userID
  userName: string, // Public userID if not set
  minutesSaved: float, // Minutes saved
  segmentCount: int, // Total number of segments excluding ignored/ hidden segments
  ignoredSegmentCount: int, // Total number of ignored/ hidden segments
  viewCount: int, // Total number of views excluding view on ignored/ hidden segments
  ignoredViewCount: int, // Total number of view on ignored/ hidden segments
  warnings: int, // Currently enabled warnings
  reputation: float,
  vip: int, // VIP status
  lastSegmentID: string, // UUID of last submitted segment
  permissions: { // Can the user submit segments of this category?
    category: boolean // [1]
  }
}

错误状态码

400: 参数错误

2. 用户统计数据

GET /api/userStats

参数

{
  userID: string       // 私人用户ID
  // 或
  publicUserID: string // 公开用户ID

  fetchCategoryStats: boolean    // 是否获取类别统计,默认为false
  fetchActionTypeStats: boolean  // 是否获取动作类型统计,默认为false
}

返回

{
  userID: string          // 公开用户ID
  userName: string        // 用户名
  overallStats: {
    minutesSaved: integer // same as userInfo
    segmentCount: integer // same as userInfo
  }
  // 如果指明
  categoryCount: {
    sponsor: integer
    intro: integer
    outro: integer
    interaction: integer
    selfpromo: integer
    music_offtopic: integer
    preview: integer
    poi_highlight: integer
    filler: integer,
    exclusive_access: integer,
    chapter: integer
  }
  // 如果指明
  actionTypeCount: {
    skip: integer,
    mute: integer,
    full: integer,
    poi: integer,
    chapter: integer
  }
}

错误状态码

400: 参数错误

3. 获取用户跳过量

获取一个用户提交的片段跳过数量的总数

GET /api/getViewsForUser

参数

{
  userID: string   // 私人用户ID
}

返回

{
  viewCount: int
}

错误状态码

404: Not Found

4. 获取节省时间

获取一个用户提交片段节省的总时间

GET /api/getSavedTimeForUser

参数

{
  userID: string  // 私人用户ID
}

返回

{
  timeSaved: float // 单位分钟
}

错误状态码

404: Not Found

5. 设置用户名

POST /api/setUsername

参数

为自己设置用户名

{
  userID: string,   // 私人用户ID
  username: string, // 可选,用户名
}

为他人设置用户名(管理员)

{
  userID: string,     // 公开用户ID
  username: string,   // 可选,用户名
  adminUserID: string // 管理员私人用户ID
}

返回

错误状态码

400: 参数错误

403: 权限不足

6. 获取用户名

GET /api/getUsername

参数

{
  userID: string   // 私人用户ID
}

返回

{
  userName: string // 如果没有设置用户名,返回公开用户ID
}

错误状态码

400: 参数错误

7. 搜索用户名

搜索匹配用户名的公开用户ID

GET /api/userID

参数

{
  username: string, // 搜索用户名,区分大小写。模糊搜索最小3个字符,最大64个字符
  exact: boolean    // 是否精确匹配
}

返回

[{ // 用户列表,最多10个
  userName: string,
  userID: string
}]

错误状态码

400: 参数错误

404: Not Found

5. 统计信息

1. 获取排行榜

GET /api/getTopUsers

参数

{
  sortType: int
    // 排序类型
    // 0 -> 节省时间
    // 1 -> 跳过数量
    // 2 ->	提交数量
    // 3 ->	点赞数量
    // 4 ->	绑定搬运视频
}

返回

{
  userNames: string[],
  viewCounts: int[],
  totalSubmissions: int[],
  minutesSaved: float[]
}

错误状态码

400: 参数错误

2. 获取分类型排行榜

GET /api/getTopCategoryUsers

参数

{
  sortType: int
    // 排序类型
    // 0 -> 节省时间
    // 1 -> 跳过数量
    // 2 ->	提交数量
    // 3 ->	点赞数量
    // 4 ->	绑定搬运视频
  category: string  // 分类
}

返回

{
  userNames: string[],
  viewCounts: int[],
  totalSubmissions: int[],
  minutesSaved: float[]
}

错误状态码

400: 参数错误

3. 获取总体统计信息

GET /api/getTotalStats

参数

{
  countContributingUsers: boolean // Optional, default false
}

返回

{
  userCount: int, // Only if countContributingUsers was true
  activeUsers: int, // Sum of public install stats from Chrome webstore and Firefox addons store
  apiUsers: int, // 48-hour active API users (https://github.com/ajayyy/PrivacyUserCount)
  viewCount: int,
  totalSubmissions: int,
  minutesSaved: float
}

4. 获取总节省时间

GET /api/getDaysSavedFormatted

参数

返回

{
  daysSaved: float (2 decimal places)
}

5. 服务器状态

可以使用返回体中的任意键作为value传入,不传入将返回所有值。

GET /api/status/:value

返回

{
  uptime: int, // Uptime of server in seconds
  commit: string, // Full SHA hash of latest git commit, development or test
  db: int, // Current database version
  startTime: int, // Unix time (miliseconds) that request was received
  processTime: int, // Delay between DB request made and response received (miliseconds)
  redisProcessTime: int, // Delay between redis request made and response received (miliseconds)
  loadavg: int[], // 5 and 15 minute loadavg
  statusRequests: int, // number of /status requests in the last minute
  hostname: string // hostname of current server
}

错误状态码

404: Not Found

管理员操作(可以参考原文档)

https://wiki.sponsor.ajay.app/w/API_Docs

1. GET /api/isUserVIP

1. POST /api/lockCategories

1. DELETE /api/lockCategories

1. POST /api/shadowBanUser

1. POST /api/warnUser

1. POST /api/clearCache

1. POST /api/purgeAllSegments

1. POST /api/segmentShift

1. POST /api/addUserAsTempVIP

1. POST /api/feature

1. POST /api/addUserAsVIP