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;
|
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 imported = match.Groups[1].Value.Replace(" as ", ":");
|
||||||
var source = convertToRuntimeSource(match.Groups[2].Value);
|
var source = convertToRuntimeSource(match.Groups[2].Value);
|
||||||
@ -173,6 +173,7 @@ namespace BilibiliEvolved.Build
|
|||||||
};
|
};
|
||||||
})();";
|
})();";
|
||||||
}
|
}
|
||||||
|
// Console.WriteLine(input);
|
||||||
return new UglifyJs().Run(input);
|
return new UglifyJs().Run(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bilibili-evolved-video-link-downloader",
|
"name": "bilibili-evolved-video-link-downloader",
|
||||||
"version": "1.8.1",
|
"version": "1.8.2",
|
||||||
"description": "Bilibili Evolved 视频链接下载器",
|
"description": "Bilibili Evolved 视频链接下载器",
|
||||||
"main": "video-link-downloader.js",
|
"main": "video-link-downloader.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
@ -64,8 +64,8 @@ class Downloader {
|
|||||||
headers: {
|
headers: {
|
||||||
Range: range,
|
Range: range,
|
||||||
Origin: "https://www.bilibili.com",
|
Origin: "https://www.bilibili.com",
|
||||||
Referer: "https://www.bilibili.com",
|
Referer: this.inputData.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",
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0",
|
||||||
},
|
},
|
||||||
}).on("complete", response => {
|
}).on("complete", response => {
|
||||||
if (response.statusCode.toString()[0] === "2") {
|
if (response.statusCode.toString()[0] === "2") {
|
||||||
@ -78,15 +78,15 @@ class Downloader {
|
|||||||
this.progressMap.set(req, this.progressMap.get(req) + data.length);
|
this.progressMap.set(req, this.progressMap.get(req) + data.length);
|
||||||
this.updateProgress();
|
this.updateProgress();
|
||||||
}).on("error", error => {
|
}).on("error", error => {
|
||||||
// stream.close();
|
|
||||||
// fs.unlinkSync(partFilename);
|
// fs.unlinkSync(partFilename);
|
||||||
// this.progressMap.delete(req);
|
this.progressMap.delete(req);
|
||||||
// this.progressMap.set(makeRequest(), 0);
|
this.progressMap.set(makeRequest(), 0);
|
||||||
// this.updateProgress();
|
this.updateProgress();
|
||||||
reject(`\n片段下载失败: ${error}`);
|
reject(`\n片段下载失败: ${error}`);
|
||||||
});
|
});
|
||||||
console.log(`created stream ${partFilename}`);
|
req.pipe(fs.createWriteStream(partFilename, {
|
||||||
req.pipe(fs.createWriteStream(partFilename));
|
autoClose: true
|
||||||
|
}));
|
||||||
return req;
|
return req;
|
||||||
};
|
};
|
||||||
this.progressMap.set(makeRequest(), 0);
|
this.progressMap.set(makeRequest(), 0);
|
||||||
|
|||||||
@ -6,31 +6,29 @@ import fs = require("fs");
|
|||||||
import ProgressBar = require("progress");
|
import ProgressBar = require("progress");
|
||||||
import "colors";
|
import "colors";
|
||||||
|
|
||||||
interface Fragment
|
interface Fragment {
|
||||||
{
|
length: number;
|
||||||
length: number;
|
size: number;
|
||||||
size: number;
|
url: string;
|
||||||
url: string;
|
backupUrls: string[];
|
||||||
backupUrls: string[];
|
|
||||||
}
|
}
|
||||||
interface InputData
|
interface InputData {
|
||||||
{
|
fragments: Fragment[];
|
||||||
fragments: Fragment[];
|
title: string;
|
||||||
title: string;
|
totalSize: number;
|
||||||
totalSize: number;
|
referer: string;
|
||||||
}
|
}
|
||||||
interface Settings
|
interface Settings {
|
||||||
{
|
parts: number;
|
||||||
parts: number;
|
info: string;
|
||||||
info: string;
|
output: string;
|
||||||
output: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const optionDefinitions = [
|
const optionDefinitions = [
|
||||||
{ name: 'danmaku', alias: 'd', defaultValue: false, type: Boolean },
|
{ name: 'danmaku', alias: 'd', defaultValue: false, type: Boolean },
|
||||||
{ name: 'info', alias: 'i', defaultOption: true, type: String, defaultValue: undefined },
|
{ name: 'info', alias: 'i', defaultOption: true, type: String, defaultValue: undefined },
|
||||||
{ name: 'parts', alias: 'p', type: Number, defaultValue: 12 },
|
{ name: 'parts', alias: 'p', type: Number, defaultValue: 12 },
|
||||||
{ name: 'output', alias: 'o', type: String, defaultValue: '.' },
|
{ name: 'output', alias: 'o', type: String, defaultValue: '.' },
|
||||||
];
|
];
|
||||||
const commandLineOptions = commandLineArgs(optionDefinitions) as Settings;
|
const commandLineOptions = commandLineArgs(optionDefinitions) as Settings;
|
||||||
let options = commandLineOptions;
|
let options = commandLineOptions;
|
||||||
@ -41,277 +39,224 @@ options.parts = Math.round(options.parts);
|
|||||||
// options = Object.assign(jsonOptions, options);
|
// options = Object.assign(jsonOptions, options);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (options.parts < 1)
|
if (options.parts < 1) {
|
||||||
{
|
console.error("分段数不能小于1".red);
|
||||||
console.error("分段数不能小于1".red);
|
process.exit();
|
||||||
process.exit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Downloader
|
class Downloader {
|
||||||
{
|
static workingDownloader: Downloader | null = null;
|
||||||
static workingDownloader: Downloader | null = null;
|
private progressMap = new Map<request.Request | string, number>();
|
||||||
private progressMap = new Map<request.Request | string, number>();
|
private progressBar = new ProgressBar(":percent [:bar]", {
|
||||||
private progressBar = new ProgressBar(":percent [:bar]", {
|
total: this.inputData.totalSize,
|
||||||
total: this.inputData.totalSize,
|
width: 20,
|
||||||
width: 20,
|
incomplete: ' ',
|
||||||
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;
|
this.progressBar.terminate();
|
||||||
private title: string | string[];
|
const files = fs.readdirSync(".");
|
||||||
|
const parts = files.filter(it => it.includes(this.inputData.title));
|
||||||
constructor(
|
parts.forEach(file => fs.unlinkSync(file));
|
||||||
private inputData: InputData,
|
console.log("已取消下载".blue);
|
||||||
)
|
}
|
||||||
{ }
|
private downloadFragmentPart(url: string, range: string, partFilename: string) {
|
||||||
private getExtension(fragment: Fragment)
|
return new Promise((resolve, reject) => {
|
||||||
{
|
const makeRequest = () => {
|
||||||
this.extension = fragment.url.includes(".flv") ? ".flv" : ".mp4";
|
const req = request({
|
||||||
}
|
url: url,
|
||||||
private updateProgress()
|
method: "GET",
|
||||||
{
|
headers: {
|
||||||
const progress = this.progressMap ?
|
Range: range,
|
||||||
[...this.progressMap.values()].reduce((a, b) => a + b, 0) / this.inputData.totalSize : 0;
|
Origin: "https://www.bilibili.com",
|
||||||
this.progressBar.update(progress);
|
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",
|
||||||
cancelDownload()
|
},
|
||||||
{
|
}).on("complete", response => {
|
||||||
[...this.progressMap.keys()].forEach(it =>
|
if (response.statusCode.toString()[0] === "2") {
|
||||||
{
|
resolve(response);
|
||||||
if (typeof it !== "string")
|
}
|
||||||
{
|
else {
|
||||||
it.abort();
|
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();
|
req.pipe(fs.createWriteStream(partFilename, {
|
||||||
const files = fs.readdirSync(".");
|
autoClose: true
|
||||||
const parts = files.filter(it => it.includes(this.inputData.title));
|
}))
|
||||||
parts.forEach(file => fs.unlinkSync(file));
|
return req
|
||||||
console.log("已取消下载".blue);
|
};
|
||||||
|
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)
|
let startByte = 0;
|
||||||
{
|
let part = 0;
|
||||||
return new Promise((resolve, reject) =>
|
const promises = [];
|
||||||
{
|
while (startByte < fragment.size) {
|
||||||
const makeRequest = () =>
|
const partFilename = `${title}.part${part}`;
|
||||||
{
|
// if (fs.existsSync(partFilename))
|
||||||
const req = request({
|
// {
|
||||||
url: url,
|
// this.progressMap.set(partFilename, partialLength);
|
||||||
method: "GET",
|
// this.updateProgress();
|
||||||
headers: {
|
// }
|
||||||
Range: range,
|
// else
|
||||||
Origin: "https://www.bilibili.com",
|
// {
|
||||||
Referer: "https://www.bilibili.com",
|
const endByte = Math.min(fragment.size - 1, Math.round(startByte + partialLength));
|
||||||
"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",
|
const range = `bytes=${startByte}-${endByte}`;
|
||||||
},
|
promises.push(this.downloadFragmentPart(fragment.url, range, partFilename));
|
||||||
// maxAttempts: 8,
|
// }
|
||||||
// retryDelay: 1000,
|
startByte = Math.round(startByte + partialLength) + 1;
|
||||||
}).on("complete", response =>
|
part++;
|
||||||
{
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
private async downloadFragment(fragment: Fragment, index: number = -1)
|
await Promise.all(promises);
|
||||||
{
|
return title;
|
||||||
const partialLength = Math.round(fragment.size / options.parts);
|
}
|
||||||
const title = (index === -1 ? this.inputData.title : this.inputData.title + " - " + index.toString());
|
async download() {
|
||||||
if (fs.existsSync(title + this.extension))
|
console.log(`正在下载: ${this.inputData.title}`.green);
|
||||||
{
|
Downloader.workingDownloader = this;
|
||||||
this.progressBar.terminate();
|
this.progressBar.render();
|
||||||
console.log(`跳过了已存在的文件 ${title + this.extension}`);
|
const [fragment] = this.inputData.fragments;
|
||||||
return title;
|
this.getExtension(fragment);
|
||||||
}
|
if (this.inputData.fragments.length === 1) {
|
||||||
let startByte = 0;
|
this.title = await this.downloadFragment(fragment);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
async download()
|
else {
|
||||||
{
|
this.title = await Promise.all(this.inputData.fragments.map((f, i) => this.downloadFragment(f, i)));
|
||||||
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)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private async mergeFragment(title: string, index = -1)
|
}
|
||||||
{
|
private async mergeFragment(title: string, index = -1) {
|
||||||
const dest = title + this.extension;
|
const dest = title + this.extension;
|
||||||
if (!this.progressBar.complete)
|
if (!this.progressBar.complete) {
|
||||||
{
|
this.progressBar.update(1);
|
||||||
this.progressBar.update(1);
|
this.progressBar.terminate();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
async merge()
|
if (fs.existsSync(dest)) {
|
||||||
{
|
return dest;
|
||||||
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 (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 () =>
|
(async () => {
|
||||||
{
|
try {
|
||||||
try
|
let jsonText = '';
|
||||||
{
|
if (fs.existsSync(options.info)) {
|
||||||
let jsonText = '';
|
jsonText = fs.readFileSync(options.info).toString("utf-8");
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (error)
|
else {
|
||||||
{
|
jsonText = await clipboardy.read();
|
||||||
console.log(`[无数据] 未在剪贴板检测到有效数据/没有指定输入文件/输入文件的数据无效.`.red);
|
|
||||||
}
|
}
|
||||||
})();
|
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
|
class VideoEpisodeBatch {
|
||||||
{
|
static async test () {
|
||||||
static async test()
|
if (!document.URL.includes('/www.bilibili.com/video/av')) {
|
||||||
{
|
return false
|
||||||
if (!document.URL.includes("/www.bilibili.com/video/av"))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return await SpinQuery.select("#multi_page") !== null;
|
|
||||||
}
|
}
|
||||||
async collectData(quality)
|
return await SpinQuery.select('#multi_page') !== null
|
||||||
{
|
}
|
||||||
const api = `https://api.bilibili.com/x/web-interface/view?aid=${unsafeWindow.aid}`;
|
async collectData (quality) {
|
||||||
const json = await Ajax.getJson(api);
|
const api = `https://api.bilibili.com/x/web-interface/view?aid=${unsafeWindow.aid}`
|
||||||
if (json.code !== 0)
|
const json = await Ajax.getJson(api)
|
||||||
{
|
if (json.code !== 0) {
|
||||||
Toast.error(`获取视频选集列表失败, message=${json.message}`, "批量下载");
|
Toast.error(`获取视频选集列表失败, message=${json.message}`, '批量下载')
|
||||||
return "";
|
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);
|
|
||||||
}
|
}
|
||||||
|
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
|
class BangumiBatch {
|
||||||
{
|
static async test () {
|
||||||
static async test()
|
return document.URL.includes('/www.bilibili.com/bangumi')
|
||||||
{
|
}
|
||||||
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 seasonId = metaUrl.getAttribute('content').match(/play\/ss(\d+)/)[1]
|
||||||
{
|
if (seasonId === undefined) {
|
||||||
const metaUrl = document.querySelector("meta[property='og:url']");
|
Toast.error('获取番剧数据失败: 无法解析 Season ID', '批量下载')
|
||||||
if (metaUrl === null)
|
return ''
|
||||||
{
|
|
||||||
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 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];
|
const extractors = [BangumiBatch, VideoEpisodeBatch]
|
||||||
let ExtractorClass = null;
|
let ExtractorClass = null
|
||||||
export class BatchExtractor
|
export class BatchExtractor {
|
||||||
{
|
static async test () {
|
||||||
static async test()
|
for (const e of extractors) {
|
||||||
{
|
if (await e.test() === true) {
|
||||||
for (const e of extractors)
|
ExtractorClass = e
|
||||||
{
|
return true
|
||||||
if (await e.test() === true)
|
}
|
||||||
{
|
|
||||||
ExtractorClass = e;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ExtractorClass = null;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
async collectData(format, toast)
|
ExtractorClass = null
|
||||||
{
|
return false
|
||||||
if (ExtractorClass === null)
|
}
|
||||||
{
|
async collectData (format, toast) {
|
||||||
logError("[批量下载] 未找到合适的解析模块.");
|
if (ExtractorClass === null) {
|
||||||
return null;
|
logError('[批量下载] 未找到合适的解析模块.')
|
||||||
}
|
return null
|
||||||
const extractor = new ExtractorClass;
|
|
||||||
const result = await extractor.collectData(format.quality);
|
|
||||||
toast.dismiss();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
const extractor = new ExtractorClass()
|
||||||
|
const result = await extractor.collectData(format.quality)
|
||||||
|
toast.dismiss()
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export default {
|
export default {
|
||||||
export: {
|
export: {
|
||||||
BatchExtractor,
|
BatchExtractor
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user