灵猴市集Linghou Market

读取哔哩哔哩视频详情

哔哩哔哩bilibili-video-detail

用途 读取单个哔哩哔哩视频的公开元数据,包括标题、作者、分区、发布时间、时长、互动统计、分 P 信息、封面、简介和付费标记。 执行前提 需要在 bilibili.com 页面执行。脚本通过页面环境请求公开接口,接口可能因为风控、地域、权限或视频状态返回失败。 参数 bvid:可选,视频 BV 号,例如 BV1rz7569EWw。 url:可选,视频 URL…

视频详情内容提取
版本1.0.0
扫描passed
更新2026-06-28
超时30000ms

匹配范围

*://*.bilibili.com/*

排除范围

未声明

能力声明

dom.read

代码

源码已展开
1const input = (typeof params === "object" && params !== null) ? params : {};2const host = String(location.hostname || "").toLowerCase();3 4if (!/(^|\.)bilibili\.com$/.test(host)) {5  throw new Error("请在 bilibili.com 页面执行。");6}7 8function pickString(value, field) {9  if (value === undefined || value === null || value === "") return "";10  if (typeof value !== "string") {11    throw new Error(field + " 必须是字符串。");12  }13  return value.trim();14}15 16function extractBvid(value) {17  const text = String(value || "");18  const match = text.match(/(?:\/video\/)?(BV[0-9A-Za-z]{10,})/);19  return match ? match[1] : "";20}21 22function parsePage(value) {23  if (value === undefined || value === null || value === "") return null;24  const number = Number(value);25  if (!Number.isInteger(number) || number < 1) {26    throw new Error("page 必须是从 1 开始的整数。");27  }28  return number;29}30 31function formatDuration(seconds) {32  const total = Number(seconds);33  if (!Number.isFinite(total) || total < 0) return "";34  const value = Math.floor(total);35  const hours = Math.floor(value / 3600);36  const minutes = Math.floor((value % 3600) / 60);37  const secs = value % 60;38  const pad = (item) => String(item).padStart(2, "0");39  return hours > 040    ? hours + ":" + pad(minutes) + ":" + pad(secs)41    : minutes + ":" + pad(secs);42}43 44function toIsoTime(seconds) {45  const value = Number(seconds);46  if (!Number.isFinite(value) || value <= 0) return null;47  return new Date(value * 1000).toISOString();48}49 50const explicitBvid = pickString(input.bvid, "bvid");51const explicitUrl = pickString(input.url, "url");52const page = parsePage(input.page);53const bvid = explicitBvid || extractBvid(explicitUrl) || extractBvid(location.href);54 55if (!bvid) {56  throw new Error("请提供 bvid、url,或在哔哩哔哩视频页面执行。");57}58 59const apiUrl = "https://api.bilibili.com/x/web-interface/view?bvid=" + encodeURIComponent(bvid);60const response = await fetch(apiUrl, { credentials: "include" });61 62if (!response.ok) {63  throw new Error("读取视频详情失败,接口状态码:" + response.status + "。");64}65 66const payload = await response.json();67 68if (!payload || payload.code !== 0 || !payload.data) {69  const message = payload && payload.message ? payload.message : "未知错误";70  throw new Error("读取视频详情失败:" + message + "。");71}72 73const data = payload.data;74const pages = Array.isArray(data.pages) ? data.pages : [];75const parts = pages.map((item) => ({76  page: Number(item.page),77  cid: item.cid,78  title: item.part || "",79  duration_seconds: Number(item.duration || 0),80  duration: formatDuration(item.duration),81  url: "https://www.bilibili.com/video/" + (data.bvid || bvid) + "?p=" + Number(item.page)82}));83const selectedPart = page === null ? null : parts.find((item) => item.page === page);84 85if (page !== null && !selectedPart) {86  throw new Error("page 超出分 P 范围。");87}88 89const rights = data.rights && typeof data.rights === "object" ? data.rights : {};90let paymentType = null;91 92if (Number(rights.pay) === 1) {93  paymentType = "pay";94} else if (Number(rights.ugc_pay) === 1) {95  paymentType = "ugc_pay";96} else if (Number(rights.arc_pay) === 1) {97  paymentType = "arc_pay";98}99 100const durationSeconds = selectedPart101  ? selectedPart.duration_seconds102  : Number(data.duration || 0);103 104return {105  bvid: data.bvid || bvid,106  aid: data.aid,107  title: selectedPart ? selectedPart.title : (data.title || ""),108  series_title: data.title || "",109  author: data.owner && data.owner.name ? data.owner.name : "",110  mid: data.owner && data.owner.mid ? data.owner.mid : null,111  category: data.tname || "",112  publish_time: toIsoTime(data.pubdate),113  duration_seconds: durationSeconds,114  duration: formatDuration(durationSeconds),115  stat: {116    view: data.stat ? data.stat.view : 0,117    danmaku: data.stat ? data.stat.danmaku : 0,118    reply: data.stat ? data.stat.reply : 0,119    like: data.stat ? data.stat.like : 0,120    coin: data.stat ? data.stat.coin : 0,121    favorite: data.stat ? data.stat.favorite : 0,122    share: data.stat ? data.stat.share : 0123  },124  parts,125  cid: selectedPart ? selectedPart.cid : data.cid,126  page: selectedPart ? selectedPart.page : null,127  thumbnail: data.pic || "",128  description: data.desc || "",129  requires_payment: Boolean(paymentType),130  payment_type: paymentType,131  pay_preview: Boolean(Number(rights.ugc_pay_preview) === 1 || Number(rights.pay_free_watch) === 1),132  redirect_url: data.redirect_url || "",133  url: "https://www.bilibili.com/video/" + (data.bvid || bvid)134};