mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Fix vld bugs
This commit is contained in:
parent
9f9077d047
commit
7f93200f86
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -154,7 +154,7 @@ namespace BilibiliEvolved.Build
|
||||
}
|
||||
return source;
|
||||
};
|
||||
input = RegexReplacer.Replace(input, @"import (.*) from ([^;]*)", match =>
|
||||
input = RegexReplacer.Replace(input, @"import (.*) from ([^\r\n;]*)", match =>
|
||||
{
|
||||
var imported = match.Groups[1].Value.Replace(" as ", ":");
|
||||
var source = convertToRuntimeSource(match.Groups[2].Value);
|
||||
@ -173,6 +173,7 @@ namespace BilibiliEvolved.Build
|
||||
};
|
||||
})();";
|
||||
}
|
||||
// Console.WriteLine(input);
|
||||
return new UglifyJs().Run(input);
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bilibili-evolved-video-link-downloader",
|
||||
"version": "1.8.1",
|
||||
"version": "1.8.2",
|
||||
"description": "Bilibili Evolved 视频链接下载器",
|
||||
"main": "video-link-downloader.js",
|
||||
"bin": {
|
||||
|
||||
@ -64,8 +64,8 @@ class Downloader {
|
||||
headers: {
|
||||
Range: range,
|
||||
Origin: "https://www.bilibili.com",
|
||||
Referer: "https://www.bilibili.com",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36",
|
||||
Referer: this.inputData.referer || "https://www.bilibili.com",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0",
|
||||
},
|
||||
}).on("complete", response => {
|
||||
if (response.statusCode.toString()[0] === "2") {
|
||||
@ -78,15 +78,15 @@ class Downloader {
|
||||
this.progressMap.set(req, this.progressMap.get(req) + data.length);
|
||||
this.updateProgress();
|
||||
}).on("error", error => {
|
||||
// stream.close();
|
||||
// fs.unlinkSync(partFilename);
|
||||
// this.progressMap.delete(req);
|
||||
// this.progressMap.set(makeRequest(), 0);
|
||||
// this.updateProgress();
|
||||
this.progressMap.delete(req);
|
||||
this.progressMap.set(makeRequest(), 0);
|
||||
this.updateProgress();
|
||||
reject(`\n片段下载失败: ${error}`);
|
||||
});
|
||||
console.log(`created stream ${partFilename}`);
|
||||
req.pipe(fs.createWriteStream(partFilename));
|
||||
req.pipe(fs.createWriteStream(partFilename, {
|
||||
autoClose: true
|
||||
}));
|
||||
return req;
|
||||
};
|
||||
this.progressMap.set(makeRequest(), 0);
|
||||
|
||||
@ -6,31 +6,29 @@ import fs = require("fs");
|
||||
import ProgressBar = require("progress");
|
||||
import "colors";
|
||||
|
||||
interface Fragment
|
||||
{
|
||||
length: number;
|
||||
size: number;
|
||||
url: string;
|
||||
backupUrls: string[];
|
||||
interface Fragment {
|
||||
length: number;
|
||||
size: number;
|
||||
url: string;
|
||||
backupUrls: string[];
|
||||
}
|
||||
interface InputData
|
||||
{
|
||||
fragments: Fragment[];
|
||||
title: string;
|
||||
totalSize: number;
|
||||
interface InputData {
|
||||
fragments: Fragment[];
|
||||
title: string;
|
||||
totalSize: number;
|
||||
referer: string;
|
||||
}
|
||||
interface Settings
|
||||
{
|
||||
parts: number;
|
||||
info: string;
|
||||
output: string;
|
||||
interface Settings {
|
||||
parts: number;
|
||||
info: string;
|
||||
output: string;
|
||||
}
|
||||
|
||||
const optionDefinitions = [
|
||||
{ name: 'danmaku', alias: 'd', defaultValue: false, type: Boolean },
|
||||
{ name: 'info', alias: 'i', defaultOption: true, type: String, defaultValue: undefined },
|
||||
{ name: 'parts', alias: 'p', type: Number, defaultValue: 12 },
|
||||
{ name: 'output', alias: 'o', type: String, defaultValue: '.' },
|
||||
{ name: 'danmaku', alias: 'd', defaultValue: false, type: Boolean },
|
||||
{ name: 'info', alias: 'i', defaultOption: true, type: String, defaultValue: undefined },
|
||||
{ name: 'parts', alias: 'p', type: Number, defaultValue: 12 },
|
||||
{ name: 'output', alias: 'o', type: String, defaultValue: '.' },
|
||||
];
|
||||
const commandLineOptions = commandLineArgs(optionDefinitions) as Settings;
|
||||
let options = commandLineOptions;
|
||||
@ -41,277 +39,224 @@ options.parts = Math.round(options.parts);
|
||||
// options = Object.assign(jsonOptions, options);
|
||||
// }
|
||||
|
||||
if (options.parts < 1)
|
||||
{
|
||||
console.error("分段数不能小于1".red);
|
||||
process.exit();
|
||||
if (options.parts < 1) {
|
||||
console.error("分段数不能小于1".red);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
class Downloader
|
||||
{
|
||||
static workingDownloader: Downloader | null = null;
|
||||
private progressMap = new Map<request.Request | string, number>();
|
||||
private progressBar = new ProgressBar(":percent [:bar]", {
|
||||
total: this.inputData.totalSize,
|
||||
width: 20,
|
||||
incomplete: ' ',
|
||||
class Downloader {
|
||||
static workingDownloader: Downloader | null = null;
|
||||
private progressMap = new Map<request.Request | string, number>();
|
||||
private progressBar = new ProgressBar(":percent [:bar]", {
|
||||
total: this.inputData.totalSize,
|
||||
width: 20,
|
||||
incomplete: ' ',
|
||||
});
|
||||
private extension: string;
|
||||
private title: string | string[];
|
||||
|
||||
constructor(
|
||||
private inputData: InputData,
|
||||
) { }
|
||||
private getExtension(fragment: Fragment) {
|
||||
this.extension = fragment.url.includes(".flv") ? ".flv" : ".mp4";
|
||||
}
|
||||
private updateProgress() {
|
||||
const progress = this.progressMap ?
|
||||
[...this.progressMap.values()].reduce((a, b) => a + b, 0) / this.inputData.totalSize : 0;
|
||||
this.progressBar.update(progress);
|
||||
}
|
||||
cancelDownload() {
|
||||
[...this.progressMap.keys()].forEach(it => {
|
||||
if (typeof it !== "string") {
|
||||
it.abort();
|
||||
}
|
||||
});
|
||||
private extension: string;
|
||||
private title: string | string[];
|
||||
|
||||
constructor(
|
||||
private inputData: InputData,
|
||||
)
|
||||
{ }
|
||||
private getExtension(fragment: Fragment)
|
||||
{
|
||||
this.extension = fragment.url.includes(".flv") ? ".flv" : ".mp4";
|
||||
}
|
||||
private updateProgress()
|
||||
{
|
||||
const progress = this.progressMap ?
|
||||
[...this.progressMap.values()].reduce((a, b) => a + b, 0) / this.inputData.totalSize : 0;
|
||||
this.progressBar.update(progress);
|
||||
}
|
||||
cancelDownload()
|
||||
{
|
||||
[...this.progressMap.keys()].forEach(it =>
|
||||
{
|
||||
if (typeof it !== "string")
|
||||
{
|
||||
it.abort();
|
||||
}
|
||||
this.progressBar.terminate();
|
||||
const files = fs.readdirSync(".");
|
||||
const parts = files.filter(it => it.includes(this.inputData.title));
|
||||
parts.forEach(file => fs.unlinkSync(file));
|
||||
console.log("已取消下载".blue);
|
||||
}
|
||||
private downloadFragmentPart(url: string, range: string, partFilename: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const makeRequest = () => {
|
||||
const req = request({
|
||||
url: url,
|
||||
method: "GET",
|
||||
headers: {
|
||||
Range: range,
|
||||
Origin: "https://www.bilibili.com",
|
||||
Referer: this.inputData.referer || "https://www.bilibili.com",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0",
|
||||
},
|
||||
}).on("complete", response => {
|
||||
if (response.statusCode.toString()[0] === "2") {
|
||||
resolve(response);
|
||||
}
|
||||
else {
|
||||
reject(`请求失败: ${response.statusCode}`);
|
||||
}
|
||||
}).on("data", data => {
|
||||
this.progressMap.set(req, this.progressMap.get(req)! + data.length);
|
||||
this.updateProgress();
|
||||
}).on("error", error => {
|
||||
// fs.unlinkSync(partFilename);
|
||||
this.progressMap.delete(req);
|
||||
this.progressMap.set(makeRequest(), 0);
|
||||
this.updateProgress();
|
||||
reject(`\n片段下载失败: ${error}`);
|
||||
});
|
||||
this.progressBar.terminate();
|
||||
const files = fs.readdirSync(".");
|
||||
const parts = files.filter(it => it.includes(this.inputData.title));
|
||||
parts.forEach(file => fs.unlinkSync(file));
|
||||
console.log("已取消下载".blue);
|
||||
req.pipe(fs.createWriteStream(partFilename, {
|
||||
autoClose: true
|
||||
}))
|
||||
return req
|
||||
};
|
||||
this.progressMap.set(makeRequest(), 0);
|
||||
});
|
||||
}
|
||||
private async downloadFragment(fragment: Fragment, index: number = -1) {
|
||||
const partialLength = Math.round(fragment.size / options.parts);
|
||||
const title = (index === -1 ? this.inputData.title : this.inputData.title + " - " + index.toString());
|
||||
if (fs.existsSync(title + this.extension)) {
|
||||
this.progressBar.terminate();
|
||||
console.log(`跳过了已存在的文件 ${title + this.extension}`);
|
||||
return title;
|
||||
}
|
||||
private downloadFragmentPart(url: string, range: string, partFilename: string)
|
||||
{
|
||||
return new Promise((resolve, reject) =>
|
||||
{
|
||||
const makeRequest = () =>
|
||||
{
|
||||
const req = request({
|
||||
url: url,
|
||||
method: "GET",
|
||||
headers: {
|
||||
Range: range,
|
||||
Origin: "https://www.bilibili.com",
|
||||
Referer: "https://www.bilibili.com",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36",
|
||||
},
|
||||
// maxAttempts: 8,
|
||||
// retryDelay: 1000,
|
||||
}).on("complete", response =>
|
||||
{
|
||||
if (response.statusCode.toString()[0] === "2")
|
||||
{
|
||||
resolve(response);
|
||||
}
|
||||
else
|
||||
{
|
||||
reject(`请求失败: ${response.statusCode}`);
|
||||
}
|
||||
}).on("data", data =>
|
||||
{
|
||||
this.progressMap.set(req, this.progressMap.get(req)! + data.length);
|
||||
this.updateProgress();
|
||||
}).on("error", error =>
|
||||
{
|
||||
// stream.close();
|
||||
// fs.unlinkSync(partFilename);
|
||||
// this.progressMap.delete(req);
|
||||
// this.progressMap.set(makeRequest(), 0);
|
||||
// this.updateProgress();
|
||||
reject(`\n片段下载失败: ${error}`);
|
||||
});
|
||||
req.pipe(fs.createWriteStream(partFilename))
|
||||
return req
|
||||
};
|
||||
this.progressMap.set(makeRequest(), 0);
|
||||
});
|
||||
let startByte = 0;
|
||||
let part = 0;
|
||||
const promises = [];
|
||||
while (startByte < fragment.size) {
|
||||
const partFilename = `${title}.part${part}`;
|
||||
// if (fs.existsSync(partFilename))
|
||||
// {
|
||||
// this.progressMap.set(partFilename, partialLength);
|
||||
// this.updateProgress();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
const endByte = Math.min(fragment.size - 1, Math.round(startByte + partialLength));
|
||||
const range = `bytes=${startByte}-${endByte}`;
|
||||
promises.push(this.downloadFragmentPart(fragment.url, range, partFilename));
|
||||
// }
|
||||
startByte = Math.round(startByte + partialLength) + 1;
|
||||
part++;
|
||||
}
|
||||
private async downloadFragment(fragment: Fragment, index: number = -1)
|
||||
{
|
||||
const partialLength = Math.round(fragment.size / options.parts);
|
||||
const title = (index === -1 ? this.inputData.title : this.inputData.title + " - " + index.toString());
|
||||
if (fs.existsSync(title + this.extension))
|
||||
{
|
||||
this.progressBar.terminate();
|
||||
console.log(`跳过了已存在的文件 ${title + this.extension}`);
|
||||
return title;
|
||||
}
|
||||
let startByte = 0;
|
||||
let part = 0;
|
||||
const promises = [];
|
||||
while (startByte < fragment.size)
|
||||
{
|
||||
const partFilename = `${title}.part${part}`;
|
||||
// if (fs.existsSync(partFilename))
|
||||
// {
|
||||
// this.progressMap.set(partFilename, partialLength);
|
||||
// this.updateProgress();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
const endByte = Math.min(fragment.size - 1, Math.round(startByte + partialLength));
|
||||
const range = `bytes=${startByte}-${endByte}`;
|
||||
promises.push(this.downloadFragmentPart(fragment.url, range, partFilename));
|
||||
// }
|
||||
startByte = Math.round(startByte + partialLength) + 1;
|
||||
part++;
|
||||
}
|
||||
await Promise.all(promises);
|
||||
return title;
|
||||
await Promise.all(promises);
|
||||
return title;
|
||||
}
|
||||
async download() {
|
||||
console.log(`正在下载: ${this.inputData.title}`.green);
|
||||
Downloader.workingDownloader = this;
|
||||
this.progressBar.render();
|
||||
const [fragment] = this.inputData.fragments;
|
||||
this.getExtension(fragment);
|
||||
if (this.inputData.fragments.length === 1) {
|
||||
this.title = await this.downloadFragment(fragment);
|
||||
}
|
||||
async download()
|
||||
{
|
||||
console.log(`正在下载: ${this.inputData.title}`.green);
|
||||
Downloader.workingDownloader = this;
|
||||
this.progressBar.render();
|
||||
const [fragment] = this.inputData.fragments;
|
||||
this.getExtension(fragment);
|
||||
if (this.inputData.fragments.length === 1)
|
||||
{
|
||||
this.title = await this.downloadFragment(fragment);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.title = await Promise.all(this.inputData.fragments.map((f, i) => this.downloadFragment(f, i)));
|
||||
}
|
||||
else {
|
||||
this.title = await Promise.all(this.inputData.fragments.map((f, i) => this.downloadFragment(f, i)));
|
||||
}
|
||||
private async mergeFragment(title: string, index = -1)
|
||||
{
|
||||
const dest = title + this.extension;
|
||||
if (!this.progressBar.complete)
|
||||
{
|
||||
this.progressBar.update(1);
|
||||
this.progressBar.terminate();
|
||||
}
|
||||
if (fs.existsSync(dest))
|
||||
{
|
||||
return dest;
|
||||
}
|
||||
if (index !== -1)
|
||||
{
|
||||
console.log(`正在合并片段${index.toString()}...`.blue);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log("正在合并文件...".blue);
|
||||
}
|
||||
if (options.parts === 1)
|
||||
{
|
||||
fs.renameSync(title + ".part0", dest);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fs.existsSync(dest))
|
||||
{
|
||||
fs.unlinkSync(dest);
|
||||
}
|
||||
const files = fs.readdirSync(".");
|
||||
const parts = files.filter(it => it.includes(title + ".part"));
|
||||
const partRegex = /.*\.part([\d]+)/;
|
||||
parts.sort((a, b) =>
|
||||
{
|
||||
const partA = parseInt(a.replace(partRegex, "$1"));
|
||||
const partB = parseInt(b.replace(partRegex, "$1"));
|
||||
return partA - partB;
|
||||
}).forEach(file =>
|
||||
{
|
||||
const buffer = fs.readFileSync(file);
|
||||
fs.appendFileSync(dest, buffer);
|
||||
});
|
||||
parts.forEach(file => fs.unlinkSync(file));
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
private async mergeFragment(title: string, index = -1) {
|
||||
const dest = title + this.extension;
|
||||
if (!this.progressBar.complete) {
|
||||
this.progressBar.update(1);
|
||||
this.progressBar.terminate();
|
||||
}
|
||||
async merge()
|
||||
{
|
||||
let result: string | string[];
|
||||
if (typeof this.title === "string")
|
||||
{
|
||||
result = await this.mergeFragment(this.title);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await Promise.all(this.title.map((t, i) => this.mergeFragment(t, i)));
|
||||
}
|
||||
console.log(`完成: `.green);
|
||||
if (typeof result === "string")
|
||||
{
|
||||
console.log(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.forEach(it => console.log(it));
|
||||
}
|
||||
if (fs.existsSync(dest)) {
|
||||
return dest;
|
||||
}
|
||||
if (index !== -1) {
|
||||
console.log(`正在合并片段${index.toString()}...`.blue);
|
||||
}
|
||||
else {
|
||||
console.log("正在合并文件...".blue);
|
||||
}
|
||||
if (options.parts === 1) {
|
||||
fs.renameSync(title + ".part0", dest);
|
||||
}
|
||||
else {
|
||||
if (fs.existsSync(dest)) {
|
||||
fs.unlinkSync(dest);
|
||||
}
|
||||
const files = fs.readdirSync(".");
|
||||
const parts = files.filter(it => it.includes(title + ".part"));
|
||||
const partRegex = /.*\.part([\d]+)/;
|
||||
parts.sort((a, b) => {
|
||||
const partA = parseInt(a.replace(partRegex, "$1"));
|
||||
const partB = parseInt(b.replace(partRegex, "$1"));
|
||||
return partA - partB;
|
||||
}).forEach(file => {
|
||||
const buffer = fs.readFileSync(file);
|
||||
fs.appendFileSync(dest, buffer);
|
||||
});
|
||||
parts.forEach(file => fs.unlinkSync(file));
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
async merge() {
|
||||
let result: string | string[];
|
||||
if (typeof this.title === "string") {
|
||||
result = await this.mergeFragment(this.title);
|
||||
}
|
||||
else {
|
||||
result = await Promise.all(this.title.map((t, i) => this.mergeFragment(t, i)));
|
||||
}
|
||||
console.log(`完成: `.green);
|
||||
if (typeof result === "string") {
|
||||
console.log(result);
|
||||
}
|
||||
else {
|
||||
result.forEach(it => console.log(it));
|
||||
}
|
||||
}
|
||||
}
|
||||
(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
let jsonText = '';
|
||||
if (fs.existsSync(options.info))
|
||||
{
|
||||
jsonText = fs.readFileSync(options.info).toString("utf-8");
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonText = await clipboardy.read();
|
||||
}
|
||||
const inputData = JSON.parse(jsonText) as InputData | InputData[];
|
||||
if (!(function (data: any): data is InputData | InputData[]
|
||||
{
|
||||
if (Array.isArray(data) && data.length > 0)
|
||||
{
|
||||
return data[0].fragments !== undefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
return data.fragments !== undefined;
|
||||
}
|
||||
})(inputData))
|
||||
{
|
||||
throw new Error();
|
||||
}
|
||||
try
|
||||
{
|
||||
process.chdir(options.output);
|
||||
process.on("SIGINT", () =>
|
||||
{
|
||||
Downloader.workingDownloader && Downloader.workingDownloader.cancelDownload();
|
||||
process.exit();
|
||||
});
|
||||
if (Array.isArray(inputData))
|
||||
{
|
||||
const downloaders = inputData.map(data => new Downloader(data));
|
||||
for (const downloader of downloaders)
|
||||
{
|
||||
await downloader.download();
|
||||
await downloader.merge();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const downloader = new Downloader(inputData);
|
||||
await downloader.download();
|
||||
await downloader.merge();
|
||||
}
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
Downloader.workingDownloader && Downloader.workingDownloader.cancelDownload();
|
||||
console.error(`\n错误: ${error}`.red);
|
||||
}
|
||||
(async () => {
|
||||
try {
|
||||
let jsonText = '';
|
||||
if (fs.existsSync(options.info)) {
|
||||
jsonText = fs.readFileSync(options.info).toString("utf-8");
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.log(`[无数据] 未在剪贴板检测到有效数据/没有指定输入文件/输入文件的数据无效.`.red);
|
||||
else {
|
||||
jsonText = await clipboardy.read();
|
||||
}
|
||||
})();
|
||||
const inputData = JSON.parse(jsonText) as InputData | InputData[];
|
||||
if (!(function (data: any): data is InputData | InputData[] {
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
return data[0].fragments !== undefined;
|
||||
}
|
||||
else {
|
||||
return data.fragments !== undefined;
|
||||
}
|
||||
})(inputData)) {
|
||||
throw new Error();
|
||||
}
|
||||
try {
|
||||
process.chdir(options.output);
|
||||
process.on("SIGINT", () => {
|
||||
Downloader.workingDownloader && Downloader.workingDownloader.cancelDownload();
|
||||
process.exit();
|
||||
});
|
||||
if (Array.isArray(inputData)) {
|
||||
const downloaders = inputData.map(data => new Downloader(data));
|
||||
for (const downloader of downloaders) {
|
||||
await downloader.download();
|
||||
await downloader.merge();
|
||||
}
|
||||
}
|
||||
else {
|
||||
const downloader = new Downloader(inputData);
|
||||
await downloader.download();
|
||||
await downloader.merge();
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
Downloader.workingDownloader && Downloader.workingDownloader.cancelDownload();
|
||||
console.error(`\n错误: ${error}`.red);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.log(`[无数据] 未在剪贴板检测到有效数据/没有指定输入文件/输入文件的数据无效.`.red);
|
||||
}
|
||||
})();
|
||||
|
||||
2
min/batch-download.min.js
vendored
2
min/batch-download.min.js
vendored
@ -1 +1 @@
|
||||
(()=>{return(t,e)=>{class i{static async test(){if(!document.URL.includes("/www.bilibili.com/video/av")){return false}return await SpinQuery.select("#multi_page")!==null}async collectData(t){const e=`https://api.bilibili.com/x/web-interface/view?aid=${unsafeWindow.aid}`;const i=await Ajax.getJson(e);if(i.code!==0){Toast.error(`获取视频选集列表失败, message=${i.message}`,"批量下载");return""}const s=i.data.pages;if(s===undefined){Toast.error(`获取视频选集列表失败, 没有找到选集信息.`,"批量下载");return""}const a=[];for(const e of s){const i=`https://api.bilibili.com/x/player/playurl?avid=${unsafeWindow.aid}&cid=${e.cid}&qn=${t}&otype=json`;const s=await Ajax.getJsonWithCredentials(i);const r=s.data||s.result||s;if(r.quality!==t){Toast.error("获取下载链接失败, 请确认当前账号有下载权限后重试.","批量下载");return""}const n=r.durl.map(t=>{return{length:t.length,size:t.size,url:t.url}});a.push({fragments:n,title:`${e.page} - ${e.part}`,totalSize:n.map(t=>t.size).reduce((t,e)=>t+e),cid:e.cid})}return JSON.stringify(a)}}class s{static async test(){return document.URL.includes("/www.bilibili.com/bangumi")}async collectData(t){const e=document.querySelector("meta[property='og:url']");if(e===null){Toast.error("获取番剧数据失败: 无法找到 Season ID","批量下载");return""}const i=e.getAttribute("content").match(/play\/ss(\d+)/)[1];if(i===undefined){Toast.error("获取番剧数据失败: 无法解析 Season ID","批量下载");return""}const s=await Ajax.getJson(`https://api.bilibili.com/pgc/web/season/section?season_id=${i}`);if(s.code!==0){Toast.error(`获取番剧数据失败: 无法获取番剧集数列表, message=${s.message}`,"批量下载");return""}const a=s.result.main_section.episodes.map(t=>{return{aid:t.aid,cid:t.cid,number:t.title,title:t.long_title}});const r=[];for(const e of a){const i=`https://api.bilibili.com/pgc/player/web/playurl?avid=${e.aid}&cid=${e.cid}&qn=${t}&otype=json`;const s=await Ajax.getJsonWithCredentials(i);const a=s.data||s.result||s;if(a.quality!==t){Toast.error("获取下载链接失败, 请确认当前账号有下载权限后重试.","批量下载");return""}const n=a.durl.map(t=>{return{length:t.length,size:t.size,url:t.url}});r.push({fragments:n,title:`${e.number} - ${e.title}`,totalSize:n.map(t=>t.size).reduce((t,e)=>t+e),cid:e.cid})}return JSON.stringify(r)}}const a=[s,i];let r=null;class n{static async test(){for(const t of a){if(await t.test()===true){r=t;return true}}r=null;return false}async collectData(t,e){if(r===null){logError("[批量下载] 未找到合适的解析模块.");return null}const i=new r;const s=await i.collectData(t.quality);e.dismiss();return s}}return{export:{BatchExtractor:n}}}})();
|
||||
(()=>{return(t,e)=>{class i{static async test(){if(!document.URL.includes("/www.bilibili.com/video/av")){return false}return await SpinQuery.select("#multi_page")!==null}async collectData(t){const e=`https://api.bilibili.com/x/web-interface/view?aid=${unsafeWindow.aid}`;const i=await Ajax.getJson(e);if(i.code!==0){Toast.error(`获取视频选集列表失败, message=${i.message}`,"批量下载");return""}const r=i.data.pages;if(r===undefined){Toast.error(`获取视频选集列表失败, 没有找到选集信息.`,"批量下载");return""}const a=[];for(const e of r){const i=`https://api.bilibili.com/x/player/playurl?avid=${unsafeWindow.aid}&cid=${e.cid}&qn=${t}&otype=json`;const r=await Ajax.getJsonWithCredentials(i);const s=r.data||r.result||r;if(s.quality!==t){Toast.error("获取下载链接失败, 请确认当前账号有下载权限后重试.","批量下载");return""}const n=s.durl.map(t=>{return{length:t.length,size:t.size,url:t.url}});a.push({fragments:n,title:`${e.page} - ${e.part}`,totalSize:n.map(t=>t.size).reduce((t,e)=>t+e),cid:e.cid,referer:document.URL.replace(window.location.search,"")})}return JSON.stringify(a)}}class r{static async test(){return document.URL.includes("/www.bilibili.com/bangumi")}async collectData(t){const e=document.querySelector("meta[property='og:url']");if(e===null){Toast.error("获取番剧数据失败: 无法找到 Season ID","批量下载");return""}const i=e.getAttribute("content").match(/play\/ss(\d+)/)[1];if(i===undefined){Toast.error("获取番剧数据失败: 无法解析 Season ID","批量下载");return""}const r=await Ajax.getJson(`https://api.bilibili.com/pgc/web/season/section?season_id=${i}`);if(r.code!==0){Toast.error(`获取番剧数据失败: 无法获取番剧集数列表, message=${r.message}`,"批量下载");return""}const a=r.result.main_section.episodes.map(t=>{return{aid:t.aid,cid:t.cid,number:t.title,title:t.long_title}});const s=[];for(const e of a){const i=`https://api.bilibili.com/pgc/player/web/playurl?avid=${e.aid}&cid=${e.cid}&qn=${t}&otype=json`;const r=await Ajax.getJsonWithCredentials(i);const a=r.data||r.result||r;if(a.quality!==t){Toast.error("获取下载链接失败, 请确认当前账号有下载权限后重试.","批量下载");return""}const n=a.durl.map(t=>{return{length:t.length,size:t.size,url:t.url}});s.push({fragments:n,title:`${e.number} - ${e.title}`,totalSize:n.map(t=>t.size).reduce((t,e)=>t+e),cid:e.cid})}return JSON.stringify(s)}}const a=[r,i];let s=null;class n{static async test(){for(const t of a){if(await t.test()===true){s=t;return true}}s=null;return false}async collectData(t,e){if(s===null){logError("[批量下载] 未找到合适的解析模块.");return null}const i=new s;const r=await i.collectData(t.quality);e.dismiss();return r}}return{export:{BatchExtractor:n}}}})();
|
||||
2
min/download-video.min.js
vendored
2
min/download-video.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,148 +1,124 @@
|
||||
class VideoEpisodeBatch
|
||||
{
|
||||
static async test()
|
||||
{
|
||||
if (!document.URL.includes("/www.bilibili.com/video/av"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return await SpinQuery.select("#multi_page") !== null;
|
||||
class VideoEpisodeBatch {
|
||||
static async test () {
|
||||
if (!document.URL.includes('/www.bilibili.com/video/av')) {
|
||||
return false
|
||||
}
|
||||
async collectData(quality)
|
||||
{
|
||||
const api = `https://api.bilibili.com/x/web-interface/view?aid=${unsafeWindow.aid}`;
|
||||
const json = await Ajax.getJson(api);
|
||||
if (json.code !== 0)
|
||||
{
|
||||
Toast.error(`获取视频选集列表失败, message=${json.message}`, "批量下载");
|
||||
return "";
|
||||
}
|
||||
const pages = json.data.pages;
|
||||
if (pages === undefined)
|
||||
{
|
||||
Toast.error(`获取视频选集列表失败, 没有找到选集信息.`, "批量下载");
|
||||
return "";
|
||||
}
|
||||
const result = [];
|
||||
for (const page of pages)
|
||||
{
|
||||
const url = `https://api.bilibili.com/x/player/playurl?avid=${unsafeWindow.aid}&cid=${page.cid}&qn=${quality}&otype=json`;
|
||||
const json = await Ajax.getJsonWithCredentials(url);
|
||||
const data = json.data || json.result || json;
|
||||
if (data.quality !== quality)
|
||||
{
|
||||
Toast.error("获取下载链接失败, 请确认当前账号有下载权限后重试.", "批量下载");
|
||||
return "";
|
||||
}
|
||||
const fragments = data.durl.map(it =>
|
||||
{
|
||||
return {
|
||||
length: it.length,
|
||||
size: it.size,
|
||||
url: it.url,
|
||||
};
|
||||
});
|
||||
result.push({
|
||||
fragments,
|
||||
title: `${page.page} - ${page.part}`,
|
||||
totalSize: fragments.map(it => it.size).reduce((acc, it) => acc + it),
|
||||
cid: page.cid,
|
||||
});
|
||||
}
|
||||
return JSON.stringify(result);
|
||||
return await SpinQuery.select('#multi_page') !== null
|
||||
}
|
||||
async collectData (quality) {
|
||||
const api = `https://api.bilibili.com/x/web-interface/view?aid=${unsafeWindow.aid}`
|
||||
const json = await Ajax.getJson(api)
|
||||
if (json.code !== 0) {
|
||||
Toast.error(`获取视频选集列表失败, message=${json.message}`, '批量下载')
|
||||
return ''
|
||||
}
|
||||
const pages = json.data.pages
|
||||
if (pages === undefined) {
|
||||
Toast.error(`获取视频选集列表失败, 没有找到选集信息.`, '批量下载')
|
||||
return ''
|
||||
}
|
||||
const result = []
|
||||
for (const page of pages) {
|
||||
const url = `https://api.bilibili.com/x/player/playurl?avid=${unsafeWindow.aid}&cid=${page.cid}&qn=${quality}&otype=json`
|
||||
const json = await Ajax.getJsonWithCredentials(url)
|
||||
const data = json.data || json.result || json
|
||||
if (data.quality !== quality) {
|
||||
Toast.error('获取下载链接失败, 请确认当前账号有下载权限后重试.', '批量下载')
|
||||
return ''
|
||||
}
|
||||
const fragments = data.durl.map(it => {
|
||||
return {
|
||||
length: it.length,
|
||||
size: it.size,
|
||||
url: it.url
|
||||
}
|
||||
})
|
||||
result.push({
|
||||
fragments,
|
||||
title: `${page.page} - ${page.part}`,
|
||||
totalSize: fragments.map(it => it.size).reduce((acc, it) => acc + it),
|
||||
cid: page.cid,
|
||||
referer: document.URL.replace(window.location.search, '')
|
||||
})
|
||||
}
|
||||
return JSON.stringify(result)
|
||||
}
|
||||
}
|
||||
class BangumiBatch
|
||||
{
|
||||
static async test()
|
||||
{
|
||||
return document.URL.includes("/www.bilibili.com/bangumi");
|
||||
class BangumiBatch {
|
||||
static async test () {
|
||||
return document.URL.includes('/www.bilibili.com/bangumi')
|
||||
}
|
||||
async collectData (quality) {
|
||||
const metaUrl = document.querySelector("meta[property='og:url']")
|
||||
if (metaUrl === null) {
|
||||
Toast.error('获取番剧数据失败: 无法找到 Season ID', '批量下载')
|
||||
return ''
|
||||
}
|
||||
async collectData(quality)
|
||||
{
|
||||
const metaUrl = document.querySelector("meta[property='og:url']");
|
||||
if (metaUrl === null)
|
||||
{
|
||||
Toast.error("获取番剧数据失败: 无法找到 Season ID", "批量下载");
|
||||
return "";
|
||||
}
|
||||
const seasonId = metaUrl.getAttribute("content").match(/play\/ss(\d+)/)[1];
|
||||
if (seasonId === undefined)
|
||||
{
|
||||
Toast.error("获取番剧数据失败: 无法解析 Season ID", "批量下载");
|
||||
return "";
|
||||
}
|
||||
const json = await Ajax.getJson(`https://api.bilibili.com/pgc/web/season/section?season_id=${seasonId}`);
|
||||
if (json.code !== 0)
|
||||
{
|
||||
Toast.error(`获取番剧数据失败: 无法获取番剧集数列表, message=${json.message}`, "批量下载");
|
||||
return "";
|
||||
}
|
||||
const pages = json.result.main_section.episodes.map(it =>
|
||||
{
|
||||
return { aid: it.aid, cid: it.cid, number: it.title, title: it.long_title };
|
||||
});
|
||||
const result = [];
|
||||
for (const page of pages)
|
||||
{
|
||||
const url = `https://api.bilibili.com/pgc/player/web/playurl?avid=${page.aid}&cid=${page.cid}&qn=${quality}&otype=json`;
|
||||
const json = await Ajax.getJsonWithCredentials(url);
|
||||
const data = json.data || json.result || json;
|
||||
if (data.quality !== quality)
|
||||
{
|
||||
Toast.error("获取下载链接失败, 请确认当前账号有下载权限后重试.", "批量下载");
|
||||
return "";
|
||||
}
|
||||
const fragments = data.durl.map(it =>
|
||||
{
|
||||
return {
|
||||
length: it.length,
|
||||
size: it.size,
|
||||
url: it.url,
|
||||
};
|
||||
});
|
||||
result.push({
|
||||
fragments,
|
||||
title: `${page.number} - ${page.title}`,
|
||||
totalSize: fragments.map(it => it.size).reduce((acc, it) => acc + it),
|
||||
cid: page.cid,
|
||||
});
|
||||
}
|
||||
return JSON.stringify(result);
|
||||
const seasonId = metaUrl.getAttribute('content').match(/play\/ss(\d+)/)[1]
|
||||
if (seasonId === undefined) {
|
||||
Toast.error('获取番剧数据失败: 无法解析 Season ID', '批量下载')
|
||||
return ''
|
||||
}
|
||||
const json = await Ajax.getJson(`https://api.bilibili.com/pgc/web/season/section?season_id=${seasonId}`)
|
||||
if (json.code !== 0) {
|
||||
Toast.error(`获取番剧数据失败: 无法获取番剧集数列表, message=${json.message}`, '批量下载')
|
||||
return ''
|
||||
}
|
||||
const pages = json.result.main_section.episodes.map(it => {
|
||||
return { aid: it.aid, cid: it.cid, number: it.title, title: it.long_title }
|
||||
})
|
||||
const result = []
|
||||
for (const page of pages) {
|
||||
const url = `https://api.bilibili.com/pgc/player/web/playurl?avid=${page.aid}&cid=${page.cid}&qn=${quality}&otype=json`
|
||||
const json = await Ajax.getJsonWithCredentials(url)
|
||||
const data = json.data || json.result || json
|
||||
if (data.quality !== quality) {
|
||||
Toast.error('获取下载链接失败, 请确认当前账号有下载权限后重试.', '批量下载')
|
||||
return ''
|
||||
}
|
||||
const fragments = data.durl.map(it => {
|
||||
return {
|
||||
length: it.length,
|
||||
size: it.size,
|
||||
url: it.url
|
||||
}
|
||||
})
|
||||
result.push({
|
||||
fragments,
|
||||
title: `${page.number} - ${page.title}`,
|
||||
totalSize: fragments.map(it => it.size).reduce((acc, it) => acc + it),
|
||||
cid: page.cid
|
||||
})
|
||||
}
|
||||
return JSON.stringify(result)
|
||||
}
|
||||
}
|
||||
const extractors = [BangumiBatch, VideoEpisodeBatch];
|
||||
let ExtractorClass = null;
|
||||
export class BatchExtractor
|
||||
{
|
||||
static async test()
|
||||
{
|
||||
for (const e of extractors)
|
||||
{
|
||||
if (await e.test() === true)
|
||||
{
|
||||
ExtractorClass = e;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ExtractorClass = null;
|
||||
return false;
|
||||
const extractors = [BangumiBatch, VideoEpisodeBatch]
|
||||
let ExtractorClass = null
|
||||
export class BatchExtractor {
|
||||
static async test () {
|
||||
for (const e of extractors) {
|
||||
if (await e.test() === true) {
|
||||
ExtractorClass = e
|
||||
return true
|
||||
}
|
||||
}
|
||||
async collectData(format, toast)
|
||||
{
|
||||
if (ExtractorClass === null)
|
||||
{
|
||||
logError("[批量下载] 未找到合适的解析模块.");
|
||||
return null;
|
||||
}
|
||||
const extractor = new ExtractorClass;
|
||||
const result = await extractor.collectData(format.quality);
|
||||
toast.dismiss();
|
||||
return result;
|
||||
ExtractorClass = null
|
||||
return false
|
||||
}
|
||||
async collectData (format, toast) {
|
||||
if (ExtractorClass === null) {
|
||||
logError('[批量下载] 未找到合适的解析模块.')
|
||||
return null
|
||||
}
|
||||
const extractor = new ExtractorClass()
|
||||
const result = await extractor.collectData(format.quality)
|
||||
toast.dismiss()
|
||||
return result
|
||||
}
|
||||
}
|
||||
export default {
|
||||
export: {
|
||||
BatchExtractor,
|
||||
},
|
||||
};
|
||||
export: {
|
||||
BatchExtractor
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user