UNPKG

@lpb_name/down

Version:

A Node.js download manager with multi-threading support

119 lines (103 loc) 3.84 kB
#!/usr/bin/env node import { program } from 'commander'; import { createDownloadManager } from '../src/downloader.js'; import path from 'path'; import { fileURLToPath } from 'url'; import fs from 'fs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // 读取package.json获取版本信息 const packageJsonPath = path.join(__dirname, '..', 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); program .name('down') .description('A powerful multi-threaded download manager') .version(packageJson.version); program .argument('<url>', 'URL to download') .option('-o, --output <filename>', 'Output filename') .option('-t, --threads <number>', 'Number of download threads', '3') .option('-d, --directory <path>', 'Download directory', process.cwd()) .action(async (url, options) => { try { // 验证URL格式 new URL(url); const threads = parseInt(options.threads); if (isNaN(threads) || threads < 1 || threads > 20) { console.error('错误: 线程数必须在1-20之间'); process.exit(1); } // 确定输出文件名 let output = options.output; if (!output) { try { output = path.basename(new URL(url).pathname); if (!output || output === '/') { output = 'download'; } } catch { output = 'download'; } } // 确定完整的输出路径 const outputPath = path.isAbsolute(output) ? output : path.join(options.directory, output); // 确保输出目录存在 const outputDir = path.dirname(outputPath); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } console.log(`开始下载: ${url}`); console.log(`输出文件: ${outputPath}`); console.log(`线程数: ${threads}`); console.log('---'); const downloader = createDownloadManager({ url, output: outputPath, threads }); await downloader.start(); } catch (error) { if (error.message.includes('Invalid URL')) { console.error('错误: 无效的URL格式'); } else if (error.message.includes('服务器不支持分片下载')) { console.log('\n检测到服务器不支持分片下载,正在尝试单线程下载...'); try { // 重新计算outputPath,因为在catch块中可能不可访问 let fallbackOutput = options.output; if (!fallbackOutput) { try { fallbackOutput = path.basename(new URL(url).pathname); if (!fallbackOutput || fallbackOutput === '/') { fallbackOutput = 'download'; } } catch { fallbackOutput = 'download'; } } const fallbackOutputPath = path.isAbsolute(fallbackOutput) ? fallbackOutput : path.join(options.directory, fallbackOutput); const singleThreadDownloader = createDownloadManager({ url, output: fallbackOutputPath, threads: 1 }); await singleThreadDownloader.start(); return; } catch (fallbackError) { console.error('单线程下载也失败了:', fallbackError.message); } } else { console.error('下载失败:', error.message); } process.exit(1); } }); // 显示帮助信息 program.on('--help', () => { console.log(''); console.log('示例:'); console.log(' $ down https://example.com/file.zip'); console.log(' $ down https://example.com/file.zip -o myfile.zip'); console.log(' $ down https://example.com/file.zip -t 10 -d ./downloads'); console.log(''); }); program.parse();