// ==UserScript== // @name B-HTML5-Video // @version 1.0.0 // @description 获取B站视频地址,Dash // @author zwb // @match https://www.bilibili.com/video/BV* // @match https://www.bilibili.com/video/av* // @run-at document-start // @grant none // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAC4jAAAuIwF4pT92AAAIVklEQVR4Xu2bWYwVRRSGzyzIBYwbAmpwFzPigqCi0ZgoJooa3GKMGBg3VMAIcY3xQREFNW5xfXAF1AiKPrhEURl3E6O4srggojCCIIIgIuIM/t+cauZ6p293j3jDZZiHL5lkpk7956/q6upTNdbY2GgNDQ1bLHbjZ0tszKc/Z6FC7CmOF0PEKHGxOEv0F9uL6gxxNpbq0Ff/0PclYrQYOsa1obEiQ5wmrGrSbKuaOCsLlWKAuFNME3PFR2KquEzsJXKiIkOs/0pF6GOv0Cd9zxDzxGtVrg2NlRliNWE2cbbZhFlpdBX7i9FiuvhW/CrqxUwxWVwpjhPdRHWGmK2lOsQ+LvQ1JfT9k1gu5oo3zDWitWuGmJkN2FecKR4Vv4h1okH8HX5eKD4QY0SNyImKDHGzUhFi1oQ+6Ks+9I2GhvDzUnONaN03Q9zMBtDx2WKCuduNBawUC8SL4mr7f2dC/sgT+4XQ10prqYNZOdFca02G2K0yYHAIHmdANBMYlffFjaK3bfxMiEa+d4hJ7IXWPAPjDJgkzhH7ZYif2YAe4lBxg/gydPRXjABG5UfzmXCVGCC6m49ia42oDm0HhFgvhthxI48WNKENjWjdKUMfmQ1ATGfzqfWy+E6ssZZCmAmIYZTeNR+1g0QnUZmhn4ho5A8MMYi1IMT+O6ZftKAJbWhEa7bHL6MBCCKBfmKk+aPwlRWfCavMR+sla54JzKK0mcDvqsLfHmvZRx4tTP1LzTVWpvTTagMiuoidxXnmbvPqSZsJ74ixoo+lzwR+lwt/Oza0TRr5P8w1oOV8c21bJ8TPZAAiWHVZeI4Wp5ovgBeIEebv2YfFZ2KxWGsthRXOhNfFeDHKfAaNKMLI8DfjQ5tiIx9B32hAyyPm2oiDVjSjnRzIhZxaml9gANOGacoI1JoLeU68J+aI+eYbD6Yd7jMycatx/kxAJEYsCW2hvgjR75eENmstfuQj6BsNaIk2ZmhEK5rRTg7kQk4tH8FgQAfzhYNF5wxxnfkzVWf+fC0Sv5sLSkp4UxMZjlY0o73OPBdyIjdyJFdy3mAAzzYLz4XmGw2mFKPA9FtjzSPRYOVtQKQvMgLt5MAOkZzIjRzJlZybDOC5YHoMNX+O2Ocvs+Spt7lBLuTEgkmO5HqwsSbIAKbCEPNX1myx2tKf7c2N/LWCHHlrYEIHDODL6SZzd1hI2tLIF4IR5Mim6Wbjq1EG1Jp/WrJwtLWRLySaCcxych6KAXeYvzLWxTRgAeG1NMN8AXlGPFXmoBGtH5trj9tHMMvZXt+OAa/qh28sfuqzEcGcW8UJ4hDzr6yaMgVtaETrLeba2UnGGfC1eAUD2DTwyoub+u+bJ3+a+dcVW9lse+xNA9rQiFZ2gWineBL3KPxsLIgyYIX56hhnwJPiGLG76Git+6LbVKARrWg+xjyHOANYB37FABaFYgWG+0OgThk6LjfQjHZyKMwLyHktBkS7pzgD7hY72uYx8oWgGe3kEGeA7xhlwPoifwB3iu0ydFauoJ0ciuXX2G7ARhrAYrOD2EccZV7FAaoyPcU25hWeNKHFqAoxeoaYUXz66hX67pjQvuQGUGToK4aZl8leCNwmBpnX5jsntE+jc4gxKMSM4tPXRaHvbgntS24A3xEk/5j5ZmppgF0WG5FTLFlgGrQdFGK9mxefvh4Pfe+f0L7kBgwUT5sfUXFe8GegXnxo/pG1d0L7NGg7NsRamBd/eeiTvgcmtC+5AdTd+E5gv53/LYHI6JTmgIT2adB2QoiVX3xdF/qk78EJ7UtuQK152amwaotATOCL66CE9mnQdnKIlW9wVPGh79qE9iU3gFL0vIT2z5tXXtISLQZtn0uIT9/nJ7RvN8BKbECt+WdlYSElmqJ8m/dJaJ8Gj8CUECv/EYgKG/Rdm9C+5AawAH1iXsMvXARZqVkED0xon0bSIrgq9H1OQvuSG3CSeNZ8MfrNml9T1OQRx6FEr4T2afQKMVjtqe6sDfFXhD6nipMT2pfcAEZ3uHjCvJzO7RE2KhRSKLVxENEjoX0aHI+fHmIRc5k1b4Q47OAYLOktU3IDEEgJijM9NiXTjDLThFl3mVeRKFN1SWifRpcQAxP4rH1NUMKj9jc89N09oX3JDciZf3NTi+MjhV0Z9bjDzYsR21rWc/p4qkOMPcQRIf7A0FdN6DuX0L7kBpQ77QZYuwHtBrQbYO0GJBvAD8XK4rzPqbuV82lQMdCMdnKIS35DWTz/vm3hH90ndrPkd225guZdzXOIM8DvC8gAPirYX8cZQN2NTQ3Xz9Lu+JULaOxgfj7Y37xeGTf6HI0tw4ComBlnwNvmV09PNN9y5qy8TYgOR/n+QPP15jnEGcCB8BwMqDO/MRF3PD5fvBkCsRWl/r+LubvlCNr4gjzSfODQTg6FeZErN2KmY8C95kfIcRck+Oz8wbwkzSnrQ+JB8UCZgjYucaKVuwFoJ4c4A8j5HgzggIHS1Rorvhi2FaIrdH+a5zwMA/ik5NSFqbLc2v4lqWhWk3M/DNjK/PLgW+ZrAe60tZkQjTwVpXnmCyOnSk3X5DiAPMy8ukJl53trezOBXBh5ZjnrA7nyimy6KMnrgyvmrKJUdurMy1vR3eD8jVKxHWO5kK8RzX4LxHNh1eetwP8UkKtfq7fmy9KUn/hXk1rzQiQFR1ZKZgT7hCy3wzc1Ubl8tbnmeeY5kAs5nWs+28mVnM1yT8yx3KTZERWiWhwqLhL3ijoxSywWK8TvYnWZgjY0LhIzxfSc50Au5ERu5LghZxv3xVIb9/mSfCrFzqKvOEEMFcPFFeIacW2Zg8bLx7lmtJMDuZATuf0rX1u/fr3xD9RbKu0GYMCWzD+zDS/KNPfarwAAAABJRU5ErkJggg== // @namespace https://greasyfork.org/zh-CN/users/135090 // @license CC0 // ==/UserScript== (function () { /* ---------- 面板样式 ---------- */ const style = document.createElement("style"); style.textContent = ` #bdl-panel{position:fixed;top:100px;right:16px;z-index:999999;width:250px; font:13px/1.5 "Microsoft YaHei",system-ui,sans-serif; background:rgba(24,25,28,.94);color:#eee;border-radius:12px;overflow:hidden; box-shadow:0 8px 30px rgba(0,0,0,.4);border:1px solid rgba(255,255,255,.08)} #bdl-panel .bdl-head{display:flex;align-items:center;justify-content:space-between; padding:10px 14px;background:linear-gradient(90deg,#fb7299,#fc5572); cursor:move;user-select:none} #bdl-panel .bdl-title{font-weight:700;color:#fff;font-size:14px} #bdl-panel .bdl-toggle{background:rgba(255,255,255,.22);border:none;color:#fff; width:24px;height:24px;border-radius:6px;cursor:pointer;font-size:15px;line-height:1} #bdl-panel.collapsed .bdl-body{display:none} #bdl-panel .bdl-body{padding:12px 14px} #bdl-panel .bdl-row{margin-bottom:10px} #bdl-panel label{display:block;font-size:12px;color:#9aa0a6;margin-bottom:4px} #bdl-panel select{width:100%;padding:6px 8px;border-radius:8px;background:#2b2c30; color:#eee;border:1px solid rgba(255,255,255,.12);outline:none;font-size:12px;cursor:pointer} #bdl-panel .bdl-btns{display:flex;gap:8px;margin-top:4px} #bdl-panel .bdl-btn{flex:1;text-align:center;padding:8px 4px;border-radius:8px; font-size:12px;cursor:pointer;text-decoration:none;border:none;color:#fff;transition:filter .15s} #bdl-panel .bdl-btn:hover{filter:brightness(1.12)} #bdl-panel .bdl-v{background:#23ade5} #bdl-panel .bdl-a{background:#4bb464} #bdl-panel .bdl-both{background:#fb7299;flex:1.2} #bdl-panel .bdl-tip{margin-top:8px;font-size:11px;color:#7f868c;text-align:center} `; (document.head || document.documentElement).appendChild(style); /* ---------- 工具函数 ---------- */ const BVID = window?.__INITIAL_STATE__?.bvid || location.pathname.split("/").filter(Boolean)[1] || "video"; function fmtSize(bytes) { if (!bytes) return ""; const mb = bytes / 1048576; return mb >= 1024 ? (mb / 1024).toFixed(2) + " GB" : mb.toFixed(1) + " MB"; } function download(durl, thename) { fetch(durl, { method: "GET", referrer: location.href, mode: "cors" }) .then(res => res.blob()) .then(blob => { const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.download = thename; a.href = url; a.click(); URL.revokeObjectURL(url); }).catch(err => console.error(err)); } /* ---------- 构建面板 ---------- */ function buildPanel(pdata) { const bvid = window?.__INITIAL_STATE__?.bvid || BVID; const vlist = (pdata.dash.video || []).slice(); const alist = (pdata.dash.audio || []).slice(); const qname = id => { const i = (pdata.accept_quality || []).indexOf(id); return i >= 0 ? pdata.accept_description[i] : String(id); }; // 视频: 分辨率高->低, 同分辨率优先 av01 > hevc > avc const codecRank = c => c.startsWith("av01") ? 3 : c.startsWith("hev") ? 2 : c.startsWith("avc") ? 1 : 0; vlist.sort((a, b) => b.width - a.width || codecRank(b.codecs) - codecRank(a.codecs) || a.id - b.id); alist.sort((a, b) => b.bandwidth - a.bandwidth); const panel = document.createElement("div"); panel.id = "bdl-panel"; panel.innerHTML = '
\uD83C\uDFAC B站下载' + '
' + '
' + '
' + '
' + '
' + '下载视频' + '下载音频' + '' + '
' + '
下载后请自行合并音视频
' + '
'; document.body.appendChild(panel); const vsel = panel.querySelector(".bdl-vsel"); const asel = panel.querySelector(".bdl-asel"); const vbtn = panel.querySelector(".bdl-v"); const abtn = panel.querySelector(".bdl-a"); const both = panel.querySelector(".bdl-both"); vlist.forEach((v, i) => { const opt = document.createElement("option"); opt.value = i; opt.textContent = `${qname(v.id)} ${v.height || ""}P ${v.codecs.slice(0, 4)}` + (v.size ? ` [${fmtSize(v.size)}]` : ""); vsel.appendChild(opt); }); alist.forEach((a, i) => { const opt = document.createElement("option"); opt.value = i; opt.textContent = `${a.codecs.slice(0, 4)}-${a.id}` + (a.bandwidth ? ` [${(a.bandwidth / 1000).toFixed(0)}kbps]` : ""); asel.appendChild(opt); }); if (!alist.length) { asel.disabled = true; abtn.style.display = "none"; } const curV = () => vlist[+vsel.value] || vlist[0]; const curA = () => alist[+asel.value] || alist[0]; function sync() { const v = curV(), a = curA(); vbtn.href = v.base_url; vbtn.download = `${bvid}.${v.codecs.slice(0, 4)}.m4v`; abtn.href = a ? a.base_url : ""; abtn.download = `${bvid}.m4a`; } vsel.onchange = asel.onchange = sync; sync(); panel.querySelector(".bdl-toggle").onclick = () => panel.classList.toggle("collapsed"); both.onclick = () => { const v = curV(), a = curA(); if (a) download(a.base_url, `${bvid}.m4a`); download(v.base_url, `${bvid}.m4v`); }; // 拖拽移动 const head = panel.querySelector(".bdl-head"); head.addEventListener("pointerdown", e => { if (e.target.tagName === "BUTTON") return; const rect = panel.getBoundingClientRect(); const ox = e.clientX - rect.left, oy = e.clientY - rect.top; const move = ev => { panel.style.left = (ev.clientX - ox) + "px"; panel.style.top = (ev.clientY - oy) + "px"; panel.style.right = "auto"; }; const up = () => { document.removeEventListener("pointermove", move); document.removeEventListener("pointerup", up); }; document.addEventListener("pointermove", move); document.addEventListener("pointerup", up); }); } /* ---------- 初始化:等待数据就绪 ---------- */ let timer = null; let curBvid = ""; const readBvid = () => window?.__INITIAL_STATE__?.bvid || location.pathname.split("/").filter(Boolean)[1] || ""; // 清理旧面板与等待中的定时器 function teardown() { if (timer) { clearTimeout(timer); timer = null; } document.getElementById("bdl-panel")?.remove(); } // 针对当前视频构建面板 function start() { teardown(); curBvid = readBvid(); (function init() { const bvid = readBvid(); if (bvid !== curBvid) return; // 等待期间已切换到别的视频,交给下一次 start() const pdata = window?.__playinfo__?.data; const stateBvid = window?.__INITIAL_STATE__?.bvid; // 需同时满足:有数据且 playinfo 属于当前视频 if (!pdata?.dash?.video?.length || (stateBvid && stateBvid !== bvid)) { timer = setTimeout(init, 800); return; } buildPanel(pdata); })(); } /* ---------- SPA 导航监听(B站切换分P/相关视频不刷新页面) ---------- */ function maybeRestart() { const b = readBvid(); if (b && b !== curBvid) start(); } // 劫持 pushState / replaceState,转发为自定义事件 ["pushState", "replaceState"].forEach(fn => { const orig = history[fn].bind(history); history[fn] = function () { const r = orig.apply(history, arguments); window.dispatchEvent(new Event("bdl-nav")); return r; }; }); window.addEventListener("bdl-nav", maybeRestart); window.addEventListener("popstate", maybeRestart); // 轮询兼容:部分跳转未触发上述事件 setInterval(maybeRestart, 1000); start(); })();