B站视频下载-油猴脚本
简介:调用B站API下载B站网页视频-可下载普通视频、分集视频、合集视频、番剧。
分享一个2022年5月写的脚本,当时下载b站视频一直用的是别人的油猴脚本,诚然那个脚本很强,还能下载字幕、音频等等(不过这些功能我都用不上),但是有个缺点就是文件命名不是视频标题(记得是一串数字字母组合,不好分辨不同的视频),下载完成后要自己手动修改,不太方便,于是就写了这个脚本。最近下载视频的时候发现分集视频页面元素发生了一点点小变化,导致脚本失效了,现在修复好了顺便分享出来。
效果图(普通视频点击下载后直接下载,没有分集选择框):
可以取消下载,下载好的文件命名就是视频标题。
调用的API下载的是1080p的flv格式的视频。
代码:
// ==UserScript==
// @name B站视频下载
// @namespace http://tampermonkey.net/
// @version 1.0
// @description try to take over the world!
// @author harglo
// @include *://www.bilibili.com*
// @include *://*.bilibili.com/video/*
// @include *://*.bilibili.com/bangumi/play/*
// @require https://unpkg.com/axios@0.27.2/dist/axios.min.js
// @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js
// @grant none
// ==/UserScript==
(function() {
'use strict';
setTimeout(function () {
let xmlHTTP = new XMLHttpRequest();
let xmlHttpAbort=false;
function myDownloadTip(number,text,width,height){
let tip=$('<div id="downloadVideoTip"><span id="downloadVideoTipText">'+text+'</span><button id="cancelDownload" style="float: right;margin-top: 7px;margin-right: 20px;height: 35px;line-height: 35px;">取消下载</button></div>');
let tip_style="z-index:9999;position: fixed;top:20px;left: calc(50% - "+width/2+"px);width: "+width+"px;height: "+height+"px;border-radius: 5px;text-align: center;line-height: 50px;font-size: 16px;";
number===1?tip.get(0).style.cssText=tip_style+"background-color: #dff0d8;color: #3c763d;":tip.get(0).style.cssText=tip_style+"background-color: #f2dede;color: #a94442;";
$('body').append(tip);
document.getElementById('cancelDownload').onclick=function () {
this.style.display="none";
xmlHttpAbort=true;
xmlHTTP.abort();
$('#downloadVideoTipText').text("已取消下载");
$('#downloadVideoTip').fadeTo(3000,0);
setTimeout(function () {
$('#downloadVideoTip').remove();
},3100);
};
}
function byteUnitConversion(size) {
if (!size)
return "";
let num = 1024.00; //byte
if (size < num)
return size + "B";
if (size < Math.pow(num, 2))
return (size / num).toFixed(2) + "KB"; //kb
if (size < Math.pow(num, 3))
return (size / Math.pow(num, 2)).toFixed(2) + "MB"; //M
if (size < Math.pow(num, 4))
return (size / Math.pow(num, 3)).toFixed(2) + "G"; //G
return (size / Math.pow(num, 4)).toFixed(2) + "T"; //T
}
function myDownloadFunction(fileUrl,fileName) {
myDownloadTip(1,"正在下载",550,50);
let blob;
xmlHTTP.open('GET', fileUrl, true);
xmlHTTP.responseType = 'arraybuffer';
xmlHTTP.onload = function(e) {
blob = new Blob([this.response]);
};
xmlHTTP.onprogress = function(pr) {
//document.getElementById('downloadVideo').textContent="正在下载:"+Number(pr.loaded/pr.total*100).toFixed(2)+"%";
$('#downloadVideoTipText').text("正在下载:"+byteUnitConversion(pr.loaded)+"/"+byteUnitConversion(pr.total)+" 进度: "+Number(pr.loaded/pr.total*100).toFixed(2)+"%");
if(Number(pr.loaded/pr.total*100).toFixed(2)==100){
$('#downloadVideoTipText').text("下载完成");
$('#downloadVideoTip').fadeTo(3000,0);
setTimeout(function () {
$('#downloadVideoTip').remove();
},3100);
}
};
let fileName2 = fileName;
xmlHTTP.onloadend = function(e){
if(!xmlHttpAbort){
console.log("fileName="+fileName2);
let tempEl = document.createElement("a");
document.body.appendChild(tempEl);
tempEl.style = "display: none";
let downUrl = window.URL.createObjectURL(blob);
tempEl.href = downUrl;
tempEl.download = fileName2;
tempEl.click();
window.URL.revokeObjectURL(downUrl);
}
};
xmlHTTP.send();
}
//上面↑是方法
//批量下载的弹出框
let body = document.body;
let toastDiv = document.createElement("div");
toastDiv.setAttribute("id","myToastDiv");
toastDiv.style.cssText="display:none;z-index:9999;position:fixed;bottom:8px;left:8px;background-color:#f4f4f4;border: skyblue 2px solid;";
let toastDivChild1 = document.createElement("div");
toastDivChild1.style.cssText="margin:8px";
toastDivChild1.textContent="下载视频";
toastDiv.appendChild(toastDivChild1);
let htmlSpanElement = document.createElement("span");
htmlSpanElement.textContent="X";
htmlSpanElement.style.cssText="float:right;cursor:pointer;";
htmlSpanElement.onclick=function(){
$('#myToastDiv').animate({width: 'toggle'}, 460);
};
toastDivChild1.appendChild(htmlSpanElement);
let toastDivChild2 = document.createElement("div");
toastDiv.appendChild(toastDivChild2);
toastDivChild2.style.cssText="border: skyblue 2px solid;margin:8px;max-height:200px;max-width:400px;overflow-y:auto;display:flex;flex-direction:row;flex-wrap:wrap;background-color:#f4f4f4";
body.appendChild(toastDiv);
//弹出框↑
let href = location.href;
axios.defaults.withCredentials=true;
if(href.includes(".bilibili.com/video")){
let bvid,cid,url;
if(document.getElementById('multi_page')){
console.log("分集视频");//bvid是一样的,cid不同
let href1 = document.getElementsByClassName('watched on')[0].firstElementChild.href;
bvid = href1.match(/(?=BV).*?(?=(\?|\/|$))/)[0];
console.log(bvid);
let myInterval=setInterval(function () {
console.log(document.getElementsByClassName('bpx-player-ctrl-eplist-menu')[0]);
if(document.getElementsByClassName('bpx-player-ctrl-eplist-menu')[0]){
clearInterval(myInterval);
let li1 = document.getElementsByClassName('bpx-player-ctrl-eplist-menu')[0].getElementsByTagName('li');
console.log(li1.length);
for (let i = 0; i < li1.length; i++) {
cid=li1[i].getAttribute("data-cid");
let title=li1[i].textContent;
console.log(cid);
console.log(title);
let htmlButtonElement = document.createElement("button");
htmlButtonElement.style.cssText="cursor:pointer;margin:5px;border: skyblue 2px solid;border-radius:10px;background-color:white;";
htmlButtonElement.textContent=title+".flv";
htmlButtonElement.onmouseover=function(){
this.style.backgroundColor="skyblue"
};
htmlButtonElement.onmouseout=function(){
this.style.backgroundColor="white";
};
htmlButtonElement.onclick=function(){
url="https://api.bilibili.com/x/player/playurl?bvid="+bvid+"&cid="+cid+"&qn=80&fnval=0&fnver=0&fourk=0";
console.log("url="+url);
axios.get(url).then((resp)=>{
console.log(resp.data.data.durl[0].url);
myDownloadFunction(resp.data.data.durl[0].url,title+".flv");
});
};
toastDivChild2.appendChild(htmlButtonElement);
}
let span = document.createElement('span');
span.title="下载视频";
span.innerHTML='<i class="van-icon-videodetails_share" style="transform: rotate(90deg)"></i>下载';
document.getElementsByClassName('collect')[0].after(span);
span.onclick=function () {
$('#myToastDiv').animate({width: 'toggle'}, 460);
}
}
},300);
}else if(document.getElementsByClassName('base-video-sections')[0]){
console.log("合集视频");
$.get(window.location.href,function(res){
let regExpMatchArray = res.match(/"aid":\d+,"cid":\d+,"title":(.*?),/g);
let resultArray=[...new Set(regExpMatchArray)];
console.log(resultArray);
for (let i = 0; i < resultArray.length; i++) {
let aid = resultArray[i].match(/(?<="aid":)(.*?)(?=,)/)[0];
let cid = resultArray[i].match(/(?<="cid":)(.*?)(?=,)/)[0];
let title = resultArray[i].match(/(?<="title":)(.*?)(?=,)/)[0];
console.log(aid+"-"+cid+"-"+title);
let htmlButtonElement = document.createElement("button");
htmlButtonElement.style.cssText="cursor:pointer;margin:5px;border: skyblue 2px solid;border-radius:10px;background-color:white;";
htmlButtonElement.textContent=title+".flv";
htmlButtonElement.onmouseover=function(){
this.style.backgroundColor="skyblue"
};
htmlButtonElement.onmouseout=function(){
this.style.backgroundColor="white";
};
htmlButtonElement.onclick=function(){
url="https://api.bilibili.com/x/player/playurl?avid="+aid+"&cid="+cid+"&qn=80&fnval=0&fnver=0&fourk=0";
console.log("url="+url);
axios.get(url).then((resp)=>{
console.log(resp.data.data.durl[0].url);
myDownloadFunction(resp.data.data.durl[0].url,title+".flv");
});
};
toastDivChild2.appendChild(htmlButtonElement);
}
let span = document.createElement('span');
span.title="下载视频";
span.innerHTML='<i class="van-icon-videodetails_share" style="transform: rotate(90deg)"></i>下载';
document.getElementsByClassName('collect')[0].after(span);
span.onclick=function () {
$('#myToastDiv').animate({width: 'toggle'}, 460);
}
});
}else{
console.log("普通视频");
let span = document.createElement('span');
span.title="下载视频";
span.innerHTML='<i class="van-icon-videodetails_share" style="transform: rotate(90deg)"></i>下载';
document.getElementsByClassName('collect')[0].after(span);
span.onclick=function () {
bvid = location.href.match(/(?=BV).*?(?=(\?|\/|$))/)[0];
axios.all([
axios.get("https://api.bilibili.com/x/player/pagelist?bvid="+bvid).then(resp=>{
console.log(resp.data);
console.log(resp.data.data[0].cid);
cid=resp.data.data[0].cid;
url="https://api.bilibili.com/x/player/playurl?bvid="+bvid+"&cid="+cid+"&qn=80&fnval=0&fnver=0&fourk=0";
})
]).then(()=>{
console.log("url="+url);
axios.get(url).then((resp)=>{
console.log(resp.data.data.durl[0].url);
console.log(document.getElementsByClassName('tit')[0].textContent+".flv");
//window.open(resp.data.data.durl[0].url+"&attname="+document.getElementsByClassName('tit')[0].textContent+".flv");
myDownloadFunction(resp.data.data.durl[0].url,document.getElementsByClassName('tit')[0].textContent+".flv");
})
})
}
}
}else if(href.includes(".bilibili.com/bangumi/play/")){
console.log("番剧");
//弹出框
//弹出框↑
let ep_id=document.getElementsByClassName('ep-item cursor visited')[0].firstElementChild.href.match(/(?<=ep).*?(?=\/)/)[0];
axios.get("https://api.bilibili.com/pgc/view/web/season?ep_id="+ep_id).then((resp)=>{
console.log(resp.data.result.episodes);
for (let i = 0; i < resp.data.result.episodes.length; i++) {
let epid=resp.data.result.episodes[i].link.match(/(?<=ep).*?(?=$)/)[0];
console.log(epid);
let title=resp.data.result.episodes[i].share_copy;
console.log(title);
let htmlButtonElement = document.createElement("button");
htmlButtonElement.style.cssText="cursor:pointer;margin:5px;border: skyblue 2px solid;border-radius:10px;background-color:white;";
htmlButtonElement.textContent=title+".flv";
htmlButtonElement.onmouseover=function(){
this.style.backgroundColor="skyblue"
};
htmlButtonElement.onmouseout=function(){
this.style.backgroundColor="white";
};
htmlButtonElement.onclick=function(){
let requestUrl="https://api.bilibili.com/pgc/player/web/playurl?ep_id="+epid+"&qn=80&fnval=0&fnver=0&fourk=0";
console.log("url="+requestUrl);
axios.get(requestUrl).then((resp)=>{
console.log(resp.data.result.durl[0].url);
myDownloadFunction(resp.data.result.durl[0].url,title+".flv");
});
};
toastDivChild2.appendChild(htmlButtonElement);
}
let div = document.createElement('div');
div.title="下载视频";
div.setAttribute("class","share-info");
div.innerHTML='<i class="iconfont icon-share" style="transform: rotate(90deg)"></i><span>下载</span>';
document.getElementsByClassName('coin-info')[0].after(div);
div.onclick=function () {
// myTip(1,"请在右侧点击下载",350,50);
$('#myToastDiv').animate({width: 'toggle'}, 460);
}
});
}
},5000);
})();