diff --git a/bilibili-evolved.offline.user.js b/bilibili-evolved.offline.user.js index c572fccbe..29300d9fd 100644 --- a/bilibili-evolved.offline.user.js +++ b/bilibili-evolved.offline.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name Bilibili Evolved (Offline) -// @version 442.18 +// @version 443.82 // @description Bilibili Evolved 的离线版, 所有功能都已内置于脚本中. // @author Grant Howard, Coulomb-G // @copyright 2019, Grant Howard (https://github.com/the1812) & Coulomb-G (https://github.com/Coulomb-G) @@ -1072,8 +1072,8 @@ offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/m offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-audio.min.js"] = (()=>{return(e,t)=>{class n{constructor(){this.sid=null;this.progress=null}async getDownloadUrl(){const e=`https://www.bilibili.com/audio/music-service-c/web/url?sid=${this.sid}&privilege=2&quality=2`;const t=await Ajax.getJsonWithCredentials(e);if(t.code===0){return t.data.cdns.shift()}else{logError("获取下载链接失败, 请确保当前账号有下载权限.","下载音频",1e4);return null}}async download(){const e=await this.getDownloadUrl();return new Promise((t,n)=>{const s=new XMLHttpRequest;s.open("GET",e);s.responseType="blob";s.addEventListener("load",()=>t(s.response));s.addEventListener("error",()=>n(s.status));s.addEventListener("progress",e=>this.progress&&this.progress(100*e.loaded/e.total));s.send()})}}const s="下载音频";return{export:n,widget:{content:`\n\n\n${s}\n\n`,condition:()=>document.URL.includes("bilibili.com/audio"),success:async()=>{await SpinQuery.select(()=>document.querySelector("#app"));const e=document.querySelector("#download-audio");const t=e.querySelector("span");const o=new n;o.progress=(e=>{t.innerHTML=`${Math.round(e)}%`});const i=document.querySelector("#download-audio-link");e.addEventListener("click",async e=>{if(o.sid===null||e.target===i){return}const n=await o.download();t.innerHTML=s;const r=i.getAttribute("href");if(r){URL.revokeObjectURL(r)}i.setAttribute("href",URL.createObjectURL(n));const d=(()=>{const e=document.querySelector(".song-title");if(e){return e.getAttribute("title")}else{return"神秘音频"}})();i.setAttribute("download",d+".m4a");i.click()});Observer.childList("#app",()=>{const t=document.URL.match(/bilibili\.com\/audio\/au([\d]+)/);if(t&&t[1]){e.disabled=false;o.sid=t[1]}else{e.disabled=true}})}}}}})(); offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-danmaku.min.js"] = (()=>{return(t,e)=>{const{getFriendlyTitle:n}=e.import("title");const{DanmakuInfo:i}=e.import("video-info");const{DanmakuConverter:a}=e.import("danmaku-converter");async function o(t){const e=n();let i={title:e};try{await loadDanmakuSettingsPanel();const t=t=>{const e=parseFloat(dq(t).style.transform.replace(/translateX\(([\d\.]+)/,"$1"));const n={0:0,44:1,94:2,144:3,188:4}[e];return n};i.font=dq(".bilibili-player-video-danmaku-setting-right-font .bui-select-result").innerText;i.alpha=parseFloat(dq(".bilibili-player-setting-opacity .bui-bar").style.transform.replace(/scaleX\(([\d\.]+)\)/,"$1"));i.duration=(()=>{const e=[18,14,10,8,6][t(".bilibili-player-setting-speedplus .bui-thumb")];return t=>{switch(t.type){case 4:case 5:return 4;default:return e}}})();i.blockTypes=(()=>{let t=[];const e={".bilibili-player-block-filter-type[ftype=scroll]":[1,2,3],".bilibili-player-block-filter-type[ftype=top]":[5],".bilibili-player-block-filter-type[ftype=bottom]":[4],".bilibili-player-block-filter-type[ftype=color]":["color"]};for(const[n,i]of Object.entries(e)){if(dq(n).classList.contains("disabled")){t=t.concat(i)}}return t.concat(7,8)})();const e=[1.4,1.2,1,.8,.6][t(".bilibili-player-setting-fontsize .bui-thumb")];i.resolution={x:1920*e,y:1080*e};i.bottomMarginPercent=[.75,.5,.25,0,0][t(".bilibili-player-setting-area .bui-thumb")];if(i.bottomMarginPercent===0&&dq(".bilibili-player-video-danmaku-setting-left-preventshade input").checked){i.bottomMarginPercent=.15}i.bold=dq(".bilibili-player-video-danmaku-setting-right-font-bold input").checked}catch(t){i={font:"微软雅黑",alpha:.6,duration:t=>{switch(t.type){case 4:case 5:return 4;default:return 6}},blockTypes:[7,8],resolution:{x:1920,y:1080},bottomMarginPercent:.15,bold:false}}const o=new a(i);const l=o.convertToAssDocument(t);return l.generateAss()}async function l(t,e){const a=n();const l=new i((unsafeWindow||window).cid);await l.fetchInfo();const r=await(async()=>{if(t===true){return new Blob([await o(l.rawXML)],{type:"text/plain"})}else{return new Blob([l.rawXML],{type:"text/plain"})}})();const s=URL.createObjectURL(r);const c=dq("#danmaku-link");const d=c.getAttribute("href");if(d){URL.revokeObjectURL(d)}clearTimeout(e);dq("#download-danmaku>span").innerHTML="下载弹幕";c.setAttribute("download",`${a}.${t?"ass":"xml"}`);c.setAttribute("href",s);c.click()}return{export:{downloadDanmaku:l,convertToAss:o},widget:{content:`\n\n\n下载弹幕\n\n`,condition:async()=>{let t=await SpinQuery.select(()=>(unsafeWindow||window).cid);return Boolean(t)},success:()=>{const t=document.querySelector("#danmaku-link");dq("#download-danmaku").addEventListener("click",e=>{if(e.target!==t){const t=setTimeout(()=>dq("#download-danmaku>span").innerHTML="请稍侯...",200);l(e.shiftKey,t)}})}}}}})(); offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.css"] = `.download-video{--download-video-background:white;--download-video-foreground:black;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) scale(.95);background:var(--download-video-background);color:var(--download-video-foreground);box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;z-index:10001;pointer-events:none;opacity:0;padding:16px;width:300px;display:flex;flex-direction:column;align-items:stretch;justify-content:space-between;max-height:90%}.download-video,.download-video *{transition:.2s ease-out;-webkit-tap-highlight-color:transparent!important}.download-video.opened{pointer-events:initial;opacity:1;transform:translate(-50%,-50%) scale(1)}body.dark .download-video{--download-video-background:#222;--download-video-foreground:#eee}.round-corner .download-video,.round-corner .download-video .episode-list .v-checkbox,.round-corner .download-video .rpc-settings{border-radius:var(--large-corner-radius)}.round-corner .download-video .button,.round-corner .download-video .cover,.round-corner .download-video .direct-download .progress .background,.round-corner .download-video .direct-download .progress .foreground,.round-corner .download-video .rpc-settings input[type=text],.round-corner .download-video .title,.round-corner .download-video button{border-radius:var(--corner-radius)}.download-video .cover{width:240px;min-height:135px;background-color:#8882}.download-video .header{display:flex;justify-content:space-between;align-items:center}.download-video h1{font-size:16pt;font-weight:700;color:var(--download-video-foreground)}.download-video .header .mdi-close{font-size:16pt;color:var(--download-video-foreground);cursor:pointer}.download-video .header .mdi-close:hover{color:var(--theme-color)}.download-video .info{display:flex;flex-direction:column;align-items:center;margin-top:12px}.download-video .title{display:flex;flex-direction:column;justify-content:space-between;align-items:stretch;margin-top:12px}.download-video .title .bold{width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center}.download-video .title .size{font-size:10pt;color:var(--download-video-foreground);align-self:center;display:flex;align-items:center;height:24px}.download-video .bold{color:var(--download-video-foreground);font-weight:700;font-size:12pt}.download-video .option-item{display:flex;justify-content:space-between;align-items:center;padding-top:12px;font-size:10pt}.download-video .tabs{display:flex;padding-top:12px;font-size:10pt}.download-video .tabs .tab{padding:6px 12px;border-bottom:2px solid transparent;cursor:pointer}.download-video .tabs .tab.active{color:var(--theme-color);border-bottom-color:var(--theme-color)}.download-video .separator{height:1px;background:#8884;width:100%;margin:12px 0;flex-shrink:0}.download-video h2{font-size:12pt;font-weight:700;color:var(--download-video-foreground)}.download-video .direct-download{display:flex;flex-direction:column;align-items:start;justify-content:space-between}.download-video .button,.download-video button{border:none;outline:0!important;padding:6px 16px;font-size:10pt;cursor:pointer;background:#8884;color:var(--download-video-foreground)}.download-video .button:hover,.download-video button:hover{background:#8886}.download-video .primary{background:var(--theme-color);color:var(--foreground-color);align-self:center}.download-video .primary:hover{background:var(--theme-color-90)}.download-video .busy .button,.download-video .busy .primary,.download-video .busy button,.download-video button:disabled{background-color:#0001;color:#8884;cursor:not-allowed}.download-video .busy .button,.download-video .busy button{cursor:wait!important}.download-video .direct-download .progress{align-self:stretch;display:flex;align-items:center;margin-top:8px}.download-video .direct-download .progress .background{flex-grow:1;height:4px;background:#8884;margin-right:4px;position:relative}.download-video .direct-download .progress .background .foreground{position:absolute;top:0;left:0;height:4px;background:linear-gradient(to right,var(--theme-color),var(--theme-color-60))}.download-video .direct-download .progress .percent{width:50px;text-align:right}.download-video .actions{display:flex;flex-wrap:wrap;justify-content:space-between;margin:8px -4px 0}.download-video .actions>*{margin:4px;flex-grow:1;flex-basis:calc(50% - 16px);display:flex;justify-content:center;align-items:center}.download-video i.mdi-alert-circle{font-size:14pt;color:#ff9800}.download-video .size-warning{position:relative;padding:0 8px}.download-video .size-warning-tip{position:absolute;bottom:100%;left:50%;transform:translateX(-50%);background:var(--download-video-background);color:var(--download-video-foreground);border:1px dashed var(--theme-color);z-index:2;font-size:10pt;width:200px;padding:8px;box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;pointer-events:none;opacity:0}.download-video .size-warning:hover .size-warning-tip{opacity:1}.download-video .episode-list .v-checkbox{padding:4px 8px;margin:4px 0;position:relative;background-color:transparent}.download-video .episode-list .v-checkbox.checked{background-color:#8882}.download-video .episode-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.download-video .episode-list{overflow:auto;max-height:320px}.download-video button.list-tool{padding:4px;line-height:1;font-size:12pt;margin:0 4px}.download-video .episode-header h2{flex-grow:1}.download-video .selected-count{width:64px;text-align:right}.download-video .aria2-rpc .mdi{margin-left:8px;font-size:14pt;line-height:1}.download-video .aria2-rpc{position:relative}.download-video .rpc-settings{position:absolute;display:flex;flex-direction:column;align-items:stretch;color:var(--download-video-foreground);background:#fff;bottom:120%;left:50%;cursor:initial;box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;transform-origin:bottom;transform:translateX(-50%) scale(.9);opacity:0;pointer-events:none;padding:8px;z-index:3;transition:.3s cubic-bezier(.18,.89,.32,1.28);width:100%}body.dark .download-video .rpc-settings{background:#282828}.download-video .rpc-settings>*{margin:8px}.download-video .rpc-settings.show{opacity:1;transform:translateX(-50%) scale(1);pointer-events:initial}.download-video .rpc-settings input[type=text]{background:0 0;color:var(--download-video-foreground);border:1px solid #8884;padding:4px;margin-left:8px}.download-video .rpc-settings input[type=text]:focus{border-color:var(--theme-color)}.download-video .rpc-settings .operations{display:flex;align-items:center;justify-content:space-evenly}.download-video .rpc-settings-item{display:flex;align-items:center;justify-content:space-between;white-space:nowrap}.download-video .rpc-settings-item input[type=text]{flex-grow:1;margin-left:24px}.download-video button.cancel-download,.download-video button.start-download{margin-top:8px}.download-video .direct-download-header{align-self:stretch;display:flex;justify-content:space-between;align-items:center}body.dark .download-video .direct-download .progress .foreground{background:linear-gradient(to right,var(--theme-color-70),var(--theme-color))}`; -offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.html"] = `

下载视频

单个视频
批量导出
警告: 过大的视频大小会在直接下载时占用大量内存, 并可能导致浏览器标签页崩溃. 请考虑降低清晰度或使用导出选项.
预计大小: {{displaySize}}
格式
清晰度
弹幕

直接下载

{{speed}}
{{progressPercent}}%

选集

{{selectedEpisodeCount}}/{{episodeList.length}}

导出

{{showRpcSettings ? '取消' : 'aria2 RPC'}}

aria2 RPC

配置

主机
端口
密钥
限速
默认路径
路径
最终路径: {{rpcSettings.baseDir + rpcSettings.dir}}
方法
开始下载
{{saveRpcSettingsText}}
`; -offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.js"] = (()=>{return(e,t)=>{const{getFriendlyTitle:s}=t.import("title");const{VideoInfo:a,DanmakuInfo:i}=t.import("video-info");class n{constructor(){this.menuClasses=["quality","action","progress"];[this.currentMenuClass]=this.menuClasses}get menuPanel(){return document.querySelector(".download-video-panel")}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}closeMenu(){this.menuPanel.classList.remove("opened");setTimeout(()=>this.resetMenuClass(),200)}addError(){this.menuPanel.classList.add("error")}removeError(){this.menuPanel.classList.remove("error");this.resetMenuClass()}async getDashUrl(e){throw new Error("video dash is not supported")}async getUrl(e){if(e){return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&otype=json`}}}class o extends n{async getDashUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&qn=${e}&otype=json&fourk=1&fnval=16`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&otype=json&fourk=1&fnval=16`}}async getUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=&otype=json`}}}const r={entity:new n,aid:"",cid:""};let l=[];let c=null;class d{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async downloadInfo(e=false){const t=new h(this);await t.fetchVideoInfo(e);return t}static parseFormats(e){const t=e.accept_quality;const s=e.accept_format.split(",");const a=e.accept_description;const i=t.map((e,t)=>{return new d(e,s[t],a[t])});return i}static async getAvailableDashFormats(){const e=await r.entity.getDashUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}return d.parseFormats(t.result)}static async getAvailableFormats(){const e=await r.entity.getUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}const s=t.data||t.result||t;return d.parseFormats(s)}}class h{constructor(e,t){this.fragmentSplitFactor=6*2;this.workingXhr=null;this.progressMap=new Map;this.format=e;this.fragments=t||[];this.videoSpeed=new u(this)}get totalSize(){return this.fragments.map(e=>e.size).reduce((e,t)=>e+t)}async fetchVideoInfo(e=false){if(!e){const e=await r.entity.getUrl(this.format.quality);const t=await Ajax.getTextWithCredentials(e);const s=JSON.parse(t.replace(/http:/g,"https:"));const a=s.data||s.result||s;if(a.quality!==this.format.quality){throw new Error("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const i=a.durl;this.fragments=i.map(e=>{return{length:e.length,size:e.size,url:e.url,backupUrls:e.backup_url}})}else{const{dashToFragment:e,getDashInfo:s}=await t.importAsync("video-dash");const a=await s(r.aid,r.cid,this.format.quality);const i=a.videoDashes.sort(descendingSort(e=>e.bandWidth))[0];const n=a.audioDashes.sort(descendingSort(e=>e.bandWidth))[0];this.fragments=[e(i),e(n)]}return this.fragments}updateProgress(){const e=this.progressMap?[...this.progressMap.values()].reduce((e,t)=>e+t,0)/this.totalSize:0;if(e>1||e<0){console.error(`[下载视频] 进度异常: ${e}`,this.progressMap.values())}this.progress&&this.progress(e)}cancelDownload(){this.videoSpeed.stopMeasure();if(this.workingXhr!==null){this.workingXhr.forEach(e=>e.abort())}else{logError("Cancel Download Failed: forEach in this.workingXhr not found.")}}downloadFragment(e){const t=[];this.workingXhr=[];this.progressMap=new Map;this.updateProgress();let s;if(e.size<=96*1024*1024){s=e.size/this.fragmentSplitFactor}else{s=16*1024*1024}let a=0;const i=e=>[...this.progressMap.keys()].indexOf(e)+1;while(a{const a=new XMLHttpRequest;a.open("GET",e.url);a.responseType="arraybuffer";a.withCredentials=false;a.addEventListener("progress",e=>{console.log(`[下载视频] 视频片段${i(a)}下载进度: ${e.loaded}/${r} bytes loaded, ${o}`);this.progressMap.set(a,e.loaded);this.updateProgress()});a.addEventListener("load",()=>{if((""+a.status)[0]==="2"){console.log(`[下载视频] 视频片段${i(a)}下载完成`);t(a.response)}else{s(`视频片段${i(a)}请求失败, response = ${a.status}`)}});a.addEventListener("abort",()=>s("canceled"));a.addEventListener("error",()=>{console.error(`[下载视频] 视频片段${i(a)}下载失败: ${o}`);this.progressMap.set(a,0);this.updateProgress();a.open("GET",e.url);a.setRequestHeader("Range",o);a.send()});a.setRequestHeader("Range",o);this.progressMap.set(a,0);a.send();this.workingXhr.push(a)}));a=Math.round(a+s)+1}return Promise.all(t)}async copyUrl(){const e=this.fragments.map(e=>e.url).reduce((e,t)=>e+"\r\n"+t);GM_setClipboard(e,"text")}static downloadBlob(e,t){const s=document.createElement("a");let a;if(typeof e==="string"){a=e}else{a=URL.createObjectURL(e)}s.setAttribute("href",a);s.setAttribute("download",t);document.body.appendChild(s);s.click();s.remove();URL.revokeObjectURL(a)}async exportData(e=false){const t=JSON.stringify([{fragments:this.fragments,title:s(),totalSize:this.fragments.map(e=>e.size).reduce((e,t)=>e+t),referer:document.URL.replace(window.location.search,"")}]);if(e){GM_setClipboard(t,"text")}else{const e=new Blob([t],{type:"text/json"});const a=await this.downloadDanmaku();if(a!==null){const t=new JSZip;t.file(`${s()}.json`,e);t.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await t.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(e,`${s()}.json`)}}}async exportAria2(a=false){if(a){const a=await this.downloadDanmaku();if(a!==null){h.downloadBlob(new Blob([a]),`${s()}.${this.danmakuOption==="ASS"?"ass":"xml"}`)}const i=e.aria2RpcOption;const n=this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}const n=[];if(i.secretKey!==""){n.push(`token:${i.secretKey}`)}n.push([e.url]);n.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${s()}${a}${this.extension(e)}`,split:this.fragmentSplitFactor,dir:i.baseDir+i.dir||undefined,"max-download-limit":i.maxDownloadLimit||undefined});const o=encodeURIComponent(`${s()}${a}`);return{params:n,id:o}});const{sendRpc:o}=await t.importAsync("aria2-rpc");await o(n)}else{const e=`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}return`\n${e.url}\nreferer=${document.URL.replace(window.location.search,"")}\nuser-agent=${UserAgent}\nout=${s()}${a}${this.extension(e)}\nsplit=${this.fragmentSplitFactor}\n`.trim()}).join("\n")}\n`.trim();const t=new Blob([e],{type:"text/plain"});const a=await this.downloadDanmaku();if(a!==null){const e=new JSZip;e.file(`${s()}.txt`,t);e.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await e.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(t,`${s()}.txt`)}}}extension(e){const t=e||this.fragments[0];const s=[".flv",".mp4",".m4s"].find(e=>t.url.includes(e));if(s){return s}else{console.warn("No extension detected.");return".flv"}}makeBlob(e,t){return new Blob(Array.isArray(e)?e:[e],{type:this.extension(t)===".flv"?"video/x-flv":"video/mp4"})}cleanUpOldBlobUrl(){const e=dq("a#video-complete").getAttribute("href");if(e&&!dq(`.link[href="${e}"]`)){URL.revokeObjectURL(e)}dqa(".toast-card-header").filter(e=>e.innerText.includes("下载视频")).forEach(e=>e.querySelector(".toast-card-dismiss").click())}async downloadDanmaku(){if(this.danmakuOption!=="无"){const e=new i(r.cid);await e.fetchInfo();if(this.danmakuOption==="XML"){return e.rawXML}else{const{convertToAss:s}=await t.importAsync("download-danmaku");return s(e.rawXML)}}else{return null}}async downloadSingle(e){const t=await this.downloadDanmaku();const[a]=e;if(t===null){const e=this.makeBlob(a);const t=s()+this.extension();return{blob:e,filename:t}}else{const e=new JSZip;e.file(s()+this.extension(),this.makeBlob(a));e.file(s()+"."+this.danmakuOption.toLowerCase(),t);const i=await e.generateAsync({type:"blob"});const n=s()+".zip";return{blob:i,filename:n}}}async downloadMultiple(e){const t=new JSZip;const a=s();if(e.length>1){e.forEach((e,s)=>{const i=this.fragments[s];t.file(`${a} - ${s+1}${this.extension(i)}`,this.makeBlob(e,i))})}else{const[s]=e;t.file(`${a}${this.extension()}`,this.makeBlob(s))}const i=await this.downloadDanmaku();if(i!==null){t.file(s()+"."+this.danmakuOption.toLowerCase(),i)}const n=await t.generateAsync({type:"blob"});const o=a+".zip";return{blob:n,filename:o}}async download(){const e=[];this.videoSpeed.startMeasure();for(const t of this.fragments){const s=await this.downloadFragment(t);e.push(s)}if(e.length<1){throw new Error("下载失败.")}let{blob:t,filename:s}=await(async()=>{if(e.length===1){return await this.downloadSingle(e)}else{return await this.downloadMultiple(e)}})();this.cleanUpOldBlobUrl();const a=URL.createObjectURL(t);this.progress&&this.progress(0);this.videoSpeed.stopMeasure();return{url:a,filename:s}}}class u{constructor(e){this.lastProgress=0;this.measureInterval=1e3;this.workingDownloader=e}startMeasure(){this.intervalTimer=setInterval(()=>{const e=this.workingDownloader.progressMap?[...this.workingDownloader.progressMap.values()].reduce((e,t)=>e+t,0):0;const t=e-this.lastProgress;if(this.speedUpdate!==undefined){this.speedUpdate(formatFileSize(t)+"/s")}this.lastProgress=e},this.measureInterval)}stopMeasure(){clearInterval(this.intervalTimer)}}async function p(){const e=await SpinQuery.select(()=>(unsafeWindow||window).aid);const t=await SpinQuery.select(()=>(unsafeWindow||window).cid);if(!(e&&t)){return false}r.aid=e;r.cid=t;if(document.URL.indexOf("bangumi")!==-1){r.entity=new o}else{r.entity=new n}try{l=await d.getAvailableFormats()}catch(e){return false}return true}async function f(){c=l[0];t.applyStyle("downloadVideoStyle");dq("#download-video").addEventListener("click",()=>{dq(".download-video").classList.toggle("opened");dq(".gui-settings-mask").click()});dq("#download-video").addEventListener("mouseover",()=>{document.body.insertAdjacentHTML("beforeend",t.import("downloadVideoHtml"));m()},{once:true})}async function m(){let n;const o=new Map;const u=new Vue({el:".download-video",components:{VDropdown:()=>t.importAsync("v-dropdown.vue"),VCheckbox:()=>t.importAsync("v-checkbox.vue"),RpcProfiles:()=>t.importAsync("aria2-rpc-profiles.vue")},data:{downloadSingle:true,coverUrl:EmptyImageUrl,aid:r.aid,cid:r.cid,dashModel:{value:e.downloadVideoFormat,items:["flv","dash"]},qualityModel:{value:c.displayName,items:l.map(e=>e.displayName)},danmakuModel:{value:e.downloadVideoDefaultDanmaku,items:["无","XML","ASS"]},progressPercent:0,size:"获取大小中",blobUrl:"",episodeList:[],downloading:false,speed:"",batch:false,rpcSettings:e.aria2RpcOption,showRpcSettings:false,busy:false,saveRpcSettingsText:"保存配置",enableDash:e.enableDashDownload},computed:{displaySize(){if(typeof this.size==="string"){return this.size}return formatFileSize(this.size)},sizeWarning(){if(typeof this.size==="string"){return false}return this.size>1073741824},selectedEpisodeCount(){return this.episodeList.filter(e=>e.checked).length},dash(){return this.dashModel.value==="dash"}},methods:{close(){this.$el.classList.remove("opened")},danmakuOptionChange(){e.downloadVideoDefaultDanmaku=this.danmakuModel.value},async dashChange(){const e=this.dashModel.value;let t=[];if(e==="flv"){t=await d.getAvailableFormats()}else{t=await d.getAvailableDashFormats()}l=t;[c]=e;this.qualityModel.items=t.map(e=>e.displayName);[this.qualityModel.value]=this.qualityModel.items},async formatChange(){const e=this.getFormat();const t=o.get(e);if(t){this.size=t;return}try{this.size="获取大小中";const t=await e.downloadInfo(this.dash);this.size=t.totalSize;o.set(e,this.size)}catch(e){this.size="获取大小失败"}},getFormat(){const e=l.find(e=>e.displayName===this.qualityModel.value);if(!e){console.error(`No format found. model value = ${this.qualityModel.value}`);return null}return e},async exportData(e){if(this.busy===true){return}try{this.busy=true;if(!this.downloadSingle){await this.exportBatchData(e);return}const t=this.getFormat();const s=await t.downloadInfo(this.dash);s.danmakuOption=this.danmakuModel.value;switch(e){case"copyLink":await s.copyUrl();Toast.success("已复制链接到剪贴板.","下载视频",3e3);break;case"aria2":await s.exportAria2(false);break;case"aria2RPC":await s.exportAria2(true);break;case"copyVLD":await s.exportData(true);Toast.success("已复制VLD数据到剪贴板.","下载视频",3e3);break;case"exportVLD":await s.exportData(false);break;default:break}}catch(e){logError(e)}finally{this.busy=false}},async exportBatchData(e){const a=this.episodeList;if(a.every(e=>e.checked===false)){Toast.info("请至少选择1集或以上的数量!","批量导出",3e3);return}const n=e=>{const t=a.find(t=>t.cid===e.cid);if(t===undefined){return false}return t.checked};const o=this.getFormat();if(this.danmakuModel.value!=="无"){const e=Toast.info("下载弹幕中...","批量导出");const s=new JSZip;try{if(this.danmakuModel.value==="XML"){for(const e of a.filter(n)){const t=new i(e.cid);await t.fetchInfo();s.file(e.title+".xml",t.rawXML)}}else{const{convertToAss:e}=await t.importAsync("download-danmaku");for(const t of a.filter(n)){const a=new i(t.cid);await a.fetchInfo();s.file(t.title+".ass",await e(a.rawXML))}}h.downloadBlob(await s.generateAsync({type:"blob"}),this.cid+".danmakus.zip")}catch(e){logError(`弹幕下载失败`)}finally{e.dismiss()}}const r=Toast.info("获取链接中...","批量导出");this.batchExtractor.itemFilter=n;let l;try{switch(e){case"aria2":l=await this.batchExtractor.collectAria2(o,r);h.downloadBlob(new Blob([l],{type:"text/plain"}),s(false)+".txt");return;case"aria2RPC":await this.batchExtractor.collectAria2(o,r,true);Toast.success(`成功发送了批量请求.`,"aria2 RPC",3e3);return;case"copyVLD":GM_setClipboard(await this.batchExtractor.collectData(o,r),{mimetype:"text/plain"});Toast.success("已复制批量vld数据到剪贴板.","批量导出",3e3);return;case"exportVLD":l=await this.batchExtractor.collectData(o,r);h.downloadBlob(new Blob([l],{type:"text/json"}),s(false)+".json");return;default:return}}catch(e){logError(e)}finally{r.dismiss()}},async checkBatch(){const e=["/www.bilibili.com/bangumi","/www.bilibili.com/video/av"];if(!e.some(e=>document.URL.includes(e))){this.batch=false;this.episodeList=[];return}const{BatchExtractor:s}=await t.importAsync("batch-download");if(await s.test()!==true){this.batch=false;this.episodeList=[];return}this.batchExtractor=new s;this.batch=true;this.episodeList=(await this.batchExtractor.getItemList()).map((e,t)=>{return{aid:e.aid,cid:e.cid,title:e.title,index:t,checked:true}})},cancelDownload(){if(n){n.cancelDownload()}},async startDownload(){const e=this.getFormat();try{this.downloading=true;const t=await e.downloadInfo(this.dash);t.videoSpeed.speedUpdate=(e=>this.speed=e);t.danmakuOption=this.danmakuModel.value;t.progress=(e=>{this.progressPercent=Math.trunc(e*100)});n=t;const s=await t.download();const a=document.getElementById("video-complete");a.setAttribute("href",s.url);a.setAttribute("download",s.filename);a.click();Toast.success(`下载完成: ${s.filename} 再次保存`,"下载视频")}catch(e){if(e!=="canceled"){logError(e)}this.progressPercent=0}finally{this.downloading=false;this.speed=""}},selectAllEpisodes(){this.episodeList.forEach(e=>e.checked=true)},unselectAllEpisodes(){this.episodeList.forEach(e=>e.checked=false)},inverseAllEpisodes(){this.episodeList.forEach(e=>e.checked=!e.checked)},toggleRpcSettings(){this.showRpcSettings=!this.showRpcSettings},saveRpcSettings(){if(this.rpcSettings.host===""){this.rpcSettings.host="127.0.0.1"}if(this.rpcSettings.port===""){this.rpcSettings.port="6800"}e.aria2RpcOption=this.rpcSettings;const t=e.aria2RpcOptionProfiles.find(t=>t.name===e.aria2RpcOptionSelectedProfile);if(t){Object.assign(t,this.rpcSettings);e.aria2RpcOptionProfiles=e.aria2RpcOptionProfiles}this.saveRpcSettingsText="已保存";setTimeout(()=>this.saveRpcSettingsText="保存配置",2e3)},updateProfile(t){e.aria2RpcOption=this.rpcSettings=_.omit(t,"name")}}});Observer.videoChange(async()=>{u.close();u.batch=false;u.downloadSingle=true;const e=dq("#download-video");const t=await p();e.style.display=t?"flex":"none";if(!t){return}u.aid=r.aid;u.cid=r.cid;const s=new a(r.aid);await s.fetchInfo();u.coverUrl=s.coverUrl.replace("http:","https:");l=await d.getAvailableFormats();[c]=l;u.qualityModel={value:c.displayName,items:l.map(e=>e.displayName)};u.formatChange();await u.checkBatch()})}return{widget:{content:`\n`,condition:p,success:f}}}})(); +offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.html"] = `

下载视频

单个视频
批量导出
警告: 过大的视频大小会在直接下载时占用大量内存, 并可能导致浏览器标签页崩溃. 请考虑降低清晰度或使用导出选项.
预计大小: {{displaySize}}
格式
清晰度
弹幕

直接下载

{{speed}}
{{progressPercent}}%

选集

{{selectedEpisodeCount}}/{{episodeList.length}}

导出

{{showRpcSettings ? '取消' : 'aria2 RPC'}}

aria2 RPC

配置

主机
端口
密钥
限速
默认路径
路径
最终路径: {{rpcSettings.baseDir + rpcSettings.dir}}
方法
开始下载
{{saveRpcSettingsText}}
`; +offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.js"] = (()=>{return(e,t)=>{const{getFriendlyTitle:s}=t.import("title");const{VideoInfo:a,DanmakuInfo:i}=t.import("video-info");class n{constructor(){this.menuClasses=["quality","action","progress"];[this.currentMenuClass]=this.menuClasses}get menuPanel(){return document.querySelector(".download-video-panel")}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}closeMenu(){this.menuPanel.classList.remove("opened");setTimeout(()=>this.resetMenuClass(),200)}addError(){this.menuPanel.classList.add("error")}removeError(){this.menuPanel.classList.remove("error");this.resetMenuClass()}async getDashUrl(e){throw new Error("video dash is not supported")}async getUrl(e){if(e){return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&otype=json`}}}class o extends n{async getDashUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&qn=${e}&otype=json&fourk=1&fnval=16`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&otype=json&fourk=1&fnval=16`}}async getUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=&otype=json`}}}const r={entity:new n,aid:"",cid:""};let l=[];let c=null;class d{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async downloadInfo(e=false){const t=new h(this);await t.fetchVideoInfo(e);return t}static parseFormats(e){const t=e.accept_quality;const s=e.accept_format.split(",");const a=e.accept_description;const i=t.map((e,t)=>{return new d(e,s[t],a[t])});return i}static async getAvailableDashFormats(){const e=await r.entity.getDashUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}return d.parseFormats(t.result)}static async getAvailableFormats(){const e=await r.entity.getUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}const s=t.data||t.result||t;return d.parseFormats(s)}}class h{constructor(e,t){this.fragmentSplitFactor=6*2;this.workingXhr=null;this.progressMap=new Map;this.format=e;this.fragments=t||[];this.videoSpeed=new u(this)}get totalSize(){return this.fragments.map(e=>e.size).reduce((e,t)=>e+t)}async fetchVideoInfo(e=false){if(!e){const e=await r.entity.getUrl(this.format.quality);const t=await Ajax.getTextWithCredentials(e);const s=JSON.parse(t.replace(/http:/g,"https:"));const a=s.data||s.result||s;if(a.quality!==this.format.quality){throw new Error("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const i=a.durl;this.fragments=i.map(e=>{return{length:e.length,size:e.size,url:e.url,backupUrls:e.backup_url}})}else{const{dashToFragment:e,getDashInfo:s}=await t.importAsync("video-dash");const a=await s(r.aid,r.cid,this.format.quality);const i=a.videoDashes.sort(descendingSort(e=>e.bandWidth))[0];const n=a.audioDashes.sort(descendingSort(e=>e.bandWidth))[0];this.fragments=[e(i),e(n)]}return this.fragments}updateProgress(){const e=this.progressMap?[...this.progressMap.values()].reduce((e,t)=>e+t,0)/this.totalSize:0;if(e>1||e<0){console.error(`[下载视频] 进度异常: ${e}`,this.progressMap.values())}this.progress&&this.progress(e)}cancelDownload(){this.videoSpeed.stopMeasure();if(this.workingXhr!==null){this.workingXhr.forEach(e=>e.abort())}else{logError("Cancel Download Failed: forEach in this.workingXhr not found.")}}downloadFragment(e){const t=[];this.workingXhr=[];this.progressMap=new Map;this.updateProgress();let s;if(e.size<=96*1024*1024){s=e.size/this.fragmentSplitFactor}else{s=16*1024*1024}let a=0;const i=e=>[...this.progressMap.keys()].indexOf(e)+1;while(a{const a=new XMLHttpRequest;a.open("GET",e.url);a.responseType="arraybuffer";a.withCredentials=false;a.addEventListener("progress",e=>{console.log(`[下载视频] 视频片段${i(a)}下载进度: ${e.loaded}/${r} bytes loaded, ${o}`);this.progressMap.set(a,e.loaded);this.updateProgress()});a.addEventListener("load",()=>{if((""+a.status)[0]==="2"){console.log(`[下载视频] 视频片段${i(a)}下载完成`);t(a.response)}else{s(`视频片段${i(a)}请求失败, response = ${a.status}`)}});a.addEventListener("abort",()=>s("canceled"));a.addEventListener("error",()=>{console.error(`[下载视频] 视频片段${i(a)}下载失败: ${o}`);this.progressMap.set(a,0);this.updateProgress();a.open("GET",e.url);a.setRequestHeader("Range",o);a.send()});a.setRequestHeader("Range",o);this.progressMap.set(a,0);a.send();this.workingXhr.push(a)}));a=Math.round(a+s)+1}return Promise.all(t)}async copyUrl(){const e=this.fragments.map(e=>e.url).reduce((e,t)=>e+"\r\n"+t);GM_setClipboard(e,"text")}async showUrl(){const e=this.fragments.map(e=>`\n${e.url}\n`).reduce((e,t)=>e+"\r\n"+t);Toast.success(e,"显示链接")}static downloadBlob(e,t){const s=document.createElement("a");let a;if(typeof e==="string"){a=e}else{a=URL.createObjectURL(e)}s.setAttribute("href",a);s.setAttribute("download",t);document.body.appendChild(s);s.click();s.remove();URL.revokeObjectURL(a)}async exportData(e=false){const t=JSON.stringify([{fragments:this.fragments,title:s(),totalSize:this.fragments.map(e=>e.size).reduce((e,t)=>e+t),referer:document.URL.replace(window.location.search,"")}]);if(e){GM_setClipboard(t,"text")}else{const e=new Blob([t],{type:"text/json"});const a=await this.downloadDanmaku();if(a!==null){const t=new JSZip;t.file(`${s()}.json`,e);t.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await t.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(e,`${s()}.json`)}}}async exportAria2(a=false){if(a){const a=await this.downloadDanmaku();if(a!==null){h.downloadBlob(new Blob([a]),`${s()}.${this.danmakuOption==="ASS"?"ass":"xml"}`)}const i=e.aria2RpcOption;const n=this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}const n=[];if(i.secretKey!==""){n.push(`token:${i.secretKey}`)}n.push([e.url]);n.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${s()}${a}${this.extension(e)}`,split:this.fragmentSplitFactor,dir:i.baseDir+i.dir||undefined,"max-download-limit":i.maxDownloadLimit||undefined});const o=encodeURIComponent(`${s()}${a}`);return{params:n,id:o}});const{sendRpc:o}=await t.importAsync("aria2-rpc");await o(n)}else{const e=`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}return`\n${e.url}\nreferer=${document.URL.replace(window.location.search,"")}\nuser-agent=${UserAgent}\nout=${s()}${a}${this.extension(e)}\nsplit=${this.fragmentSplitFactor}\n`.trim()}).join("\n")}\n`.trim();const t=new Blob([e],{type:"text/plain"});const a=await this.downloadDanmaku();if(a!==null){const e=new JSZip;e.file(`${s()}.txt`,t);e.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await e.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(t,`${s()}.txt`)}}}extension(e){const t=e||this.fragments[0];const s=[".flv",".mp4",".m4s"].find(e=>t.url.includes(e));if(s){return s}else{console.warn("No extension detected.");return".flv"}}makeBlob(e,t){return new Blob(Array.isArray(e)?e:[e],{type:this.extension(t)===".flv"?"video/x-flv":"video/mp4"})}cleanUpOldBlobUrl(){const e=dq("a#video-complete").getAttribute("href");if(e&&!dq(`.link[href="${e}"]`)){URL.revokeObjectURL(e)}dqa(".toast-card-header").filter(e=>e.innerText.includes("下载视频")).forEach(e=>e.querySelector(".toast-card-dismiss").click())}async downloadDanmaku(){if(this.danmakuOption!=="无"){const e=new i(r.cid);await e.fetchInfo();if(this.danmakuOption==="XML"){return e.rawXML}else{const{convertToAss:s}=await t.importAsync("download-danmaku");return s(e.rawXML)}}else{return null}}async downloadSingle(e){const t=await this.downloadDanmaku();const[a]=e;if(t===null){const e=this.makeBlob(a);const t=s()+this.extension();return{blob:e,filename:t}}else{const e=new JSZip;e.file(s()+this.extension(),this.makeBlob(a));e.file(s()+"."+this.danmakuOption.toLowerCase(),t);const i=await e.generateAsync({type:"blob"});const n=s()+".zip";return{blob:i,filename:n}}}async downloadMultiple(e){const t=new JSZip;const a=s();if(e.length>1){e.forEach((e,s)=>{const i=this.fragments[s];t.file(`${a} - ${s+1}${this.extension(i)}`,this.makeBlob(e,i))})}else{const[s]=e;t.file(`${a}${this.extension()}`,this.makeBlob(s))}const i=await this.downloadDanmaku();if(i!==null){t.file(s()+"."+this.danmakuOption.toLowerCase(),i)}const n=await t.generateAsync({type:"blob"});const o=a+".zip";return{blob:n,filename:o}}async download(){const e=[];this.videoSpeed.startMeasure();for(const t of this.fragments){const s=await this.downloadFragment(t);e.push(s)}if(e.length<1){throw new Error("下载失败.")}let{blob:t,filename:s}=await(async()=>{if(e.length===1){return await this.downloadSingle(e)}else{return await this.downloadMultiple(e)}})();this.cleanUpOldBlobUrl();const a=URL.createObjectURL(t);this.progress&&this.progress(0);this.videoSpeed.stopMeasure();return{url:a,filename:s}}}class u{constructor(e){this.lastProgress=0;this.measureInterval=1e3;this.workingDownloader=e}startMeasure(){this.intervalTimer=setInterval(()=>{const e=this.workingDownloader.progressMap?[...this.workingDownloader.progressMap.values()].reduce((e,t)=>e+t,0):0;const t=e-this.lastProgress;if(this.speedUpdate!==undefined){this.speedUpdate(formatFileSize(t)+"/s")}this.lastProgress=e},this.measureInterval)}stopMeasure(){clearInterval(this.intervalTimer)}}async function p(){const e=await SpinQuery.select(()=>(unsafeWindow||window).aid);const t=await SpinQuery.select(()=>(unsafeWindow||window).cid);if(!(e&&t)){return false}r.aid=e;r.cid=t;if(document.URL.indexOf("bangumi")!==-1){r.entity=new o}else{r.entity=new n}try{l=await d.getAvailableFormats()}catch(e){return false}return true}async function f(){c=l[0];t.applyStyle("downloadVideoStyle");dq("#download-video").addEventListener("click",()=>{dq(".download-video").classList.toggle("opened");dq(".gui-settings-mask").click()});dq("#download-video").addEventListener("mouseover",()=>{document.body.insertAdjacentHTML("beforeend",t.import("downloadVideoHtml"));m()},{once:true})}async function m(){let n;const o=new Map;const u=new Vue({el:".download-video",components:{VDropdown:()=>t.importAsync("v-dropdown.vue"),VCheckbox:()=>t.importAsync("v-checkbox.vue"),RpcProfiles:()=>t.importAsync("aria2-rpc-profiles.vue")},data:{downloadSingle:true,coverUrl:EmptyImageUrl,aid:r.aid,cid:r.cid,dashModel:{value:e.downloadVideoFormat,items:["flv","dash"]},qualityModel:{value:c.displayName,items:l.map(e=>e.displayName)},danmakuModel:{value:e.downloadVideoDefaultDanmaku,items:["无","XML","ASS"]},progressPercent:0,size:"获取大小中",blobUrl:"",episodeList:[],downloading:false,speed:"",batch:false,rpcSettings:e.aria2RpcOption,showRpcSettings:false,busy:false,saveRpcSettingsText:"保存配置",enableDash:e.enableDashDownload},computed:{displaySize(){if(typeof this.size==="string"){return this.size}return formatFileSize(this.size)},sizeWarning(){if(typeof this.size==="string"){return false}return this.size>1073741824},selectedEpisodeCount(){return this.episodeList.filter(e=>e.checked).length},dash(){return this.dashModel.value==="dash"}},methods:{close(){this.$el.classList.remove("opened")},danmakuOptionChange(){e.downloadVideoDefaultDanmaku=this.danmakuModel.value},async dashChange(){const e=this.dashModel.value;let t=[];if(e==="flv"){t=await d.getAvailableFormats()}else{t=await d.getAvailableDashFormats()}l=t;[c]=e;this.qualityModel.items=t.map(e=>e.displayName);[this.qualityModel.value]=this.qualityModel.items},async formatChange(){const e=this.getFormat();const t=o.get(e);if(t){this.size=t;return}try{this.size="获取大小中";const t=await e.downloadInfo(this.dash);this.size=t.totalSize;o.set(e,this.size)}catch(e){this.size="获取大小失败"}},getFormat(){const e=l.find(e=>e.displayName===this.qualityModel.value);if(!e){console.error(`No format found. model value = ${this.qualityModel.value}`);return null}return e},async exportData(e){if(this.busy===true){return}try{this.busy=true;if(!this.downloadSingle){await this.exportBatchData(e);return}const t=this.getFormat();const s=await t.downloadInfo(this.dash);s.danmakuOption=this.danmakuModel.value;switch(e){case"copyLink":await s.copyUrl();Toast.success("已复制链接到剪贴板.","下载视频",3e3);break;case"showLink":await s.showUrl();break;case"aria2":await s.exportAria2(false);break;case"aria2RPC":await s.exportAria2(true);break;case"copyVLD":await s.exportData(true);Toast.success("已复制VLD数据到剪贴板.","下载视频",3e3);break;case"exportVLD":await s.exportData(false);break;default:break}}catch(e){logError(e)}finally{this.busy=false}},async exportBatchData(e){const a=this.episodeList;if(a.every(e=>e.checked===false)){Toast.info("请至少选择1集或以上的数量!","批量导出",3e3);return}const n=e=>{const t=a.find(t=>t.cid===e.cid);if(t===undefined){return false}return t.checked};const o=this.getFormat();if(this.danmakuModel.value!=="无"){const e=Toast.info("下载弹幕中...","批量导出");const s=new JSZip;try{if(this.danmakuModel.value==="XML"){for(const e of a.filter(n)){const t=new i(e.cid);await t.fetchInfo();s.file(e.title+".xml",t.rawXML)}}else{const{convertToAss:e}=await t.importAsync("download-danmaku");for(const t of a.filter(n)){const a=new i(t.cid);await a.fetchInfo();s.file(t.title+".ass",await e(a.rawXML))}}h.downloadBlob(await s.generateAsync({type:"blob"}),this.cid+".danmakus.zip")}catch(e){logError(`弹幕下载失败`)}finally{e.dismiss()}}const r=Toast.info("获取链接中...","批量导出");this.batchExtractor.itemFilter=n;let l;try{switch(e){case"aria2":l=await this.batchExtractor.collectAria2(o,r);h.downloadBlob(new Blob([l],{type:"text/plain"}),s(false)+".txt");return;case"aria2RPC":await this.batchExtractor.collectAria2(o,r,true);Toast.success(`成功发送了批量请求.`,"aria2 RPC",3e3);return;case"copyVLD":GM_setClipboard(await this.batchExtractor.collectData(o,r),{mimetype:"text/plain"});Toast.success("已复制批量vld数据到剪贴板.","批量导出",3e3);return;case"exportVLD":l=await this.batchExtractor.collectData(o,r);h.downloadBlob(new Blob([l],{type:"text/json"}),s(false)+".json");return;default:return}}catch(e){logError(e)}finally{r.dismiss()}},async checkBatch(){const e=["/www.bilibili.com/bangumi","/www.bilibili.com/video/av"];if(!e.some(e=>document.URL.includes(e))){this.batch=false;this.episodeList=[];return}const{BatchExtractor:s}=await t.importAsync("batch-download");if(await s.test()!==true){this.batch=false;this.episodeList=[];return}this.batchExtractor=new s;this.batch=true;this.episodeList=(await this.batchExtractor.getItemList()).map((e,t)=>{return{aid:e.aid,cid:e.cid,title:e.title,index:t,checked:true}})},cancelDownload(){if(n){n.cancelDownload()}},async startDownload(){const e=this.getFormat();try{this.downloading=true;const t=await e.downloadInfo(this.dash);t.videoSpeed.speedUpdate=(e=>this.speed=e);t.danmakuOption=this.danmakuModel.value;t.progress=(e=>{this.progressPercent=Math.trunc(e*100)});n=t;const s=await t.download();const a=document.getElementById("video-complete");a.setAttribute("href",s.url);a.setAttribute("download",s.filename);a.click();Toast.success(`下载完成: ${s.filename} 再次保存`,"下载视频")}catch(e){if(e!=="canceled"){logError(e)}this.progressPercent=0}finally{this.downloading=false;this.speed=""}},selectAllEpisodes(){this.episodeList.forEach(e=>e.checked=true)},unselectAllEpisodes(){this.episodeList.forEach(e=>e.checked=false)},inverseAllEpisodes(){this.episodeList.forEach(e=>e.checked=!e.checked)},toggleRpcSettings(){this.showRpcSettings=!this.showRpcSettings},saveRpcSettings(){if(this.rpcSettings.host===""){this.rpcSettings.host="127.0.0.1"}if(this.rpcSettings.port===""){this.rpcSettings.port="6800"}e.aria2RpcOption=this.rpcSettings;const t=e.aria2RpcOptionProfiles.find(t=>t.name===e.aria2RpcOptionSelectedProfile);if(t){Object.assign(t,this.rpcSettings);e.aria2RpcOptionProfiles=e.aria2RpcOptionProfiles}this.saveRpcSettingsText="已保存";setTimeout(()=>this.saveRpcSettingsText="保存配置",2e3)},updateProfile(t){e.aria2RpcOption=this.rpcSettings=_.omit(t,"name")}}});Observer.videoChange(async()=>{u.close();u.batch=false;u.downloadSingle=true;const e=dq("#download-video");const t=await p();e.style.display=t?"flex":"none";if(!t){return}u.aid=r.aid;u.cid=r.cid;const s=new a(r.aid);await s.fetchInfo();u.coverUrl=s.coverUrl.replace("http:","https:");l=await d.getAvailableFormats();[c]=l;u.qualityModel={value:c.displayName,items:l.map(e=>e.displayName)};u.formatChange();await u.checkBatch()})}return{widget:{content:`\n`,condition:p,success:f}}}})(); offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/dpi-img.vue.min.js"] = (()=>{return(t,e)=>{const i=``;return{export:Object.assign({template:i},{props:["size","src","blur"],computed:{srcset(){if(!this.src||!this.size){return null}return getDpiSourceSet(this.src,this.size)},width(){if(typeof this.size==="object"&&"width"in this.size){return this.size.width}return null},height(){if(typeof this.size==="object"&&"height"in this.size){return this.size.height}return null}}})}}})(); offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/expand-danmaku.min.js"] = (()=>{return(e,a)=>{if(typeof isEmbeddedPlayer!=="undefined"&&isEmbeddedPlayer()){return}Observer.videoChange(async()=>{const e=await SpinQuery.select(".bui-collapse-wrap");if(e&&e.classList.contains("bui-collapse-wrap-folded")){const e=await SpinQuery.select(".bui-collapse-header");e.click()}})}})(); offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/expand-description.min.css"] = `.play-up-info .play-up-self,.video-desc .info{height:auto!important}.play-up-info .play-up-self-btn,.video-desc .btn{display:none!important}`; diff --git a/bilibili-evolved.preview-offline.user.js b/bilibili-evolved.preview-offline.user.js index 7cd803d6b..140a32024 100644 --- a/bilibili-evolved.preview-offline.user.js +++ b/bilibili-evolved.preview-offline.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name Bilibili Evolved (Preview Offline) -// @version 442.18 +// @version 443.82 // @description Bilibili Evolved 的预览离线版, 可以抢先体验新功能, 并且所有功能都已内置于脚本中. // @author Grant Howard, Coulomb-G // @copyright 2019, Grant Howard (https://github.com/the1812) & Coulomb-G (https://github.com/Coulomb-G) @@ -1072,8 +1072,8 @@ offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/m offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-audio.min.js"] = (()=>{return(e,t)=>{class n{constructor(){this.sid=null;this.progress=null}async getDownloadUrl(){const e=`https://www.bilibili.com/audio/music-service-c/web/url?sid=${this.sid}&privilege=2&quality=2`;const t=await Ajax.getJsonWithCredentials(e);if(t.code===0){return t.data.cdns.shift()}else{logError("获取下载链接失败, 请确保当前账号有下载权限.","下载音频",1e4);return null}}async download(){const e=await this.getDownloadUrl();return new Promise((t,n)=>{const s=new XMLHttpRequest;s.open("GET",e);s.responseType="blob";s.addEventListener("load",()=>t(s.response));s.addEventListener("error",()=>n(s.status));s.addEventListener("progress",e=>this.progress&&this.progress(100*e.loaded/e.total));s.send()})}}const s="下载音频";return{export:n,widget:{content:`\n\n\n${s}\n\n`,condition:()=>document.URL.includes("bilibili.com/audio"),success:async()=>{await SpinQuery.select(()=>document.querySelector("#app"));const e=document.querySelector("#download-audio");const t=e.querySelector("span");const o=new n;o.progress=(e=>{t.innerHTML=`${Math.round(e)}%`});const i=document.querySelector("#download-audio-link");e.addEventListener("click",async e=>{if(o.sid===null||e.target===i){return}const n=await o.download();t.innerHTML=s;const r=i.getAttribute("href");if(r){URL.revokeObjectURL(r)}i.setAttribute("href",URL.createObjectURL(n));const d=(()=>{const e=document.querySelector(".song-title");if(e){return e.getAttribute("title")}else{return"神秘音频"}})();i.setAttribute("download",d+".m4a");i.click()});Observer.childList("#app",()=>{const t=document.URL.match(/bilibili\.com\/audio\/au([\d]+)/);if(t&&t[1]){e.disabled=false;o.sid=t[1]}else{e.disabled=true}})}}}}})(); offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-danmaku.min.js"] = (()=>{return(t,e)=>{const{getFriendlyTitle:n}=e.import("title");const{DanmakuInfo:i}=e.import("video-info");const{DanmakuConverter:a}=e.import("danmaku-converter");async function o(t){const e=n();let i={title:e};try{await loadDanmakuSettingsPanel();const t=t=>{const e=parseFloat(dq(t).style.transform.replace(/translateX\(([\d\.]+)/,"$1"));const n={0:0,44:1,94:2,144:3,188:4}[e];return n};i.font=dq(".bilibili-player-video-danmaku-setting-right-font .bui-select-result").innerText;i.alpha=parseFloat(dq(".bilibili-player-setting-opacity .bui-bar").style.transform.replace(/scaleX\(([\d\.]+)\)/,"$1"));i.duration=(()=>{const e=[18,14,10,8,6][t(".bilibili-player-setting-speedplus .bui-thumb")];return t=>{switch(t.type){case 4:case 5:return 4;default:return e}}})();i.blockTypes=(()=>{let t=[];const e={".bilibili-player-block-filter-type[ftype=scroll]":[1,2,3],".bilibili-player-block-filter-type[ftype=top]":[5],".bilibili-player-block-filter-type[ftype=bottom]":[4],".bilibili-player-block-filter-type[ftype=color]":["color"]};for(const[n,i]of Object.entries(e)){if(dq(n).classList.contains("disabled")){t=t.concat(i)}}return t.concat(7,8)})();const e=[1.4,1.2,1,.8,.6][t(".bilibili-player-setting-fontsize .bui-thumb")];i.resolution={x:1920*e,y:1080*e};i.bottomMarginPercent=[.75,.5,.25,0,0][t(".bilibili-player-setting-area .bui-thumb")];if(i.bottomMarginPercent===0&&dq(".bilibili-player-video-danmaku-setting-left-preventshade input").checked){i.bottomMarginPercent=.15}i.bold=dq(".bilibili-player-video-danmaku-setting-right-font-bold input").checked}catch(t){i={font:"微软雅黑",alpha:.6,duration:t=>{switch(t.type){case 4:case 5:return 4;default:return 6}},blockTypes:[7,8],resolution:{x:1920,y:1080},bottomMarginPercent:.15,bold:false}}const o=new a(i);const l=o.convertToAssDocument(t);return l.generateAss()}async function l(t,e){const a=n();const l=new i((unsafeWindow||window).cid);await l.fetchInfo();const r=await(async()=>{if(t===true){return new Blob([await o(l.rawXML)],{type:"text/plain"})}else{return new Blob([l.rawXML],{type:"text/plain"})}})();const s=URL.createObjectURL(r);const c=dq("#danmaku-link");const d=c.getAttribute("href");if(d){URL.revokeObjectURL(d)}clearTimeout(e);dq("#download-danmaku>span").innerHTML="下载弹幕";c.setAttribute("download",`${a}.${t?"ass":"xml"}`);c.setAttribute("href",s);c.click()}return{export:{downloadDanmaku:l,convertToAss:o},widget:{content:`\n\n\n下载弹幕\n\n`,condition:async()=>{let t=await SpinQuery.select(()=>(unsafeWindow||window).cid);return Boolean(t)},success:()=>{const t=document.querySelector("#danmaku-link");dq("#download-danmaku").addEventListener("click",e=>{if(e.target!==t){const t=setTimeout(()=>dq("#download-danmaku>span").innerHTML="请稍侯...",200);l(e.shiftKey,t)}})}}}}})(); offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.css"] = `.download-video{--download-video-background:white;--download-video-foreground:black;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) scale(.95);background:var(--download-video-background);color:var(--download-video-foreground);box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;z-index:10001;pointer-events:none;opacity:0;padding:16px;width:300px;display:flex;flex-direction:column;align-items:stretch;justify-content:space-between;max-height:90%}.download-video,.download-video *{transition:.2s ease-out;-webkit-tap-highlight-color:transparent!important}.download-video.opened{pointer-events:initial;opacity:1;transform:translate(-50%,-50%) scale(1)}body.dark .download-video{--download-video-background:#222;--download-video-foreground:#eee}.round-corner .download-video,.round-corner .download-video .episode-list .v-checkbox,.round-corner .download-video .rpc-settings{border-radius:var(--large-corner-radius)}.round-corner .download-video .button,.round-corner .download-video .cover,.round-corner .download-video .direct-download .progress .background,.round-corner .download-video .direct-download .progress .foreground,.round-corner .download-video .rpc-settings input[type=text],.round-corner .download-video .title,.round-corner .download-video button{border-radius:var(--corner-radius)}.download-video .cover{width:240px;min-height:135px;background-color:#8882}.download-video .header{display:flex;justify-content:space-between;align-items:center}.download-video h1{font-size:16pt;font-weight:700;color:var(--download-video-foreground)}.download-video .header .mdi-close{font-size:16pt;color:var(--download-video-foreground);cursor:pointer}.download-video .header .mdi-close:hover{color:var(--theme-color)}.download-video .info{display:flex;flex-direction:column;align-items:center;margin-top:12px}.download-video .title{display:flex;flex-direction:column;justify-content:space-between;align-items:stretch;margin-top:12px}.download-video .title .bold{width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center}.download-video .title .size{font-size:10pt;color:var(--download-video-foreground);align-self:center;display:flex;align-items:center;height:24px}.download-video .bold{color:var(--download-video-foreground);font-weight:700;font-size:12pt}.download-video .option-item{display:flex;justify-content:space-between;align-items:center;padding-top:12px;font-size:10pt}.download-video .tabs{display:flex;padding-top:12px;font-size:10pt}.download-video .tabs .tab{padding:6px 12px;border-bottom:2px solid transparent;cursor:pointer}.download-video .tabs .tab.active{color:var(--theme-color);border-bottom-color:var(--theme-color)}.download-video .separator{height:1px;background:#8884;width:100%;margin:12px 0;flex-shrink:0}.download-video h2{font-size:12pt;font-weight:700;color:var(--download-video-foreground)}.download-video .direct-download{display:flex;flex-direction:column;align-items:start;justify-content:space-between}.download-video .button,.download-video button{border:none;outline:0!important;padding:6px 16px;font-size:10pt;cursor:pointer;background:#8884;color:var(--download-video-foreground)}.download-video .button:hover,.download-video button:hover{background:#8886}.download-video .primary{background:var(--theme-color);color:var(--foreground-color);align-self:center}.download-video .primary:hover{background:var(--theme-color-90)}.download-video .busy .button,.download-video .busy .primary,.download-video .busy button,.download-video button:disabled{background-color:#0001;color:#8884;cursor:not-allowed}.download-video .busy .button,.download-video .busy button{cursor:wait!important}.download-video .direct-download .progress{align-self:stretch;display:flex;align-items:center;margin-top:8px}.download-video .direct-download .progress .background{flex-grow:1;height:4px;background:#8884;margin-right:4px;position:relative}.download-video .direct-download .progress .background .foreground{position:absolute;top:0;left:0;height:4px;background:linear-gradient(to right,var(--theme-color),var(--theme-color-60))}.download-video .direct-download .progress .percent{width:50px;text-align:right}.download-video .actions{display:flex;flex-wrap:wrap;justify-content:space-between;margin:8px -4px 0}.download-video .actions>*{margin:4px;flex-grow:1;flex-basis:calc(50% - 16px);display:flex;justify-content:center;align-items:center}.download-video i.mdi-alert-circle{font-size:14pt;color:#ff9800}.download-video .size-warning{position:relative;padding:0 8px}.download-video .size-warning-tip{position:absolute;bottom:100%;left:50%;transform:translateX(-50%);background:var(--download-video-background);color:var(--download-video-foreground);border:1px dashed var(--theme-color);z-index:2;font-size:10pt;width:200px;padding:8px;box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;pointer-events:none;opacity:0}.download-video .size-warning:hover .size-warning-tip{opacity:1}.download-video .episode-list .v-checkbox{padding:4px 8px;margin:4px 0;position:relative;background-color:transparent}.download-video .episode-list .v-checkbox.checked{background-color:#8882}.download-video .episode-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.download-video .episode-list{overflow:auto;max-height:320px}.download-video button.list-tool{padding:4px;line-height:1;font-size:12pt;margin:0 4px}.download-video .episode-header h2{flex-grow:1}.download-video .selected-count{width:64px;text-align:right}.download-video .aria2-rpc .mdi{margin-left:8px;font-size:14pt;line-height:1}.download-video .aria2-rpc{position:relative}.download-video .rpc-settings{position:absolute;display:flex;flex-direction:column;align-items:stretch;color:var(--download-video-foreground);background:#fff;bottom:120%;left:50%;cursor:initial;box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;transform-origin:bottom;transform:translateX(-50%) scale(.9);opacity:0;pointer-events:none;padding:8px;z-index:3;transition:.3s cubic-bezier(.18,.89,.32,1.28);width:100%}body.dark .download-video .rpc-settings{background:#282828}.download-video .rpc-settings>*{margin:8px}.download-video .rpc-settings.show{opacity:1;transform:translateX(-50%) scale(1);pointer-events:initial}.download-video .rpc-settings input[type=text]{background:0 0;color:var(--download-video-foreground);border:1px solid #8884;padding:4px;margin-left:8px}.download-video .rpc-settings input[type=text]:focus{border-color:var(--theme-color)}.download-video .rpc-settings .operations{display:flex;align-items:center;justify-content:space-evenly}.download-video .rpc-settings-item{display:flex;align-items:center;justify-content:space-between;white-space:nowrap}.download-video .rpc-settings-item input[type=text]{flex-grow:1;margin-left:24px}.download-video button.cancel-download,.download-video button.start-download{margin-top:8px}.download-video .direct-download-header{align-self:stretch;display:flex;justify-content:space-between;align-items:center}body.dark .download-video .direct-download .progress .foreground{background:linear-gradient(to right,var(--theme-color-70),var(--theme-color))}`; -offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.html"] = `

下载视频

单个视频
批量导出
警告: 过大的视频大小会在直接下载时占用大量内存, 并可能导致浏览器标签页崩溃. 请考虑降低清晰度或使用导出选项.
预计大小: {{displaySize}}
格式
清晰度
弹幕

直接下载

{{speed}}
{{progressPercent}}%

选集

{{selectedEpisodeCount}}/{{episodeList.length}}

导出

{{showRpcSettings ? '取消' : 'aria2 RPC'}}

aria2 RPC

配置

主机
端口
密钥
限速
默认路径
路径
最终路径: {{rpcSettings.baseDir + rpcSettings.dir}}
方法
开始下载
{{saveRpcSettingsText}}
`; -offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.js"] = (()=>{return(e,t)=>{const{getFriendlyTitle:s}=t.import("title");const{VideoInfo:a,DanmakuInfo:i}=t.import("video-info");class n{constructor(){this.menuClasses=["quality","action","progress"];[this.currentMenuClass]=this.menuClasses}get menuPanel(){return document.querySelector(".download-video-panel")}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}closeMenu(){this.menuPanel.classList.remove("opened");setTimeout(()=>this.resetMenuClass(),200)}addError(){this.menuPanel.classList.add("error")}removeError(){this.menuPanel.classList.remove("error");this.resetMenuClass()}async getDashUrl(e){throw new Error("video dash is not supported")}async getUrl(e){if(e){return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&otype=json`}}}class o extends n{async getDashUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&qn=${e}&otype=json&fourk=1&fnval=16`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&otype=json&fourk=1&fnval=16`}}async getUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=&otype=json`}}}const r={entity:new n,aid:"",cid:""};let l=[];let c=null;class d{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async downloadInfo(e=false){const t=new h(this);await t.fetchVideoInfo(e);return t}static parseFormats(e){const t=e.accept_quality;const s=e.accept_format.split(",");const a=e.accept_description;const i=t.map((e,t)=>{return new d(e,s[t],a[t])});return i}static async getAvailableDashFormats(){const e=await r.entity.getDashUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}return d.parseFormats(t.result)}static async getAvailableFormats(){const e=await r.entity.getUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}const s=t.data||t.result||t;return d.parseFormats(s)}}class h{constructor(e,t){this.fragmentSplitFactor=6*2;this.workingXhr=null;this.progressMap=new Map;this.format=e;this.fragments=t||[];this.videoSpeed=new u(this)}get totalSize(){return this.fragments.map(e=>e.size).reduce((e,t)=>e+t)}async fetchVideoInfo(e=false){if(!e){const e=await r.entity.getUrl(this.format.quality);const t=await Ajax.getTextWithCredentials(e);const s=JSON.parse(t.replace(/http:/g,"https:"));const a=s.data||s.result||s;if(a.quality!==this.format.quality){throw new Error("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const i=a.durl;this.fragments=i.map(e=>{return{length:e.length,size:e.size,url:e.url,backupUrls:e.backup_url}})}else{const{dashToFragment:e,getDashInfo:s}=await t.importAsync("video-dash");const a=await s(r.aid,r.cid,this.format.quality);const i=a.videoDashes.sort(descendingSort(e=>e.bandWidth))[0];const n=a.audioDashes.sort(descendingSort(e=>e.bandWidth))[0];this.fragments=[e(i),e(n)]}return this.fragments}updateProgress(){const e=this.progressMap?[...this.progressMap.values()].reduce((e,t)=>e+t,0)/this.totalSize:0;if(e>1||e<0){console.error(`[下载视频] 进度异常: ${e}`,this.progressMap.values())}this.progress&&this.progress(e)}cancelDownload(){this.videoSpeed.stopMeasure();if(this.workingXhr!==null){this.workingXhr.forEach(e=>e.abort())}else{logError("Cancel Download Failed: forEach in this.workingXhr not found.")}}downloadFragment(e){const t=[];this.workingXhr=[];this.progressMap=new Map;this.updateProgress();let s;if(e.size<=96*1024*1024){s=e.size/this.fragmentSplitFactor}else{s=16*1024*1024}let a=0;const i=e=>[...this.progressMap.keys()].indexOf(e)+1;while(a{const a=new XMLHttpRequest;a.open("GET",e.url);a.responseType="arraybuffer";a.withCredentials=false;a.addEventListener("progress",e=>{console.log(`[下载视频] 视频片段${i(a)}下载进度: ${e.loaded}/${r} bytes loaded, ${o}`);this.progressMap.set(a,e.loaded);this.updateProgress()});a.addEventListener("load",()=>{if((""+a.status)[0]==="2"){console.log(`[下载视频] 视频片段${i(a)}下载完成`);t(a.response)}else{s(`视频片段${i(a)}请求失败, response = ${a.status}`)}});a.addEventListener("abort",()=>s("canceled"));a.addEventListener("error",()=>{console.error(`[下载视频] 视频片段${i(a)}下载失败: ${o}`);this.progressMap.set(a,0);this.updateProgress();a.open("GET",e.url);a.setRequestHeader("Range",o);a.send()});a.setRequestHeader("Range",o);this.progressMap.set(a,0);a.send();this.workingXhr.push(a)}));a=Math.round(a+s)+1}return Promise.all(t)}async copyUrl(){const e=this.fragments.map(e=>e.url).reduce((e,t)=>e+"\r\n"+t);GM_setClipboard(e,"text")}static downloadBlob(e,t){const s=document.createElement("a");let a;if(typeof e==="string"){a=e}else{a=URL.createObjectURL(e)}s.setAttribute("href",a);s.setAttribute("download",t);document.body.appendChild(s);s.click();s.remove();URL.revokeObjectURL(a)}async exportData(e=false){const t=JSON.stringify([{fragments:this.fragments,title:s(),totalSize:this.fragments.map(e=>e.size).reduce((e,t)=>e+t),referer:document.URL.replace(window.location.search,"")}]);if(e){GM_setClipboard(t,"text")}else{const e=new Blob([t],{type:"text/json"});const a=await this.downloadDanmaku();if(a!==null){const t=new JSZip;t.file(`${s()}.json`,e);t.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await t.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(e,`${s()}.json`)}}}async exportAria2(a=false){if(a){const a=await this.downloadDanmaku();if(a!==null){h.downloadBlob(new Blob([a]),`${s()}.${this.danmakuOption==="ASS"?"ass":"xml"}`)}const i=e.aria2RpcOption;const n=this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}const n=[];if(i.secretKey!==""){n.push(`token:${i.secretKey}`)}n.push([e.url]);n.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${s()}${a}${this.extension(e)}`,split:this.fragmentSplitFactor,dir:i.baseDir+i.dir||undefined,"max-download-limit":i.maxDownloadLimit||undefined});const o=encodeURIComponent(`${s()}${a}`);return{params:n,id:o}});const{sendRpc:o}=await t.importAsync("aria2-rpc");await o(n)}else{const e=`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}return`\n${e.url}\nreferer=${document.URL.replace(window.location.search,"")}\nuser-agent=${UserAgent}\nout=${s()}${a}${this.extension(e)}\nsplit=${this.fragmentSplitFactor}\n`.trim()}).join("\n")}\n`.trim();const t=new Blob([e],{type:"text/plain"});const a=await this.downloadDanmaku();if(a!==null){const e=new JSZip;e.file(`${s()}.txt`,t);e.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await e.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(t,`${s()}.txt`)}}}extension(e){const t=e||this.fragments[0];const s=[".flv",".mp4",".m4s"].find(e=>t.url.includes(e));if(s){return s}else{console.warn("No extension detected.");return".flv"}}makeBlob(e,t){return new Blob(Array.isArray(e)?e:[e],{type:this.extension(t)===".flv"?"video/x-flv":"video/mp4"})}cleanUpOldBlobUrl(){const e=dq("a#video-complete").getAttribute("href");if(e&&!dq(`.link[href="${e}"]`)){URL.revokeObjectURL(e)}dqa(".toast-card-header").filter(e=>e.innerText.includes("下载视频")).forEach(e=>e.querySelector(".toast-card-dismiss").click())}async downloadDanmaku(){if(this.danmakuOption!=="无"){const e=new i(r.cid);await e.fetchInfo();if(this.danmakuOption==="XML"){return e.rawXML}else{const{convertToAss:s}=await t.importAsync("download-danmaku");return s(e.rawXML)}}else{return null}}async downloadSingle(e){const t=await this.downloadDanmaku();const[a]=e;if(t===null){const e=this.makeBlob(a);const t=s()+this.extension();return{blob:e,filename:t}}else{const e=new JSZip;e.file(s()+this.extension(),this.makeBlob(a));e.file(s()+"."+this.danmakuOption.toLowerCase(),t);const i=await e.generateAsync({type:"blob"});const n=s()+".zip";return{blob:i,filename:n}}}async downloadMultiple(e){const t=new JSZip;const a=s();if(e.length>1){e.forEach((e,s)=>{const i=this.fragments[s];t.file(`${a} - ${s+1}${this.extension(i)}`,this.makeBlob(e,i))})}else{const[s]=e;t.file(`${a}${this.extension()}`,this.makeBlob(s))}const i=await this.downloadDanmaku();if(i!==null){t.file(s()+"."+this.danmakuOption.toLowerCase(),i)}const n=await t.generateAsync({type:"blob"});const o=a+".zip";return{blob:n,filename:o}}async download(){const e=[];this.videoSpeed.startMeasure();for(const t of this.fragments){const s=await this.downloadFragment(t);e.push(s)}if(e.length<1){throw new Error("下载失败.")}let{blob:t,filename:s}=await(async()=>{if(e.length===1){return await this.downloadSingle(e)}else{return await this.downloadMultiple(e)}})();this.cleanUpOldBlobUrl();const a=URL.createObjectURL(t);this.progress&&this.progress(0);this.videoSpeed.stopMeasure();return{url:a,filename:s}}}class u{constructor(e){this.lastProgress=0;this.measureInterval=1e3;this.workingDownloader=e}startMeasure(){this.intervalTimer=setInterval(()=>{const e=this.workingDownloader.progressMap?[...this.workingDownloader.progressMap.values()].reduce((e,t)=>e+t,0):0;const t=e-this.lastProgress;if(this.speedUpdate!==undefined){this.speedUpdate(formatFileSize(t)+"/s")}this.lastProgress=e},this.measureInterval)}stopMeasure(){clearInterval(this.intervalTimer)}}async function p(){const e=await SpinQuery.select(()=>(unsafeWindow||window).aid);const t=await SpinQuery.select(()=>(unsafeWindow||window).cid);if(!(e&&t)){return false}r.aid=e;r.cid=t;if(document.URL.indexOf("bangumi")!==-1){r.entity=new o}else{r.entity=new n}try{l=await d.getAvailableFormats()}catch(e){return false}return true}async function f(){c=l[0];t.applyStyle("downloadVideoStyle");dq("#download-video").addEventListener("click",()=>{dq(".download-video").classList.toggle("opened");dq(".gui-settings-mask").click()});dq("#download-video").addEventListener("mouseover",()=>{document.body.insertAdjacentHTML("beforeend",t.import("downloadVideoHtml"));m()},{once:true})}async function m(){let n;const o=new Map;const u=new Vue({el:".download-video",components:{VDropdown:()=>t.importAsync("v-dropdown.vue"),VCheckbox:()=>t.importAsync("v-checkbox.vue"),RpcProfiles:()=>t.importAsync("aria2-rpc-profiles.vue")},data:{downloadSingle:true,coverUrl:EmptyImageUrl,aid:r.aid,cid:r.cid,dashModel:{value:e.downloadVideoFormat,items:["flv","dash"]},qualityModel:{value:c.displayName,items:l.map(e=>e.displayName)},danmakuModel:{value:e.downloadVideoDefaultDanmaku,items:["无","XML","ASS"]},progressPercent:0,size:"获取大小中",blobUrl:"",episodeList:[],downloading:false,speed:"",batch:false,rpcSettings:e.aria2RpcOption,showRpcSettings:false,busy:false,saveRpcSettingsText:"保存配置",enableDash:e.enableDashDownload},computed:{displaySize(){if(typeof this.size==="string"){return this.size}return formatFileSize(this.size)},sizeWarning(){if(typeof this.size==="string"){return false}return this.size>1073741824},selectedEpisodeCount(){return this.episodeList.filter(e=>e.checked).length},dash(){return this.dashModel.value==="dash"}},methods:{close(){this.$el.classList.remove("opened")},danmakuOptionChange(){e.downloadVideoDefaultDanmaku=this.danmakuModel.value},async dashChange(){const e=this.dashModel.value;let t=[];if(e==="flv"){t=await d.getAvailableFormats()}else{t=await d.getAvailableDashFormats()}l=t;[c]=e;this.qualityModel.items=t.map(e=>e.displayName);[this.qualityModel.value]=this.qualityModel.items},async formatChange(){const e=this.getFormat();const t=o.get(e);if(t){this.size=t;return}try{this.size="获取大小中";const t=await e.downloadInfo(this.dash);this.size=t.totalSize;o.set(e,this.size)}catch(e){this.size="获取大小失败"}},getFormat(){const e=l.find(e=>e.displayName===this.qualityModel.value);if(!e){console.error(`No format found. model value = ${this.qualityModel.value}`);return null}return e},async exportData(e){if(this.busy===true){return}try{this.busy=true;if(!this.downloadSingle){await this.exportBatchData(e);return}const t=this.getFormat();const s=await t.downloadInfo(this.dash);s.danmakuOption=this.danmakuModel.value;switch(e){case"copyLink":await s.copyUrl();Toast.success("已复制链接到剪贴板.","下载视频",3e3);break;case"aria2":await s.exportAria2(false);break;case"aria2RPC":await s.exportAria2(true);break;case"copyVLD":await s.exportData(true);Toast.success("已复制VLD数据到剪贴板.","下载视频",3e3);break;case"exportVLD":await s.exportData(false);break;default:break}}catch(e){logError(e)}finally{this.busy=false}},async exportBatchData(e){const a=this.episodeList;if(a.every(e=>e.checked===false)){Toast.info("请至少选择1集或以上的数量!","批量导出",3e3);return}const n=e=>{const t=a.find(t=>t.cid===e.cid);if(t===undefined){return false}return t.checked};const o=this.getFormat();if(this.danmakuModel.value!=="无"){const e=Toast.info("下载弹幕中...","批量导出");const s=new JSZip;try{if(this.danmakuModel.value==="XML"){for(const e of a.filter(n)){const t=new i(e.cid);await t.fetchInfo();s.file(e.title+".xml",t.rawXML)}}else{const{convertToAss:e}=await t.importAsync("download-danmaku");for(const t of a.filter(n)){const a=new i(t.cid);await a.fetchInfo();s.file(t.title+".ass",await e(a.rawXML))}}h.downloadBlob(await s.generateAsync({type:"blob"}),this.cid+".danmakus.zip")}catch(e){logError(`弹幕下载失败`)}finally{e.dismiss()}}const r=Toast.info("获取链接中...","批量导出");this.batchExtractor.itemFilter=n;let l;try{switch(e){case"aria2":l=await this.batchExtractor.collectAria2(o,r);h.downloadBlob(new Blob([l],{type:"text/plain"}),s(false)+".txt");return;case"aria2RPC":await this.batchExtractor.collectAria2(o,r,true);Toast.success(`成功发送了批量请求.`,"aria2 RPC",3e3);return;case"copyVLD":GM_setClipboard(await this.batchExtractor.collectData(o,r),{mimetype:"text/plain"});Toast.success("已复制批量vld数据到剪贴板.","批量导出",3e3);return;case"exportVLD":l=await this.batchExtractor.collectData(o,r);h.downloadBlob(new Blob([l],{type:"text/json"}),s(false)+".json");return;default:return}}catch(e){logError(e)}finally{r.dismiss()}},async checkBatch(){const e=["/www.bilibili.com/bangumi","/www.bilibili.com/video/av"];if(!e.some(e=>document.URL.includes(e))){this.batch=false;this.episodeList=[];return}const{BatchExtractor:s}=await t.importAsync("batch-download");if(await s.test()!==true){this.batch=false;this.episodeList=[];return}this.batchExtractor=new s;this.batch=true;this.episodeList=(await this.batchExtractor.getItemList()).map((e,t)=>{return{aid:e.aid,cid:e.cid,title:e.title,index:t,checked:true}})},cancelDownload(){if(n){n.cancelDownload()}},async startDownload(){const e=this.getFormat();try{this.downloading=true;const t=await e.downloadInfo(this.dash);t.videoSpeed.speedUpdate=(e=>this.speed=e);t.danmakuOption=this.danmakuModel.value;t.progress=(e=>{this.progressPercent=Math.trunc(e*100)});n=t;const s=await t.download();const a=document.getElementById("video-complete");a.setAttribute("href",s.url);a.setAttribute("download",s.filename);a.click();Toast.success(`下载完成: ${s.filename} 再次保存`,"下载视频")}catch(e){if(e!=="canceled"){logError(e)}this.progressPercent=0}finally{this.downloading=false;this.speed=""}},selectAllEpisodes(){this.episodeList.forEach(e=>e.checked=true)},unselectAllEpisodes(){this.episodeList.forEach(e=>e.checked=false)},inverseAllEpisodes(){this.episodeList.forEach(e=>e.checked=!e.checked)},toggleRpcSettings(){this.showRpcSettings=!this.showRpcSettings},saveRpcSettings(){if(this.rpcSettings.host===""){this.rpcSettings.host="127.0.0.1"}if(this.rpcSettings.port===""){this.rpcSettings.port="6800"}e.aria2RpcOption=this.rpcSettings;const t=e.aria2RpcOptionProfiles.find(t=>t.name===e.aria2RpcOptionSelectedProfile);if(t){Object.assign(t,this.rpcSettings);e.aria2RpcOptionProfiles=e.aria2RpcOptionProfiles}this.saveRpcSettingsText="已保存";setTimeout(()=>this.saveRpcSettingsText="保存配置",2e3)},updateProfile(t){e.aria2RpcOption=this.rpcSettings=_.omit(t,"name")}}});Observer.videoChange(async()=>{u.close();u.batch=false;u.downloadSingle=true;const e=dq("#download-video");const t=await p();e.style.display=t?"flex":"none";if(!t){return}u.aid=r.aid;u.cid=r.cid;const s=new a(r.aid);await s.fetchInfo();u.coverUrl=s.coverUrl.replace("http:","https:");l=await d.getAvailableFormats();[c]=l;u.qualityModel={value:c.displayName,items:l.map(e=>e.displayName)};u.formatChange();await u.checkBatch()})}return{widget:{content:`\n`,condition:p,success:f}}}})(); +offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.html"] = `

下载视频

单个视频
批量导出
警告: 过大的视频大小会在直接下载时占用大量内存, 并可能导致浏览器标签页崩溃. 请考虑降低清晰度或使用导出选项.
预计大小: {{displaySize}}
格式
清晰度
弹幕

直接下载

{{speed}}
{{progressPercent}}%

选集

{{selectedEpisodeCount}}/{{episodeList.length}}

导出

{{showRpcSettings ? '取消' : 'aria2 RPC'}}

aria2 RPC

配置

主机
端口
密钥
限速
默认路径
路径
最终路径: {{rpcSettings.baseDir + rpcSettings.dir}}
方法
开始下载
{{saveRpcSettingsText}}
`; +offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.js"] = (()=>{return(e,t)=>{const{getFriendlyTitle:s}=t.import("title");const{VideoInfo:a,DanmakuInfo:i}=t.import("video-info");class n{constructor(){this.menuClasses=["quality","action","progress"];[this.currentMenuClass]=this.menuClasses}get menuPanel(){return document.querySelector(".download-video-panel")}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}closeMenu(){this.menuPanel.classList.remove("opened");setTimeout(()=>this.resetMenuClass(),200)}addError(){this.menuPanel.classList.add("error")}removeError(){this.menuPanel.classList.remove("error");this.resetMenuClass()}async getDashUrl(e){throw new Error("video dash is not supported")}async getUrl(e){if(e){return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&otype=json`}}}class o extends n{async getDashUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&qn=${e}&otype=json&fourk=1&fnval=16`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&otype=json&fourk=1&fnval=16`}}async getUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=&otype=json`}}}const r={entity:new n,aid:"",cid:""};let l=[];let c=null;class d{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async downloadInfo(e=false){const t=new h(this);await t.fetchVideoInfo(e);return t}static parseFormats(e){const t=e.accept_quality;const s=e.accept_format.split(",");const a=e.accept_description;const i=t.map((e,t)=>{return new d(e,s[t],a[t])});return i}static async getAvailableDashFormats(){const e=await r.entity.getDashUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}return d.parseFormats(t.result)}static async getAvailableFormats(){const e=await r.entity.getUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}const s=t.data||t.result||t;return d.parseFormats(s)}}class h{constructor(e,t){this.fragmentSplitFactor=6*2;this.workingXhr=null;this.progressMap=new Map;this.format=e;this.fragments=t||[];this.videoSpeed=new u(this)}get totalSize(){return this.fragments.map(e=>e.size).reduce((e,t)=>e+t)}async fetchVideoInfo(e=false){if(!e){const e=await r.entity.getUrl(this.format.quality);const t=await Ajax.getTextWithCredentials(e);const s=JSON.parse(t.replace(/http:/g,"https:"));const a=s.data||s.result||s;if(a.quality!==this.format.quality){throw new Error("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const i=a.durl;this.fragments=i.map(e=>{return{length:e.length,size:e.size,url:e.url,backupUrls:e.backup_url}})}else{const{dashToFragment:e,getDashInfo:s}=await t.importAsync("video-dash");const a=await s(r.aid,r.cid,this.format.quality);const i=a.videoDashes.sort(descendingSort(e=>e.bandWidth))[0];const n=a.audioDashes.sort(descendingSort(e=>e.bandWidth))[0];this.fragments=[e(i),e(n)]}return this.fragments}updateProgress(){const e=this.progressMap?[...this.progressMap.values()].reduce((e,t)=>e+t,0)/this.totalSize:0;if(e>1||e<0){console.error(`[下载视频] 进度异常: ${e}`,this.progressMap.values())}this.progress&&this.progress(e)}cancelDownload(){this.videoSpeed.stopMeasure();if(this.workingXhr!==null){this.workingXhr.forEach(e=>e.abort())}else{logError("Cancel Download Failed: forEach in this.workingXhr not found.")}}downloadFragment(e){const t=[];this.workingXhr=[];this.progressMap=new Map;this.updateProgress();let s;if(e.size<=96*1024*1024){s=e.size/this.fragmentSplitFactor}else{s=16*1024*1024}let a=0;const i=e=>[...this.progressMap.keys()].indexOf(e)+1;while(a{const a=new XMLHttpRequest;a.open("GET",e.url);a.responseType="arraybuffer";a.withCredentials=false;a.addEventListener("progress",e=>{console.log(`[下载视频] 视频片段${i(a)}下载进度: ${e.loaded}/${r} bytes loaded, ${o}`);this.progressMap.set(a,e.loaded);this.updateProgress()});a.addEventListener("load",()=>{if((""+a.status)[0]==="2"){console.log(`[下载视频] 视频片段${i(a)}下载完成`);t(a.response)}else{s(`视频片段${i(a)}请求失败, response = ${a.status}`)}});a.addEventListener("abort",()=>s("canceled"));a.addEventListener("error",()=>{console.error(`[下载视频] 视频片段${i(a)}下载失败: ${o}`);this.progressMap.set(a,0);this.updateProgress();a.open("GET",e.url);a.setRequestHeader("Range",o);a.send()});a.setRequestHeader("Range",o);this.progressMap.set(a,0);a.send();this.workingXhr.push(a)}));a=Math.round(a+s)+1}return Promise.all(t)}async copyUrl(){const e=this.fragments.map(e=>e.url).reduce((e,t)=>e+"\r\n"+t);GM_setClipboard(e,"text")}async showUrl(){const e=this.fragments.map(e=>`\n${e.url}\n`).reduce((e,t)=>e+"\r\n"+t);Toast.success(e,"显示链接")}static downloadBlob(e,t){const s=document.createElement("a");let a;if(typeof e==="string"){a=e}else{a=URL.createObjectURL(e)}s.setAttribute("href",a);s.setAttribute("download",t);document.body.appendChild(s);s.click();s.remove();URL.revokeObjectURL(a)}async exportData(e=false){const t=JSON.stringify([{fragments:this.fragments,title:s(),totalSize:this.fragments.map(e=>e.size).reduce((e,t)=>e+t),referer:document.URL.replace(window.location.search,"")}]);if(e){GM_setClipboard(t,"text")}else{const e=new Blob([t],{type:"text/json"});const a=await this.downloadDanmaku();if(a!==null){const t=new JSZip;t.file(`${s()}.json`,e);t.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await t.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(e,`${s()}.json`)}}}async exportAria2(a=false){if(a){const a=await this.downloadDanmaku();if(a!==null){h.downloadBlob(new Blob([a]),`${s()}.${this.danmakuOption==="ASS"?"ass":"xml"}`)}const i=e.aria2RpcOption;const n=this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}const n=[];if(i.secretKey!==""){n.push(`token:${i.secretKey}`)}n.push([e.url]);n.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${s()}${a}${this.extension(e)}`,split:this.fragmentSplitFactor,dir:i.baseDir+i.dir||undefined,"max-download-limit":i.maxDownloadLimit||undefined});const o=encodeURIComponent(`${s()}${a}`);return{params:n,id:o}});const{sendRpc:o}=await t.importAsync("aria2-rpc");await o(n)}else{const e=`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}return`\n${e.url}\nreferer=${document.URL.replace(window.location.search,"")}\nuser-agent=${UserAgent}\nout=${s()}${a}${this.extension(e)}\nsplit=${this.fragmentSplitFactor}\n`.trim()}).join("\n")}\n`.trim();const t=new Blob([e],{type:"text/plain"});const a=await this.downloadDanmaku();if(a!==null){const e=new JSZip;e.file(`${s()}.txt`,t);e.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await e.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(t,`${s()}.txt`)}}}extension(e){const t=e||this.fragments[0];const s=[".flv",".mp4",".m4s"].find(e=>t.url.includes(e));if(s){return s}else{console.warn("No extension detected.");return".flv"}}makeBlob(e,t){return new Blob(Array.isArray(e)?e:[e],{type:this.extension(t)===".flv"?"video/x-flv":"video/mp4"})}cleanUpOldBlobUrl(){const e=dq("a#video-complete").getAttribute("href");if(e&&!dq(`.link[href="${e}"]`)){URL.revokeObjectURL(e)}dqa(".toast-card-header").filter(e=>e.innerText.includes("下载视频")).forEach(e=>e.querySelector(".toast-card-dismiss").click())}async downloadDanmaku(){if(this.danmakuOption!=="无"){const e=new i(r.cid);await e.fetchInfo();if(this.danmakuOption==="XML"){return e.rawXML}else{const{convertToAss:s}=await t.importAsync("download-danmaku");return s(e.rawXML)}}else{return null}}async downloadSingle(e){const t=await this.downloadDanmaku();const[a]=e;if(t===null){const e=this.makeBlob(a);const t=s()+this.extension();return{blob:e,filename:t}}else{const e=new JSZip;e.file(s()+this.extension(),this.makeBlob(a));e.file(s()+"."+this.danmakuOption.toLowerCase(),t);const i=await e.generateAsync({type:"blob"});const n=s()+".zip";return{blob:i,filename:n}}}async downloadMultiple(e){const t=new JSZip;const a=s();if(e.length>1){e.forEach((e,s)=>{const i=this.fragments[s];t.file(`${a} - ${s+1}${this.extension(i)}`,this.makeBlob(e,i))})}else{const[s]=e;t.file(`${a}${this.extension()}`,this.makeBlob(s))}const i=await this.downloadDanmaku();if(i!==null){t.file(s()+"."+this.danmakuOption.toLowerCase(),i)}const n=await t.generateAsync({type:"blob"});const o=a+".zip";return{blob:n,filename:o}}async download(){const e=[];this.videoSpeed.startMeasure();for(const t of this.fragments){const s=await this.downloadFragment(t);e.push(s)}if(e.length<1){throw new Error("下载失败.")}let{blob:t,filename:s}=await(async()=>{if(e.length===1){return await this.downloadSingle(e)}else{return await this.downloadMultiple(e)}})();this.cleanUpOldBlobUrl();const a=URL.createObjectURL(t);this.progress&&this.progress(0);this.videoSpeed.stopMeasure();return{url:a,filename:s}}}class u{constructor(e){this.lastProgress=0;this.measureInterval=1e3;this.workingDownloader=e}startMeasure(){this.intervalTimer=setInterval(()=>{const e=this.workingDownloader.progressMap?[...this.workingDownloader.progressMap.values()].reduce((e,t)=>e+t,0):0;const t=e-this.lastProgress;if(this.speedUpdate!==undefined){this.speedUpdate(formatFileSize(t)+"/s")}this.lastProgress=e},this.measureInterval)}stopMeasure(){clearInterval(this.intervalTimer)}}async function p(){const e=await SpinQuery.select(()=>(unsafeWindow||window).aid);const t=await SpinQuery.select(()=>(unsafeWindow||window).cid);if(!(e&&t)){return false}r.aid=e;r.cid=t;if(document.URL.indexOf("bangumi")!==-1){r.entity=new o}else{r.entity=new n}try{l=await d.getAvailableFormats()}catch(e){return false}return true}async function f(){c=l[0];t.applyStyle("downloadVideoStyle");dq("#download-video").addEventListener("click",()=>{dq(".download-video").classList.toggle("opened");dq(".gui-settings-mask").click()});dq("#download-video").addEventListener("mouseover",()=>{document.body.insertAdjacentHTML("beforeend",t.import("downloadVideoHtml"));m()},{once:true})}async function m(){let n;const o=new Map;const u=new Vue({el:".download-video",components:{VDropdown:()=>t.importAsync("v-dropdown.vue"),VCheckbox:()=>t.importAsync("v-checkbox.vue"),RpcProfiles:()=>t.importAsync("aria2-rpc-profiles.vue")},data:{downloadSingle:true,coverUrl:EmptyImageUrl,aid:r.aid,cid:r.cid,dashModel:{value:e.downloadVideoFormat,items:["flv","dash"]},qualityModel:{value:c.displayName,items:l.map(e=>e.displayName)},danmakuModel:{value:e.downloadVideoDefaultDanmaku,items:["无","XML","ASS"]},progressPercent:0,size:"获取大小中",blobUrl:"",episodeList:[],downloading:false,speed:"",batch:false,rpcSettings:e.aria2RpcOption,showRpcSettings:false,busy:false,saveRpcSettingsText:"保存配置",enableDash:e.enableDashDownload},computed:{displaySize(){if(typeof this.size==="string"){return this.size}return formatFileSize(this.size)},sizeWarning(){if(typeof this.size==="string"){return false}return this.size>1073741824},selectedEpisodeCount(){return this.episodeList.filter(e=>e.checked).length},dash(){return this.dashModel.value==="dash"}},methods:{close(){this.$el.classList.remove("opened")},danmakuOptionChange(){e.downloadVideoDefaultDanmaku=this.danmakuModel.value},async dashChange(){const e=this.dashModel.value;let t=[];if(e==="flv"){t=await d.getAvailableFormats()}else{t=await d.getAvailableDashFormats()}l=t;[c]=e;this.qualityModel.items=t.map(e=>e.displayName);[this.qualityModel.value]=this.qualityModel.items},async formatChange(){const e=this.getFormat();const t=o.get(e);if(t){this.size=t;return}try{this.size="获取大小中";const t=await e.downloadInfo(this.dash);this.size=t.totalSize;o.set(e,this.size)}catch(e){this.size="获取大小失败"}},getFormat(){const e=l.find(e=>e.displayName===this.qualityModel.value);if(!e){console.error(`No format found. model value = ${this.qualityModel.value}`);return null}return e},async exportData(e){if(this.busy===true){return}try{this.busy=true;if(!this.downloadSingle){await this.exportBatchData(e);return}const t=this.getFormat();const s=await t.downloadInfo(this.dash);s.danmakuOption=this.danmakuModel.value;switch(e){case"copyLink":await s.copyUrl();Toast.success("已复制链接到剪贴板.","下载视频",3e3);break;case"showLink":await s.showUrl();break;case"aria2":await s.exportAria2(false);break;case"aria2RPC":await s.exportAria2(true);break;case"copyVLD":await s.exportData(true);Toast.success("已复制VLD数据到剪贴板.","下载视频",3e3);break;case"exportVLD":await s.exportData(false);break;default:break}}catch(e){logError(e)}finally{this.busy=false}},async exportBatchData(e){const a=this.episodeList;if(a.every(e=>e.checked===false)){Toast.info("请至少选择1集或以上的数量!","批量导出",3e3);return}const n=e=>{const t=a.find(t=>t.cid===e.cid);if(t===undefined){return false}return t.checked};const o=this.getFormat();if(this.danmakuModel.value!=="无"){const e=Toast.info("下载弹幕中...","批量导出");const s=new JSZip;try{if(this.danmakuModel.value==="XML"){for(const e of a.filter(n)){const t=new i(e.cid);await t.fetchInfo();s.file(e.title+".xml",t.rawXML)}}else{const{convertToAss:e}=await t.importAsync("download-danmaku");for(const t of a.filter(n)){const a=new i(t.cid);await a.fetchInfo();s.file(t.title+".ass",await e(a.rawXML))}}h.downloadBlob(await s.generateAsync({type:"blob"}),this.cid+".danmakus.zip")}catch(e){logError(`弹幕下载失败`)}finally{e.dismiss()}}const r=Toast.info("获取链接中...","批量导出");this.batchExtractor.itemFilter=n;let l;try{switch(e){case"aria2":l=await this.batchExtractor.collectAria2(o,r);h.downloadBlob(new Blob([l],{type:"text/plain"}),s(false)+".txt");return;case"aria2RPC":await this.batchExtractor.collectAria2(o,r,true);Toast.success(`成功发送了批量请求.`,"aria2 RPC",3e3);return;case"copyVLD":GM_setClipboard(await this.batchExtractor.collectData(o,r),{mimetype:"text/plain"});Toast.success("已复制批量vld数据到剪贴板.","批量导出",3e3);return;case"exportVLD":l=await this.batchExtractor.collectData(o,r);h.downloadBlob(new Blob([l],{type:"text/json"}),s(false)+".json");return;default:return}}catch(e){logError(e)}finally{r.dismiss()}},async checkBatch(){const e=["/www.bilibili.com/bangumi","/www.bilibili.com/video/av"];if(!e.some(e=>document.URL.includes(e))){this.batch=false;this.episodeList=[];return}const{BatchExtractor:s}=await t.importAsync("batch-download");if(await s.test()!==true){this.batch=false;this.episodeList=[];return}this.batchExtractor=new s;this.batch=true;this.episodeList=(await this.batchExtractor.getItemList()).map((e,t)=>{return{aid:e.aid,cid:e.cid,title:e.title,index:t,checked:true}})},cancelDownload(){if(n){n.cancelDownload()}},async startDownload(){const e=this.getFormat();try{this.downloading=true;const t=await e.downloadInfo(this.dash);t.videoSpeed.speedUpdate=(e=>this.speed=e);t.danmakuOption=this.danmakuModel.value;t.progress=(e=>{this.progressPercent=Math.trunc(e*100)});n=t;const s=await t.download();const a=document.getElementById("video-complete");a.setAttribute("href",s.url);a.setAttribute("download",s.filename);a.click();Toast.success(`下载完成: ${s.filename} 再次保存`,"下载视频")}catch(e){if(e!=="canceled"){logError(e)}this.progressPercent=0}finally{this.downloading=false;this.speed=""}},selectAllEpisodes(){this.episodeList.forEach(e=>e.checked=true)},unselectAllEpisodes(){this.episodeList.forEach(e=>e.checked=false)},inverseAllEpisodes(){this.episodeList.forEach(e=>e.checked=!e.checked)},toggleRpcSettings(){this.showRpcSettings=!this.showRpcSettings},saveRpcSettings(){if(this.rpcSettings.host===""){this.rpcSettings.host="127.0.0.1"}if(this.rpcSettings.port===""){this.rpcSettings.port="6800"}e.aria2RpcOption=this.rpcSettings;const t=e.aria2RpcOptionProfiles.find(t=>t.name===e.aria2RpcOptionSelectedProfile);if(t){Object.assign(t,this.rpcSettings);e.aria2RpcOptionProfiles=e.aria2RpcOptionProfiles}this.saveRpcSettingsText="已保存";setTimeout(()=>this.saveRpcSettingsText="保存配置",2e3)},updateProfile(t){e.aria2RpcOption=this.rpcSettings=_.omit(t,"name")}}});Observer.videoChange(async()=>{u.close();u.batch=false;u.downloadSingle=true;const e=dq("#download-video");const t=await p();e.style.display=t?"flex":"none";if(!t){return}u.aid=r.aid;u.cid=r.cid;const s=new a(r.aid);await s.fetchInfo();u.coverUrl=s.coverUrl.replace("http:","https:");l=await d.getAvailableFormats();[c]=l;u.qualityModel={value:c.displayName,items:l.map(e=>e.displayName)};u.formatChange();await u.checkBatch()})}return{widget:{content:`\n`,condition:p,success:f}}}})(); offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/dpi-img.vue.min.js"] = (()=>{return(t,e)=>{const i=``;return{export:Object.assign({template:i},{props:["size","src","blur"],computed:{srcset(){if(!this.src||!this.size){return null}return getDpiSourceSet(this.src,this.size)},width(){if(typeof this.size==="object"&&"width"in this.size){return this.size.width}return null},height(){if(typeof this.size==="object"&&"height"in this.size){return this.size.height}return null}}})}}})(); offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/expand-danmaku.min.js"] = (()=>{return(e,a)=>{if(typeof isEmbeddedPlayer!=="undefined"&&isEmbeddedPlayer()){return}Observer.videoChange(async()=>{const e=await SpinQuery.select(".bui-collapse-wrap");if(e&&e.classList.contains("bui-collapse-wrap-folded")){const e=await SpinQuery.select(".bui-collapse-header");e.click()}})}})(); offlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/expand-description.min.css"] = `.play-up-info .play-up-self,.video-desc .info{height:auto!important}.play-up-info .play-up-self-btn,.video-desc .btn{display:none!important}`; diff --git a/bilibili-evolved.preview.user.js b/bilibili-evolved.preview.user.js index 6c6fd2c4d..e6d9cf845 100644 --- a/bilibili-evolved.preview.user.js +++ b/bilibili-evolved.preview.user.js @@ -1071,8 +1071,8 @@ onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/m onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/min/download-audio.min.js"] = (()=>{return(e,t)=>{class n{constructor(){this.sid=null;this.progress=null}async getDownloadUrl(){const e=`https://www.bilibili.com/audio/music-service-c/web/url?sid=${this.sid}&privilege=2&quality=2`;const t=await Ajax.getJsonWithCredentials(e);if(t.code===0){return t.data.cdns.shift()}else{logError("获取下载链接失败, 请确保当前账号有下载权限.","下载音频",1e4);return null}}async download(){const e=await this.getDownloadUrl();return new Promise((t,n)=>{const s=new XMLHttpRequest;s.open("GET",e);s.responseType="blob";s.addEventListener("load",()=>t(s.response));s.addEventListener("error",()=>n(s.status));s.addEventListener("progress",e=>this.progress&&this.progress(100*e.loaded/e.total));s.send()})}}const s="下载音频";return{export:n,widget:{content:`\n\n\n${s}\n\n`,condition:()=>document.URL.includes("bilibili.com/audio"),success:async()=>{await SpinQuery.select(()=>document.querySelector("#app"));const e=document.querySelector("#download-audio");const t=e.querySelector("span");const o=new n;o.progress=(e=>{t.innerHTML=`${Math.round(e)}%`});const i=document.querySelector("#download-audio-link");e.addEventListener("click",async e=>{if(o.sid===null||e.target===i){return}const n=await o.download();t.innerHTML=s;const r=i.getAttribute("href");if(r){URL.revokeObjectURL(r)}i.setAttribute("href",URL.createObjectURL(n));const d=(()=>{const e=document.querySelector(".song-title");if(e){return e.getAttribute("title")}else{return"神秘音频"}})();i.setAttribute("download",d+".m4a");i.click()});Observer.childList("#app",()=>{const t=document.URL.match(/bilibili\.com\/audio\/au([\d]+)/);if(t&&t[1]){e.disabled=false;o.sid=t[1]}else{e.disabled=true}})}}}}})(); onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/min/download-danmaku.min.js"] = (()=>{return(t,e)=>{const{getFriendlyTitle:n}=e.import("title");const{DanmakuInfo:i}=e.import("video-info");const{DanmakuConverter:a}=e.import("danmaku-converter");async function o(t){const e=n();let i={title:e};try{await loadDanmakuSettingsPanel();const t=t=>{const e=parseFloat(dq(t).style.transform.replace(/translateX\(([\d\.]+)/,"$1"));const n={0:0,44:1,94:2,144:3,188:4}[e];return n};i.font=dq(".bilibili-player-video-danmaku-setting-right-font .bui-select-result").innerText;i.alpha=parseFloat(dq(".bilibili-player-setting-opacity .bui-bar").style.transform.replace(/scaleX\(([\d\.]+)\)/,"$1"));i.duration=(()=>{const e=[18,14,10,8,6][t(".bilibili-player-setting-speedplus .bui-thumb")];return t=>{switch(t.type){case 4:case 5:return 4;default:return e}}})();i.blockTypes=(()=>{let t=[];const e={".bilibili-player-block-filter-type[ftype=scroll]":[1,2,3],".bilibili-player-block-filter-type[ftype=top]":[5],".bilibili-player-block-filter-type[ftype=bottom]":[4],".bilibili-player-block-filter-type[ftype=color]":["color"]};for(const[n,i]of Object.entries(e)){if(dq(n).classList.contains("disabled")){t=t.concat(i)}}return t.concat(7,8)})();const e=[1.4,1.2,1,.8,.6][t(".bilibili-player-setting-fontsize .bui-thumb")];i.resolution={x:1920*e,y:1080*e};i.bottomMarginPercent=[.75,.5,.25,0,0][t(".bilibili-player-setting-area .bui-thumb")];if(i.bottomMarginPercent===0&&dq(".bilibili-player-video-danmaku-setting-left-preventshade input").checked){i.bottomMarginPercent=.15}i.bold=dq(".bilibili-player-video-danmaku-setting-right-font-bold input").checked}catch(t){i={font:"微软雅黑",alpha:.6,duration:t=>{switch(t.type){case 4:case 5:return 4;default:return 6}},blockTypes:[7,8],resolution:{x:1920,y:1080},bottomMarginPercent:.15,bold:false}}const o=new a(i);const l=o.convertToAssDocument(t);return l.generateAss()}async function l(t,e){const a=n();const l=new i((unsafeWindow||window).cid);await l.fetchInfo();const r=await(async()=>{if(t===true){return new Blob([await o(l.rawXML)],{type:"text/plain"})}else{return new Blob([l.rawXML],{type:"text/plain"})}})();const s=URL.createObjectURL(r);const c=dq("#danmaku-link");const d=c.getAttribute("href");if(d){URL.revokeObjectURL(d)}clearTimeout(e);dq("#download-danmaku>span").innerHTML="下载弹幕";c.setAttribute("download",`${a}.${t?"ass":"xml"}`);c.setAttribute("href",s);c.click()}return{export:{downloadDanmaku:l,convertToAss:o},widget:{content:`\n\n\n下载弹幕\n\n`,condition:async()=>{let t=await SpinQuery.select(()=>(unsafeWindow||window).cid);return Boolean(t)},success:()=>{const t=document.querySelector("#danmaku-link");dq("#download-danmaku").addEventListener("click",e=>{if(e.target!==t){const t=setTimeout(()=>dq("#download-danmaku>span").innerHTML="请稍侯...",200);l(e.shiftKey,t)}})}}}}})(); onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/min/download-video.min.css"] = `.download-video{--download-video-background:white;--download-video-foreground:black;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) scale(.95);background:var(--download-video-background);color:var(--download-video-foreground);box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;z-index:10001;pointer-events:none;opacity:0;padding:16px;width:300px;display:flex;flex-direction:column;align-items:stretch;justify-content:space-between;max-height:90%}.download-video,.download-video *{transition:.2s ease-out;-webkit-tap-highlight-color:transparent!important}.download-video.opened{pointer-events:initial;opacity:1;transform:translate(-50%,-50%) scale(1)}body.dark .download-video{--download-video-background:#222;--download-video-foreground:#eee}.round-corner .download-video,.round-corner .download-video .episode-list .v-checkbox,.round-corner .download-video .rpc-settings{border-radius:var(--large-corner-radius)}.round-corner .download-video .button,.round-corner .download-video .cover,.round-corner .download-video .direct-download .progress .background,.round-corner .download-video .direct-download .progress .foreground,.round-corner .download-video .rpc-settings input[type=text],.round-corner .download-video .title,.round-corner .download-video button{border-radius:var(--corner-radius)}.download-video .cover{width:240px;min-height:135px;background-color:#8882}.download-video .header{display:flex;justify-content:space-between;align-items:center}.download-video h1{font-size:16pt;font-weight:700;color:var(--download-video-foreground)}.download-video .header .mdi-close{font-size:16pt;color:var(--download-video-foreground);cursor:pointer}.download-video .header .mdi-close:hover{color:var(--theme-color)}.download-video .info{display:flex;flex-direction:column;align-items:center;margin-top:12px}.download-video .title{display:flex;flex-direction:column;justify-content:space-between;align-items:stretch;margin-top:12px}.download-video .title .bold{width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center}.download-video .title .size{font-size:10pt;color:var(--download-video-foreground);align-self:center;display:flex;align-items:center;height:24px}.download-video .bold{color:var(--download-video-foreground);font-weight:700;font-size:12pt}.download-video .option-item{display:flex;justify-content:space-between;align-items:center;padding-top:12px;font-size:10pt}.download-video .tabs{display:flex;padding-top:12px;font-size:10pt}.download-video .tabs .tab{padding:6px 12px;border-bottom:2px solid transparent;cursor:pointer}.download-video .tabs .tab.active{color:var(--theme-color);border-bottom-color:var(--theme-color)}.download-video .separator{height:1px;background:#8884;width:100%;margin:12px 0;flex-shrink:0}.download-video h2{font-size:12pt;font-weight:700;color:var(--download-video-foreground)}.download-video .direct-download{display:flex;flex-direction:column;align-items:start;justify-content:space-between}.download-video .button,.download-video button{border:none;outline:0!important;padding:6px 16px;font-size:10pt;cursor:pointer;background:#8884;color:var(--download-video-foreground)}.download-video .button:hover,.download-video button:hover{background:#8886}.download-video .primary{background:var(--theme-color);color:var(--foreground-color);align-self:center}.download-video .primary:hover{background:var(--theme-color-90)}.download-video .busy .button,.download-video .busy .primary,.download-video .busy button,.download-video button:disabled{background-color:#0001;color:#8884;cursor:not-allowed}.download-video .busy .button,.download-video .busy button{cursor:wait!important}.download-video .direct-download .progress{align-self:stretch;display:flex;align-items:center;margin-top:8px}.download-video .direct-download .progress .background{flex-grow:1;height:4px;background:#8884;margin-right:4px;position:relative}.download-video .direct-download .progress .background .foreground{position:absolute;top:0;left:0;height:4px;background:linear-gradient(to right,var(--theme-color),var(--theme-color-60))}.download-video .direct-download .progress .percent{width:50px;text-align:right}.download-video .actions{display:flex;flex-wrap:wrap;justify-content:space-between;margin:8px -4px 0}.download-video .actions>*{margin:4px;flex-grow:1;flex-basis:calc(50% - 16px);display:flex;justify-content:center;align-items:center}.download-video i.mdi-alert-circle{font-size:14pt;color:#ff9800}.download-video .size-warning{position:relative;padding:0 8px}.download-video .size-warning-tip{position:absolute;bottom:100%;left:50%;transform:translateX(-50%);background:var(--download-video-background);color:var(--download-video-foreground);border:1px dashed var(--theme-color);z-index:2;font-size:10pt;width:200px;padding:8px;box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;pointer-events:none;opacity:0}.download-video .size-warning:hover .size-warning-tip{opacity:1}.download-video .episode-list .v-checkbox{padding:4px 8px;margin:4px 0;position:relative;background-color:transparent}.download-video .episode-list .v-checkbox.checked{background-color:#8882}.download-video .episode-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.download-video .episode-list{overflow:auto;max-height:320px}.download-video button.list-tool{padding:4px;line-height:1;font-size:12pt;margin:0 4px}.download-video .episode-header h2{flex-grow:1}.download-video .selected-count{width:64px;text-align:right}.download-video .aria2-rpc .mdi{margin-left:8px;font-size:14pt;line-height:1}.download-video .aria2-rpc{position:relative}.download-video .rpc-settings{position:absolute;display:flex;flex-direction:column;align-items:stretch;color:var(--download-video-foreground);background:#fff;bottom:120%;left:50%;cursor:initial;box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;transform-origin:bottom;transform:translateX(-50%) scale(.9);opacity:0;pointer-events:none;padding:8px;z-index:3;transition:.3s cubic-bezier(.18,.89,.32,1.28);width:100%}body.dark .download-video .rpc-settings{background:#282828}.download-video .rpc-settings>*{margin:8px}.download-video .rpc-settings.show{opacity:1;transform:translateX(-50%) scale(1);pointer-events:initial}.download-video .rpc-settings input[type=text]{background:0 0;color:var(--download-video-foreground);border:1px solid #8884;padding:4px;margin-left:8px}.download-video .rpc-settings input[type=text]:focus{border-color:var(--theme-color)}.download-video .rpc-settings .operations{display:flex;align-items:center;justify-content:space-evenly}.download-video .rpc-settings-item{display:flex;align-items:center;justify-content:space-between;white-space:nowrap}.download-video .rpc-settings-item input[type=text]{flex-grow:1;margin-left:24px}.download-video button.cancel-download,.download-video button.start-download{margin-top:8px}.download-video .direct-download-header{align-self:stretch;display:flex;justify-content:space-between;align-items:center}body.dark .download-video .direct-download .progress .foreground{background:linear-gradient(to right,var(--theme-color-70),var(--theme-color))}`; -onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/min/download-video.min.html"] = `

下载视频

单个视频
批量导出
警告: 过大的视频大小会在直接下载时占用大量内存, 并可能导致浏览器标签页崩溃. 请考虑降低清晰度或使用导出选项.
预计大小: {{displaySize}}
格式
清晰度
弹幕

直接下载

{{speed}}
{{progressPercent}}%

选集

{{selectedEpisodeCount}}/{{episodeList.length}}

导出

{{showRpcSettings ? '取消' : 'aria2 RPC'}}

aria2 RPC

配置

主机
端口
密钥
限速
默认路径
路径
最终路径: {{rpcSettings.baseDir + rpcSettings.dir}}
方法
开始下载
{{saveRpcSettingsText}}
`; -onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/min/download-video.min.js"] = (()=>{return(e,t)=>{const{getFriendlyTitle:s}=t.import("title");const{VideoInfo:a,DanmakuInfo:i}=t.import("video-info");class n{constructor(){this.menuClasses=["quality","action","progress"];[this.currentMenuClass]=this.menuClasses}get menuPanel(){return document.querySelector(".download-video-panel")}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}closeMenu(){this.menuPanel.classList.remove("opened");setTimeout(()=>this.resetMenuClass(),200)}addError(){this.menuPanel.classList.add("error")}removeError(){this.menuPanel.classList.remove("error");this.resetMenuClass()}async getDashUrl(e){throw new Error("video dash is not supported")}async getUrl(e){if(e){return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&otype=json`}}}class o extends n{async getDashUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&qn=${e}&otype=json&fourk=1&fnval=16`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&otype=json&fourk=1&fnval=16`}}async getUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=&otype=json`}}}const r={entity:new n,aid:"",cid:""};let l=[];let c=null;class d{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async downloadInfo(e=false){const t=new h(this);await t.fetchVideoInfo(e);return t}static parseFormats(e){const t=e.accept_quality;const s=e.accept_format.split(",");const a=e.accept_description;const i=t.map((e,t)=>{return new d(e,s[t],a[t])});return i}static async getAvailableDashFormats(){const e=await r.entity.getDashUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}return d.parseFormats(t.result)}static async getAvailableFormats(){const e=await r.entity.getUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}const s=t.data||t.result||t;return d.parseFormats(s)}}class h{constructor(e,t){this.fragmentSplitFactor=6*2;this.workingXhr=null;this.progressMap=new Map;this.format=e;this.fragments=t||[];this.videoSpeed=new u(this)}get totalSize(){return this.fragments.map(e=>e.size).reduce((e,t)=>e+t)}async fetchVideoInfo(e=false){if(!e){const e=await r.entity.getUrl(this.format.quality);const t=await Ajax.getTextWithCredentials(e);const s=JSON.parse(t.replace(/http:/g,"https:"));const a=s.data||s.result||s;if(a.quality!==this.format.quality){throw new Error("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const i=a.durl;this.fragments=i.map(e=>{return{length:e.length,size:e.size,url:e.url,backupUrls:e.backup_url}})}else{const{dashToFragment:e,getDashInfo:s}=await t.importAsync("video-dash");const a=await s(r.aid,r.cid,this.format.quality);const i=a.videoDashes.sort(descendingSort(e=>e.bandWidth))[0];const n=a.audioDashes.sort(descendingSort(e=>e.bandWidth))[0];this.fragments=[e(i),e(n)]}return this.fragments}updateProgress(){const e=this.progressMap?[...this.progressMap.values()].reduce((e,t)=>e+t,0)/this.totalSize:0;if(e>1||e<0){console.error(`[下载视频] 进度异常: ${e}`,this.progressMap.values())}this.progress&&this.progress(e)}cancelDownload(){this.videoSpeed.stopMeasure();if(this.workingXhr!==null){this.workingXhr.forEach(e=>e.abort())}else{logError("Cancel Download Failed: forEach in this.workingXhr not found.")}}downloadFragment(e){const t=[];this.workingXhr=[];this.progressMap=new Map;this.updateProgress();let s;if(e.size<=96*1024*1024){s=e.size/this.fragmentSplitFactor}else{s=16*1024*1024}let a=0;const i=e=>[...this.progressMap.keys()].indexOf(e)+1;while(a{const a=new XMLHttpRequest;a.open("GET",e.url);a.responseType="arraybuffer";a.withCredentials=false;a.addEventListener("progress",e=>{console.log(`[下载视频] 视频片段${i(a)}下载进度: ${e.loaded}/${r} bytes loaded, ${o}`);this.progressMap.set(a,e.loaded);this.updateProgress()});a.addEventListener("load",()=>{if((""+a.status)[0]==="2"){console.log(`[下载视频] 视频片段${i(a)}下载完成`);t(a.response)}else{s(`视频片段${i(a)}请求失败, response = ${a.status}`)}});a.addEventListener("abort",()=>s("canceled"));a.addEventListener("error",()=>{console.error(`[下载视频] 视频片段${i(a)}下载失败: ${o}`);this.progressMap.set(a,0);this.updateProgress();a.open("GET",e.url);a.setRequestHeader("Range",o);a.send()});a.setRequestHeader("Range",o);this.progressMap.set(a,0);a.send();this.workingXhr.push(a)}));a=Math.round(a+s)+1}return Promise.all(t)}async copyUrl(){const e=this.fragments.map(e=>e.url).reduce((e,t)=>e+"\r\n"+t);GM_setClipboard(e,"text")}static downloadBlob(e,t){const s=document.createElement("a");let a;if(typeof e==="string"){a=e}else{a=URL.createObjectURL(e)}s.setAttribute("href",a);s.setAttribute("download",t);document.body.appendChild(s);s.click();s.remove();URL.revokeObjectURL(a)}async exportData(e=false){const t=JSON.stringify([{fragments:this.fragments,title:s(),totalSize:this.fragments.map(e=>e.size).reduce((e,t)=>e+t),referer:document.URL.replace(window.location.search,"")}]);if(e){GM_setClipboard(t,"text")}else{const e=new Blob([t],{type:"text/json"});const a=await this.downloadDanmaku();if(a!==null){const t=new JSZip;t.file(`${s()}.json`,e);t.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await t.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(e,`${s()}.json`)}}}async exportAria2(a=false){if(a){const a=await this.downloadDanmaku();if(a!==null){h.downloadBlob(new Blob([a]),`${s()}.${this.danmakuOption==="ASS"?"ass":"xml"}`)}const i=e.aria2RpcOption;const n=this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}const n=[];if(i.secretKey!==""){n.push(`token:${i.secretKey}`)}n.push([e.url]);n.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${s()}${a}${this.extension(e)}`,split:this.fragmentSplitFactor,dir:i.baseDir+i.dir||undefined,"max-download-limit":i.maxDownloadLimit||undefined});const o=encodeURIComponent(`${s()}${a}`);return{params:n,id:o}});const{sendRpc:o}=await t.importAsync("aria2-rpc");await o(n)}else{const e=`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}return`\n${e.url}\nreferer=${document.URL.replace(window.location.search,"")}\nuser-agent=${UserAgent}\nout=${s()}${a}${this.extension(e)}\nsplit=${this.fragmentSplitFactor}\n`.trim()}).join("\n")}\n`.trim();const t=new Blob([e],{type:"text/plain"});const a=await this.downloadDanmaku();if(a!==null){const e=new JSZip;e.file(`${s()}.txt`,t);e.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await e.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(t,`${s()}.txt`)}}}extension(e){const t=e||this.fragments[0];const s=[".flv",".mp4",".m4s"].find(e=>t.url.includes(e));if(s){return s}else{console.warn("No extension detected.");return".flv"}}makeBlob(e,t){return new Blob(Array.isArray(e)?e:[e],{type:this.extension(t)===".flv"?"video/x-flv":"video/mp4"})}cleanUpOldBlobUrl(){const e=dq("a#video-complete").getAttribute("href");if(e&&!dq(`.link[href="${e}"]`)){URL.revokeObjectURL(e)}dqa(".toast-card-header").filter(e=>e.innerText.includes("下载视频")).forEach(e=>e.querySelector(".toast-card-dismiss").click())}async downloadDanmaku(){if(this.danmakuOption!=="无"){const e=new i(r.cid);await e.fetchInfo();if(this.danmakuOption==="XML"){return e.rawXML}else{const{convertToAss:s}=await t.importAsync("download-danmaku");return s(e.rawXML)}}else{return null}}async downloadSingle(e){const t=await this.downloadDanmaku();const[a]=e;if(t===null){const e=this.makeBlob(a);const t=s()+this.extension();return{blob:e,filename:t}}else{const e=new JSZip;e.file(s()+this.extension(),this.makeBlob(a));e.file(s()+"."+this.danmakuOption.toLowerCase(),t);const i=await e.generateAsync({type:"blob"});const n=s()+".zip";return{blob:i,filename:n}}}async downloadMultiple(e){const t=new JSZip;const a=s();if(e.length>1){e.forEach((e,s)=>{const i=this.fragments[s];t.file(`${a} - ${s+1}${this.extension(i)}`,this.makeBlob(e,i))})}else{const[s]=e;t.file(`${a}${this.extension()}`,this.makeBlob(s))}const i=await this.downloadDanmaku();if(i!==null){t.file(s()+"."+this.danmakuOption.toLowerCase(),i)}const n=await t.generateAsync({type:"blob"});const o=a+".zip";return{blob:n,filename:o}}async download(){const e=[];this.videoSpeed.startMeasure();for(const t of this.fragments){const s=await this.downloadFragment(t);e.push(s)}if(e.length<1){throw new Error("下载失败.")}let{blob:t,filename:s}=await(async()=>{if(e.length===1){return await this.downloadSingle(e)}else{return await this.downloadMultiple(e)}})();this.cleanUpOldBlobUrl();const a=URL.createObjectURL(t);this.progress&&this.progress(0);this.videoSpeed.stopMeasure();return{url:a,filename:s}}}class u{constructor(e){this.lastProgress=0;this.measureInterval=1e3;this.workingDownloader=e}startMeasure(){this.intervalTimer=setInterval(()=>{const e=this.workingDownloader.progressMap?[...this.workingDownloader.progressMap.values()].reduce((e,t)=>e+t,0):0;const t=e-this.lastProgress;if(this.speedUpdate!==undefined){this.speedUpdate(formatFileSize(t)+"/s")}this.lastProgress=e},this.measureInterval)}stopMeasure(){clearInterval(this.intervalTimer)}}async function p(){const e=await SpinQuery.select(()=>(unsafeWindow||window).aid);const t=await SpinQuery.select(()=>(unsafeWindow||window).cid);if(!(e&&t)){return false}r.aid=e;r.cid=t;if(document.URL.indexOf("bangumi")!==-1){r.entity=new o}else{r.entity=new n}try{l=await d.getAvailableFormats()}catch(e){return false}return true}async function f(){c=l[0];t.applyStyle("downloadVideoStyle");dq("#download-video").addEventListener("click",()=>{dq(".download-video").classList.toggle("opened");dq(".gui-settings-mask").click()});dq("#download-video").addEventListener("mouseover",()=>{document.body.insertAdjacentHTML("beforeend",t.import("downloadVideoHtml"));m()},{once:true})}async function m(){let n;const o=new Map;const u=new Vue({el:".download-video",components:{VDropdown:()=>t.importAsync("v-dropdown.vue"),VCheckbox:()=>t.importAsync("v-checkbox.vue"),RpcProfiles:()=>t.importAsync("aria2-rpc-profiles.vue")},data:{downloadSingle:true,coverUrl:EmptyImageUrl,aid:r.aid,cid:r.cid,dashModel:{value:e.downloadVideoFormat,items:["flv","dash"]},qualityModel:{value:c.displayName,items:l.map(e=>e.displayName)},danmakuModel:{value:e.downloadVideoDefaultDanmaku,items:["无","XML","ASS"]},progressPercent:0,size:"获取大小中",blobUrl:"",episodeList:[],downloading:false,speed:"",batch:false,rpcSettings:e.aria2RpcOption,showRpcSettings:false,busy:false,saveRpcSettingsText:"保存配置",enableDash:e.enableDashDownload},computed:{displaySize(){if(typeof this.size==="string"){return this.size}return formatFileSize(this.size)},sizeWarning(){if(typeof this.size==="string"){return false}return this.size>1073741824},selectedEpisodeCount(){return this.episodeList.filter(e=>e.checked).length},dash(){return this.dashModel.value==="dash"}},methods:{close(){this.$el.classList.remove("opened")},danmakuOptionChange(){e.downloadVideoDefaultDanmaku=this.danmakuModel.value},async dashChange(){const e=this.dashModel.value;let t=[];if(e==="flv"){t=await d.getAvailableFormats()}else{t=await d.getAvailableDashFormats()}l=t;[c]=e;this.qualityModel.items=t.map(e=>e.displayName);[this.qualityModel.value]=this.qualityModel.items},async formatChange(){const e=this.getFormat();const t=o.get(e);if(t){this.size=t;return}try{this.size="获取大小中";const t=await e.downloadInfo(this.dash);this.size=t.totalSize;o.set(e,this.size)}catch(e){this.size="获取大小失败"}},getFormat(){const e=l.find(e=>e.displayName===this.qualityModel.value);if(!e){console.error(`No format found. model value = ${this.qualityModel.value}`);return null}return e},async exportData(e){if(this.busy===true){return}try{this.busy=true;if(!this.downloadSingle){await this.exportBatchData(e);return}const t=this.getFormat();const s=await t.downloadInfo(this.dash);s.danmakuOption=this.danmakuModel.value;switch(e){case"copyLink":await s.copyUrl();Toast.success("已复制链接到剪贴板.","下载视频",3e3);break;case"aria2":await s.exportAria2(false);break;case"aria2RPC":await s.exportAria2(true);break;case"copyVLD":await s.exportData(true);Toast.success("已复制VLD数据到剪贴板.","下载视频",3e3);break;case"exportVLD":await s.exportData(false);break;default:break}}catch(e){logError(e)}finally{this.busy=false}},async exportBatchData(e){const a=this.episodeList;if(a.every(e=>e.checked===false)){Toast.info("请至少选择1集或以上的数量!","批量导出",3e3);return}const n=e=>{const t=a.find(t=>t.cid===e.cid);if(t===undefined){return false}return t.checked};const o=this.getFormat();if(this.danmakuModel.value!=="无"){const e=Toast.info("下载弹幕中...","批量导出");const s=new JSZip;try{if(this.danmakuModel.value==="XML"){for(const e of a.filter(n)){const t=new i(e.cid);await t.fetchInfo();s.file(e.title+".xml",t.rawXML)}}else{const{convertToAss:e}=await t.importAsync("download-danmaku");for(const t of a.filter(n)){const a=new i(t.cid);await a.fetchInfo();s.file(t.title+".ass",await e(a.rawXML))}}h.downloadBlob(await s.generateAsync({type:"blob"}),this.cid+".danmakus.zip")}catch(e){logError(`弹幕下载失败`)}finally{e.dismiss()}}const r=Toast.info("获取链接中...","批量导出");this.batchExtractor.itemFilter=n;let l;try{switch(e){case"aria2":l=await this.batchExtractor.collectAria2(o,r);h.downloadBlob(new Blob([l],{type:"text/plain"}),s(false)+".txt");return;case"aria2RPC":await this.batchExtractor.collectAria2(o,r,true);Toast.success(`成功发送了批量请求.`,"aria2 RPC",3e3);return;case"copyVLD":GM_setClipboard(await this.batchExtractor.collectData(o,r),{mimetype:"text/plain"});Toast.success("已复制批量vld数据到剪贴板.","批量导出",3e3);return;case"exportVLD":l=await this.batchExtractor.collectData(o,r);h.downloadBlob(new Blob([l],{type:"text/json"}),s(false)+".json");return;default:return}}catch(e){logError(e)}finally{r.dismiss()}},async checkBatch(){const e=["/www.bilibili.com/bangumi","/www.bilibili.com/video/av"];if(!e.some(e=>document.URL.includes(e))){this.batch=false;this.episodeList=[];return}const{BatchExtractor:s}=await t.importAsync("batch-download");if(await s.test()!==true){this.batch=false;this.episodeList=[];return}this.batchExtractor=new s;this.batch=true;this.episodeList=(await this.batchExtractor.getItemList()).map((e,t)=>{return{aid:e.aid,cid:e.cid,title:e.title,index:t,checked:true}})},cancelDownload(){if(n){n.cancelDownload()}},async startDownload(){const e=this.getFormat();try{this.downloading=true;const t=await e.downloadInfo(this.dash);t.videoSpeed.speedUpdate=(e=>this.speed=e);t.danmakuOption=this.danmakuModel.value;t.progress=(e=>{this.progressPercent=Math.trunc(e*100)});n=t;const s=await t.download();const a=document.getElementById("video-complete");a.setAttribute("href",s.url);a.setAttribute("download",s.filename);a.click();Toast.success(`下载完成: ${s.filename} 再次保存`,"下载视频")}catch(e){if(e!=="canceled"){logError(e)}this.progressPercent=0}finally{this.downloading=false;this.speed=""}},selectAllEpisodes(){this.episodeList.forEach(e=>e.checked=true)},unselectAllEpisodes(){this.episodeList.forEach(e=>e.checked=false)},inverseAllEpisodes(){this.episodeList.forEach(e=>e.checked=!e.checked)},toggleRpcSettings(){this.showRpcSettings=!this.showRpcSettings},saveRpcSettings(){if(this.rpcSettings.host===""){this.rpcSettings.host="127.0.0.1"}if(this.rpcSettings.port===""){this.rpcSettings.port="6800"}e.aria2RpcOption=this.rpcSettings;const t=e.aria2RpcOptionProfiles.find(t=>t.name===e.aria2RpcOptionSelectedProfile);if(t){Object.assign(t,this.rpcSettings);e.aria2RpcOptionProfiles=e.aria2RpcOptionProfiles}this.saveRpcSettingsText="已保存";setTimeout(()=>this.saveRpcSettingsText="保存配置",2e3)},updateProfile(t){e.aria2RpcOption=this.rpcSettings=_.omit(t,"name")}}});Observer.videoChange(async()=>{u.close();u.batch=false;u.downloadSingle=true;const e=dq("#download-video");const t=await p();e.style.display=t?"flex":"none";if(!t){return}u.aid=r.aid;u.cid=r.cid;const s=new a(r.aid);await s.fetchInfo();u.coverUrl=s.coverUrl.replace("http:","https:");l=await d.getAvailableFormats();[c]=l;u.qualityModel={value:c.displayName,items:l.map(e=>e.displayName)};u.formatChange();await u.checkBatch()})}return{widget:{content:`\n`,condition:p,success:f}}}})(); +onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/min/download-video.min.html"] = `

下载视频

单个视频
批量导出
警告: 过大的视频大小会在直接下载时占用大量内存, 并可能导致浏览器标签页崩溃. 请考虑降低清晰度或使用导出选项.
预计大小: {{displaySize}}
格式
清晰度
弹幕

直接下载

{{speed}}
{{progressPercent}}%

选集

{{selectedEpisodeCount}}/{{episodeList.length}}

导出

{{showRpcSettings ? '取消' : 'aria2 RPC'}}

aria2 RPC

配置

主机
端口
密钥
限速
默认路径
路径
最终路径: {{rpcSettings.baseDir + rpcSettings.dir}}
方法
开始下载
{{saveRpcSettingsText}}
`; +onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/min/download-video.min.js"] = (()=>{return(e,t)=>{const{getFriendlyTitle:s}=t.import("title");const{VideoInfo:a,DanmakuInfo:i}=t.import("video-info");class n{constructor(){this.menuClasses=["quality","action","progress"];[this.currentMenuClass]=this.menuClasses}get menuPanel(){return document.querySelector(".download-video-panel")}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}closeMenu(){this.menuPanel.classList.remove("opened");setTimeout(()=>this.resetMenuClass(),200)}addError(){this.menuPanel.classList.add("error")}removeError(){this.menuPanel.classList.remove("error");this.resetMenuClass()}async getDashUrl(e){throw new Error("video dash is not supported")}async getUrl(e){if(e){return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&otype=json`}}}class o extends n{async getDashUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&qn=${e}&otype=json&fourk=1&fnval=16`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&otype=json&fourk=1&fnval=16`}}async getUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=&otype=json`}}}const r={entity:new n,aid:"",cid:""};let l=[];let c=null;class d{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async downloadInfo(e=false){const t=new h(this);await t.fetchVideoInfo(e);return t}static parseFormats(e){const t=e.accept_quality;const s=e.accept_format.split(",");const a=e.accept_description;const i=t.map((e,t)=>{return new d(e,s[t],a[t])});return i}static async getAvailableDashFormats(){const e=await r.entity.getDashUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}return d.parseFormats(t.result)}static async getAvailableFormats(){const e=await r.entity.getUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}const s=t.data||t.result||t;return d.parseFormats(s)}}class h{constructor(e,t){this.fragmentSplitFactor=6*2;this.workingXhr=null;this.progressMap=new Map;this.format=e;this.fragments=t||[];this.videoSpeed=new u(this)}get totalSize(){return this.fragments.map(e=>e.size).reduce((e,t)=>e+t)}async fetchVideoInfo(e=false){if(!e){const e=await r.entity.getUrl(this.format.quality);const t=await Ajax.getTextWithCredentials(e);const s=JSON.parse(t.replace(/http:/g,"https:"));const a=s.data||s.result||s;if(a.quality!==this.format.quality){throw new Error("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const i=a.durl;this.fragments=i.map(e=>{return{length:e.length,size:e.size,url:e.url,backupUrls:e.backup_url}})}else{const{dashToFragment:e,getDashInfo:s}=await t.importAsync("video-dash");const a=await s(r.aid,r.cid,this.format.quality);const i=a.videoDashes.sort(descendingSort(e=>e.bandWidth))[0];const n=a.audioDashes.sort(descendingSort(e=>e.bandWidth))[0];this.fragments=[e(i),e(n)]}return this.fragments}updateProgress(){const e=this.progressMap?[...this.progressMap.values()].reduce((e,t)=>e+t,0)/this.totalSize:0;if(e>1||e<0){console.error(`[下载视频] 进度异常: ${e}`,this.progressMap.values())}this.progress&&this.progress(e)}cancelDownload(){this.videoSpeed.stopMeasure();if(this.workingXhr!==null){this.workingXhr.forEach(e=>e.abort())}else{logError("Cancel Download Failed: forEach in this.workingXhr not found.")}}downloadFragment(e){const t=[];this.workingXhr=[];this.progressMap=new Map;this.updateProgress();let s;if(e.size<=96*1024*1024){s=e.size/this.fragmentSplitFactor}else{s=16*1024*1024}let a=0;const i=e=>[...this.progressMap.keys()].indexOf(e)+1;while(a{const a=new XMLHttpRequest;a.open("GET",e.url);a.responseType="arraybuffer";a.withCredentials=false;a.addEventListener("progress",e=>{console.log(`[下载视频] 视频片段${i(a)}下载进度: ${e.loaded}/${r} bytes loaded, ${o}`);this.progressMap.set(a,e.loaded);this.updateProgress()});a.addEventListener("load",()=>{if((""+a.status)[0]==="2"){console.log(`[下载视频] 视频片段${i(a)}下载完成`);t(a.response)}else{s(`视频片段${i(a)}请求失败, response = ${a.status}`)}});a.addEventListener("abort",()=>s("canceled"));a.addEventListener("error",()=>{console.error(`[下载视频] 视频片段${i(a)}下载失败: ${o}`);this.progressMap.set(a,0);this.updateProgress();a.open("GET",e.url);a.setRequestHeader("Range",o);a.send()});a.setRequestHeader("Range",o);this.progressMap.set(a,0);a.send();this.workingXhr.push(a)}));a=Math.round(a+s)+1}return Promise.all(t)}async copyUrl(){const e=this.fragments.map(e=>e.url).reduce((e,t)=>e+"\r\n"+t);GM_setClipboard(e,"text")}async showUrl(){const e=this.fragments.map(e=>`\n${e.url}\n`).reduce((e,t)=>e+"\r\n"+t);Toast.success(e,"显示链接")}static downloadBlob(e,t){const s=document.createElement("a");let a;if(typeof e==="string"){a=e}else{a=URL.createObjectURL(e)}s.setAttribute("href",a);s.setAttribute("download",t);document.body.appendChild(s);s.click();s.remove();URL.revokeObjectURL(a)}async exportData(e=false){const t=JSON.stringify([{fragments:this.fragments,title:s(),totalSize:this.fragments.map(e=>e.size).reduce((e,t)=>e+t),referer:document.URL.replace(window.location.search,"")}]);if(e){GM_setClipboard(t,"text")}else{const e=new Blob([t],{type:"text/json"});const a=await this.downloadDanmaku();if(a!==null){const t=new JSZip;t.file(`${s()}.json`,e);t.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await t.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(e,`${s()}.json`)}}}async exportAria2(a=false){if(a){const a=await this.downloadDanmaku();if(a!==null){h.downloadBlob(new Blob([a]),`${s()}.${this.danmakuOption==="ASS"?"ass":"xml"}`)}const i=e.aria2RpcOption;const n=this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}const n=[];if(i.secretKey!==""){n.push(`token:${i.secretKey}`)}n.push([e.url]);n.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${s()}${a}${this.extension(e)}`,split:this.fragmentSplitFactor,dir:i.baseDir+i.dir||undefined,"max-download-limit":i.maxDownloadLimit||undefined});const o=encodeURIComponent(`${s()}${a}`);return{params:n,id:o}});const{sendRpc:o}=await t.importAsync("aria2-rpc");await o(n)}else{const e=`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}return`\n${e.url}\nreferer=${document.URL.replace(window.location.search,"")}\nuser-agent=${UserAgent}\nout=${s()}${a}${this.extension(e)}\nsplit=${this.fragmentSplitFactor}\n`.trim()}).join("\n")}\n`.trim();const t=new Blob([e],{type:"text/plain"});const a=await this.downloadDanmaku();if(a!==null){const e=new JSZip;e.file(`${s()}.txt`,t);e.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await e.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(t,`${s()}.txt`)}}}extension(e){const t=e||this.fragments[0];const s=[".flv",".mp4",".m4s"].find(e=>t.url.includes(e));if(s){return s}else{console.warn("No extension detected.");return".flv"}}makeBlob(e,t){return new Blob(Array.isArray(e)?e:[e],{type:this.extension(t)===".flv"?"video/x-flv":"video/mp4"})}cleanUpOldBlobUrl(){const e=dq("a#video-complete").getAttribute("href");if(e&&!dq(`.link[href="${e}"]`)){URL.revokeObjectURL(e)}dqa(".toast-card-header").filter(e=>e.innerText.includes("下载视频")).forEach(e=>e.querySelector(".toast-card-dismiss").click())}async downloadDanmaku(){if(this.danmakuOption!=="无"){const e=new i(r.cid);await e.fetchInfo();if(this.danmakuOption==="XML"){return e.rawXML}else{const{convertToAss:s}=await t.importAsync("download-danmaku");return s(e.rawXML)}}else{return null}}async downloadSingle(e){const t=await this.downloadDanmaku();const[a]=e;if(t===null){const e=this.makeBlob(a);const t=s()+this.extension();return{blob:e,filename:t}}else{const e=new JSZip;e.file(s()+this.extension(),this.makeBlob(a));e.file(s()+"."+this.danmakuOption.toLowerCase(),t);const i=await e.generateAsync({type:"blob"});const n=s()+".zip";return{blob:i,filename:n}}}async downloadMultiple(e){const t=new JSZip;const a=s();if(e.length>1){e.forEach((e,s)=>{const i=this.fragments[s];t.file(`${a} - ${s+1}${this.extension(i)}`,this.makeBlob(e,i))})}else{const[s]=e;t.file(`${a}${this.extension()}`,this.makeBlob(s))}const i=await this.downloadDanmaku();if(i!==null){t.file(s()+"."+this.danmakuOption.toLowerCase(),i)}const n=await t.generateAsync({type:"blob"});const o=a+".zip";return{blob:n,filename:o}}async download(){const e=[];this.videoSpeed.startMeasure();for(const t of this.fragments){const s=await this.downloadFragment(t);e.push(s)}if(e.length<1){throw new Error("下载失败.")}let{blob:t,filename:s}=await(async()=>{if(e.length===1){return await this.downloadSingle(e)}else{return await this.downloadMultiple(e)}})();this.cleanUpOldBlobUrl();const a=URL.createObjectURL(t);this.progress&&this.progress(0);this.videoSpeed.stopMeasure();return{url:a,filename:s}}}class u{constructor(e){this.lastProgress=0;this.measureInterval=1e3;this.workingDownloader=e}startMeasure(){this.intervalTimer=setInterval(()=>{const e=this.workingDownloader.progressMap?[...this.workingDownloader.progressMap.values()].reduce((e,t)=>e+t,0):0;const t=e-this.lastProgress;if(this.speedUpdate!==undefined){this.speedUpdate(formatFileSize(t)+"/s")}this.lastProgress=e},this.measureInterval)}stopMeasure(){clearInterval(this.intervalTimer)}}async function p(){const e=await SpinQuery.select(()=>(unsafeWindow||window).aid);const t=await SpinQuery.select(()=>(unsafeWindow||window).cid);if(!(e&&t)){return false}r.aid=e;r.cid=t;if(document.URL.indexOf("bangumi")!==-1){r.entity=new o}else{r.entity=new n}try{l=await d.getAvailableFormats()}catch(e){return false}return true}async function f(){c=l[0];t.applyStyle("downloadVideoStyle");dq("#download-video").addEventListener("click",()=>{dq(".download-video").classList.toggle("opened");dq(".gui-settings-mask").click()});dq("#download-video").addEventListener("mouseover",()=>{document.body.insertAdjacentHTML("beforeend",t.import("downloadVideoHtml"));m()},{once:true})}async function m(){let n;const o=new Map;const u=new Vue({el:".download-video",components:{VDropdown:()=>t.importAsync("v-dropdown.vue"),VCheckbox:()=>t.importAsync("v-checkbox.vue"),RpcProfiles:()=>t.importAsync("aria2-rpc-profiles.vue")},data:{downloadSingle:true,coverUrl:EmptyImageUrl,aid:r.aid,cid:r.cid,dashModel:{value:e.downloadVideoFormat,items:["flv","dash"]},qualityModel:{value:c.displayName,items:l.map(e=>e.displayName)},danmakuModel:{value:e.downloadVideoDefaultDanmaku,items:["无","XML","ASS"]},progressPercent:0,size:"获取大小中",blobUrl:"",episodeList:[],downloading:false,speed:"",batch:false,rpcSettings:e.aria2RpcOption,showRpcSettings:false,busy:false,saveRpcSettingsText:"保存配置",enableDash:e.enableDashDownload},computed:{displaySize(){if(typeof this.size==="string"){return this.size}return formatFileSize(this.size)},sizeWarning(){if(typeof this.size==="string"){return false}return this.size>1073741824},selectedEpisodeCount(){return this.episodeList.filter(e=>e.checked).length},dash(){return this.dashModel.value==="dash"}},methods:{close(){this.$el.classList.remove("opened")},danmakuOptionChange(){e.downloadVideoDefaultDanmaku=this.danmakuModel.value},async dashChange(){const e=this.dashModel.value;let t=[];if(e==="flv"){t=await d.getAvailableFormats()}else{t=await d.getAvailableDashFormats()}l=t;[c]=e;this.qualityModel.items=t.map(e=>e.displayName);[this.qualityModel.value]=this.qualityModel.items},async formatChange(){const e=this.getFormat();const t=o.get(e);if(t){this.size=t;return}try{this.size="获取大小中";const t=await e.downloadInfo(this.dash);this.size=t.totalSize;o.set(e,this.size)}catch(e){this.size="获取大小失败"}},getFormat(){const e=l.find(e=>e.displayName===this.qualityModel.value);if(!e){console.error(`No format found. model value = ${this.qualityModel.value}`);return null}return e},async exportData(e){if(this.busy===true){return}try{this.busy=true;if(!this.downloadSingle){await this.exportBatchData(e);return}const t=this.getFormat();const s=await t.downloadInfo(this.dash);s.danmakuOption=this.danmakuModel.value;switch(e){case"copyLink":await s.copyUrl();Toast.success("已复制链接到剪贴板.","下载视频",3e3);break;case"showLink":await s.showUrl();break;case"aria2":await s.exportAria2(false);break;case"aria2RPC":await s.exportAria2(true);break;case"copyVLD":await s.exportData(true);Toast.success("已复制VLD数据到剪贴板.","下载视频",3e3);break;case"exportVLD":await s.exportData(false);break;default:break}}catch(e){logError(e)}finally{this.busy=false}},async exportBatchData(e){const a=this.episodeList;if(a.every(e=>e.checked===false)){Toast.info("请至少选择1集或以上的数量!","批量导出",3e3);return}const n=e=>{const t=a.find(t=>t.cid===e.cid);if(t===undefined){return false}return t.checked};const o=this.getFormat();if(this.danmakuModel.value!=="无"){const e=Toast.info("下载弹幕中...","批量导出");const s=new JSZip;try{if(this.danmakuModel.value==="XML"){for(const e of a.filter(n)){const t=new i(e.cid);await t.fetchInfo();s.file(e.title+".xml",t.rawXML)}}else{const{convertToAss:e}=await t.importAsync("download-danmaku");for(const t of a.filter(n)){const a=new i(t.cid);await a.fetchInfo();s.file(t.title+".ass",await e(a.rawXML))}}h.downloadBlob(await s.generateAsync({type:"blob"}),this.cid+".danmakus.zip")}catch(e){logError(`弹幕下载失败`)}finally{e.dismiss()}}const r=Toast.info("获取链接中...","批量导出");this.batchExtractor.itemFilter=n;let l;try{switch(e){case"aria2":l=await this.batchExtractor.collectAria2(o,r);h.downloadBlob(new Blob([l],{type:"text/plain"}),s(false)+".txt");return;case"aria2RPC":await this.batchExtractor.collectAria2(o,r,true);Toast.success(`成功发送了批量请求.`,"aria2 RPC",3e3);return;case"copyVLD":GM_setClipboard(await this.batchExtractor.collectData(o,r),{mimetype:"text/plain"});Toast.success("已复制批量vld数据到剪贴板.","批量导出",3e3);return;case"exportVLD":l=await this.batchExtractor.collectData(o,r);h.downloadBlob(new Blob([l],{type:"text/json"}),s(false)+".json");return;default:return}}catch(e){logError(e)}finally{r.dismiss()}},async checkBatch(){const e=["/www.bilibili.com/bangumi","/www.bilibili.com/video/av"];if(!e.some(e=>document.URL.includes(e))){this.batch=false;this.episodeList=[];return}const{BatchExtractor:s}=await t.importAsync("batch-download");if(await s.test()!==true){this.batch=false;this.episodeList=[];return}this.batchExtractor=new s;this.batch=true;this.episodeList=(await this.batchExtractor.getItemList()).map((e,t)=>{return{aid:e.aid,cid:e.cid,title:e.title,index:t,checked:true}})},cancelDownload(){if(n){n.cancelDownload()}},async startDownload(){const e=this.getFormat();try{this.downloading=true;const t=await e.downloadInfo(this.dash);t.videoSpeed.speedUpdate=(e=>this.speed=e);t.danmakuOption=this.danmakuModel.value;t.progress=(e=>{this.progressPercent=Math.trunc(e*100)});n=t;const s=await t.download();const a=document.getElementById("video-complete");a.setAttribute("href",s.url);a.setAttribute("download",s.filename);a.click();Toast.success(`下载完成: ${s.filename} 再次保存`,"下载视频")}catch(e){if(e!=="canceled"){logError(e)}this.progressPercent=0}finally{this.downloading=false;this.speed=""}},selectAllEpisodes(){this.episodeList.forEach(e=>e.checked=true)},unselectAllEpisodes(){this.episodeList.forEach(e=>e.checked=false)},inverseAllEpisodes(){this.episodeList.forEach(e=>e.checked=!e.checked)},toggleRpcSettings(){this.showRpcSettings=!this.showRpcSettings},saveRpcSettings(){if(this.rpcSettings.host===""){this.rpcSettings.host="127.0.0.1"}if(this.rpcSettings.port===""){this.rpcSettings.port="6800"}e.aria2RpcOption=this.rpcSettings;const t=e.aria2RpcOptionProfiles.find(t=>t.name===e.aria2RpcOptionSelectedProfile);if(t){Object.assign(t,this.rpcSettings);e.aria2RpcOptionProfiles=e.aria2RpcOptionProfiles}this.saveRpcSettingsText="已保存";setTimeout(()=>this.saveRpcSettingsText="保存配置",2e3)},updateProfile(t){e.aria2RpcOption=this.rpcSettings=_.omit(t,"name")}}});Observer.videoChange(async()=>{u.close();u.batch=false;u.downloadSingle=true;const e=dq("#download-video");const t=await p();e.style.display=t?"flex":"none";if(!t){return}u.aid=r.aid;u.cid=r.cid;const s=new a(r.aid);await s.fetchInfo();u.coverUrl=s.coverUrl.replace("http:","https:");l=await d.getAvailableFormats();[c]=l;u.qualityModel={value:c.displayName,items:l.map(e=>e.displayName)};u.formatChange();await u.checkBatch()})}return{widget:{content:`\n`,condition:p,success:f}}}})(); onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/min/dpi-img.vue.min.js"] = (()=>{return(t,e)=>{const i=``;return{export:Object.assign({template:i},{props:["size","src","blur"],computed:{srcset(){if(!this.src||!this.size){return null}return getDpiSourceSet(this.src,this.size)},width(){if(typeof this.size==="object"&&"width"in this.size){return this.size.width}return null},height(){if(typeof this.size==="object"&&"height"in this.size){return this.size.height}return null}}})}}})(); onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/min/expand-danmaku.min.js"] = (()=>{return(e,a)=>{if(typeof isEmbeddedPlayer!=="undefined"&&isEmbeddedPlayer()){return}Observer.videoChange(async()=>{const e=await SpinQuery.select(".bui-collapse-wrap");if(e&&e.classList.contains("bui-collapse-wrap-folded")){const e=await SpinQuery.select(".bui-collapse-header");e.click()}})}})(); onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/min/expand-description.min.css"] = `.play-up-info .play-up-self,.video-desc .info{height:auto!important}.play-up-info .play-up-self-btn,.video-desc .btn{display:none!important}`; diff --git a/bilibili-evolved.user.js b/bilibili-evolved.user.js index f8eeca47b..851ce1851 100644 --- a/bilibili-evolved.user.js +++ b/bilibili-evolved.user.js @@ -1071,8 +1071,8 @@ onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/mi onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-audio.min.js"] = (()=>{return(e,t)=>{class n{constructor(){this.sid=null;this.progress=null}async getDownloadUrl(){const e=`https://www.bilibili.com/audio/music-service-c/web/url?sid=${this.sid}&privilege=2&quality=2`;const t=await Ajax.getJsonWithCredentials(e);if(t.code===0){return t.data.cdns.shift()}else{logError("获取下载链接失败, 请确保当前账号有下载权限.","下载音频",1e4);return null}}async download(){const e=await this.getDownloadUrl();return new Promise((t,n)=>{const s=new XMLHttpRequest;s.open("GET",e);s.responseType="blob";s.addEventListener("load",()=>t(s.response));s.addEventListener("error",()=>n(s.status));s.addEventListener("progress",e=>this.progress&&this.progress(100*e.loaded/e.total));s.send()})}}const s="下载音频";return{export:n,widget:{content:`\n\n\n${s}\n\n`,condition:()=>document.URL.includes("bilibili.com/audio"),success:async()=>{await SpinQuery.select(()=>document.querySelector("#app"));const e=document.querySelector("#download-audio");const t=e.querySelector("span");const o=new n;o.progress=(e=>{t.innerHTML=`${Math.round(e)}%`});const i=document.querySelector("#download-audio-link");e.addEventListener("click",async e=>{if(o.sid===null||e.target===i){return}const n=await o.download();t.innerHTML=s;const r=i.getAttribute("href");if(r){URL.revokeObjectURL(r)}i.setAttribute("href",URL.createObjectURL(n));const d=(()=>{const e=document.querySelector(".song-title");if(e){return e.getAttribute("title")}else{return"神秘音频"}})();i.setAttribute("download",d+".m4a");i.click()});Observer.childList("#app",()=>{const t=document.URL.match(/bilibili\.com\/audio\/au([\d]+)/);if(t&&t[1]){e.disabled=false;o.sid=t[1]}else{e.disabled=true}})}}}}})(); onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-danmaku.min.js"] = (()=>{return(t,e)=>{const{getFriendlyTitle:n}=e.import("title");const{DanmakuInfo:i}=e.import("video-info");const{DanmakuConverter:a}=e.import("danmaku-converter");async function o(t){const e=n();let i={title:e};try{await loadDanmakuSettingsPanel();const t=t=>{const e=parseFloat(dq(t).style.transform.replace(/translateX\(([\d\.]+)/,"$1"));const n={0:0,44:1,94:2,144:3,188:4}[e];return n};i.font=dq(".bilibili-player-video-danmaku-setting-right-font .bui-select-result").innerText;i.alpha=parseFloat(dq(".bilibili-player-setting-opacity .bui-bar").style.transform.replace(/scaleX\(([\d\.]+)\)/,"$1"));i.duration=(()=>{const e=[18,14,10,8,6][t(".bilibili-player-setting-speedplus .bui-thumb")];return t=>{switch(t.type){case 4:case 5:return 4;default:return e}}})();i.blockTypes=(()=>{let t=[];const e={".bilibili-player-block-filter-type[ftype=scroll]":[1,2,3],".bilibili-player-block-filter-type[ftype=top]":[5],".bilibili-player-block-filter-type[ftype=bottom]":[4],".bilibili-player-block-filter-type[ftype=color]":["color"]};for(const[n,i]of Object.entries(e)){if(dq(n).classList.contains("disabled")){t=t.concat(i)}}return t.concat(7,8)})();const e=[1.4,1.2,1,.8,.6][t(".bilibili-player-setting-fontsize .bui-thumb")];i.resolution={x:1920*e,y:1080*e};i.bottomMarginPercent=[.75,.5,.25,0,0][t(".bilibili-player-setting-area .bui-thumb")];if(i.bottomMarginPercent===0&&dq(".bilibili-player-video-danmaku-setting-left-preventshade input").checked){i.bottomMarginPercent=.15}i.bold=dq(".bilibili-player-video-danmaku-setting-right-font-bold input").checked}catch(t){i={font:"微软雅黑",alpha:.6,duration:t=>{switch(t.type){case 4:case 5:return 4;default:return 6}},blockTypes:[7,8],resolution:{x:1920,y:1080},bottomMarginPercent:.15,bold:false}}const o=new a(i);const l=o.convertToAssDocument(t);return l.generateAss()}async function l(t,e){const a=n();const l=new i((unsafeWindow||window).cid);await l.fetchInfo();const r=await(async()=>{if(t===true){return new Blob([await o(l.rawXML)],{type:"text/plain"})}else{return new Blob([l.rawXML],{type:"text/plain"})}})();const s=URL.createObjectURL(r);const c=dq("#danmaku-link");const d=c.getAttribute("href");if(d){URL.revokeObjectURL(d)}clearTimeout(e);dq("#download-danmaku>span").innerHTML="下载弹幕";c.setAttribute("download",`${a}.${t?"ass":"xml"}`);c.setAttribute("href",s);c.click()}return{export:{downloadDanmaku:l,convertToAss:o},widget:{content:`\n\n\n下载弹幕\n\n`,condition:async()=>{let t=await SpinQuery.select(()=>(unsafeWindow||window).cid);return Boolean(t)},success:()=>{const t=document.querySelector("#danmaku-link");dq("#download-danmaku").addEventListener("click",e=>{if(e.target!==t){const t=setTimeout(()=>dq("#download-danmaku>span").innerHTML="请稍侯...",200);l(e.shiftKey,t)}})}}}}})(); onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.css"] = `.download-video{--download-video-background:white;--download-video-foreground:black;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) scale(.95);background:var(--download-video-background);color:var(--download-video-foreground);box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;z-index:10001;pointer-events:none;opacity:0;padding:16px;width:300px;display:flex;flex-direction:column;align-items:stretch;justify-content:space-between;max-height:90%}.download-video,.download-video *{transition:.2s ease-out;-webkit-tap-highlight-color:transparent!important}.download-video.opened{pointer-events:initial;opacity:1;transform:translate(-50%,-50%) scale(1)}body.dark .download-video{--download-video-background:#222;--download-video-foreground:#eee}.round-corner .download-video,.round-corner .download-video .episode-list .v-checkbox,.round-corner .download-video .rpc-settings{border-radius:var(--large-corner-radius)}.round-corner .download-video .button,.round-corner .download-video .cover,.round-corner .download-video .direct-download .progress .background,.round-corner .download-video .direct-download .progress .foreground,.round-corner .download-video .rpc-settings input[type=text],.round-corner .download-video .title,.round-corner .download-video button{border-radius:var(--corner-radius)}.download-video .cover{width:240px;min-height:135px;background-color:#8882}.download-video .header{display:flex;justify-content:space-between;align-items:center}.download-video h1{font-size:16pt;font-weight:700;color:var(--download-video-foreground)}.download-video .header .mdi-close{font-size:16pt;color:var(--download-video-foreground);cursor:pointer}.download-video .header .mdi-close:hover{color:var(--theme-color)}.download-video .info{display:flex;flex-direction:column;align-items:center;margin-top:12px}.download-video .title{display:flex;flex-direction:column;justify-content:space-between;align-items:stretch;margin-top:12px}.download-video .title .bold{width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center}.download-video .title .size{font-size:10pt;color:var(--download-video-foreground);align-self:center;display:flex;align-items:center;height:24px}.download-video .bold{color:var(--download-video-foreground);font-weight:700;font-size:12pt}.download-video .option-item{display:flex;justify-content:space-between;align-items:center;padding-top:12px;font-size:10pt}.download-video .tabs{display:flex;padding-top:12px;font-size:10pt}.download-video .tabs .tab{padding:6px 12px;border-bottom:2px solid transparent;cursor:pointer}.download-video .tabs .tab.active{color:var(--theme-color);border-bottom-color:var(--theme-color)}.download-video .separator{height:1px;background:#8884;width:100%;margin:12px 0;flex-shrink:0}.download-video h2{font-size:12pt;font-weight:700;color:var(--download-video-foreground)}.download-video .direct-download{display:flex;flex-direction:column;align-items:start;justify-content:space-between}.download-video .button,.download-video button{border:none;outline:0!important;padding:6px 16px;font-size:10pt;cursor:pointer;background:#8884;color:var(--download-video-foreground)}.download-video .button:hover,.download-video button:hover{background:#8886}.download-video .primary{background:var(--theme-color);color:var(--foreground-color);align-self:center}.download-video .primary:hover{background:var(--theme-color-90)}.download-video .busy .button,.download-video .busy .primary,.download-video .busy button,.download-video button:disabled{background-color:#0001;color:#8884;cursor:not-allowed}.download-video .busy .button,.download-video .busy button{cursor:wait!important}.download-video .direct-download .progress{align-self:stretch;display:flex;align-items:center;margin-top:8px}.download-video .direct-download .progress .background{flex-grow:1;height:4px;background:#8884;margin-right:4px;position:relative}.download-video .direct-download .progress .background .foreground{position:absolute;top:0;left:0;height:4px;background:linear-gradient(to right,var(--theme-color),var(--theme-color-60))}.download-video .direct-download .progress .percent{width:50px;text-align:right}.download-video .actions{display:flex;flex-wrap:wrap;justify-content:space-between;margin:8px -4px 0}.download-video .actions>*{margin:4px;flex-grow:1;flex-basis:calc(50% - 16px);display:flex;justify-content:center;align-items:center}.download-video i.mdi-alert-circle{font-size:14pt;color:#ff9800}.download-video .size-warning{position:relative;padding:0 8px}.download-video .size-warning-tip{position:absolute;bottom:100%;left:50%;transform:translateX(-50%);background:var(--download-video-background);color:var(--download-video-foreground);border:1px dashed var(--theme-color);z-index:2;font-size:10pt;width:200px;padding:8px;box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;pointer-events:none;opacity:0}.download-video .size-warning:hover .size-warning-tip{opacity:1}.download-video .episode-list .v-checkbox{padding:4px 8px;margin:4px 0;position:relative;background-color:transparent}.download-video .episode-list .v-checkbox.checked{background-color:#8882}.download-video .episode-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.download-video .episode-list{overflow:auto;max-height:320px}.download-video button.list-tool{padding:4px;line-height:1;font-size:12pt;margin:0 4px}.download-video .episode-header h2{flex-grow:1}.download-video .selected-count{width:64px;text-align:right}.download-video .aria2-rpc .mdi{margin-left:8px;font-size:14pt;line-height:1}.download-video .aria2-rpc{position:relative}.download-video .rpc-settings{position:absolute;display:flex;flex-direction:column;align-items:stretch;color:var(--download-video-foreground);background:#fff;bottom:120%;left:50%;cursor:initial;box-shadow:rgba(0,0,0,.2) 0 4px 8px 0;transform-origin:bottom;transform:translateX(-50%) scale(.9);opacity:0;pointer-events:none;padding:8px;z-index:3;transition:.3s cubic-bezier(.18,.89,.32,1.28);width:100%}body.dark .download-video .rpc-settings{background:#282828}.download-video .rpc-settings>*{margin:8px}.download-video .rpc-settings.show{opacity:1;transform:translateX(-50%) scale(1);pointer-events:initial}.download-video .rpc-settings input[type=text]{background:0 0;color:var(--download-video-foreground);border:1px solid #8884;padding:4px;margin-left:8px}.download-video .rpc-settings input[type=text]:focus{border-color:var(--theme-color)}.download-video .rpc-settings .operations{display:flex;align-items:center;justify-content:space-evenly}.download-video .rpc-settings-item{display:flex;align-items:center;justify-content:space-between;white-space:nowrap}.download-video .rpc-settings-item input[type=text]{flex-grow:1;margin-left:24px}.download-video button.cancel-download,.download-video button.start-download{margin-top:8px}.download-video .direct-download-header{align-self:stretch;display:flex;justify-content:space-between;align-items:center}body.dark .download-video .direct-download .progress .foreground{background:linear-gradient(to right,var(--theme-color-70),var(--theme-color))}`; -onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.html"] = `

下载视频

单个视频
批量导出
警告: 过大的视频大小会在直接下载时占用大量内存, 并可能导致浏览器标签页崩溃. 请考虑降低清晰度或使用导出选项.
预计大小: {{displaySize}}
格式
清晰度
弹幕

直接下载

{{speed}}
{{progressPercent}}%

选集

{{selectedEpisodeCount}}/{{episodeList.length}}

导出

{{showRpcSettings ? '取消' : 'aria2 RPC'}}

aria2 RPC

配置

主机
端口
密钥
限速
默认路径
路径
最终路径: {{rpcSettings.baseDir + rpcSettings.dir}}
方法
开始下载
{{saveRpcSettingsText}}
`; -onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.js"] = (()=>{return(e,t)=>{const{getFriendlyTitle:s}=t.import("title");const{VideoInfo:a,DanmakuInfo:i}=t.import("video-info");class n{constructor(){this.menuClasses=["quality","action","progress"];[this.currentMenuClass]=this.menuClasses}get menuPanel(){return document.querySelector(".download-video-panel")}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}closeMenu(){this.menuPanel.classList.remove("opened");setTimeout(()=>this.resetMenuClass(),200)}addError(){this.menuPanel.classList.add("error")}removeError(){this.menuPanel.classList.remove("error");this.resetMenuClass()}async getDashUrl(e){throw new Error("video dash is not supported")}async getUrl(e){if(e){return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&otype=json`}}}class o extends n{async getDashUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&qn=${e}&otype=json&fourk=1&fnval=16`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&otype=json&fourk=1&fnval=16`}}async getUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=&otype=json`}}}const r={entity:new n,aid:"",cid:""};let l=[];let c=null;class d{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async downloadInfo(e=false){const t=new h(this);await t.fetchVideoInfo(e);return t}static parseFormats(e){const t=e.accept_quality;const s=e.accept_format.split(",");const a=e.accept_description;const i=t.map((e,t)=>{return new d(e,s[t],a[t])});return i}static async getAvailableDashFormats(){const e=await r.entity.getDashUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}return d.parseFormats(t.result)}static async getAvailableFormats(){const e=await r.entity.getUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}const s=t.data||t.result||t;return d.parseFormats(s)}}class h{constructor(e,t){this.fragmentSplitFactor=6*2;this.workingXhr=null;this.progressMap=new Map;this.format=e;this.fragments=t||[];this.videoSpeed=new u(this)}get totalSize(){return this.fragments.map(e=>e.size).reduce((e,t)=>e+t)}async fetchVideoInfo(e=false){if(!e){const e=await r.entity.getUrl(this.format.quality);const t=await Ajax.getTextWithCredentials(e);const s=JSON.parse(t.replace(/http:/g,"https:"));const a=s.data||s.result||s;if(a.quality!==this.format.quality){throw new Error("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const i=a.durl;this.fragments=i.map(e=>{return{length:e.length,size:e.size,url:e.url,backupUrls:e.backup_url}})}else{const{dashToFragment:e,getDashInfo:s}=await t.importAsync("video-dash");const a=await s(r.aid,r.cid,this.format.quality);const i=a.videoDashes.sort(descendingSort(e=>e.bandWidth))[0];const n=a.audioDashes.sort(descendingSort(e=>e.bandWidth))[0];this.fragments=[e(i),e(n)]}return this.fragments}updateProgress(){const e=this.progressMap?[...this.progressMap.values()].reduce((e,t)=>e+t,0)/this.totalSize:0;if(e>1||e<0){console.error(`[下载视频] 进度异常: ${e}`,this.progressMap.values())}this.progress&&this.progress(e)}cancelDownload(){this.videoSpeed.stopMeasure();if(this.workingXhr!==null){this.workingXhr.forEach(e=>e.abort())}else{logError("Cancel Download Failed: forEach in this.workingXhr not found.")}}downloadFragment(e){const t=[];this.workingXhr=[];this.progressMap=new Map;this.updateProgress();let s;if(e.size<=96*1024*1024){s=e.size/this.fragmentSplitFactor}else{s=16*1024*1024}let a=0;const i=e=>[...this.progressMap.keys()].indexOf(e)+1;while(a{const a=new XMLHttpRequest;a.open("GET",e.url);a.responseType="arraybuffer";a.withCredentials=false;a.addEventListener("progress",e=>{console.log(`[下载视频] 视频片段${i(a)}下载进度: ${e.loaded}/${r} bytes loaded, ${o}`);this.progressMap.set(a,e.loaded);this.updateProgress()});a.addEventListener("load",()=>{if((""+a.status)[0]==="2"){console.log(`[下载视频] 视频片段${i(a)}下载完成`);t(a.response)}else{s(`视频片段${i(a)}请求失败, response = ${a.status}`)}});a.addEventListener("abort",()=>s("canceled"));a.addEventListener("error",()=>{console.error(`[下载视频] 视频片段${i(a)}下载失败: ${o}`);this.progressMap.set(a,0);this.updateProgress();a.open("GET",e.url);a.setRequestHeader("Range",o);a.send()});a.setRequestHeader("Range",o);this.progressMap.set(a,0);a.send();this.workingXhr.push(a)}));a=Math.round(a+s)+1}return Promise.all(t)}async copyUrl(){const e=this.fragments.map(e=>e.url).reduce((e,t)=>e+"\r\n"+t);GM_setClipboard(e,"text")}static downloadBlob(e,t){const s=document.createElement("a");let a;if(typeof e==="string"){a=e}else{a=URL.createObjectURL(e)}s.setAttribute("href",a);s.setAttribute("download",t);document.body.appendChild(s);s.click();s.remove();URL.revokeObjectURL(a)}async exportData(e=false){const t=JSON.stringify([{fragments:this.fragments,title:s(),totalSize:this.fragments.map(e=>e.size).reduce((e,t)=>e+t),referer:document.URL.replace(window.location.search,"")}]);if(e){GM_setClipboard(t,"text")}else{const e=new Blob([t],{type:"text/json"});const a=await this.downloadDanmaku();if(a!==null){const t=new JSZip;t.file(`${s()}.json`,e);t.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await t.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(e,`${s()}.json`)}}}async exportAria2(a=false){if(a){const a=await this.downloadDanmaku();if(a!==null){h.downloadBlob(new Blob([a]),`${s()}.${this.danmakuOption==="ASS"?"ass":"xml"}`)}const i=e.aria2RpcOption;const n=this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}const n=[];if(i.secretKey!==""){n.push(`token:${i.secretKey}`)}n.push([e.url]);n.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${s()}${a}${this.extension(e)}`,split:this.fragmentSplitFactor,dir:i.baseDir+i.dir||undefined,"max-download-limit":i.maxDownloadLimit||undefined});const o=encodeURIComponent(`${s()}${a}`);return{params:n,id:o}});const{sendRpc:o}=await t.importAsync("aria2-rpc");await o(n)}else{const e=`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}return`\n${e.url}\nreferer=${document.URL.replace(window.location.search,"")}\nuser-agent=${UserAgent}\nout=${s()}${a}${this.extension(e)}\nsplit=${this.fragmentSplitFactor}\n`.trim()}).join("\n")}\n`.trim();const t=new Blob([e],{type:"text/plain"});const a=await this.downloadDanmaku();if(a!==null){const e=new JSZip;e.file(`${s()}.txt`,t);e.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await e.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(t,`${s()}.txt`)}}}extension(e){const t=e||this.fragments[0];const s=[".flv",".mp4",".m4s"].find(e=>t.url.includes(e));if(s){return s}else{console.warn("No extension detected.");return".flv"}}makeBlob(e,t){return new Blob(Array.isArray(e)?e:[e],{type:this.extension(t)===".flv"?"video/x-flv":"video/mp4"})}cleanUpOldBlobUrl(){const e=dq("a#video-complete").getAttribute("href");if(e&&!dq(`.link[href="${e}"]`)){URL.revokeObjectURL(e)}dqa(".toast-card-header").filter(e=>e.innerText.includes("下载视频")).forEach(e=>e.querySelector(".toast-card-dismiss").click())}async downloadDanmaku(){if(this.danmakuOption!=="无"){const e=new i(r.cid);await e.fetchInfo();if(this.danmakuOption==="XML"){return e.rawXML}else{const{convertToAss:s}=await t.importAsync("download-danmaku");return s(e.rawXML)}}else{return null}}async downloadSingle(e){const t=await this.downloadDanmaku();const[a]=e;if(t===null){const e=this.makeBlob(a);const t=s()+this.extension();return{blob:e,filename:t}}else{const e=new JSZip;e.file(s()+this.extension(),this.makeBlob(a));e.file(s()+"."+this.danmakuOption.toLowerCase(),t);const i=await e.generateAsync({type:"blob"});const n=s()+".zip";return{blob:i,filename:n}}}async downloadMultiple(e){const t=new JSZip;const a=s();if(e.length>1){e.forEach((e,s)=>{const i=this.fragments[s];t.file(`${a} - ${s+1}${this.extension(i)}`,this.makeBlob(e,i))})}else{const[s]=e;t.file(`${a}${this.extension()}`,this.makeBlob(s))}const i=await this.downloadDanmaku();if(i!==null){t.file(s()+"."+this.danmakuOption.toLowerCase(),i)}const n=await t.generateAsync({type:"blob"});const o=a+".zip";return{blob:n,filename:o}}async download(){const e=[];this.videoSpeed.startMeasure();for(const t of this.fragments){const s=await this.downloadFragment(t);e.push(s)}if(e.length<1){throw new Error("下载失败.")}let{blob:t,filename:s}=await(async()=>{if(e.length===1){return await this.downloadSingle(e)}else{return await this.downloadMultiple(e)}})();this.cleanUpOldBlobUrl();const a=URL.createObjectURL(t);this.progress&&this.progress(0);this.videoSpeed.stopMeasure();return{url:a,filename:s}}}class u{constructor(e){this.lastProgress=0;this.measureInterval=1e3;this.workingDownloader=e}startMeasure(){this.intervalTimer=setInterval(()=>{const e=this.workingDownloader.progressMap?[...this.workingDownloader.progressMap.values()].reduce((e,t)=>e+t,0):0;const t=e-this.lastProgress;if(this.speedUpdate!==undefined){this.speedUpdate(formatFileSize(t)+"/s")}this.lastProgress=e},this.measureInterval)}stopMeasure(){clearInterval(this.intervalTimer)}}async function p(){const e=await SpinQuery.select(()=>(unsafeWindow||window).aid);const t=await SpinQuery.select(()=>(unsafeWindow||window).cid);if(!(e&&t)){return false}r.aid=e;r.cid=t;if(document.URL.indexOf("bangumi")!==-1){r.entity=new o}else{r.entity=new n}try{l=await d.getAvailableFormats()}catch(e){return false}return true}async function f(){c=l[0];t.applyStyle("downloadVideoStyle");dq("#download-video").addEventListener("click",()=>{dq(".download-video").classList.toggle("opened");dq(".gui-settings-mask").click()});dq("#download-video").addEventListener("mouseover",()=>{document.body.insertAdjacentHTML("beforeend",t.import("downloadVideoHtml"));m()},{once:true})}async function m(){let n;const o=new Map;const u=new Vue({el:".download-video",components:{VDropdown:()=>t.importAsync("v-dropdown.vue"),VCheckbox:()=>t.importAsync("v-checkbox.vue"),RpcProfiles:()=>t.importAsync("aria2-rpc-profiles.vue")},data:{downloadSingle:true,coverUrl:EmptyImageUrl,aid:r.aid,cid:r.cid,dashModel:{value:e.downloadVideoFormat,items:["flv","dash"]},qualityModel:{value:c.displayName,items:l.map(e=>e.displayName)},danmakuModel:{value:e.downloadVideoDefaultDanmaku,items:["无","XML","ASS"]},progressPercent:0,size:"获取大小中",blobUrl:"",episodeList:[],downloading:false,speed:"",batch:false,rpcSettings:e.aria2RpcOption,showRpcSettings:false,busy:false,saveRpcSettingsText:"保存配置",enableDash:e.enableDashDownload},computed:{displaySize(){if(typeof this.size==="string"){return this.size}return formatFileSize(this.size)},sizeWarning(){if(typeof this.size==="string"){return false}return this.size>1073741824},selectedEpisodeCount(){return this.episodeList.filter(e=>e.checked).length},dash(){return this.dashModel.value==="dash"}},methods:{close(){this.$el.classList.remove("opened")},danmakuOptionChange(){e.downloadVideoDefaultDanmaku=this.danmakuModel.value},async dashChange(){const e=this.dashModel.value;let t=[];if(e==="flv"){t=await d.getAvailableFormats()}else{t=await d.getAvailableDashFormats()}l=t;[c]=e;this.qualityModel.items=t.map(e=>e.displayName);[this.qualityModel.value]=this.qualityModel.items},async formatChange(){const e=this.getFormat();const t=o.get(e);if(t){this.size=t;return}try{this.size="获取大小中";const t=await e.downloadInfo(this.dash);this.size=t.totalSize;o.set(e,this.size)}catch(e){this.size="获取大小失败"}},getFormat(){const e=l.find(e=>e.displayName===this.qualityModel.value);if(!e){console.error(`No format found. model value = ${this.qualityModel.value}`);return null}return e},async exportData(e){if(this.busy===true){return}try{this.busy=true;if(!this.downloadSingle){await this.exportBatchData(e);return}const t=this.getFormat();const s=await t.downloadInfo(this.dash);s.danmakuOption=this.danmakuModel.value;switch(e){case"copyLink":await s.copyUrl();Toast.success("已复制链接到剪贴板.","下载视频",3e3);break;case"aria2":await s.exportAria2(false);break;case"aria2RPC":await s.exportAria2(true);break;case"copyVLD":await s.exportData(true);Toast.success("已复制VLD数据到剪贴板.","下载视频",3e3);break;case"exportVLD":await s.exportData(false);break;default:break}}catch(e){logError(e)}finally{this.busy=false}},async exportBatchData(e){const a=this.episodeList;if(a.every(e=>e.checked===false)){Toast.info("请至少选择1集或以上的数量!","批量导出",3e3);return}const n=e=>{const t=a.find(t=>t.cid===e.cid);if(t===undefined){return false}return t.checked};const o=this.getFormat();if(this.danmakuModel.value!=="无"){const e=Toast.info("下载弹幕中...","批量导出");const s=new JSZip;try{if(this.danmakuModel.value==="XML"){for(const e of a.filter(n)){const t=new i(e.cid);await t.fetchInfo();s.file(e.title+".xml",t.rawXML)}}else{const{convertToAss:e}=await t.importAsync("download-danmaku");for(const t of a.filter(n)){const a=new i(t.cid);await a.fetchInfo();s.file(t.title+".ass",await e(a.rawXML))}}h.downloadBlob(await s.generateAsync({type:"blob"}),this.cid+".danmakus.zip")}catch(e){logError(`弹幕下载失败`)}finally{e.dismiss()}}const r=Toast.info("获取链接中...","批量导出");this.batchExtractor.itemFilter=n;let l;try{switch(e){case"aria2":l=await this.batchExtractor.collectAria2(o,r);h.downloadBlob(new Blob([l],{type:"text/plain"}),s(false)+".txt");return;case"aria2RPC":await this.batchExtractor.collectAria2(o,r,true);Toast.success(`成功发送了批量请求.`,"aria2 RPC",3e3);return;case"copyVLD":GM_setClipboard(await this.batchExtractor.collectData(o,r),{mimetype:"text/plain"});Toast.success("已复制批量vld数据到剪贴板.","批量导出",3e3);return;case"exportVLD":l=await this.batchExtractor.collectData(o,r);h.downloadBlob(new Blob([l],{type:"text/json"}),s(false)+".json");return;default:return}}catch(e){logError(e)}finally{r.dismiss()}},async checkBatch(){const e=["/www.bilibili.com/bangumi","/www.bilibili.com/video/av"];if(!e.some(e=>document.URL.includes(e))){this.batch=false;this.episodeList=[];return}const{BatchExtractor:s}=await t.importAsync("batch-download");if(await s.test()!==true){this.batch=false;this.episodeList=[];return}this.batchExtractor=new s;this.batch=true;this.episodeList=(await this.batchExtractor.getItemList()).map((e,t)=>{return{aid:e.aid,cid:e.cid,title:e.title,index:t,checked:true}})},cancelDownload(){if(n){n.cancelDownload()}},async startDownload(){const e=this.getFormat();try{this.downloading=true;const t=await e.downloadInfo(this.dash);t.videoSpeed.speedUpdate=(e=>this.speed=e);t.danmakuOption=this.danmakuModel.value;t.progress=(e=>{this.progressPercent=Math.trunc(e*100)});n=t;const s=await t.download();const a=document.getElementById("video-complete");a.setAttribute("href",s.url);a.setAttribute("download",s.filename);a.click();Toast.success(`下载完成: ${s.filename} 再次保存`,"下载视频")}catch(e){if(e!=="canceled"){logError(e)}this.progressPercent=0}finally{this.downloading=false;this.speed=""}},selectAllEpisodes(){this.episodeList.forEach(e=>e.checked=true)},unselectAllEpisodes(){this.episodeList.forEach(e=>e.checked=false)},inverseAllEpisodes(){this.episodeList.forEach(e=>e.checked=!e.checked)},toggleRpcSettings(){this.showRpcSettings=!this.showRpcSettings},saveRpcSettings(){if(this.rpcSettings.host===""){this.rpcSettings.host="127.0.0.1"}if(this.rpcSettings.port===""){this.rpcSettings.port="6800"}e.aria2RpcOption=this.rpcSettings;const t=e.aria2RpcOptionProfiles.find(t=>t.name===e.aria2RpcOptionSelectedProfile);if(t){Object.assign(t,this.rpcSettings);e.aria2RpcOptionProfiles=e.aria2RpcOptionProfiles}this.saveRpcSettingsText="已保存";setTimeout(()=>this.saveRpcSettingsText="保存配置",2e3)},updateProfile(t){e.aria2RpcOption=this.rpcSettings=_.omit(t,"name")}}});Observer.videoChange(async()=>{u.close();u.batch=false;u.downloadSingle=true;const e=dq("#download-video");const t=await p();e.style.display=t?"flex":"none";if(!t){return}u.aid=r.aid;u.cid=r.cid;const s=new a(r.aid);await s.fetchInfo();u.coverUrl=s.coverUrl.replace("http:","https:");l=await d.getAvailableFormats();[c]=l;u.qualityModel={value:c.displayName,items:l.map(e=>e.displayName)};u.formatChange();await u.checkBatch()})}return{widget:{content:`\n`,condition:p,success:f}}}})(); +onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.html"] = `

下载视频

单个视频
批量导出
警告: 过大的视频大小会在直接下载时占用大量内存, 并可能导致浏览器标签页崩溃. 请考虑降低清晰度或使用导出选项.
预计大小: {{displaySize}}
格式
清晰度
弹幕

直接下载

{{speed}}
{{progressPercent}}%

选集

{{selectedEpisodeCount}}/{{episodeList.length}}

导出

{{showRpcSettings ? '取消' : 'aria2 RPC'}}

aria2 RPC

配置

主机
端口
密钥
限速
默认路径
路径
最终路径: {{rpcSettings.baseDir + rpcSettings.dir}}
方法
开始下载
{{saveRpcSettingsText}}
`; +onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/download-video.min.js"] = (()=>{return(e,t)=>{const{getFriendlyTitle:s}=t.import("title");const{VideoInfo:a,DanmakuInfo:i}=t.import("video-info");class n{constructor(){this.menuClasses=["quality","action","progress"];[this.currentMenuClass]=this.menuClasses}get menuPanel(){return document.querySelector(".download-video-panel")}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}closeMenu(){this.menuPanel.classList.remove("opened");setTimeout(()=>this.resetMenuClass(),200)}addError(){this.menuPanel.classList.add("error")}removeError(){this.menuPanel.classList.remove("error");this.resetMenuClass()}async getDashUrl(e){throw new Error("video dash is not supported")}async getUrl(e){if(e){return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&otype=json`}}}class o extends n{async getDashUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&qn=${e}&otype=json&fourk=1&fnval=16`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&otype=json&fourk=1&fnval=16`}}async getUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=&otype=json`}}}const r={entity:new n,aid:"",cid:""};let l=[];let c=null;class d{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async downloadInfo(e=false){const t=new h(this);await t.fetchVideoInfo(e);return t}static parseFormats(e){const t=e.accept_quality;const s=e.accept_format.split(",");const a=e.accept_description;const i=t.map((e,t)=>{return new d(e,s[t],a[t])});return i}static async getAvailableDashFormats(){const e=await r.entity.getDashUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}return d.parseFormats(t.result)}static async getAvailableFormats(){const e=await r.entity.getUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}const s=t.data||t.result||t;return d.parseFormats(s)}}class h{constructor(e,t){this.fragmentSplitFactor=6*2;this.workingXhr=null;this.progressMap=new Map;this.format=e;this.fragments=t||[];this.videoSpeed=new u(this)}get totalSize(){return this.fragments.map(e=>e.size).reduce((e,t)=>e+t)}async fetchVideoInfo(e=false){if(!e){const e=await r.entity.getUrl(this.format.quality);const t=await Ajax.getTextWithCredentials(e);const s=JSON.parse(t.replace(/http:/g,"https:"));const a=s.data||s.result||s;if(a.quality!==this.format.quality){throw new Error("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const i=a.durl;this.fragments=i.map(e=>{return{length:e.length,size:e.size,url:e.url,backupUrls:e.backup_url}})}else{const{dashToFragment:e,getDashInfo:s}=await t.importAsync("video-dash");const a=await s(r.aid,r.cid,this.format.quality);const i=a.videoDashes.sort(descendingSort(e=>e.bandWidth))[0];const n=a.audioDashes.sort(descendingSort(e=>e.bandWidth))[0];this.fragments=[e(i),e(n)]}return this.fragments}updateProgress(){const e=this.progressMap?[...this.progressMap.values()].reduce((e,t)=>e+t,0)/this.totalSize:0;if(e>1||e<0){console.error(`[下载视频] 进度异常: ${e}`,this.progressMap.values())}this.progress&&this.progress(e)}cancelDownload(){this.videoSpeed.stopMeasure();if(this.workingXhr!==null){this.workingXhr.forEach(e=>e.abort())}else{logError("Cancel Download Failed: forEach in this.workingXhr not found.")}}downloadFragment(e){const t=[];this.workingXhr=[];this.progressMap=new Map;this.updateProgress();let s;if(e.size<=96*1024*1024){s=e.size/this.fragmentSplitFactor}else{s=16*1024*1024}let a=0;const i=e=>[...this.progressMap.keys()].indexOf(e)+1;while(a{const a=new XMLHttpRequest;a.open("GET",e.url);a.responseType="arraybuffer";a.withCredentials=false;a.addEventListener("progress",e=>{console.log(`[下载视频] 视频片段${i(a)}下载进度: ${e.loaded}/${r} bytes loaded, ${o}`);this.progressMap.set(a,e.loaded);this.updateProgress()});a.addEventListener("load",()=>{if((""+a.status)[0]==="2"){console.log(`[下载视频] 视频片段${i(a)}下载完成`);t(a.response)}else{s(`视频片段${i(a)}请求失败, response = ${a.status}`)}});a.addEventListener("abort",()=>s("canceled"));a.addEventListener("error",()=>{console.error(`[下载视频] 视频片段${i(a)}下载失败: ${o}`);this.progressMap.set(a,0);this.updateProgress();a.open("GET",e.url);a.setRequestHeader("Range",o);a.send()});a.setRequestHeader("Range",o);this.progressMap.set(a,0);a.send();this.workingXhr.push(a)}));a=Math.round(a+s)+1}return Promise.all(t)}async copyUrl(){const e=this.fragments.map(e=>e.url).reduce((e,t)=>e+"\r\n"+t);GM_setClipboard(e,"text")}async showUrl(){const e=this.fragments.map(e=>`\n${e.url}\n`).reduce((e,t)=>e+"\r\n"+t);Toast.success(e,"显示链接")}static downloadBlob(e,t){const s=document.createElement("a");let a;if(typeof e==="string"){a=e}else{a=URL.createObjectURL(e)}s.setAttribute("href",a);s.setAttribute("download",t);document.body.appendChild(s);s.click();s.remove();URL.revokeObjectURL(a)}async exportData(e=false){const t=JSON.stringify([{fragments:this.fragments,title:s(),totalSize:this.fragments.map(e=>e.size).reduce((e,t)=>e+t),referer:document.URL.replace(window.location.search,"")}]);if(e){GM_setClipboard(t,"text")}else{const e=new Blob([t],{type:"text/json"});const a=await this.downloadDanmaku();if(a!==null){const t=new JSZip;t.file(`${s()}.json`,e);t.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await t.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(e,`${s()}.json`)}}}async exportAria2(a=false){if(a){const a=await this.downloadDanmaku();if(a!==null){h.downloadBlob(new Blob([a]),`${s()}.${this.danmakuOption==="ASS"?"ass":"xml"}`)}const i=e.aria2RpcOption;const n=this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}const n=[];if(i.secretKey!==""){n.push(`token:${i.secretKey}`)}n.push([e.url]);n.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${s()}${a}${this.extension(e)}`,split:this.fragmentSplitFactor,dir:i.baseDir+i.dir||undefined,"max-download-limit":i.maxDownloadLimit||undefined});const o=encodeURIComponent(`${s()}${a}`);return{params:n,id:o}});const{sendRpc:o}=await t.importAsync("aria2-rpc");await o(n)}else{const e=`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}return`\n${e.url}\nreferer=${document.URL.replace(window.location.search,"")}\nuser-agent=${UserAgent}\nout=${s()}${a}${this.extension(e)}\nsplit=${this.fragmentSplitFactor}\n`.trim()}).join("\n")}\n`.trim();const t=new Blob([e],{type:"text/plain"});const a=await this.downloadDanmaku();if(a!==null){const e=new JSZip;e.file(`${s()}.txt`,t);e.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await e.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(t,`${s()}.txt`)}}}extension(e){const t=e||this.fragments[0];const s=[".flv",".mp4",".m4s"].find(e=>t.url.includes(e));if(s){return s}else{console.warn("No extension detected.");return".flv"}}makeBlob(e,t){return new Blob(Array.isArray(e)?e:[e],{type:this.extension(t)===".flv"?"video/x-flv":"video/mp4"})}cleanUpOldBlobUrl(){const e=dq("a#video-complete").getAttribute("href");if(e&&!dq(`.link[href="${e}"]`)){URL.revokeObjectURL(e)}dqa(".toast-card-header").filter(e=>e.innerText.includes("下载视频")).forEach(e=>e.querySelector(".toast-card-dismiss").click())}async downloadDanmaku(){if(this.danmakuOption!=="无"){const e=new i(r.cid);await e.fetchInfo();if(this.danmakuOption==="XML"){return e.rawXML}else{const{convertToAss:s}=await t.importAsync("download-danmaku");return s(e.rawXML)}}else{return null}}async downloadSingle(e){const t=await this.downloadDanmaku();const[a]=e;if(t===null){const e=this.makeBlob(a);const t=s()+this.extension();return{blob:e,filename:t}}else{const e=new JSZip;e.file(s()+this.extension(),this.makeBlob(a));e.file(s()+"."+this.danmakuOption.toLowerCase(),t);const i=await e.generateAsync({type:"blob"});const n=s()+".zip";return{blob:i,filename:n}}}async downloadMultiple(e){const t=new JSZip;const a=s();if(e.length>1){e.forEach((e,s)=>{const i=this.fragments[s];t.file(`${a} - ${s+1}${this.extension(i)}`,this.makeBlob(e,i))})}else{const[s]=e;t.file(`${a}${this.extension()}`,this.makeBlob(s))}const i=await this.downloadDanmaku();if(i!==null){t.file(s()+"."+this.danmakuOption.toLowerCase(),i)}const n=await t.generateAsync({type:"blob"});const o=a+".zip";return{blob:n,filename:o}}async download(){const e=[];this.videoSpeed.startMeasure();for(const t of this.fragments){const s=await this.downloadFragment(t);e.push(s)}if(e.length<1){throw new Error("下载失败.")}let{blob:t,filename:s}=await(async()=>{if(e.length===1){return await this.downloadSingle(e)}else{return await this.downloadMultiple(e)}})();this.cleanUpOldBlobUrl();const a=URL.createObjectURL(t);this.progress&&this.progress(0);this.videoSpeed.stopMeasure();return{url:a,filename:s}}}class u{constructor(e){this.lastProgress=0;this.measureInterval=1e3;this.workingDownloader=e}startMeasure(){this.intervalTimer=setInterval(()=>{const e=this.workingDownloader.progressMap?[...this.workingDownloader.progressMap.values()].reduce((e,t)=>e+t,0):0;const t=e-this.lastProgress;if(this.speedUpdate!==undefined){this.speedUpdate(formatFileSize(t)+"/s")}this.lastProgress=e},this.measureInterval)}stopMeasure(){clearInterval(this.intervalTimer)}}async function p(){const e=await SpinQuery.select(()=>(unsafeWindow||window).aid);const t=await SpinQuery.select(()=>(unsafeWindow||window).cid);if(!(e&&t)){return false}r.aid=e;r.cid=t;if(document.URL.indexOf("bangumi")!==-1){r.entity=new o}else{r.entity=new n}try{l=await d.getAvailableFormats()}catch(e){return false}return true}async function f(){c=l[0];t.applyStyle("downloadVideoStyle");dq("#download-video").addEventListener("click",()=>{dq(".download-video").classList.toggle("opened");dq(".gui-settings-mask").click()});dq("#download-video").addEventListener("mouseover",()=>{document.body.insertAdjacentHTML("beforeend",t.import("downloadVideoHtml"));m()},{once:true})}async function m(){let n;const o=new Map;const u=new Vue({el:".download-video",components:{VDropdown:()=>t.importAsync("v-dropdown.vue"),VCheckbox:()=>t.importAsync("v-checkbox.vue"),RpcProfiles:()=>t.importAsync("aria2-rpc-profiles.vue")},data:{downloadSingle:true,coverUrl:EmptyImageUrl,aid:r.aid,cid:r.cid,dashModel:{value:e.downloadVideoFormat,items:["flv","dash"]},qualityModel:{value:c.displayName,items:l.map(e=>e.displayName)},danmakuModel:{value:e.downloadVideoDefaultDanmaku,items:["无","XML","ASS"]},progressPercent:0,size:"获取大小中",blobUrl:"",episodeList:[],downloading:false,speed:"",batch:false,rpcSettings:e.aria2RpcOption,showRpcSettings:false,busy:false,saveRpcSettingsText:"保存配置",enableDash:e.enableDashDownload},computed:{displaySize(){if(typeof this.size==="string"){return this.size}return formatFileSize(this.size)},sizeWarning(){if(typeof this.size==="string"){return false}return this.size>1073741824},selectedEpisodeCount(){return this.episodeList.filter(e=>e.checked).length},dash(){return this.dashModel.value==="dash"}},methods:{close(){this.$el.classList.remove("opened")},danmakuOptionChange(){e.downloadVideoDefaultDanmaku=this.danmakuModel.value},async dashChange(){const e=this.dashModel.value;let t=[];if(e==="flv"){t=await d.getAvailableFormats()}else{t=await d.getAvailableDashFormats()}l=t;[c]=e;this.qualityModel.items=t.map(e=>e.displayName);[this.qualityModel.value]=this.qualityModel.items},async formatChange(){const e=this.getFormat();const t=o.get(e);if(t){this.size=t;return}try{this.size="获取大小中";const t=await e.downloadInfo(this.dash);this.size=t.totalSize;o.set(e,this.size)}catch(e){this.size="获取大小失败"}},getFormat(){const e=l.find(e=>e.displayName===this.qualityModel.value);if(!e){console.error(`No format found. model value = ${this.qualityModel.value}`);return null}return e},async exportData(e){if(this.busy===true){return}try{this.busy=true;if(!this.downloadSingle){await this.exportBatchData(e);return}const t=this.getFormat();const s=await t.downloadInfo(this.dash);s.danmakuOption=this.danmakuModel.value;switch(e){case"copyLink":await s.copyUrl();Toast.success("已复制链接到剪贴板.","下载视频",3e3);break;case"showLink":await s.showUrl();break;case"aria2":await s.exportAria2(false);break;case"aria2RPC":await s.exportAria2(true);break;case"copyVLD":await s.exportData(true);Toast.success("已复制VLD数据到剪贴板.","下载视频",3e3);break;case"exportVLD":await s.exportData(false);break;default:break}}catch(e){logError(e)}finally{this.busy=false}},async exportBatchData(e){const a=this.episodeList;if(a.every(e=>e.checked===false)){Toast.info("请至少选择1集或以上的数量!","批量导出",3e3);return}const n=e=>{const t=a.find(t=>t.cid===e.cid);if(t===undefined){return false}return t.checked};const o=this.getFormat();if(this.danmakuModel.value!=="无"){const e=Toast.info("下载弹幕中...","批量导出");const s=new JSZip;try{if(this.danmakuModel.value==="XML"){for(const e of a.filter(n)){const t=new i(e.cid);await t.fetchInfo();s.file(e.title+".xml",t.rawXML)}}else{const{convertToAss:e}=await t.importAsync("download-danmaku");for(const t of a.filter(n)){const a=new i(t.cid);await a.fetchInfo();s.file(t.title+".ass",await e(a.rawXML))}}h.downloadBlob(await s.generateAsync({type:"blob"}),this.cid+".danmakus.zip")}catch(e){logError(`弹幕下载失败`)}finally{e.dismiss()}}const r=Toast.info("获取链接中...","批量导出");this.batchExtractor.itemFilter=n;let l;try{switch(e){case"aria2":l=await this.batchExtractor.collectAria2(o,r);h.downloadBlob(new Blob([l],{type:"text/plain"}),s(false)+".txt");return;case"aria2RPC":await this.batchExtractor.collectAria2(o,r,true);Toast.success(`成功发送了批量请求.`,"aria2 RPC",3e3);return;case"copyVLD":GM_setClipboard(await this.batchExtractor.collectData(o,r),{mimetype:"text/plain"});Toast.success("已复制批量vld数据到剪贴板.","批量导出",3e3);return;case"exportVLD":l=await this.batchExtractor.collectData(o,r);h.downloadBlob(new Blob([l],{type:"text/json"}),s(false)+".json");return;default:return}}catch(e){logError(e)}finally{r.dismiss()}},async checkBatch(){const e=["/www.bilibili.com/bangumi","/www.bilibili.com/video/av"];if(!e.some(e=>document.URL.includes(e))){this.batch=false;this.episodeList=[];return}const{BatchExtractor:s}=await t.importAsync("batch-download");if(await s.test()!==true){this.batch=false;this.episodeList=[];return}this.batchExtractor=new s;this.batch=true;this.episodeList=(await this.batchExtractor.getItemList()).map((e,t)=>{return{aid:e.aid,cid:e.cid,title:e.title,index:t,checked:true}})},cancelDownload(){if(n){n.cancelDownload()}},async startDownload(){const e=this.getFormat();try{this.downloading=true;const t=await e.downloadInfo(this.dash);t.videoSpeed.speedUpdate=(e=>this.speed=e);t.danmakuOption=this.danmakuModel.value;t.progress=(e=>{this.progressPercent=Math.trunc(e*100)});n=t;const s=await t.download();const a=document.getElementById("video-complete");a.setAttribute("href",s.url);a.setAttribute("download",s.filename);a.click();Toast.success(`下载完成: ${s.filename} 再次保存`,"下载视频")}catch(e){if(e!=="canceled"){logError(e)}this.progressPercent=0}finally{this.downloading=false;this.speed=""}},selectAllEpisodes(){this.episodeList.forEach(e=>e.checked=true)},unselectAllEpisodes(){this.episodeList.forEach(e=>e.checked=false)},inverseAllEpisodes(){this.episodeList.forEach(e=>e.checked=!e.checked)},toggleRpcSettings(){this.showRpcSettings=!this.showRpcSettings},saveRpcSettings(){if(this.rpcSettings.host===""){this.rpcSettings.host="127.0.0.1"}if(this.rpcSettings.port===""){this.rpcSettings.port="6800"}e.aria2RpcOption=this.rpcSettings;const t=e.aria2RpcOptionProfiles.find(t=>t.name===e.aria2RpcOptionSelectedProfile);if(t){Object.assign(t,this.rpcSettings);e.aria2RpcOptionProfiles=e.aria2RpcOptionProfiles}this.saveRpcSettingsText="已保存";setTimeout(()=>this.saveRpcSettingsText="保存配置",2e3)},updateProfile(t){e.aria2RpcOption=this.rpcSettings=_.omit(t,"name")}}});Observer.videoChange(async()=>{u.close();u.batch=false;u.downloadSingle=true;const e=dq("#download-video");const t=await p();e.style.display=t?"flex":"none";if(!t){return}u.aid=r.aid;u.cid=r.cid;const s=new a(r.aid);await s.fetchInfo();u.coverUrl=s.coverUrl.replace("http:","https:");l=await d.getAvailableFormats();[c]=l;u.qualityModel={value:c.displayName,items:l.map(e=>e.displayName)};u.formatChange();await u.checkBatch()})}return{widget:{content:`\n`,condition:p,success:f}}}})(); onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/dpi-img.vue.min.js"] = (()=>{return(t,e)=>{const i=``;return{export:Object.assign({template:i},{props:["size","src","blur"],computed:{srcset(){if(!this.src||!this.size){return null}return getDpiSourceSet(this.src,this.size)},width(){if(typeof this.size==="object"&&"width"in this.size){return this.size.width}return null},height(){if(typeof this.size==="object"&&"height"in this.size){return this.size.height}return null}}})}}})(); onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/expand-danmaku.min.js"] = (()=>{return(e,a)=>{if(typeof isEmbeddedPlayer!=="undefined"&&isEmbeddedPlayer()){return}Observer.videoChange(async()=>{const e=await SpinQuery.select(".bui-collapse-wrap");if(e&&e.classList.contains("bui-collapse-wrap-folded")){const e=await SpinQuery.select(".bui-collapse-header");e.click()}})}})(); onlineData["https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/min/expand-description.min.css"] = `.play-up-info .play-up-self,.video-desc .info{height:auto!important}.play-up-info .play-up-self-btn,.video-desc .btn{display:none!important}`; diff --git a/min/bundle.json b/min/bundle.json index f856a21d7..b3f0246a0 100644 --- a/min/bundle.json +++ b/min/bundle.json @@ -43,8 +43,8 @@ "download-audio.min.js": "96245533BB7FCB3297F0049198BB29B3D41CE85B4BE77DA2AE20931A25C7401E", "download-danmaku.min.js": "B462990C301C924B64687A734A02396B621A939EAC422B96868F49D615D77927", "download-video.min.css": "3C542A4ADB01F1E93DFFFAA9789287EE6712897DB6DFB5764F47E3DFAD51959E", - "download-video.min.html": "7C1028DB1E93B27C5E17A3DFC97CB7C9FB0F69FD8CE412721CE2B4E4CAD4DE08", - "download-video.min.js": "FDB1116D7C4B37BE8133FB1D08C2B00358B3AF42C1B513B03B31F9CB5D17A705", + "download-video.min.html": "3AED52E086BDC624B6ABC1ADF99A898FDFF4C68565B790F8AA805BD184ECD286", + "download-video.min.js": "19BF033199C9C6AEE542187A8CBC87243CBB01DC098BC6D733AA0E36456B1D0B", "dpi-img.vue.min.js": "D4833A242EBDD84834B11DB4CEB68B8807BDFC9693C3E1AAE73071986FE26F78", "expand-danmaku.min.js": "B21658C40085AEA8DC49652AE62EB8610BB2EBEAF7A9C9AF69EF3B11E08EEC8D", "expand-description.min.css": "58C7710A50521B80F7D872BDC4C652610D84C4FABC6874BA66DA37B4F8759224", diff --git a/min/bundle.zip b/min/bundle.zip index d4c16e4d6..7b030dbd3 100644 Binary files a/min/bundle.zip and b/min/bundle.zip differ diff --git a/min/download-video.min.html b/min/download-video.min.html index 2802c7d70..89f423fbf 100644 --- a/min/download-video.min.html +++ b/min/download-video.min.html @@ -1 +1 @@ -

下载视频

单个视频
批量导出
警告: 过大的视频大小会在直接下载时占用大量内存, 并可能导致浏览器标签页崩溃. 请考虑降低清晰度或使用导出选项.
预计大小: {{displaySize}}
格式
清晰度
弹幕

直接下载

{{speed}}
{{progressPercent}}%

选集

{{selectedEpisodeCount}}/{{episodeList.length}}

导出

{{showRpcSettings ? '取消' : 'aria2 RPC'}}

aria2 RPC

配置

主机
端口
密钥
限速
默认路径
路径
最终路径: {{rpcSettings.baseDir + rpcSettings.dir}}
方法
开始下载
{{saveRpcSettingsText}}
\ No newline at end of file +

下载视频

单个视频
批量导出
警告: 过大的视频大小会在直接下载时占用大量内存, 并可能导致浏览器标签页崩溃. 请考虑降低清晰度或使用导出选项.
预计大小: {{displaySize}}
格式
清晰度
弹幕

直接下载

{{speed}}
{{progressPercent}}%

选集

{{selectedEpisodeCount}}/{{episodeList.length}}

导出

{{showRpcSettings ? '取消' : 'aria2 RPC'}}

aria2 RPC

配置

主机
端口
密钥
限速
默认路径
路径
最终路径: {{rpcSettings.baseDir + rpcSettings.dir}}
方法
开始下载
{{saveRpcSettingsText}}
\ No newline at end of file diff --git a/min/download-video.min.js b/min/download-video.min.js index 76111c092..a1be2a1ec 100644 --- a/min/download-video.min.js +++ b/min/download-video.min.js @@ -1 +1 @@ -(()=>{return(e,t)=>{const{getFriendlyTitle:s}=t.import("title");const{VideoInfo:a,DanmakuInfo:i}=t.import("video-info");class n{constructor(){this.menuClasses=["quality","action","progress"];[this.currentMenuClass]=this.menuClasses}get menuPanel(){return document.querySelector(".download-video-panel")}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}closeMenu(){this.menuPanel.classList.remove("opened");setTimeout(()=>this.resetMenuClass(),200)}addError(){this.menuPanel.classList.add("error")}removeError(){this.menuPanel.classList.remove("error");this.resetMenuClass()}async getDashUrl(e){throw new Error("video dash is not supported")}async getUrl(e){if(e){return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&otype=json`}}}class o extends n{async getDashUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&qn=${e}&otype=json&fourk=1&fnval=16`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&otype=json&fourk=1&fnval=16`}}async getUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=&otype=json`}}}const r={entity:new n,aid:"",cid:""};let l=[];let c=null;class d{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async downloadInfo(e=false){const t=new h(this);await t.fetchVideoInfo(e);return t}static parseFormats(e){const t=e.accept_quality;const s=e.accept_format.split(",");const a=e.accept_description;const i=t.map((e,t)=>{return new d(e,s[t],a[t])});return i}static async getAvailableDashFormats(){const e=await r.entity.getDashUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}return d.parseFormats(t.result)}static async getAvailableFormats(){const e=await r.entity.getUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}const s=t.data||t.result||t;return d.parseFormats(s)}}class h{constructor(e,t){this.fragmentSplitFactor=6*2;this.workingXhr=null;this.progressMap=new Map;this.format=e;this.fragments=t||[];this.videoSpeed=new u(this)}get totalSize(){return this.fragments.map(e=>e.size).reduce((e,t)=>e+t)}async fetchVideoInfo(e=false){if(!e){const e=await r.entity.getUrl(this.format.quality);const t=await Ajax.getTextWithCredentials(e);const s=JSON.parse(t.replace(/http:/g,"https:"));const a=s.data||s.result||s;if(a.quality!==this.format.quality){throw new Error("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const i=a.durl;this.fragments=i.map(e=>{return{length:e.length,size:e.size,url:e.url,backupUrls:e.backup_url}})}else{const{dashToFragment:e,getDashInfo:s}=await t.importAsync("video-dash");const a=await s(r.aid,r.cid,this.format.quality);const i=a.videoDashes.sort(descendingSort(e=>e.bandWidth))[0];const n=a.audioDashes.sort(descendingSort(e=>e.bandWidth))[0];this.fragments=[e(i),e(n)]}return this.fragments}updateProgress(){const e=this.progressMap?[...this.progressMap.values()].reduce((e,t)=>e+t,0)/this.totalSize:0;if(e>1||e<0){console.error(`[下载视频] 进度异常: ${e}`,this.progressMap.values())}this.progress&&this.progress(e)}cancelDownload(){this.videoSpeed.stopMeasure();if(this.workingXhr!==null){this.workingXhr.forEach(e=>e.abort())}else{logError("Cancel Download Failed: forEach in this.workingXhr not found.")}}downloadFragment(e){const t=[];this.workingXhr=[];this.progressMap=new Map;this.updateProgress();let s;if(e.size<=96*1024*1024){s=e.size/this.fragmentSplitFactor}else{s=16*1024*1024}let a=0;const i=e=>[...this.progressMap.keys()].indexOf(e)+1;while(a{const a=new XMLHttpRequest;a.open("GET",e.url);a.responseType="arraybuffer";a.withCredentials=false;a.addEventListener("progress",e=>{console.log(`[下载视频] 视频片段${i(a)}下载进度: ${e.loaded}/${r} bytes loaded, ${o}`);this.progressMap.set(a,e.loaded);this.updateProgress()});a.addEventListener("load",()=>{if((""+a.status)[0]==="2"){console.log(`[下载视频] 视频片段${i(a)}下载完成`);t(a.response)}else{s(`视频片段${i(a)}请求失败, response = ${a.status}`)}});a.addEventListener("abort",()=>s("canceled"));a.addEventListener("error",()=>{console.error(`[下载视频] 视频片段${i(a)}下载失败: ${o}`);this.progressMap.set(a,0);this.updateProgress();a.open("GET",e.url);a.setRequestHeader("Range",o);a.send()});a.setRequestHeader("Range",o);this.progressMap.set(a,0);a.send();this.workingXhr.push(a)}));a=Math.round(a+s)+1}return Promise.all(t)}async copyUrl(){const e=this.fragments.map(e=>e.url).reduce((e,t)=>e+"\r\n"+t);GM_setClipboard(e,"text")}static downloadBlob(e,t){const s=document.createElement("a");let a;if(typeof e==="string"){a=e}else{a=URL.createObjectURL(e)}s.setAttribute("href",a);s.setAttribute("download",t);document.body.appendChild(s);s.click();s.remove();URL.revokeObjectURL(a)}async exportData(e=false){const t=JSON.stringify([{fragments:this.fragments,title:s(),totalSize:this.fragments.map(e=>e.size).reduce((e,t)=>e+t),referer:document.URL.replace(window.location.search,"")}]);if(e){GM_setClipboard(t,"text")}else{const e=new Blob([t],{type:"text/json"});const a=await this.downloadDanmaku();if(a!==null){const t=new JSZip;t.file(`${s()}.json`,e);t.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await t.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(e,`${s()}.json`)}}}async exportAria2(a=false){if(a){const a=await this.downloadDanmaku();if(a!==null){h.downloadBlob(new Blob([a]),`${s()}.${this.danmakuOption==="ASS"?"ass":"xml"}`)}const i=e.aria2RpcOption;const n=this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}const n=[];if(i.secretKey!==""){n.push(`token:${i.secretKey}`)}n.push([e.url]);n.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${s()}${a}${this.extension(e)}`,split:this.fragmentSplitFactor,dir:i.baseDir+i.dir||undefined,"max-download-limit":i.maxDownloadLimit||undefined});const o=encodeURIComponent(`${s()}${a}`);return{params:n,id:o}});const{sendRpc:o}=await t.importAsync("aria2-rpc");await o(n)}else{const e=`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}return`\n${e.url}\nreferer=${document.URL.replace(window.location.search,"")}\nuser-agent=${UserAgent}\nout=${s()}${a}${this.extension(e)}\nsplit=${this.fragmentSplitFactor}\n`.trim()}).join("\n")}\n`.trim();const t=new Blob([e],{type:"text/plain"});const a=await this.downloadDanmaku();if(a!==null){const e=new JSZip;e.file(`${s()}.txt`,t);e.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await e.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(t,`${s()}.txt`)}}}extension(e){const t=e||this.fragments[0];const s=[".flv",".mp4",".m4s"].find(e=>t.url.includes(e));if(s){return s}else{console.warn("No extension detected.");return".flv"}}makeBlob(e,t){return new Blob(Array.isArray(e)?e:[e],{type:this.extension(t)===".flv"?"video/x-flv":"video/mp4"})}cleanUpOldBlobUrl(){const e=dq("a#video-complete").getAttribute("href");if(e&&!dq(`.link[href="${e}"]`)){URL.revokeObjectURL(e)}dqa(".toast-card-header").filter(e=>e.innerText.includes("下载视频")).forEach(e=>e.querySelector(".toast-card-dismiss").click())}async downloadDanmaku(){if(this.danmakuOption!=="无"){const e=new i(r.cid);await e.fetchInfo();if(this.danmakuOption==="XML"){return e.rawXML}else{const{convertToAss:s}=await t.importAsync("download-danmaku");return s(e.rawXML)}}else{return null}}async downloadSingle(e){const t=await this.downloadDanmaku();const[a]=e;if(t===null){const e=this.makeBlob(a);const t=s()+this.extension();return{blob:e,filename:t}}else{const e=new JSZip;e.file(s()+this.extension(),this.makeBlob(a));e.file(s()+"."+this.danmakuOption.toLowerCase(),t);const i=await e.generateAsync({type:"blob"});const n=s()+".zip";return{blob:i,filename:n}}}async downloadMultiple(e){const t=new JSZip;const a=s();if(e.length>1){e.forEach((e,s)=>{const i=this.fragments[s];t.file(`${a} - ${s+1}${this.extension(i)}`,this.makeBlob(e,i))})}else{const[s]=e;t.file(`${a}${this.extension()}`,this.makeBlob(s))}const i=await this.downloadDanmaku();if(i!==null){t.file(s()+"."+this.danmakuOption.toLowerCase(),i)}const n=await t.generateAsync({type:"blob"});const o=a+".zip";return{blob:n,filename:o}}async download(){const e=[];this.videoSpeed.startMeasure();for(const t of this.fragments){const s=await this.downloadFragment(t);e.push(s)}if(e.length<1){throw new Error("下载失败.")}let{blob:t,filename:s}=await(async()=>{if(e.length===1){return await this.downloadSingle(e)}else{return await this.downloadMultiple(e)}})();this.cleanUpOldBlobUrl();const a=URL.createObjectURL(t);this.progress&&this.progress(0);this.videoSpeed.stopMeasure();return{url:a,filename:s}}}class u{constructor(e){this.lastProgress=0;this.measureInterval=1e3;this.workingDownloader=e}startMeasure(){this.intervalTimer=setInterval(()=>{const e=this.workingDownloader.progressMap?[...this.workingDownloader.progressMap.values()].reduce((e,t)=>e+t,0):0;const t=e-this.lastProgress;if(this.speedUpdate!==undefined){this.speedUpdate(formatFileSize(t)+"/s")}this.lastProgress=e},this.measureInterval)}stopMeasure(){clearInterval(this.intervalTimer)}}async function p(){const e=await SpinQuery.select(()=>(unsafeWindow||window).aid);const t=await SpinQuery.select(()=>(unsafeWindow||window).cid);if(!(e&&t)){return false}r.aid=e;r.cid=t;if(document.URL.indexOf("bangumi")!==-1){r.entity=new o}else{r.entity=new n}try{l=await d.getAvailableFormats()}catch(e){return false}return true}async function f(){c=l[0];t.applyStyle("downloadVideoStyle");dq("#download-video").addEventListener("click",()=>{dq(".download-video").classList.toggle("opened");dq(".gui-settings-mask").click()});dq("#download-video").addEventListener("mouseover",()=>{document.body.insertAdjacentHTML("beforeend",t.import("downloadVideoHtml"));m()},{once:true})}async function m(){let n;const o=new Map;const u=new Vue({el:".download-video",components:{VDropdown:()=>t.importAsync("v-dropdown.vue"),VCheckbox:()=>t.importAsync("v-checkbox.vue"),RpcProfiles:()=>t.importAsync("aria2-rpc-profiles.vue")},data:{downloadSingle:true,coverUrl:EmptyImageUrl,aid:r.aid,cid:r.cid,dashModel:{value:e.downloadVideoFormat,items:["flv","dash"]},qualityModel:{value:c.displayName,items:l.map(e=>e.displayName)},danmakuModel:{value:e.downloadVideoDefaultDanmaku,items:["无","XML","ASS"]},progressPercent:0,size:"获取大小中",blobUrl:"",episodeList:[],downloading:false,speed:"",batch:false,rpcSettings:e.aria2RpcOption,showRpcSettings:false,busy:false,saveRpcSettingsText:"保存配置",enableDash:e.enableDashDownload},computed:{displaySize(){if(typeof this.size==="string"){return this.size}return formatFileSize(this.size)},sizeWarning(){if(typeof this.size==="string"){return false}return this.size>1073741824},selectedEpisodeCount(){return this.episodeList.filter(e=>e.checked).length},dash(){return this.dashModel.value==="dash"}},methods:{close(){this.$el.classList.remove("opened")},danmakuOptionChange(){e.downloadVideoDefaultDanmaku=this.danmakuModel.value},async dashChange(){const e=this.dashModel.value;let t=[];if(e==="flv"){t=await d.getAvailableFormats()}else{t=await d.getAvailableDashFormats()}l=t;[c]=e;this.qualityModel.items=t.map(e=>e.displayName);[this.qualityModel.value]=this.qualityModel.items},async formatChange(){const e=this.getFormat();const t=o.get(e);if(t){this.size=t;return}try{this.size="获取大小中";const t=await e.downloadInfo(this.dash);this.size=t.totalSize;o.set(e,this.size)}catch(e){this.size="获取大小失败"}},getFormat(){const e=l.find(e=>e.displayName===this.qualityModel.value);if(!e){console.error(`No format found. model value = ${this.qualityModel.value}`);return null}return e},async exportData(e){if(this.busy===true){return}try{this.busy=true;if(!this.downloadSingle){await this.exportBatchData(e);return}const t=this.getFormat();const s=await t.downloadInfo(this.dash);s.danmakuOption=this.danmakuModel.value;switch(e){case"copyLink":await s.copyUrl();Toast.success("已复制链接到剪贴板.","下载视频",3e3);break;case"aria2":await s.exportAria2(false);break;case"aria2RPC":await s.exportAria2(true);break;case"copyVLD":await s.exportData(true);Toast.success("已复制VLD数据到剪贴板.","下载视频",3e3);break;case"exportVLD":await s.exportData(false);break;default:break}}catch(e){logError(e)}finally{this.busy=false}},async exportBatchData(e){const a=this.episodeList;if(a.every(e=>e.checked===false)){Toast.info("请至少选择1集或以上的数量!","批量导出",3e3);return}const n=e=>{const t=a.find(t=>t.cid===e.cid);if(t===undefined){return false}return t.checked};const o=this.getFormat();if(this.danmakuModel.value!=="无"){const e=Toast.info("下载弹幕中...","批量导出");const s=new JSZip;try{if(this.danmakuModel.value==="XML"){for(const e of a.filter(n)){const t=new i(e.cid);await t.fetchInfo();s.file(e.title+".xml",t.rawXML)}}else{const{convertToAss:e}=await t.importAsync("download-danmaku");for(const t of a.filter(n)){const a=new i(t.cid);await a.fetchInfo();s.file(t.title+".ass",await e(a.rawXML))}}h.downloadBlob(await s.generateAsync({type:"blob"}),this.cid+".danmakus.zip")}catch(e){logError(`弹幕下载失败`)}finally{e.dismiss()}}const r=Toast.info("获取链接中...","批量导出");this.batchExtractor.itemFilter=n;let l;try{switch(e){case"aria2":l=await this.batchExtractor.collectAria2(o,r);h.downloadBlob(new Blob([l],{type:"text/plain"}),s(false)+".txt");return;case"aria2RPC":await this.batchExtractor.collectAria2(o,r,true);Toast.success(`成功发送了批量请求.`,"aria2 RPC",3e3);return;case"copyVLD":GM_setClipboard(await this.batchExtractor.collectData(o,r),{mimetype:"text/plain"});Toast.success("已复制批量vld数据到剪贴板.","批量导出",3e3);return;case"exportVLD":l=await this.batchExtractor.collectData(o,r);h.downloadBlob(new Blob([l],{type:"text/json"}),s(false)+".json");return;default:return}}catch(e){logError(e)}finally{r.dismiss()}},async checkBatch(){const e=["/www.bilibili.com/bangumi","/www.bilibili.com/video/av"];if(!e.some(e=>document.URL.includes(e))){this.batch=false;this.episodeList=[];return}const{BatchExtractor:s}=await t.importAsync("batch-download");if(await s.test()!==true){this.batch=false;this.episodeList=[];return}this.batchExtractor=new s;this.batch=true;this.episodeList=(await this.batchExtractor.getItemList()).map((e,t)=>{return{aid:e.aid,cid:e.cid,title:e.title,index:t,checked:true}})},cancelDownload(){if(n){n.cancelDownload()}},async startDownload(){const e=this.getFormat();try{this.downloading=true;const t=await e.downloadInfo(this.dash);t.videoSpeed.speedUpdate=(e=>this.speed=e);t.danmakuOption=this.danmakuModel.value;t.progress=(e=>{this.progressPercent=Math.trunc(e*100)});n=t;const s=await t.download();const a=document.getElementById("video-complete");a.setAttribute("href",s.url);a.setAttribute("download",s.filename);a.click();Toast.success(`下载完成: ${s.filename} 再次保存`,"下载视频")}catch(e){if(e!=="canceled"){logError(e)}this.progressPercent=0}finally{this.downloading=false;this.speed=""}},selectAllEpisodes(){this.episodeList.forEach(e=>e.checked=true)},unselectAllEpisodes(){this.episodeList.forEach(e=>e.checked=false)},inverseAllEpisodes(){this.episodeList.forEach(e=>e.checked=!e.checked)},toggleRpcSettings(){this.showRpcSettings=!this.showRpcSettings},saveRpcSettings(){if(this.rpcSettings.host===""){this.rpcSettings.host="127.0.0.1"}if(this.rpcSettings.port===""){this.rpcSettings.port="6800"}e.aria2RpcOption=this.rpcSettings;const t=e.aria2RpcOptionProfiles.find(t=>t.name===e.aria2RpcOptionSelectedProfile);if(t){Object.assign(t,this.rpcSettings);e.aria2RpcOptionProfiles=e.aria2RpcOptionProfiles}this.saveRpcSettingsText="已保存";setTimeout(()=>this.saveRpcSettingsText="保存配置",2e3)},updateProfile(t){e.aria2RpcOption=this.rpcSettings=_.omit(t,"name")}}});Observer.videoChange(async()=>{u.close();u.batch=false;u.downloadSingle=true;const e=dq("#download-video");const t=await p();e.style.display=t?"flex":"none";if(!t){return}u.aid=r.aid;u.cid=r.cid;const s=new a(r.aid);await s.fetchInfo();u.coverUrl=s.coverUrl.replace("http:","https:");l=await d.getAvailableFormats();[c]=l;u.qualityModel={value:c.displayName,items:l.map(e=>e.displayName)};u.formatChange();await u.checkBatch()})}return{widget:{content:`\n`,condition:p,success:f}}}})(); \ No newline at end of file +(()=>{return(e,t)=>{const{getFriendlyTitle:s}=t.import("title");const{VideoInfo:a,DanmakuInfo:i}=t.import("video-info");class n{constructor(){this.menuClasses=["quality","action","progress"];[this.currentMenuClass]=this.menuClasses}get menuPanel(){return document.querySelector(".download-video-panel")}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}closeMenu(){this.menuPanel.classList.remove("opened");setTimeout(()=>this.resetMenuClass(),200)}addError(){this.menuPanel.classList.add("error")}removeError(){this.menuPanel.classList.remove("error");this.resetMenuClass()}async getDashUrl(e){throw new Error("video dash is not supported")}async getUrl(e){if(e){return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/x/player/playurl?avid=${r.aid}&cid=${r.cid}&otype=json`}}}class o extends n{async getDashUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&qn=${e}&otype=json&fourk=1&fnval=16`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=56995872&cid=99547543&otype=json&fourk=1&fnval=16`}}async getUrl(e){if(e){return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=${e}&otype=json`}else{return`https://api.bilibili.com/pgc/player/web/playurl?avid=${r.aid}&cid=${r.cid}&qn=&otype=json`}}}const r={entity:new n,aid:"",cid:""};let l=[];let c=null;class d{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async downloadInfo(e=false){const t=new h(this);await t.fetchVideoInfo(e);return t}static parseFormats(e){const t=e.accept_quality;const s=e.accept_format.split(",");const a=e.accept_description;const i=t.map((e,t)=>{return new d(e,s[t],a[t])});return i}static async getAvailableDashFormats(){const e=await r.entity.getDashUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}return d.parseFormats(t.result)}static async getAvailableFormats(){const e=await r.entity.getUrl();const t=await Ajax.getJsonWithCredentials(e);if(t.code!==0){throw new Error("获取清晰度信息失败.")}const s=t.data||t.result||t;return d.parseFormats(s)}}class h{constructor(e,t){this.fragmentSplitFactor=6*2;this.workingXhr=null;this.progressMap=new Map;this.format=e;this.fragments=t||[];this.videoSpeed=new u(this)}get totalSize(){return this.fragments.map(e=>e.size).reduce((e,t)=>e+t)}async fetchVideoInfo(e=false){if(!e){const e=await r.entity.getUrl(this.format.quality);const t=await Ajax.getTextWithCredentials(e);const s=JSON.parse(t.replace(/http:/g,"https:"));const a=s.data||s.result||s;if(a.quality!==this.format.quality){throw new Error("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const i=a.durl;this.fragments=i.map(e=>{return{length:e.length,size:e.size,url:e.url,backupUrls:e.backup_url}})}else{const{dashToFragment:e,getDashInfo:s}=await t.importAsync("video-dash");const a=await s(r.aid,r.cid,this.format.quality);const i=a.videoDashes.sort(descendingSort(e=>e.bandWidth))[0];const n=a.audioDashes.sort(descendingSort(e=>e.bandWidth))[0];this.fragments=[e(i),e(n)]}return this.fragments}updateProgress(){const e=this.progressMap?[...this.progressMap.values()].reduce((e,t)=>e+t,0)/this.totalSize:0;if(e>1||e<0){console.error(`[下载视频] 进度异常: ${e}`,this.progressMap.values())}this.progress&&this.progress(e)}cancelDownload(){this.videoSpeed.stopMeasure();if(this.workingXhr!==null){this.workingXhr.forEach(e=>e.abort())}else{logError("Cancel Download Failed: forEach in this.workingXhr not found.")}}downloadFragment(e){const t=[];this.workingXhr=[];this.progressMap=new Map;this.updateProgress();let s;if(e.size<=96*1024*1024){s=e.size/this.fragmentSplitFactor}else{s=16*1024*1024}let a=0;const i=e=>[...this.progressMap.keys()].indexOf(e)+1;while(a{const a=new XMLHttpRequest;a.open("GET",e.url);a.responseType="arraybuffer";a.withCredentials=false;a.addEventListener("progress",e=>{console.log(`[下载视频] 视频片段${i(a)}下载进度: ${e.loaded}/${r} bytes loaded, ${o}`);this.progressMap.set(a,e.loaded);this.updateProgress()});a.addEventListener("load",()=>{if((""+a.status)[0]==="2"){console.log(`[下载视频] 视频片段${i(a)}下载完成`);t(a.response)}else{s(`视频片段${i(a)}请求失败, response = ${a.status}`)}});a.addEventListener("abort",()=>s("canceled"));a.addEventListener("error",()=>{console.error(`[下载视频] 视频片段${i(a)}下载失败: ${o}`);this.progressMap.set(a,0);this.updateProgress();a.open("GET",e.url);a.setRequestHeader("Range",o);a.send()});a.setRequestHeader("Range",o);this.progressMap.set(a,0);a.send();this.workingXhr.push(a)}));a=Math.round(a+s)+1}return Promise.all(t)}async copyUrl(){const e=this.fragments.map(e=>e.url).reduce((e,t)=>e+"\r\n"+t);GM_setClipboard(e,"text")}async showUrl(){const e=this.fragments.map(e=>`\n${e.url}\n`).reduce((e,t)=>e+"\r\n"+t);Toast.success(e,"显示链接")}static downloadBlob(e,t){const s=document.createElement("a");let a;if(typeof e==="string"){a=e}else{a=URL.createObjectURL(e)}s.setAttribute("href",a);s.setAttribute("download",t);document.body.appendChild(s);s.click();s.remove();URL.revokeObjectURL(a)}async exportData(e=false){const t=JSON.stringify([{fragments:this.fragments,title:s(),totalSize:this.fragments.map(e=>e.size).reduce((e,t)=>e+t),referer:document.URL.replace(window.location.search,"")}]);if(e){GM_setClipboard(t,"text")}else{const e=new Blob([t],{type:"text/json"});const a=await this.downloadDanmaku();if(a!==null){const t=new JSZip;t.file(`${s()}.json`,e);t.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await t.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(e,`${s()}.json`)}}}async exportAria2(a=false){if(a){const a=await this.downloadDanmaku();if(a!==null){h.downloadBlob(new Blob([a]),`${s()}.${this.danmakuOption==="ASS"?"ass":"xml"}`)}const i=e.aria2RpcOption;const n=this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}const n=[];if(i.secretKey!==""){n.push(`token:${i.secretKey}`)}n.push([e.url]);n.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${s()}${a}${this.extension(e)}`,split:this.fragmentSplitFactor,dir:i.baseDir+i.dir||undefined,"max-download-limit":i.maxDownloadLimit||undefined});const o=encodeURIComponent(`${s()}${a}`);return{params:n,id:o}});const{sendRpc:o}=await t.importAsync("aria2-rpc");await o(n)}else{const e=`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${this.fragments.map((e,t)=>{let a="";if(this.fragments.length>1){a=" - "+(t+1)}return`\n${e.url}\nreferer=${document.URL.replace(window.location.search,"")}\nuser-agent=${UserAgent}\nout=${s()}${a}${this.extension(e)}\nsplit=${this.fragmentSplitFactor}\n`.trim()}).join("\n")}\n`.trim();const t=new Blob([e],{type:"text/plain"});const a=await this.downloadDanmaku();if(a!==null){const e=new JSZip;e.file(`${s()}.txt`,t);e.file(s()+"."+this.danmakuOption.toLowerCase(),a);h.downloadBlob(await e.generateAsync({type:"blob"}),`${s()}.zip`)}else{h.downloadBlob(t,`${s()}.txt`)}}}extension(e){const t=e||this.fragments[0];const s=[".flv",".mp4",".m4s"].find(e=>t.url.includes(e));if(s){return s}else{console.warn("No extension detected.");return".flv"}}makeBlob(e,t){return new Blob(Array.isArray(e)?e:[e],{type:this.extension(t)===".flv"?"video/x-flv":"video/mp4"})}cleanUpOldBlobUrl(){const e=dq("a#video-complete").getAttribute("href");if(e&&!dq(`.link[href="${e}"]`)){URL.revokeObjectURL(e)}dqa(".toast-card-header").filter(e=>e.innerText.includes("下载视频")).forEach(e=>e.querySelector(".toast-card-dismiss").click())}async downloadDanmaku(){if(this.danmakuOption!=="无"){const e=new i(r.cid);await e.fetchInfo();if(this.danmakuOption==="XML"){return e.rawXML}else{const{convertToAss:s}=await t.importAsync("download-danmaku");return s(e.rawXML)}}else{return null}}async downloadSingle(e){const t=await this.downloadDanmaku();const[a]=e;if(t===null){const e=this.makeBlob(a);const t=s()+this.extension();return{blob:e,filename:t}}else{const e=new JSZip;e.file(s()+this.extension(),this.makeBlob(a));e.file(s()+"."+this.danmakuOption.toLowerCase(),t);const i=await e.generateAsync({type:"blob"});const n=s()+".zip";return{blob:i,filename:n}}}async downloadMultiple(e){const t=new JSZip;const a=s();if(e.length>1){e.forEach((e,s)=>{const i=this.fragments[s];t.file(`${a} - ${s+1}${this.extension(i)}`,this.makeBlob(e,i))})}else{const[s]=e;t.file(`${a}${this.extension()}`,this.makeBlob(s))}const i=await this.downloadDanmaku();if(i!==null){t.file(s()+"."+this.danmakuOption.toLowerCase(),i)}const n=await t.generateAsync({type:"blob"});const o=a+".zip";return{blob:n,filename:o}}async download(){const e=[];this.videoSpeed.startMeasure();for(const t of this.fragments){const s=await this.downloadFragment(t);e.push(s)}if(e.length<1){throw new Error("下载失败.")}let{blob:t,filename:s}=await(async()=>{if(e.length===1){return await this.downloadSingle(e)}else{return await this.downloadMultiple(e)}})();this.cleanUpOldBlobUrl();const a=URL.createObjectURL(t);this.progress&&this.progress(0);this.videoSpeed.stopMeasure();return{url:a,filename:s}}}class u{constructor(e){this.lastProgress=0;this.measureInterval=1e3;this.workingDownloader=e}startMeasure(){this.intervalTimer=setInterval(()=>{const e=this.workingDownloader.progressMap?[...this.workingDownloader.progressMap.values()].reduce((e,t)=>e+t,0):0;const t=e-this.lastProgress;if(this.speedUpdate!==undefined){this.speedUpdate(formatFileSize(t)+"/s")}this.lastProgress=e},this.measureInterval)}stopMeasure(){clearInterval(this.intervalTimer)}}async function p(){const e=await SpinQuery.select(()=>(unsafeWindow||window).aid);const t=await SpinQuery.select(()=>(unsafeWindow||window).cid);if(!(e&&t)){return false}r.aid=e;r.cid=t;if(document.URL.indexOf("bangumi")!==-1){r.entity=new o}else{r.entity=new n}try{l=await d.getAvailableFormats()}catch(e){return false}return true}async function f(){c=l[0];t.applyStyle("downloadVideoStyle");dq("#download-video").addEventListener("click",()=>{dq(".download-video").classList.toggle("opened");dq(".gui-settings-mask").click()});dq("#download-video").addEventListener("mouseover",()=>{document.body.insertAdjacentHTML("beforeend",t.import("downloadVideoHtml"));m()},{once:true})}async function m(){let n;const o=new Map;const u=new Vue({el:".download-video",components:{VDropdown:()=>t.importAsync("v-dropdown.vue"),VCheckbox:()=>t.importAsync("v-checkbox.vue"),RpcProfiles:()=>t.importAsync("aria2-rpc-profiles.vue")},data:{downloadSingle:true,coverUrl:EmptyImageUrl,aid:r.aid,cid:r.cid,dashModel:{value:e.downloadVideoFormat,items:["flv","dash"]},qualityModel:{value:c.displayName,items:l.map(e=>e.displayName)},danmakuModel:{value:e.downloadVideoDefaultDanmaku,items:["无","XML","ASS"]},progressPercent:0,size:"获取大小中",blobUrl:"",episodeList:[],downloading:false,speed:"",batch:false,rpcSettings:e.aria2RpcOption,showRpcSettings:false,busy:false,saveRpcSettingsText:"保存配置",enableDash:e.enableDashDownload},computed:{displaySize(){if(typeof this.size==="string"){return this.size}return formatFileSize(this.size)},sizeWarning(){if(typeof this.size==="string"){return false}return this.size>1073741824},selectedEpisodeCount(){return this.episodeList.filter(e=>e.checked).length},dash(){return this.dashModel.value==="dash"}},methods:{close(){this.$el.classList.remove("opened")},danmakuOptionChange(){e.downloadVideoDefaultDanmaku=this.danmakuModel.value},async dashChange(){const e=this.dashModel.value;let t=[];if(e==="flv"){t=await d.getAvailableFormats()}else{t=await d.getAvailableDashFormats()}l=t;[c]=e;this.qualityModel.items=t.map(e=>e.displayName);[this.qualityModel.value]=this.qualityModel.items},async formatChange(){const e=this.getFormat();const t=o.get(e);if(t){this.size=t;return}try{this.size="获取大小中";const t=await e.downloadInfo(this.dash);this.size=t.totalSize;o.set(e,this.size)}catch(e){this.size="获取大小失败"}},getFormat(){const e=l.find(e=>e.displayName===this.qualityModel.value);if(!e){console.error(`No format found. model value = ${this.qualityModel.value}`);return null}return e},async exportData(e){if(this.busy===true){return}try{this.busy=true;if(!this.downloadSingle){await this.exportBatchData(e);return}const t=this.getFormat();const s=await t.downloadInfo(this.dash);s.danmakuOption=this.danmakuModel.value;switch(e){case"copyLink":await s.copyUrl();Toast.success("已复制链接到剪贴板.","下载视频",3e3);break;case"showLink":await s.showUrl();break;case"aria2":await s.exportAria2(false);break;case"aria2RPC":await s.exportAria2(true);break;case"copyVLD":await s.exportData(true);Toast.success("已复制VLD数据到剪贴板.","下载视频",3e3);break;case"exportVLD":await s.exportData(false);break;default:break}}catch(e){logError(e)}finally{this.busy=false}},async exportBatchData(e){const a=this.episodeList;if(a.every(e=>e.checked===false)){Toast.info("请至少选择1集或以上的数量!","批量导出",3e3);return}const n=e=>{const t=a.find(t=>t.cid===e.cid);if(t===undefined){return false}return t.checked};const o=this.getFormat();if(this.danmakuModel.value!=="无"){const e=Toast.info("下载弹幕中...","批量导出");const s=new JSZip;try{if(this.danmakuModel.value==="XML"){for(const e of a.filter(n)){const t=new i(e.cid);await t.fetchInfo();s.file(e.title+".xml",t.rawXML)}}else{const{convertToAss:e}=await t.importAsync("download-danmaku");for(const t of a.filter(n)){const a=new i(t.cid);await a.fetchInfo();s.file(t.title+".ass",await e(a.rawXML))}}h.downloadBlob(await s.generateAsync({type:"blob"}),this.cid+".danmakus.zip")}catch(e){logError(`弹幕下载失败`)}finally{e.dismiss()}}const r=Toast.info("获取链接中...","批量导出");this.batchExtractor.itemFilter=n;let l;try{switch(e){case"aria2":l=await this.batchExtractor.collectAria2(o,r);h.downloadBlob(new Blob([l],{type:"text/plain"}),s(false)+".txt");return;case"aria2RPC":await this.batchExtractor.collectAria2(o,r,true);Toast.success(`成功发送了批量请求.`,"aria2 RPC",3e3);return;case"copyVLD":GM_setClipboard(await this.batchExtractor.collectData(o,r),{mimetype:"text/plain"});Toast.success("已复制批量vld数据到剪贴板.","批量导出",3e3);return;case"exportVLD":l=await this.batchExtractor.collectData(o,r);h.downloadBlob(new Blob([l],{type:"text/json"}),s(false)+".json");return;default:return}}catch(e){logError(e)}finally{r.dismiss()}},async checkBatch(){const e=["/www.bilibili.com/bangumi","/www.bilibili.com/video/av"];if(!e.some(e=>document.URL.includes(e))){this.batch=false;this.episodeList=[];return}const{BatchExtractor:s}=await t.importAsync("batch-download");if(await s.test()!==true){this.batch=false;this.episodeList=[];return}this.batchExtractor=new s;this.batch=true;this.episodeList=(await this.batchExtractor.getItemList()).map((e,t)=>{return{aid:e.aid,cid:e.cid,title:e.title,index:t,checked:true}})},cancelDownload(){if(n){n.cancelDownload()}},async startDownload(){const e=this.getFormat();try{this.downloading=true;const t=await e.downloadInfo(this.dash);t.videoSpeed.speedUpdate=(e=>this.speed=e);t.danmakuOption=this.danmakuModel.value;t.progress=(e=>{this.progressPercent=Math.trunc(e*100)});n=t;const s=await t.download();const a=document.getElementById("video-complete");a.setAttribute("href",s.url);a.setAttribute("download",s.filename);a.click();Toast.success(`下载完成: ${s.filename} 再次保存`,"下载视频")}catch(e){if(e!=="canceled"){logError(e)}this.progressPercent=0}finally{this.downloading=false;this.speed=""}},selectAllEpisodes(){this.episodeList.forEach(e=>e.checked=true)},unselectAllEpisodes(){this.episodeList.forEach(e=>e.checked=false)},inverseAllEpisodes(){this.episodeList.forEach(e=>e.checked=!e.checked)},toggleRpcSettings(){this.showRpcSettings=!this.showRpcSettings},saveRpcSettings(){if(this.rpcSettings.host===""){this.rpcSettings.host="127.0.0.1"}if(this.rpcSettings.port===""){this.rpcSettings.port="6800"}e.aria2RpcOption=this.rpcSettings;const t=e.aria2RpcOptionProfiles.find(t=>t.name===e.aria2RpcOptionSelectedProfile);if(t){Object.assign(t,this.rpcSettings);e.aria2RpcOptionProfiles=e.aria2RpcOptionProfiles}this.saveRpcSettingsText="已保存";setTimeout(()=>this.saveRpcSettingsText="保存配置",2e3)},updateProfile(t){e.aria2RpcOption=this.rpcSettings=_.omit(t,"name")}}});Observer.videoChange(async()=>{u.close();u.batch=false;u.downloadSingle=true;const e=dq("#download-video");const t=await p();e.style.display=t?"flex":"none";if(!t){return}u.aid=r.aid;u.cid=r.cid;const s=new a(r.aid);await s.fetchInfo();u.coverUrl=s.coverUrl.replace("http:","https:");l=await d.getAvailableFormats();[c]=l;u.qualityModel={value:c.displayName,items:l.map(e=>e.displayName)};u.formatChange();await u.checkBatch()})}return{widget:{content:`\n`,condition:p,success:f}}}})(); \ No newline at end of file diff --git a/src/utils/seeds-to-coins.ts b/src/utils/seeds-to-coins.ts index 872c737fb..7e01ad1a2 100644 --- a/src/utils/seeds-to-coins.ts +++ b/src/utils/seeds-to-coins.ts @@ -1,6 +1,6 @@ const seedsToCoinsApi = `https://api.live.bilibili.com/pay/v1/Exchange/silver2coin` // const [bCoins, coupons] = (() => { -// const csrf = document.cookie.replace(/(?:(?:^|.*;\s*)bili_jct\s*\=\s*([^;]*).*$)|^.*$/, '$1') +// const csrf = getCsrf() // const vipReceive = (type: number) => { // return async (): Promise<{ code: number, message: string }> => { // return await (await fetch('https://api.bilibili.com/x/vip/privilege/receive', diff --git a/src/video/download-video/download-video.html b/src/video/download-video/download-video.html index e5e828da0..5e72e0ca1 100644 --- a/src/video/download-video/download-video.html +++ b/src/video/download-video/download-video.html @@ -79,7 +79,9 @@

导出

- + +