UNPKG

andrade-soulseek-downloader

Version:

Simple, safe Soulseek download library with built-in rate limiting to prevent bans

395 lines 15.9 kB
"use strict"; 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.Logger = void 0; const chalk_1 = __importDefault(require("chalk")); const ora_1 = __importDefault(require("ora")); const cliProgress = __importStar(require("cli-progress")); const boxen_1 = __importDefault(require("boxen")); class Logger { spinner = null; progressBar = null; multiBar = null; racingBars = new Map(); formatSize(bytes) { const sizes = ['B', 'KB', 'MB', 'GB']; if (bytes === 0) return '0 B'; const i = Math.floor(Math.log(bytes) / Math.log(1024)); return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`; } formatSpeed(speed) { if (speed < 1000) return `${speed} B/s`; if (speed < 1000000) return `${(speed / 1000).toFixed(1)} KB/s`; return `${(speed / 1000000).toFixed(1)} MB/s`; } formatTime(seconds) { if (seconds < 60) return `${seconds}s`; const minutes = Math.floor(seconds / 60); const secs = seconds % 60; return `${minutes}m ${secs}s`; } title(message) { console.log('\n' + (0, boxen_1.default)(chalk_1.default.cyan.bold(message), { padding: 1, margin: 0, borderStyle: 'round', borderColor: 'cyan' })); } info(message) { console.log(chalk_1.default.blue('ℹ ') + message); } success(message) { console.log(chalk_1.default.green('✔ ') + chalk_1.default.green(message)); } error(message) { console.log(chalk_1.default.red('✖ ') + chalk_1.default.red(message)); } warning(message) { console.log(chalk_1.default.yellow('⚠ ') + chalk_1.default.yellow(message)); } debug(message) { console.log(chalk_1.default.gray('• ') + chalk_1.default.gray(message)); } section(title) { console.log('\n' + chalk_1.default.bold.underline(title)); } searchResult(result, index, total) { const slotIcon = result.slots ? chalk_1.default.green('✓') : chalk_1.default.red('✗'); const filename = result.file.split(/[\\\/]/).pop() || result.file; // Quality indicator based on bitrate let qualityBadge = ''; if (result.bitrate >= 320) qualityBadge = chalk_1.default.green(' ⭐ HQ'); else if (result.bitrate >= 256) qualityBadge = chalk_1.default.yellow(' ◆ MQ'); else if (result.bitrate >= 192) qualityBadge = chalk_1.default.gray(' ● LQ'); console.log(chalk_1.default.gray(`[${index}/${total}]`) + ' ' + chalk_1.default.cyan(filename.substring(0, 50)) + (filename.length > 50 ? '...' : '') + qualityBadge); console.log(' ' + chalk_1.default.gray('User:') + ' ' + chalk_1.default.white(result.user) + ' ' + chalk_1.default.gray('Size:') + ' ' + chalk_1.default.yellow(this.formatSize(result.size)) + ' ' + chalk_1.default.gray('Bitrate:') + ' ' + chalk_1.default.magenta(`${result.bitrate}kbps`) + ' ' + chalk_1.default.gray('Slots:') + ' ' + slotIcon + ' ' + chalk_1.default.gray('Speed:') + ' ' + chalk_1.default.blue(this.formatSpeed(result.speed))); if (result.score !== undefined) { const scoreColor = result.score > 0.7 ? chalk_1.default.green : result.score > 0.4 ? chalk_1.default.yellow : chalk_1.default.red; console.log(' ' + chalk_1.default.gray('Match:') + ' ' + scoreColor(`${(result.score * 100).toFixed(0)}%`)); } if (result.qualityScore !== undefined) { const qColor = result.qualityScore > 80 ? chalk_1.default.green : result.qualityScore > 60 ? chalk_1.default.yellow : chalk_1.default.red; console.log(' ' + chalk_1.default.gray('Quality Score:') + ' ' + qColor(`${result.qualityScore.toFixed(0)}/100`)); } } displaySearchResults(results, maxDisplay = 20) { if (results.length === 0) { this.warning('No results to display'); return; } this.section(`📊 Search Results (showing top ${Math.min(results.length, maxDisplay)} of ${results.length})`); this.divider(); // Display top results const displayCount = Math.min(results.length, maxDisplay); for (let i = 0; i < displayCount; i++) { const result = results[i]; const filename = result.file.split(/[\\\/]/).pop() || result.file; // Rank indicator let rankIcon = ''; if (i === 0) rankIcon = '🥇'; else if (i === 1) rankIcon = '🥈'; else if (i === 2) rankIcon = '🥉'; else rankIcon = chalk_1.default.gray(`#${i + 1}`); // Quality badge let qualityBadge = ''; if (result.bitrate >= 320) qualityBadge = chalk_1.default.green('⭐'); else if (result.bitrate >= 256) qualityBadge = chalk_1.default.yellow('◆'); else qualityBadge = chalk_1.default.gray('●'); // Slots indicator // Show number of slots if available let slotIcon; if (!result.slots) { slotIcon = chalk_1.default.red('✗'); } else if (typeof result.slots === 'number') { slotIcon = result.slots >= 5 ? chalk_1.default.green(`✓(${result.slots})`) : result.slots >= 2 ? chalk_1.default.yellow(`✓(${result.slots})`) : chalk_1.default.cyan(`✓(${result.slots})`); } else { slotIcon = chalk_1.default.green('✓'); } // Speed indicator color const speedColor = result.speed > 1000000 ? chalk_1.default.green : result.speed > 500000 ? chalk_1.default.yellow : chalk_1.default.red; // Main line console.log(`${rankIcon} ${qualityBadge} ` + chalk_1.default.cyan(filename.substring(0, 45)) + (filename.length > 45 ? '...' : '')); // Details line console.log(' ' + chalk_1.default.gray('User:') + ' ' + chalk_1.default.white(result.user.substring(0, 15)) + (result.user.length > 15 ? '..' : '') + ' ' + chalk_1.default.gray('│') + ' ' + chalk_1.default.magenta(`${result.bitrate}kbps`) + ' ' + chalk_1.default.gray('│') + ' ' + chalk_1.default.yellow(this.formatSize(result.size)) + ' ' + chalk_1.default.gray('│') + ' ' + speedColor(this.formatSpeed(result.speed)) + ' ' + chalk_1.default.gray('│') + ' ' + 'Slot:' + slotIcon); // Match score if available if (result.discoseekMatchingScore !== undefined) { const score = result.discoseekMatchingScore * 100; const scoreColor = score > 70 ? chalk_1.default.green : score > 40 ? chalk_1.default.yellow : chalk_1.default.red; console.log(' ' + chalk_1.default.gray('Match:') + ' ' + scoreColor(`${score.toFixed(0)}%`)); } if (i < displayCount - 1) { console.log(chalk_1.default.gray(' ─────')); } } if (results.length > maxDisplay) { this.divider(); this.info(`... and ${results.length - maxDisplay} more results`); } this.divider(); } startSpinner(message) { this.spinner = (0, ora_1.default)({ text: message, spinner: 'dots', color: 'cyan' }).start(); } updateSpinner(message) { if (this.spinner) { this.spinner.text = message; } } succeedSpinner(message) { if (this.spinner) { this.spinner.succeed(message); this.spinner = null; } } failSpinner(message) { if (this.spinner) { this.spinner.fail(message); this.spinner = null; } } stopSpinner() { if (this.spinner) { this.spinner.stop(); this.spinner = null; } } startCountdownSpinner(message, totalMs) { const startTime = Date.now(); const totalSeconds = Math.ceil(totalMs / 1000); this.spinner = (0, ora_1.default)({ text: `${message} (${totalSeconds}s remaining)`, spinner: 'dots', color: 'cyan' }).start(); const updateInterval = setInterval(() => { if (!this.spinner) { clearInterval(updateInterval); return; } const elapsed = Date.now() - startTime; const remaining = Math.max(0, totalMs - elapsed); const remainingSeconds = Math.ceil(remaining / 1000); if (remaining <= 0) { clearInterval(updateInterval); this.spinner.text = `${message} (timeout reached)`; } else { this.spinner.text = `${message} (${remainingSeconds}s remaining)`; } }, 100); // Store interval ID for cleanup this.spinner._countdownInterval = updateInterval; } stopCountdownSpinner() { if (this.spinner) { // Clear the countdown interval if it exists const interval = this.spinner._countdownInterval; if (interval) { clearInterval(interval); } this.spinner.stop(); this.spinner = null; } } succeedCountdownSpinner(message) { if (this.spinner) { // Clear the countdown interval if it exists const interval = this.spinner._countdownInterval; if (interval) { clearInterval(interval); } this.spinner.succeed(message); this.spinner = null; } } failCountdownSpinner(message) { if (this.spinner) { // Clear the countdown interval if it exists const interval = this.spinner._countdownInterval; if (interval) { clearInterval(interval); } this.spinner.fail(message); this.spinner = null; } } startProgressBar(total, startValue = 0) { this.progressBar = new cliProgress.SingleBar({ format: chalk_1.default.cyan('{bar}') + ' | {percentage}% | {value}/{total} | {duration_formatted} | {speed}', barCompleteChar: '█', barIncompleteChar: '░', hideCursor: true, formatBar: (progress, options) => { const completeSize = Math.round(progress * (options.barsize || 40)); const incompleteSize = (options.barsize || 40) - completeSize; const complete = chalk_1.default.green(options.barCompleteString?.substring(0, completeSize)); const incomplete = chalk_1.default.gray(options.barIncompleteString?.substring(0, incompleteSize)); return complete + incomplete; } }, cliProgress.Presets.shades_classic); this.progressBar.start(total, startValue, { speed: 'N/A' }); } updateProgressBar(value, payload) { if (this.progressBar) { this.progressBar.update(value, payload); } } stopProgressBar() { if (this.progressBar) { this.progressBar.stop(); this.progressBar = null; } } // Racing progress bar methods startRacingBars(racers) { // Stop any existing progress bars this.stopProgressBar(); this.stopRacingBars(); // Create multi-bar container this.multiBar = new cliProgress.MultiBar({ clearOnComplete: false, hideCursor: true, format: '{user} [{bar}] {percentage}% | {speed} | {status}', barCompleteChar: '█', barIncompleteChar: '░', }, cliProgress.Presets.shades_classic); // Create a progress bar for each racer racers.forEach(racer => { const bar = this.multiBar.create(racer.size, 0, { user: chalk_1.default.cyan(racer.user.padEnd(15)), speed: 'Starting...', status: '' }); this.racingBars.set(racer.user, bar); }); } updateRacingBar(user, value, speed) { const bar = this.racingBars.get(user); if (bar && this.multiBar) { bar.update(value, { user: chalk_1.default.cyan(user.padEnd(15)), speed: chalk_1.default.yellow(speed), status: '' }); } } markRacingWinner(user) { const bar = this.racingBars.get(user); if (bar) { bar.update(bar.getTotal(), { user: chalk_1.default.green(user.padEnd(15)), speed: chalk_1.default.green('Complete'), status: chalk_1.default.green('🏆 WINNER!') }); } } markRacingLoser(user, reason = 'Cancelled') { const bar = this.racingBars.get(user); if (bar) { // Keep current progress but gray out the display bar.update(bar.getProgress() * bar.getTotal(), { user: chalk_1.default.gray(user.padEnd(15)), speed: chalk_1.default.gray(reason), status: chalk_1.default.gray('✗') }); } } stopRacingBars() { if (this.multiBar) { this.multiBar.stop(); this.multiBar = null; this.racingBars.clear(); } } table(data) { const maxKeyLength = Math.max(...Object.keys(data).map(k => k.length)); Object.entries(data).forEach(([key, value]) => { const paddedKey = key.padEnd(maxKeyLength); console.log(` ${chalk_1.default.gray(paddedKey)} : ${chalk_1.default.white(value)}`); }); } divider() { console.log(chalk_1.default.gray('─'.repeat(60))); } } exports.Logger = Logger; //# sourceMappingURL=logger.js.map