// ==UserScript==
// @name B站视频下载器(RPC)
// @namespace https://gitee.com/ameegle/bilibili
// @version 0.0.13
// @description 使用PRC链接Motrix的方式
// @author ameegle
// @license ISC
// @source https://gitee.com/ameegle/bilibili/raw/main/lib/bilibili.js
// @match *://www.bilibili.com/festival/*
// @match *://www.bilibili.com/video/av*
// @match *://www.bilibili.com/video/BV*
// @match *://www.bilibili.com/list/*
// @match *://www.bilibili.com/bangumi/play/ep*
// @match *://www.bilibili.com/bangumi/play/ss*
// @match *://www.bilibili.com/cheese/play/ep*
// @match *://www.bilibili.com/cheese/play/ss*
// ==/UserScript==
(function(){
var E = (e) => e.startsWith("on"), R = (e) => e === "children", k = (e) => !E(e) && !R(e), S = (e, t) => {
Object.keys(e).filter(E).forEach((r) => {
let n = r.toLowerCase().substring(2);
t.addEventListener(n, e[r]);
}), Object.keys(e).filter(k).forEach((r) => {
["innerHTML", "innerText", "textContext"].includes(r) ? t[r] = e[r] : t.setAttribute(r, e[r]);
});
}, q = (e, t) => {
let r = document.createElementNS("http://www.w3.org/2000/svg", e);
return t && S(t, r), r;
}, D = (e, t) => {
let r = document.createElement(e);
return t && S(t, r), r;
}, I = (e, t = !1) => f(e, t), f = (e, t) => {
if (x(e))
return e;
if (Array.isArray(e))
return e.map((s) => f(s, t));
let { type: r, props: n } = e;
if (typeof r == "function") {
let s = M(r, n);
return f(s, t);
}
return L(r, n, t || r === "svg");
}, x = (e) => ["string", "number", "boolean"].includes(typeof e), L = (e, t, r) => {
let n = r ? q : D, s = t.ref;
delete t.ref;
let i = n(e, t);
s && (s.current = i);
let l = t.children.map((a) => {
if (x(a))
return a;
if (!a)
return "";
if (typeof a.type == "function") {
let c = o(a.type, a.props);
return f(c, r);
}
return Array.isArray(a) ? f(a, r) : L(a.type, a.props, r || a.type === "svg");
}).flat(1 / 0);
return i.append(...l), i;
}, M = (e, t) => {
let r = e(t);
return t.ref ? Array.isArray(r) ? (r[0].props.ref = t.ref, r) : (r.props && (r.props.ref ? t.ref = r.props.ref : r.props.ref = t.ref), r) : r;
};
function $(e) {
return Array.isArray(e) ? e : x(e) ? [e] : e ? [e] : [];
}
var o = (e, t) => (t.children = $(t.children), { type: e, props: t, $$typeof: Symbol.for("rjsx.vnode") });
const z = "https://api.bilibili.com/pugv/player/web/playurl", H = "https://api.bilibili.com/x/player/playurl", U = "https://api.bilibili.com/pgc/player/web/playurl", N = "http://localhost:16800/jsonrpc", P = ".batch-btn{display:flex;align-items:center;color:#61666d;cursor:pointer;font-size:13px;line-height:18px}.batch-btn.festival{color:#f2dda4}.batch-btn .all-download{margin-left:4px}.batch-btn:hover{color:#00aeec}.v-download-wrap{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:99999;color:#18191c;background:#F1F2F3;display:flex;flex-direction:column;padding:6px;border-radius:6px;-webkit-user-select:none;user-select:none;touch-action:none;min-width:375px;border:1px solid #ebeef5;box-shadow:0 2px 12px}.v-download-wrap .v-head{padding:5px;display:flex;justify-content:space-between;border-bottom:1px solid #e3e5e7}.v-download-wrap .v-head .v-h-a-wrap{display:flex;justify-content:center;align-items:center}.v-download-wrap .v-head .v-h-a-wrap .v-h-action{width:.9em;height:.9em;cursor:pointer;border-radius:50%}.v-download-wrap .v-head .v-h-a-wrap .v-h-action.sort{background-color:orange}.v-download-wrap .v-head .v-h-a-wrap .v-h-action.download{background-color:#00aeec}.v-download-wrap .v-head .v-h-a-wrap .v-h-action.close{background-color:#fb7299}.v-download-wrap .v-head .v-h-a-wrap .v-h-action:hover{opacity:.6}.v-download-wrap .v-head .v-h-a-wrap .v-h-action:not(:last-child){margin-right:8px}.v-download-wrap .v-content{max-height:50vh;padding:4px 0;overflow:hidden auto}.v-download-wrap .v-content .v-c-empty{text-align:center;color:#9499a0}.v-download-wrap .v-content::-webkit-scrollbar{width:4px}.v-download-wrap .v-content::-webkit-scrollbar-thumb{border-radius:4px;background-color:#999}.v-download-wrap .v-item{display:flex;padding:5px 4px;align-items:center;border-radius:3px}.v-download-wrap .v-item:hover{color:#00aeec;background:#fff}.v-download-wrap .v-i-checkbox{cursor:pointer;outline:none;-webkit-appearance:auto;-moz-appearance:auto;appearance:auto;margin-right:4px}.v-download-wrap .v-i-label{width:375px;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.message-wrap{display:flex;flex-direction:column;position:fixed;top:16px;overflow:auto;z-index:999999;max-height:calc(100vh - 32px)}.message-wrap::-webkit-scrollbar{display:none}.message-wrap.right{right:16px}.message-wrap.left{left:16px}.message-wrap .message-item{display:flex;width:275px;padding:14px;border-radius:8px;box-sizing:border-box;background-color:#3f3f3f;box-shadow:0 0 4px #3f3f3f;margin:8px}.message-wrap .m-icon{display:flex;min-height:1em;min-width:1em;font-size:24px}.message-wrap .m-icon.success{color:#67c23a}.message-wrap .m-icon.info{color:#909399}.message-wrap .m-icon.warning{color:#e6a23c}.message-wrap .m-icon.error{color:#f56c6c}.message-wrap .m-content{margin-left:13px;font-size:16px;line-height:24px;color:#cfd3dc;word-break:break-all;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}", p = {
video: "/video/",
list: "/list/",
bangumi: "/bangumi/play/",
cheese: "/cheese/play/",
festival: "/festival/"
};
var v = (e, t = document.body) => {
let r = I(e);
return Array.isArray(r) ? t.append(...r.flat(1 / 0)) : t.append(r), { use: (n) => n() };
}, B = (e) => Object.prototype.hasOwnProperty.call(e, "$$typeof"), W = (e, ...t) => {
for (; e.firstChild; )
e.firstChild.remove();
e.append(...t.map((r) => B(r) ? I(r) : r).flat(1 / 0));
}, F = (...e) => e.filter(Boolean).join(" "), b = () => Object.seal({ current: void 0 }), O = (e) => Object.seal({ value: e });
const G = () => {
const e = b();
return () => (e.current || v(o("div", { ref: e, class: "message-wrap right" })), e.current);
}, J = G(), K = {
success: () => o("svg", { viewBox: "0 0 1024 1024", xmlns: "http://www.w3.org/2000/svg", children: o("path", { fill: "currentColor", d: "M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm-55.808 536.384-99.52-99.584a38.4 38.4 0 1 0-54.336 54.336l126.72 126.72a38.272 38.272 0 0 0 54.336 0l262.4-262.464a38.4 38.4 0 1 0-54.272-54.336L456.192 600.384z" }) }),
warning: () => o("svg", { viewBox: "0 0 1024 1024", xmlns: "http://www.w3.org/2000/svg", children: o("path", { fill: "currentColor", d: "M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm0 192a58.432 58.432 0 0 0-58.24 63.744l23.36 256.384a35.072 35.072 0 0 0 69.76 0l23.296-256.384A58.432 58.432 0 0 0 512 256zm0 512a51.2 51.2 0 1 0 0-102.4 51.2 51.2 0 0 0 0 102.4z" }) }),
info: () => o("svg", { viewBox: "0 0 1024 1024", xmlns: "http://www.w3.org/2000/svg", children: o("path", { fill: "currentColor", d: "M512 64a448 448 0 1 1 0 896.064A448 448 0 0 1 512 64zm67.2 275.072c33.28 0 60.288-23.104 60.288-57.344s-27.072-57.344-60.288-57.344c-33.28 0-60.16 23.104-60.16 57.344s26.88 57.344 60.16 57.344zM590.912 699.2c0-6.848 2.368-24.64 1.024-34.752l-52.608 60.544c-10.88 11.456-24.512 19.392-30.912 17.28a12.992 12.992 0 0 1-8.256-14.72l87.68-276.992c7.168-35.136-12.544-67.2-54.336-71.296-44.096 0-108.992 44.736-148.48 101.504 0 6.784-1.28 23.68.064 33.792l52.544-60.608c10.88-11.328 23.552-19.328 29.952-17.152a12.8 12.8 0 0 1 7.808 16.128L388.48 728.576c-10.048 32.256 8.96 63.872 55.04 71.04 67.84 0 107.904-43.648 147.456-100.416z" }) }),
error: () => o("svg", { viewBox: "0 0 1024 1024", xmlns: "http://www.w3.org/2000/svg", children: o("path", { fill: "currentColor", d: "M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm0 393.664L407.936 353.6a38.4 38.4 0 1 0-54.336 54.336L457.664 512 353.6 616.064a38.4 38.4 0 1 0 54.336 54.336L512 566.336 616.064 670.4a38.4 38.4 0 1 0 54.336-54.336L566.336 512 670.4 407.936a38.4 38.4 0 1 0-54.336-54.336L512 457.664z" }) })
}, m = (e, t, r) => {
const n = K[t], s = b();
v(o("div", { ref: s, class: "message-item", role: "alert", children: [
o("span", { class: `m-icon ${t}`, children: o(n, {}) }),
o("span", { class: "m-content", children: e })
] }), J()), s.current.scrollIntoView(), setTimeout(() => {
s.current.remove();
}, r);
}, h = {
success: (e, t = 3e3) => m(e, "success", t),
warning: (e, t = 3e3) => m(e, "warning", t),
info: (e, t = 3e3) => m(e, "info", t),
error: (e, t = 3e3) => m(e, "error", t)
}, Q = async (e, t) => {
var r;
try {
const { data: n, result: s } = await fetch(e, {
credentials: "include"
}).then((l) => l.json()), i = (r = n || s) == null ? void 0 : r.durl[0].url;
if (!i)
throw new Error("durl is empty");
return [i, t + ".mp4"];
} catch {
throw new Error("视频信息解析失败");
}
}, X = (e) => {
h.success("视频信息解析成功");
const t = [
`User-Agent: ${navigator.userAgent}`,
`Referer: ${location.href}`
], r = e.map(([s, i]) => {
const l = { out: i, header: t };
return {
id: window.btoa(`BParse_${Date.now()}_${Math.random()}`),
jsonrpc: "2.0",
method: "aria2.addUri",
params: ["token:", [s], l]
};
}), n = {
method: "POST",
body: JSON.stringify(r)
};
try {
return fetch(N, n);
} catch {
throw new Error("RPC请求失败, 请开启Motrix");
}
}, Y = () => {
var n;
const e = Object.keys(p).find((s) => location.pathname.startsWith(p[s])) || "?";
if (e === "cheese") {
const s = document.querySelector("div.edu-player-quality-item.active span");
return (s == null ? void 0 : s.textContent) || "80";
}
const t = e === "video" ? "li.bpx-player-ctrl-quality-menu-item.bpx-state-active" : "li.squirtle-select-item.active", r = document.querySelector(t);
return ((n = r == null ? void 0 : r.dataset) == null ? void 0 : n.value) || "80";
}, Z = () => {
const e = Object.keys(p).find((t) => location.pathname.startsWith(p[t])) || "?";
return e === "cheese" ? z : e === "video" || e === "festival" ? H : U;
}, ee = (e) => {
const t = new URLSearchParams({
qn: Y(),
fnver: "0",
fnval: "0",
fourk: "1",
type: "mp4",
otype: "json",
platform: "html5",
high_quality: "1"
}), r = e.map(({
aid: n,
bvid: s,
cid: i,
title: l,
ep_id: a = ""
}) => (t.set("avid", n.toString()), t.set("bvid", s), t.set("cid", i.toString()), t.set("ep_id", a), Q([Z(), t.toString()].join("?"), l)));
Promise.all(r).then(X).then(() => h.success("RPC请求成功, 开始下载")).catch((n) => {
console.warn(n), h.error(n.message);
});
};
function te({ title: e, bvid: t, aid: r, cid: n, main_title: s, ep_id: i }) {
const l = [encodeURIComponent(e), t, r, n, i].join("::"), a = [s, e].filter(Boolean).join("-");
return o("div", { class: "v-item", children: [
o("input", { class: "v-i-checkbox", type: "checkbox", name: "vinfoindex", id: n, value: btoa(l) }),
o("label", { class: "v-i-label", for: n, title: a, children: a })
] });
}
const A = O(!1);
function C({ desc: e }) {
const t = Object.keys(p).find((r) => location.pathname.startsWith(p[r])) || "?";
try {
let r = [];
return t === "bangumi" ? r = ne() : t === "cheese" ? r = oe() : t === "festival" ? r = se() : r = re(), e && r.reverse(), r.map((n) => o(te, { ...n }));
} catch {
return A.value = !0, h.error("获取视频信息失败"), [o("div", { class: "v-c-empty", children: "~~暂无内容~~" })];
}
}
const re = () => {
var a;
let e = [];
const {
bvid: t,
aid: r,
title: n,
cid: s,
ugc_season: i,
pages: l
} = __INITIAL_STATE__.videoData;
return i ? e = (a = i.sections) == null ? void 0 : a.map(({
title: c,
episodes: d
}) => d == null ? void 0 : d.map(({ cid: w, title: g, bvid: _, aid: V }) => ({
cid: w,
title: g,
bvid: _,
aid: V,
main_title: c
}))).flat() : l ? e = l.map(({ cid: c, part: d }) => ({ cid: c, title: d, bvid: t, aid: r })) : e.push({ cid: s, title: n, bvid: t, aid: r }), e;
}, ne = () => __NEXT_DATA__.props.pageProps.dehydratedState.queries.map(({ state: t }) => t.data.seasonInfo.mediaInfo.episodes.map(({ bvid: r, aid: n, cid: s, playerEpTitle: i, ep_id: l }) => ({ aid: n, bvid: r, cid: s, title: i, ep_id: l }))).flat(), oe = () => [], se = () => __INITIAL_STATE__.sectionEpisodes;
let y = !0;
const j = b(), T = () => {
var e;
(e = j.current) == null || e.remove(), y = !0;
}, ae = () => {
const e = O(!1), t = b();
return o("div", { ref: j, class: "v-download-wrap", role: "table", children: [
o("div", { class: "v-head", children: [
o("span", { children: "可选视频列表" }),
o("div", { class: "v-h-a-wrap", children: [
o("span", { class: "v-h-action sort", title: "排序", onClick: () => {
A.value || W(t.current, o(C, { desc: e.value = !e.value }));
} }),
o("span", { class: "v-h-action download", title: "下载", onClick: () => {
if (A.value)
return;
const l = new FormData(t.current).getAll("vinfoindex").map((a) => {
const [c, d, w, g, _] = atob(a.toString()).split("::");
return {
title: decodeURIComponent(c),
bvid: d,
aid: Number(w),
cid: Number(g),
ep_id: _
};
});
if (!l.length)
return h.warning("请选择后再操作");
ee(l);
} }),
o("span", { class: "v-h-action close", title: "关闭", onClick: T })
] })
] }),
o("form", { ref: t, class: "v-content", children: o(C, {}) })
] });
}, ie = () => {
y && (y = !1, v(o(ae, {}), document.body));
}, le = ({ cclass: e }) => o("span", { title: "批量下载", class: F("batch-btn", "video-toolbar-left-item", e), onClick: ie, children: [
o("svg", { xmlns: "http://www.w3.org/2000/svg", width: "28", height: "28", viewBox: "0 0 24 24", style: "margin-right:8px;", class: "video-toolbar-item-icon", children: o("path", { fill: "currentColor", d: "M12 2C6.49 2 2 6.49 2 12s4.49 10 10 10s10-4.49 10-10S17.51 2 12 2zm-1 8V7c0-.55.45-1 1-1s1 .45 1 1v3h1.79c.45 0 .67.54.35.85l-2.79 2.79c-.2.2-.51.2-.71 0l-2.79-2.79a.5.5 0 0 1 .36-.85H11zm5 7H8c-.55 0-1-.45-1-1s.45-1 1-1h8c.55 0 1 .45 1 1s-.45 1-1 1z" }) }),
o("span", { children: "下载" })
] });
let u;
{
let e = "";
u = document.querySelector(".video-toolbar-left, .toolbar"), u || (u = document.querySelector(".video-toolbar-content_left"), e = "festival"), v(o("style", { type: "text/css", children: P }), document.head), y = !0;
const t = new MutationObserver((r, n) => {
v(o(le, { cclass: e }), u), n.disconnect(), ce();
});
u && t.observe(u, {
childList: !0,
attributes: !0,
attributeOldValue: !0,
characterData: !0,
characterDataOldValue: !0,
subtree: !0
});
}
function ce() {
const e = document.querySelector("#bilibili-player video"), t = new MutationObserver(() => {
console.log("refresh..."), T();
});
e && t.observe(e, {
attributes: !0
});
}
})();