Add download logic

This commit is contained in:
the1812 2019-04-22 17:07:07 +08:00
parent 19b6e091eb
commit f37f42234b
4 changed files with 248 additions and 30 deletions

View File

@ -4,8 +4,12 @@
"description": "下载从 Bilibili-Evolved 复制的视频链接",
"main": "video-link-downloader.js",
"dependencies": {
"@types/progress": "^2.0.3",
"@types/request": "^2.48.1",
"clipboardy": "^2.0.0",
"command-line-args": "^5.1.1"
"command-line-args": "^5.1.1",
"progress": "^2.0.3",
"request": "^2.88.0"
},
"devDependencies": {
"@types/command-line-args": "^5.0.0",
@ -29,4 +33,4 @@
"url": "https://github.com/the1812/Bilibili-Evolved/issues"
},
"homepage": "https://github.com/the1812/Bilibili-Evolved/"
}
}

View File

@ -0,0 +1,7 @@
{
"urls": [
"https://upos-hz-mirrorcosu.acgvideo.com/upgcxcode/28/51/85135128/85135128-1-64.flv?e=ig8euxZM2rNcNbh37zUVhoMznwuBhwdEto8g5X10ugNcXBlqNxHxNEVE5XREto8KqJZHUa6m5J0SqE85tZvEuENvNC8xNEVE9EKE9IMvXBvE2ENvNCImNEVEK9GVqJIwqa80WXIekXRE9IMvXBvEuENvNCImNEVEua6m2jIxux0CkF6s2JZv5x0DQJZY2F8SkXKE9IB5QK==&deadline=1555924643&gen=playurl&nbs=1&oi=1880223831&os=cosu&platform=pc&trid=45a4f29cb06f40168f60745b18d2062e&uipk=5&upsig=c8609d4926d27f696d74375112cf51bf&uparams=e,deadline,gen,nbs,oi,os,platform,trid,uipk"
],
"title": "【东方自作Arrange】 利己主義者のススメ 【今宵は飄逸なエゴイスト】",
"totalSize": 50339950
}

View File

@ -2,20 +2,109 @@
Object.defineProperty(exports, "__esModule", { value: true });
const commandLineArgs = require("command-line-args");
const clipboardy = require("clipboardy");
const downloadFile = async (url) => {
};
const request = require("request");
const fs = require("fs");
const optionDefinitions = [
{ name: 'parts', alias: 'p', type: Number, defaultValue: 30 },
];
const options = commandLineArgs(optionDefinitions);
class Downloader {
constructor(inputData, progress) {
this.inputData = inputData;
this.progress = progress;
this.progressMap = new Map();
}
updateProgress() {
const progress = this.progressMap ?
[...this.progressMap.values()].reduce((a, b) => a + b, 0) / this.inputData.totalSize : 0;
this.progress && this.progress(progress);
}
cancelDownload() {
[...this.progressMap.keys()].forEach(it => it.abort());
console.log("已取消下载");
}
async downloadUrl(url, index = -1) {
const partialLength = Math.round(this.inputData.totalSize / options.parts);
console.log(partialLength);
let startByte = 0;
let part = 0;
const promises = [];
while (startByte < this.inputData.totalSize) {
const endByte = Math.min(this.inputData.totalSize - 1, Math.round(startByte + partialLength));
const range = `bytes=${startByte}-${endByte}`;
promises.push(new Promise((resolve, reject) => {
const makeRequest = () => {
const req = request({
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",
},
}).on("complete", response => {
if (response.statusCode.toString()[0] === "2") {
resolve(response);
}
else {
reject(`请求失败`);
}
}).on("data", data => {
this.progressMap.set(req, this.progressMap.get(req) + data.length);
this.updateProgress();
}).on("error", error => {
this.progressMap.set(req, 0);
this.updateProgress();
// makeRequest();
console.error(error);
});
req.pipe(fs.createWriteStream(`${this.inputData.title}.part${part}`));
return req;
};
this.progressMap.set(makeRequest(), 0);
}));
startByte = Math.round(startByte + partialLength) + 1;
part++;
}
await Promise.all(promises);
const dest = (index === -1 ? this.inputData.title : this.inputData.title + " - " + index.toString()) + ".flv";
await new Promise(resolve => {
fs.readdir(".", (_, files) => {
const parts = files.filter(it => it.includes(this.inputData.title));
const data = parts.map(file => fs.readFileSync(file));
const stream = fs.createWriteStream(dest);
data.forEach(it => stream.write(it));
stream.close();
parts.forEach(file => fs.unlinkSync(file));
resolve();
});
});
}
async download() {
if (this.inputData.urls.length === 1) {
await this.downloadUrl(this.inputData.urls[0]);
}
else {
await Promise.all(this.inputData.urls.map((url, i) => this.downloadUrl(url, i)));
}
}
}
(async () => {
const optionDefinitions = [
{ name: 'parts', alias: 'p', type: Number, defaultValue: 30 },
{ name: 'output', alias: 'o', type: String, defaultValue: '' },
];
const options = commandLineArgs(optionDefinitions);
const urls = (await clipboardy.read()).split("\n").map(it => it.trim());
process.on("SIGINT", () => {
console.log("break");
process.exit();
});
console.log(urls);
console.log(options);
urls.forEach(url => downloadFile(url));
const jsonText = await clipboardy.read();
try {
const inputData = JSON.parse(jsonText);
const downloader = new Downloader(inputData, progress => {
console.log((progress * 100).toFixed(2) + "%");
});
process.on("SIGINT", () => {
downloader.cancelDownload();
process.exit();
});
await downloader.download();
}
catch (error) {
console.error(`未在剪贴板找到足够的数据: ${error}`);
}
})();

View File

@ -1,6 +1,8 @@
import { ClientRequest } from "http";
import commandLineArgs = require("command-line-args");
import clipboardy = require("clipboardy");
import request = require("request");
import fs = require("fs");
import ProgressBar = require("progress");
interface InputData
{
@ -11,20 +13,114 @@ interface InputData
const optionDefinitions = [
{ name: 'parts', alias: 'p', type: Number, defaultValue: 30 },
{ name: 'output', alias: 'o', type: String, defaultValue: '' },
];
const options = commandLineArgs(optionDefinitions) as { parts: number, output: string };
process.on("SIGINT", () =>
{
console.log("break");
process.exit();
});
const options = commandLineArgs(optionDefinitions) as { parts: number };
const downloadFile = async (inputData: InputData) =>
type ProgressHandler = (p: number) => void;
class Downloader
{
const partialLength = Math.round(inputData.totalSize / options.parts);
};
constructor(
private inputData: InputData,
private progress: ProgressHandler
) { }
private progressMap = new Map<request.Request, number>();
private updateProgress()
{
const progress = this.progressMap ?
[...this.progressMap.values()].reduce((a, b) => a + b, 0) / this.inputData.totalSize : 0;
this.progress && this.progress(progress);
}
cancelDownload()
{
[...this.progressMap.keys()].forEach(it => it.abort());
fs.readdir(".", (_, files) =>
{
const parts = files.filter(it => it.includes(this.inputData.title));
parts.forEach(file => fs.unlinkSync(file));
});
console.log("已取消下载");
}
private async downloadUrl(url: string, index: number = -1)
{
const partialLength = Math.round(this.inputData.totalSize / options.parts);
const title = (index === -1 ? this.inputData.title : this.inputData.title + " - " + index.toString());
let startByte = 0;
let part = 0;
const promises = [];
while (startByte < this.inputData.totalSize)
{
const endByte = Math.min(this.inputData.totalSize - 1, Math.round(startByte + partialLength));
const range = `bytes=${startByte}-${endByte}`;
promises.push(new Promise((resolve, reject) =>
{
const makeRequest = () =>
{
const req = request({
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",
},
}).on("complete", response =>
{
if (response.statusCode.toString()[0] === "2")
{
resolve(response);
}
else
{
reject(`请求失败`);
}
}).on("data", data =>
{
this.progressMap.set(req, this.progressMap.get(req)! + data.length);
this.updateProgress();
}).on("error", error =>
{
this.progressMap.set(req, 0);
this.updateProgress();
// makeRequest();
console.error(error);
});
req.pipe(fs.createWriteStream(`${title}.part${part}`));
return req;
};
this.progressMap.set(makeRequest(), 0);
}));
startByte = Math.round(startByte + partialLength) + 1;
part++;
}
await Promise.all(promises);
const dest = title + ".flv";
return await new Promise<string>(resolve =>
{
fs.readdir(".", (_, files) =>
{
const parts = files.filter(it => it.includes(title + ".part"));
const data = parts.map(file => fs.readFileSync(file));
const stream = fs.createWriteStream(dest);
data.forEach(it => stream.write(it));
stream.close();
parts.forEach(file => fs.unlinkSync(file));
resolve(dest);
});
});
}
async download()
{
if (this.inputData.urls.length === 1)
{
return await this.downloadUrl(this.inputData.urls[0]);
}
else
{
return await Promise.all(this.inputData.urls.map((url, i) => this.downloadUrl(url, i)));
}
}
}
(async () =>
{
console.log(options);
@ -32,10 +128,32 @@ const downloadFile = async (inputData: InputData) =>
try
{
const inputData = JSON.parse(jsonText) as InputData;
await downloadFile(inputData);
const progressBar = new ProgressBar(":percent [:bar]", inputData.totalSize);
const downloader = new Downloader(inputData, progress =>
{
// console.log((progress * 100).toFixed(2) + "%");
progressBar.update(progress);
});
process.on("SIGINT", () =>
{
downloader.cancelDownload();
progressBar.terminate();
process.exit();
});
progressBar.render();
const result = await downloader.download();
console.log(`下载完成: `);
if (typeof result === "string")
{
console.log(result);
}
else
{
result.forEach(console.log);
}
}
catch (error)
{
console.error(`未在剪贴板找到足够的数据: ${error}`);
console.error(`错误: ${error}`);
}
})();