Files
163-music-dl/lib/network.js
T

85 lines
3.1 KiB
JavaScript
Executable File

const fs = require('fs');
const http = require('http');
const https = require('https');
function getProtocol(url) {
return url.startsWith('https') ? https : http;
}
function fetchJSON(url) {
return new Promise((resolve, reject) => {
getProtocol(url).get(url, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try { resolve(JSON.parse(data)); }
catch { reject(new Error(`JSON 解析失败: ${data.slice(0, 200)}`)); }
});
}).on('error', reject);
});
}
function downloadFile(url, dest, showProgress = true) {
return new Promise((resolve, reject) => {
getProtocol(url).get(url, (res) => {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
return downloadFile(res.headers.location, dest, showProgress).then(resolve).catch(reject);
}
if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}`));
const total = parseInt(res.headers['content-length'], 10);
let downloaded = 0;
let lastTime = Date.now();
let lastBytes = 0;
const file = fs.createWriteStream(dest);
res.on('data', (chunk) => {
downloaded += chunk.length;
if (!showProgress) return;
const now = Date.now();
const elapsed = (now - lastTime) / 1000;
if (elapsed < 0.3) return;
const speed = (downloaded - lastBytes) / elapsed;
lastTime = now;
lastBytes = downloaded;
const speedStr = speed >= 1048576
? `${(speed / 1048576).toFixed(1)} MB/s`
: `${(speed / 1024).toFixed(0)} KB/s`;
if (total) {
const pct = downloaded / total;
const barWidth = 30;
const filled = Math.round(pct * barWidth);
const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
const eta = speed > 0 ? Math.ceil((total - downloaded) / speed) : 0;
const etaStr = eta > 60 ? `${Math.floor(eta / 60)}m${eta % 60}s` : `${eta}s`;
process.stdout.write(`\r ⬇️ [${bar}] ${(pct * 100).toFixed(1)}% ${(downloaded / 1048576).toFixed(1)}/${(total / 1048576).toFixed(1)} MB ${speedStr} ETA ${etaStr} `);
} else {
process.stdout.write(`\r ⬇️ ${(downloaded / 1048576).toFixed(1)} MB ${speedStr} `);
}
});
res.pipe(file);
file.on('finish', () => { file.close(); if (showProgress) console.log(); resolve(); });
file.on('error', (err) => { fs.unlink(dest, () => {}); reject(err); });
}).on('error', reject);
});
}
function downloadBuffer(url) {
return new Promise((resolve, reject) => {
getProtocol(url).get(url, (res) => {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
return downloadBuffer(res.headers.location).then(resolve).catch(reject);
}
if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}`));
const chunks = [];
res.on('data', chunk => chunks.push(chunk));
res.on('end', () => resolve(Buffer.concat(chunks)));
}).on('error', reject);
});
}
module.exports = { fetchJSON, downloadFile, downloadBuffer };