Use zip if video is split

This commit is contained in:
the1812 2018-10-26 11:46:17 +08:00
parent 65685f5345
commit f5d7666619
7 changed files with 76 additions and 38 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
// ==UserScript== // ==UserScript==
// @name Bilibili Evolved (Preview) // @name Bilibili Evolved (Preview)
// @version 1.5.15 // @version 1.5.16
// @description 增强哔哩哔哩Web端体验.(预览版分支) // @description 增强哔哩哔哩Web端体验.(预览版分支)
// @author Grant Howard, Coulomb-G // @author Grant Howard, Coulomb-G
// @copyright 2018, Grant Howrad (https://github.com/the1812) // @copyright 2018, Grant Howrad (https://github.com/the1812)
@ -17,6 +17,7 @@
// @grant GM_setValue // @grant GM_setValue
// @grant GM_addValueChangeListener // @grant GM_addValueChangeListener
// @require https://code.jquery.com/jquery-3.2.1.min.js // @require https://code.jquery.com/jquery-3.2.1.min.js
// @require https://cdn.bootcss.com/jszip/3.1.5/jszip.min.js
// @icon https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/images/logo.png // @icon https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/images/logo.png
// ==/UserScript== // ==/UserScript==
(self$ => (self$ =>
@ -60,7 +61,7 @@
fixFullscreen: false, fixFullscreen: false,
downloadVideo: true, downloadVideo: true,
latestVersionLink: "https://github.com/the1812/Bilibili-Evolved/raw/preview/bilibili-evolved.preview.user.js", latestVersionLink: "https://github.com/the1812/Bilibili-Evolved/raw/preview/bilibili-evolved.preview.user.js",
currentVersion: "1.5.15" currentVersion: "1.5.16"
}; };
function loadSettings() function loadSettings()
{ {

View File

@ -1,6 +1,6 @@
// ==UserScript== // ==UserScript==
// @name Bilibili Evolved // @name Bilibili Evolved
// @version 1.5.15 // @version 1.5.16
// @description 增强哔哩哔哩Web端体验. // @description 增强哔哩哔哩Web端体验.
// @author Grant Howard, Coulomb-G // @author Grant Howard, Coulomb-G
// @copyright 2018, Grant Howrad (https://github.com/the1812) // @copyright 2018, Grant Howrad (https://github.com/the1812)
@ -17,6 +17,7 @@
// @grant GM_setValue // @grant GM_setValue
// @grant GM_addValueChangeListener // @grant GM_addValueChangeListener
// @require https://code.jquery.com/jquery-3.2.1.min.js // @require https://code.jquery.com/jquery-3.2.1.min.js
// @require https://cdn.bootcss.com/jszip/3.1.5/jszip.min.js
// @icon https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/images/logo.png // @icon https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/images/logo.png
// ==/UserScript== // ==/UserScript==
(self$ => (self$ =>
@ -60,7 +61,7 @@
fixFullscreen: false, fixFullscreen: false,
downloadVideo: true, downloadVideo: true,
latestVersionLink: "https://github.com/the1812/Bilibili-Evolved/raw/master/bilibili-evolved.user.js", latestVersionLink: "https://github.com/the1812/Bilibili-Evolved/raw/master/bilibili-evolved.user.js",
currentVersion: "1.5.15" currentVersion: "1.5.16"
}; };
function loadSettings() function loadSettings()
{ {

View File

@ -1 +1 @@
(()=>{return(e,t)=>{const s=(unsafeWindow||window).aid;const o=(unsafeWindow||window).cid;if(s===undefined||o===undefined){return}class n{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async download(){const e=new r(this);await e.fetchVideoInfo();e.progress=(e=>{$(".download-progress-value").text(`${fixed(e*100)}`);$(".download-progress-foreground").css("transform",`scaleX(${e})`)});return e.download()}static get availableFormats(){return new Promise((e,t)=>{const a=`https://api.bilibili.com/x/player/playurl?avid=${s}&cid=${o}&otype=json`;const r=new XMLHttpRequest;r.addEventListener("load",()=>{const t=JSON.parse(r.responseText).data;const s=t.accept_quality;const o=t.accept_format.split(",");const a=t.accept_description;const i=[];while(s.length>0){const e=new n(s.pop(),o.pop(),a.pop());i.push(e)}e(i)});r.addEventListener("error",()=>t(`获取清晰度信息失败.`));r.withCredentials=true;r.open("GET",a);r.send()})}}class a{constructor(e,t,s,o){this.length=e;this.size=t;this.url=s;this.backupUrls=o}}class r{constructor(e,t){this.format=e;this.fragments=t||[];this.progress=null}fetchVideoInfo(){return new Promise((e,t)=>{const n=`https://api.bilibili.com/x/player/playurl?avid=${s}&cid=${o}&qn=${this.format.quality}&otype=json`;const r=new XMLHttpRequest;r.addEventListener("load",()=>{const s=JSON.parse(r.responseText.replace("http:","https:")).data;if(s.quality!==this.format.quality){t("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const o=s.durl;this.fragments=o.map(e=>new a(e.length,e.size,e.url,e.backup_url));if(this.fragments.length>1){t("暂不支持分段视频的下载.")}e(this.fragments)});r.withCredentials=true;r.open("GET",n);r.send()})}download(){return new Promise((e,t)=>{const[s]=this.fragments;const o=new XMLHttpRequest;o.open("GET",s.url);o.responseType="arraybuffer";o.withCredentials=false;o.addEventListener("progress",e=>{this.progress&&this.progress(e.loaded/s.size)});o.addEventListener("load",()=>{if(o.status===200){const t=new Blob([o.response],{type:"video/x-flv"});const n=URL.createObjectURL(t);const a=document.title.replace("_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili","");const r=s.url.indexOf(".flv")!==-1?".flv":".mp4";const i=$("a#video-complete").attr("href");if(i){URL.revokeObjectURL(i)}$("a#video-complete").attr("href",n).attr("download",a+r);this.progress&&this.progress(0);document.getElementById("video-complete").click();e(n)}else{t(`请求失败.`)}});o.addEventListener("error",()=>{t(`下载失败.`)});o.send()})}}return{settingsWidget:{category:"视频与直播",content:t.data.downloadVideoDom.text,success:()=>{n.availableFormats.then(e=>{e.forEach(e=>{async function t(){$(".download-video-panel").removeClass("quality").addClass("progress");await e.download().catch(e=>{$(".download-video-panel").addClass("error");$(".video-error").text(e)});$(".download-video-panel").removeClass("progress").addClass("quality")}$(`<li>${e.displayName}</li>`).on("click",t).prependTo("ol.video-quality")});t.applyStyle("downloadVideoStyle");$("#download-video").on("click",()=>{$(".download-video-panel").toggleClass("opened")}).parent().removeClass("hidden")});$(".video-error").on("click",()=>{$(".video-error").text("");$(".download-video-panel").removeClass("error").removeClass("progress").addClass("quality")})}}}}})(); (()=>{return(e,t)=>{const s=(unsafeWindow||window).aid;const o=(unsafeWindow||window).cid;if(s===undefined||o===undefined){return}class a{constructor(e,t,s){this.quality=e;this.internalName=t;this.displayName=s}async download(){const e=new r(this);await e.fetchVideoInfo();e.progress=(e=>{$(".download-progress-value").text(`${fixed(e*100)}`);$(".download-progress-foreground").css("transform",`scaleX(${e})`)});return e.download()}static get availableFormats(){return new Promise((e,t)=>{const n=`https://api.bilibili.com/x/player/playurl?avid=${s}&cid=${o}&otype=json`;const r=new XMLHttpRequest;r.addEventListener("load",()=>{const t=JSON.parse(r.responseText).data;const s=t.accept_quality;const o=t.accept_format.split(",");const n=t.accept_description;const i=[];while(s.length>0){const e=new a(s.pop(),o.pop(),n.pop());i.push(e)}e(i)});r.addEventListener("error",()=>t(`获取清晰度信息失败.`));r.withCredentials=true;r.open("GET",n);r.send()})}}class n{constructor(e,t,s,o){this.length=e;this.size=t;this.url=s;this.backupUrls=o}}class r{constructor(e,t){this.format=e;this.fragments=t||[];this.progress=null;this.loaded=0;this.totalSize=null}fetchVideoInfo(){return new Promise((e,t)=>{const a=`https://api.bilibili.com/x/player/playurl?avid=${s}&cid=${o}&qn=${this.format.quality}&otype=json`;const r=new XMLHttpRequest;r.addEventListener("load",()=>{const s=JSON.parse(r.responseText.replace("http:","https:")).data;if(s.quality!==this.format.quality){t("获取下载链接失败, 请确认当前账号有下载权限后重试.")}const o=s.durl;this.fragments=o.map(e=>new n(e.length,e.size,e.url,e.backup_url));e(this.fragments)});r.withCredentials=true;r.open("GET",a);r.send()})}downloadUrl(e){return new Promise((e,t)=>{const s=new XMLHttpRequest;s.open("GET",fragment.url);s.responseType="arraybuffer";s.withCredentials=false;s.addEventListener("progress",e=>{this.progress&&this.progress((this.loaded+e.loaded)/this.totalSize)});s.addEventListener("load",()=>{if(s.status===200){e(s.response)}else{t(`请求失败.`)}});s.addEventListener("error",()=>{t(`下载失败.`)});s.send()})}async download(){const e=[];this.loaded=0;this.totalSize=this.fragments.map(e=>e.size).reduce((e,t)=>e+t);for(const t of this.fragments){const s=await this.downloadUrl(t.url);this.loaded+=t.size;e.push(s)}if(e.length<1){throw new Error("下载失败.")}let t=null;const s=document.title.replace("_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili","");const o=fragment.url.indexOf(".flv")!==-1?".flv":".mp4";if(e.length===1){const[s]=e;t=new Blob([s],{type:"video/x-flv"})}else{const a=new JSZip;e.forEach((e,t)=>{a.file(`${s} - ${t}${o}`,e)});t=await a.generateAsync({type:"blob"})}const a=URL.createObjectURL(t);const n=$("a#video-complete").attr("href");if(n){URL.revokeObjectURL(n)}$("a#video-complete").attr("href",a).attr("download",s+o);this.progress&&this.progress(0);document.getElementById("video-complete").click();return a}}return{settingsWidget:{category:"视频与直播",content:t.data.downloadVideoDom.text,success:()=>{a.availableFormats.then(e=>{e.forEach(e=>{async function t(){$(".download-video-panel").removeClass("quality").addClass("progress");await e.download().catch(e=>{$(".download-video-panel").addClass("error");$(".video-error").text(e)});$(".download-video-panel").removeClass("progress").addClass("quality")}$(`<li>${e.displayName}</li>`).on("click",t).prependTo("ol.video-quality")});t.applyStyle("downloadVideoStyle");$("#download-video").on("click",()=>{$(".download-video-panel").toggleClass("opened")}).parent().removeClass("hidden")});$(".video-error").on("click",()=>{$(".video-error").text("");$(".download-video-panel").removeClass("error").removeClass("progress").addClass("quality")})}}}}})();

View File

@ -1 +1 @@
1.5.15 1.5.16

View File

@ -75,6 +75,8 @@
this.format = format; this.format = format;
this.fragments = fragments || []; this.fragments = fragments || [];
this.progress = null; this.progress = null;
this.loaded = 0;
this.totalSize = null;
} }
fetchVideoInfo() fetchVideoInfo()
{ {
@ -95,10 +97,10 @@
it.url, it.url,
it.backup_url it.backup_url
)); ));
if (this.fragments.length > 1) // if (this.fragments.length > 1)
{ // {
reject("暂不支持分段视频的下载."); // reject("暂不支持分段视频的下载.");
} // }
resolve(this.fragments); resolve(this.fragments);
}); });
xhr.withCredentials = true; xhr.withCredentials = true;
@ -106,40 +108,23 @@
xhr.send(); xhr.send();
}); });
} }
download() downloadUrl(url)
{ {
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
{ {
const [fragment] = this.fragments;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("GET", fragment.url); xhr.open("GET", fragment.url);
xhr.responseType = "arraybuffer"; xhr.responseType = "arraybuffer";
xhr.withCredentials = false; xhr.withCredentials = false;
xhr.addEventListener("progress", (e) => xhr.addEventListener("progress", (e) =>
{ {
this.progress && this.progress(e.loaded / fragment.size); this.progress && this.progress((this.loaded + e.loaded) / this.totalSize);
}); });
xhr.addEventListener("load", () => xhr.addEventListener("load", () =>
{ {
if (xhr.status === 200) if (xhr.status === 200)
{ {
const blob = new Blob([xhr.response], { resolve(xhr.response);
type: "video/x-flv"
});
const blobUrl = URL.createObjectURL(blob);
const title = document.title.replace("_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili", "");
const extension = fragment.url.indexOf(".flv") !== -1 ? ".flv" : ".mp4";
const oldBlobUrl = $("a#video-complete").attr("href");
if (oldBlobUrl)
{
URL.revokeObjectURL(oldBlobUrl);
}
$("a#video-complete")
.attr("href", blobUrl)
.attr("download", title + extension);
this.progress && this.progress(0);
document.getElementById("video-complete").click();
resolve(blobUrl);
} }
else else
{ {
@ -153,6 +138,55 @@
xhr.send(); xhr.send();
}); });
} }
async download()
{
const downloadedData = [];
this.loaded = 0;
this.totalSize = this.fragments.map(it => it.size).reduce((acc, it) => acc + it);
for (const fragment of this.fragments)
{
const data = await this.downloadUrl(fragment.url);
this.loaded += fragment.size;
downloadedData.push(data);
}
if (downloadedData.length < 1)
{
throw new Error("下载失败.");
}
let blob = null;
const title = document.title.replace("_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili", "");
const extension = fragment.url.indexOf(".flv") !== -1 ? ".flv" : ".mp4";
if (downloadedData.length === 1)
{
const [data] = downloadedData;
blob = new Blob([data], {
type: "video/x-flv"
});
}
else
{
const zip = new JSZip();
downloadedData.forEach((data, index) =>
{
zip.file(`${title} - ${index}${extension}`, data);
});
blob = await zip.generateAsync({ type: "blob" });
}
const blobUrl = URL.createObjectURL(blob);
const oldBlobUrl = $("a#video-complete").attr("href");
if (oldBlobUrl)
{
URL.revokeObjectURL(oldBlobUrl);
}
$("a#video-complete")
.attr("href", blobUrl)
.attr("download", title + extension);
this.progress && this.progress(0);
document.getElementById("video-complete").click();
return blobUrl;
}
} }
return { return {
settingsWidget: { settingsWidget: {