netease-music-downloader
Version:
Download music from NetEase Cloud Music
181 lines (180 loc) • 8.55 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.downloadSong = downloadSong;
const axios_1 = __importDefault(require("axios"));
const fs = __importStar(require("fs"));
const netease_1 = require("../services/netease");
const proxy_1 = require("../services/proxy");
const file_1 = require("../utils/file");
const progress_1 = require("../utils/progress");
async function downloadImage(url) {
try {
const response = await (0, axios_1.default)({
method: 'get',
url,
responseType: 'arraybuffer'
});
return Buffer.from(response.data);
}
catch (error) {
console.error('下载封面图片失败 Failed to download cover image:', error instanceof Error ? error.message : 'Unknown error');
return null;
}
}
async function downloadSong(id, progressBar, options) {
const MAX_STALL_TIME = 10000; // 10 seconds
const MAX_RETRIES = 3;
let retryCount = 0;
async function attemptDownload() {
try {
let song;
try {
song = await (0, netease_1.getSongInfo)(id);
}
catch (error) {
console.error(`\n获取歌曲信息失败,跳过下载 Failed to get song info, skipping download (ID: ${id})`);
return false;
}
const songName = song.name;
const artistName = song.artists?.[0]?.name || '未知歌手 Unknown Artist';
console.log(`\n歌曲信息 Song info: ${artistName}-${songName}`);
const availability = await (0, netease_1.checkSongAvailabilityWithRetry)(id, options?.autoProxy);
if (!availability.available || !availability.url) {
console.log(`歌曲已下架或无版权,跳过下载\nSong is unavailable or no copyright, skipping download`);
return false;
}
// 获取文件格式
const fileExtension = availability.type || availability.url.split('.').pop()?.split('?')[0] || 'mp3';
console.log(`获取到音质 Quality: ${availability.quality || 'unknown'}, 比特率 Bitrate: ${availability.bitrate || 'unknown'}kbps, 格式 Format: ${fileExtension}`);
const sanitizedSongName = (0, file_1.sanitizeFileName)(songName);
const sanitizedArtistName = (0, file_1.sanitizeFileName)(artistName);
const fileName = `${sanitizedArtistName}-${sanitizedSongName}.${fileExtension}`;
const filePath = (0, file_1.getDownloadPath)('single', fileName);
const lrcPath = (0, file_1.getDownloadPath)('single', `${sanitizedArtistName}-${sanitizedSongName}.lrc`);
// 下载歌词
const lyrics = await (0, netease_1.getLyrics)(id);
if (lyrics) {
fs.writeFileSync(lrcPath, lyrics, 'utf8');
console.log('歌词下载完成 Lyrics downloaded');
console.log('网页链接 Web URL:', `https://music.163.com/#/song?id=${id}`);
}
if (fs.existsSync(filePath)) {
console.log(`文件已存在,跳过下载 File exists, skipping download: ${fileName}`);
return true;
}
console.log(`开始下载 Start downloading: ${artistName}-${songName}`);
const response = await (0, axios_1.default)({
method: 'get',
url: availability.url,
responseType: 'stream',
...(availability.needProxy ? netease_1.proxyConfig : {})
});
const totalLength = parseInt(response.headers['content-length'], 10);
const bar = (0, progress_1.createSingleBar)();
bar.start(Math.round(totalLength / 1024), 0);
const writer = fs.createWriteStream(filePath);
let downloadedBytes = 0;
let lastProgressTime = Date.now();
response.data.on('data', (chunk) => {
downloadedBytes += chunk.length;
bar.update(Math.round(downloadedBytes / 1024));
lastProgressTime = Date.now();
});
response.data.pipe(writer);
return new Promise((resolve, reject) => {
const checkProgress = setInterval(() => {
if (Date.now() - lastProgressTime > MAX_STALL_TIME) {
clearInterval(checkProgress);
writer.end();
writer.on('close', () => {
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath); // 删除未完成的文件
}
bar.stop();
console.log('\n下载停滞,准备重试 Download stalled, preparing to retry...');
resolve(false);
});
}
}, 1000);
writer.on('finish', async () => {
clearInterval(checkProgress);
bar.stop();
if (downloadedBytes >= totalLength * 0.99) { // 允许1%的误差
console.log(`\n下载完成 Download completed: ${fileName}`);
resolve(true);
}
else {
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath); // 删除不完整的文件
}
console.log('\n下载不完整,准备重试 Incomplete download, preparing to retry...');
resolve(false);
}
});
writer.on('error', (error) => {
clearInterval(checkProgress);
bar.stop();
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath); // 删除错误的文件
}
console.error('\n下载出错,准备重试 Download error, preparing to retry:', error.message);
resolve(false);
});
});
}
catch (error) {
console.error('\n下载出错,准备重试 Download error, preparing to retry:', error instanceof Error ? error.message : 'Unknown error');
return false;
}
}
while (retryCount < MAX_RETRIES) {
const success = await attemptDownload();
if (success)
return;
retryCount++;
if (retryCount < MAX_RETRIES) {
console.log(`\n第 ${retryCount}/${MAX_RETRIES} 次重试 Retry ${retryCount}/${MAX_RETRIES}`);
if (options?.autoProxy) {
console.log('重新获取代理列表 Updating proxy list...');
await (0, proxy_1.getAutoProxy)(true); // 强制更新代理列表
}
}
}
console.error('\n达到最大重试次数,下载失败 Maximum retries reached, download failed');
}