Bilibili-Evolved/min/download-video.min.js
2019-01-20 21:41:11 +08:00

1 line
7.6 KiB
JavaScript

(()=>{return(e,t)=>{const s={entity:null,aid:undefined,cid:undefined,isOldBangumi:false,isStardustBangumi:false};const n=[];class i{constructor(){this.menuPanel=document.querySelector(".download-video-panel");this.menuClasses=["quality","action","progress"];this.currentMenuClass="quality"}addMenuClass(){this.menuPanel.classList.remove(...this.menuClasses);this.menuPanel.classList.add(this.currentMenuClass);return this.currentMenuClass}resetMenuClass(){[this.currentMenuClass]=this.menuClasses;this.addMenuClass()}nextMenuClass(){const e=this.menuClasses.indexOf(this.currentMenuClass)+1;const t=this.menuClasses[e>=this.menuClasses.length?0:e];this.currentMenuClass=t;this.addMenuClass();return t}addError(){this.menuPanel.classList.add("error")}removeError(){this.menuPanel.classList.remove("error");this.resetMenuClass()}async getUrl(e){if(e){return`https://api.bilibili.com/x/player/playurl?avid=${s.aid}&cid=${s.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/x/player/playurl?avid=${s.aid}&cid=${s.cid}&otype=json`}}}class o extends i{constructor(){super();this.menuClasses=["action","progress"];this.currentMenuClass="action"}async getUrl(){const e=await SpinQuery.select(()=>n[0]).catch(()=>logError("获取番剧下载链接失败."));return e}}class a extends i{async getUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=${s.aid}&cid=${s.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=${s.aid}&cid=${s.cid}&qn=&otype=json`}}}class r{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async downloadInfo(){const e=new c(this);await e.fetchVideoInfo();return e}static get availableFormats(){return new Promise((e,t)=>{s.entity.getUrl().then(s=>{const n=new XMLHttpRequest;n.addEventListener("load",()=>{const s=JSON.parse(n.responseText);if(s.code!==0){t("获取清晰度信息失败.")}const i=s.data||s.result||s;const o=i.accept_quality;const a=i.accept_format.split(",");const l=i.accept_description;const c=[];while(o.length>0){const e=new r(o.pop(),a.pop(),l.pop());c.push(e)}e(c)});n.addEventListener("error",()=>t(`获取清晰度信息失败.`));n.withCredentials=true;n.open("GET",s);n.send()})})}}class l{constructor(e,t,s,n){this.length=e;this.size=t;this.url=s;this.backupUrls=n}}class c{constructor(e,t){this.format=e;this.fragments=t||[];this.progress=null;this.loaded=0;this.totalSize=null;this.workingXhr=null;this.fragmentSplitFactor=6*5}fetchVideoInfo(){return new Promise((e,t)=>{s.entity.getUrl(this.format.quality).then(n=>{const i=new XMLHttpRequest;i.addEventListener("load",()=>{const n=JSON.parse(i.responseText.replace(/http:/g,"https:"));const o=n.data||n.result||n;if(!s.isOldBangumi&&o.quality!==this.format.quality){t("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const a=o.durl;this.fragments=a.map(e=>new l(e.length,e.size,e.url,e.backup_url));e(this.fragments)});i.withCredentials=true;i.open("GET",n);i.send()})})}cancelDownload(){if("forEach"in this.workingXhr){this.workingXhr.forEach(e=>e.abort())}else{logError("Cancel Download Failed: forEach in this.workingXhr not found.")}}downloadFragment(e){const t=[];this.workingXhr=[];const s=Math.round(e.size/this.fragmentSplitFactor);let n=0;while(n<e.size){const i=`bytes=${n}-${Math.min(e.size-1,Math.round(n+s))}`;t.push(new Promise((t,s)=>{let n=0;const o=new XMLHttpRequest;o.open("GET",e.url);o.responseType="arraybuffer";o.withCredentials=false;o.addEventListener("progress",e=>{this.loaded+=e.loaded-n;n=e.loaded;this.progress&&this.progress(this.loaded/this.totalSize)});o.addEventListener("load",()=>{if((""+o.status)[0]==="2"){t(o.response)}else{s(`请求失败.`)}});o.addEventListener("abort",()=>s("下载已取消."));o.addEventListener("error",()=>s(`下载失败.`));o.setRequestHeader("Range",i);o.send();this.workingXhr.push(o)}));n=Math.round(n+s)+1}return Promise.all(t)}copyUrl(){const e=this.fragments.map(e=>e.url).reduce((e,t)=>e+"\r\n"+t);GM_setClipboard(e,"text")}extension(e){return(e||this.fragments[0]).url.indexOf(".flv")!==-1?".flv":".mp4"}makeBlob(e,t=null){return new Blob(Array.isArray(e)?e:[e],{type:this.extension(t)===".flv"?"video/x-flv":"video/mp4"})}cleanUpOldBlobUrl(){const e=$("a#video-complete").attr("href");if(e&&$(`.link[href=${e}]`).length===0){URL.revokeObjectURL(e)}}downloadSingle(e){const[t]=e;const s=this.makeBlob(t);const n=document.title.replace("_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili","").replace("_番剧_bilibili_哔哩哔哩","")+this.extension();return[s,n]}async downloadMultiple(e){const t=new JSZip;const s=document.title.replace("_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili","").replace("_番剧_bilibili_哔哩哔哩","");if(e.length>1){e.forEach((e,n)=>{const i=this.fragments[n];t.file(`${s} - ${n+1}${this.extension(i)}`,this.makeBlob(e,i))})}else{const[n]=e;t.file(`${s}${this.extension()}`,this.makeBlob(n))}const n=await t.generateAsync({type:"blob"});const i=s+".zip";return[n,i]}async download(){const e=[];this.loaded=0;this.totalSize=this.fragments.map(e=>e.size).reduce((e,t)=>e+t);for(const t of this.fragments){const s=await this.downloadFragment(t);e.push(s)}if(e.length<1){throw new Error("下载失败.")}let t=null;let s=null;if(e.length===1){[t,s]=this.downloadSingle(e)}else{[t,s]=await this.downloadMultiple(e)}const n=URL.createObjectURL(t);this.cleanUpOldBlobUrl();this.progress&&this.progress(0);return{url:n,filename:s}}}async function d(){const e=await SpinQuery.select(()=>(unsafeWindow||window).aid);const t=await SpinQuery.select(()=>(unsafeWindow||window).cid);s.aid=e;s.cid=t;if(document.URL.indexOf("bangumi")!==-1){const e=await SpinQuery.select(()=>document.querySelector("#bofqi"));if(!e.classList.contains("stardust-player")){s.isOldBangumi=true;s.entity=new o;await SpinQuery.unsafeJquery();unsafeWindow.$(unsafeWindow.document).ajaxSend((e,t,s)=>{if(s.url.indexOf("https://bangumi.bilibili.com/player/web_api/v2/playurl")!==-1){n.unshift(s.url)}})}else{s.isStardustBangumi=true;s.entity=new a}}else{s.entity=new i}return e!==undefined&&t!==undefined}async function u(){await d();const e=await r.availableFormats;let[n]=e;const i=()=>n.downloadInfo().catch(e=>{s.entity.addError();$(".video-error").text(e)});async function o(){if(!n){return}s.entity.nextMenuClass();const e=await i();e.progress=(e=>{$(".download-progress-value").text(`${fixed(e*100)}`);$(".download-progress-foreground").css("transform",`scaleX(${e})`)});document.querySelector(".download-progress-cancel>span").onclick=(()=>e.cancelDownload());const t=await e.download().catch(e=>{s.entity.addError();$(".video-error").text(e)});if(!t){return}const o=document.getElementById("video-complete");o.setAttribute("href",t.url);o.setAttribute("download",t.filename);o.click();const a=`下载完成. <a class="link" href="${t.url}" download="${t.filename.replace(/"/g,"&quot;")}">再次保存</a>`;Toast.success(a,"下载视频");s.entity.resetMenuClass()}async function a(){if(!n){return}const e=await i();e.copyUrl();Toast.success("已复制链接到剪贴板.","复制链接",3e3);s.entity.resetMenuClass()}$(".video-action>#video-action-download").on("click",o);$(".video-action>#video-action-copy").on("click",a);e.forEach(e=>{$(`<li>${e.displayName}</li>`).on("click",()=>{n=e;s.entity.nextMenuClass()}).prependTo("ol.video-quality")});t.applyStyle("downloadVideoStyle");const l=document.querySelector(".download-video-panel");const c=()=>$(".download-video-panel").toggleClass("opened");$("#download-video").on("click",e=>{if(!l.contains(e.target)){c()}});$(".video-error").on("click",()=>{$(".video-error").text("");s.entity.removeError()});s.entity.addMenuClass()}return{widget:{content:t.data.downloadVideoDom.text,condition:d,success:u}}}})();