UNPKG

andrade-soulseek-downloader

Version:

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

753 lines (583 loc) โ€ข 21.9 kB
# ๐ŸŽต Soulseek Downloader <div align="center"> ![npm version](https://img.shields.io/npm/v/andrade-soulseek-downloader) ![npm downloads](https://img.shields.io/npm/dm/andrade-soulseek-downloader) ![license](https://img.shields.io/npm/l/andrade-soulseek-downloader) ![node version](https://img.shields.io/node/v/andrade-soulseek-downloader) [![TypeScript](https://img.shields.io/badge/TypeScript-100%25-blue.svg)](https://www.typescriptlang.org/) [![Tests](https://img.shields.io/badge/Tests-42%20passing-green.svg)](#testing) [![Architecture](https://img.shields.io/badge/Architecture-Hexagonal-purple.svg)](#architecture) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) **๐Ÿš€ Enterprise-grade Soulseek downloader with automatic quality selection, ban protection, and clean architecture** [Installation](#-installation) โ€ข [Quick Start](#-quick-start) โ€ข [Features](#-features) โ€ข [API](#-api-reference) โ€ข [Architecture](#-architecture) โ€ข [Examples](#-examples) </div> --- ## ๐Ÿ“– Table of Contents - [โšก Key Features](#-key-features) - [๐Ÿ—๏ธ Architecture Highlights](#๏ธ-architecture-highlights) - [๐Ÿ›ก๏ธ Ban Protection](#๏ธ-ban-protection) - [๐Ÿ“ฆ Installation](#-installation) - [๐Ÿš€ Quick Start](#-quick-start) - [๐ŸŽฏ How It Works](#-how-it-works) - [๐Ÿ“š API Reference](#-api-reference) - [โš™๏ธ Configuration](#๏ธ-configuration) - [๐Ÿ’ก Examples](#-examples) - [๐ŸŽจ Quality Selection](#-quality-selection-algorithm) - [๐Ÿ“Š Architecture](#-architecture) - [๐Ÿงช Testing](#-testing) - [๐Ÿ”ง Advanced Usage](#-advanced-usage) - [๐Ÿ“ TypeScript Support](#-typescript-support) - [โ“ FAQ](#-faq) - [๐Ÿ› Troubleshooting](#-troubleshooting) - [๐Ÿค Contributing](#-contributing) ## โšก Key Features | Feature | Description | |---------|-------------| | ๐ŸŽฏ **Quality-First Downloads** | Automatically downloads the highest quality available (FLAC โ†’ 320kbps โ†’ 256kbps โ†’ 192kbps) | | ๐Ÿ›ก๏ธ **Ban Protection** | Built-in rate limiting and queue management to prevent Soulseek bans | | ๐Ÿ“Š **Smart Selection** | Intelligent file selection based on bitrate, availability, speed, and filename match | | ๐Ÿ”„ **Auto-Retry Logic** | Tries multiple sources with progressive quality fallback | | ๐ŸŽจ **Beautiful UI** | Colored output, progress bars, and status indicators | | โšก **Simple API** | Just one function: `soulseekDownload(artist, title)` | | ๐Ÿ” **Safe Defaults** | Conservative rate limits and single connection by default | | ๐Ÿ“ˆ **Progress Tracking** | Real-time download progress with speed indicators | | ๐Ÿ—๏ธ **Clean Architecture** | Hexagonal architecture with SOLID principles | | ๐Ÿ”ท **TypeScript First** | Full TypeScript support with comprehensive type definitions | | ๐Ÿงช **Well Tested** | 42 comprehensive unit tests with high coverage | | ๐Ÿ“ฆ **Barrel Exports** | Clean imports with organized module structure | ## ๐Ÿ—๏ธ Architecture Highlights This project follows **enterprise-grade software architecture principles**: ### ๐Ÿ”ท **Hexagonal Architecture (Ports & Adapters)** - **Domain Layer**: Pure business logic with entities and services - **Application Layer**: Use cases and orchestration - **Infrastructure Layer**: External adapters (Soulseek, filesystem, logging) - **Presentation Layer**: CLI and API interfaces ### ๐Ÿ”ง **SOLID Principles Applied** - **S**ingle Responsibility: Each class has one clear purpose - **O**pen/Closed: Extensible through interfaces - **L**iskov Substitution: Proper inheritance and polymorphism - **I**nterface Segregation: Focused, minimal interfaces - **D**ependency Inversion: Depend on abstractions, not concretions ### ๐Ÿญ **Dependency Injection** - Uses InversifyJS for clean dependency management - Testable and mockable components - Easy to extend and modify ### ๐Ÿ“‚ **Clean File Organization** - **One symbol per file** for maximum clarity - **kebab-case naming** following Node.js conventions - **Barrel files** for clean imports - **Structured by layer** not by feature ## ๐Ÿ›ก๏ธ Ban Protection This library includes **automatic protection** against Soulseek bans: ```mermaid graph LR A[Search Request] --> B{Rate Limiter} B -->|Wait 5s| C[Execute Search] C --> D[Download Request] D --> E{Rate Limiter} E -->|Wait 3s| F[Execute Download] F --> G{Success?} G -->|No| H[Error Cooldown 10s] H --> D G -->|Yes| I[Complete] style B fill:#ff9999 style E fill:#ff9999 style H fill:#ffcc99 ``` ### Protection Features: - โฑ๏ธ **5-second delay** between searches - โฑ๏ธ **3-second delay** between downloads - โฑ๏ธ **10-second cooldown** after errors - ๐Ÿ”’ **Single connection** limit - ๐Ÿ“ฆ **Queue management** for multiple requests - ๐Ÿ”„ **Progressive backoff** on repeated failures ## ๐Ÿ“ฆ Installation ```bash # pnpm (recommended) pnpm add andrade-soulseek-downloader # npm npm install andrade-soulseek-downloader # yarn yarn add andrade-soulseek-downloader ``` ### TypeScript Support TypeScript definitions are **included automatically** - no need for separate `@types` packages! ## ๐Ÿš€ Quick Start ### 1๏ธโƒฃ Set up environment variables Create a `.env` file in your project root: ```env # Required SOULSEEK_USER=your_username SOULSEEK_PASSWORD=your_password SOULSEEK_SHARED_MUSIC_DIR=/path/to/shared/music SOULSEEK_DOWNLOAD_DIR=/path/to/downloads # Optional (defaults shown) SOULSEEK_MIN_QUALITY_BITRATE=128 # Minimum acceptable quality SOULSEEK_SEARCH_DELAY=5000 # ms between searches SOULSEEK_DOWNLOAD_DELAY=3000 # ms between downloads SOULSEEK_MAX_ATTEMPTS=10 # Max download attempts SOULSEEK_DOWNLOAD_TIMEOUT=120000 # Download timeout in ms ``` ### 2๏ธโƒฃ Use the Simple API ```typescript import { soulseekDownload } from 'andrade-soulseek-downloader'; async function downloadTrack() { const filePath = await soulseekDownload('Daft Punk', 'One More Time'); if (filePath) { console.log(`โœ… Downloaded to: ${filePath}`); } else { console.log('โŒ Download failed'); } } downloadTrack(); ``` ### 3๏ธโƒฃ Or use CommonJS ```javascript const { soulseekDownload } = require('andrade-soulseek-downloader'); async function downloadTrack() { const filePath = await soulseekDownload('Daft Punk', 'One More Time'); if (filePath) { console.log(`โœ… Downloaded to: ${filePath}`); } else { console.log('โŒ Download failed'); } } downloadTrack(); ``` ## ๐ŸŽฏ How It Works ```mermaid flowchart TD A[Start Download] --> B[Search Soulseek Network] B --> C{Results Found?} C -->|No| D[Return null] C -->|Yes| E[Group by Quality] E --> F[Sort by Availability & Speed] F --> G[Try Highest Quality First] G --> H{Download Success?} H -->|Yes| I[Return File Path] H -->|No| J{More Options?} J -->|Yes| K[Try Next Quality] K --> G J -->|No| L[Progressive Fallback] L --> G style E fill:#e1f5fe style F fill:#e8f5e8 style G fill:#fff3e0 style H fill:#fce4ec ``` ### Quality Selection Algorithm 1. **๐Ÿ” Search Phase**: Finds all available files 2. **๐Ÿ“Š Quality Grouping**: Groups files by bitrate (FLAC, 320kbps, 256kbps, etc.) 3. **โญ Smart Scoring**: Scores each file based on: - Bitrate quality (50 points max) - Slot availability (25 points) - Connection speed (15 points) - Filename match (10 points) 4. **๐ŸŽฏ Progressive Download**: Tries best options first, falls back gracefully ## ๐Ÿ“š API Reference ### Simple API (Recommended) ```typescript // Single function for most use cases soulseekDownload(artist: string, title: string): Promise<string | null> // Clean up when done soulseekDisconnect(): Promise<void> ``` ### Advanced API ```typescript import { SoulseekDownloader, DownloadConfig, SearchOptions, SoulseekSearchResult } from 'andrade-soulseek-downloader'; const downloader = new SoulseekDownloader({ maxAttempts: 10, downloadTimeout: 120000, preferSlotsAvailable: true, minSpeed: 100000, searchDelay: 5000, downloadDelay: 3000, maxConcurrent: 1, cooldownAfterError: 10000 }); // Manual control await downloader.connect(); const results = await downloader.search(options); const filePath = await downloader.download(result, artist, title); await downloader.disconnect(); ``` ## โš™๏ธ Configuration ### Environment Variables | Variable | Default | Description | |----------|---------|-------------| | `SOULSEEK_USER` | *(required)* | Your Soulseek username | | `SOULSEEK_PASSWORD` | *(required)* | Your Soulseek password | | `SOULSEEK_SHARED_MUSIC_DIR` | *(required)* | Directory to share (can be empty) | | `SOULSEEK_DOWNLOAD_DIR` | *(required)* | Where to save downloads | | `SOULSEEK_MIN_QUALITY_BITRATE` | `128` | Minimum acceptable bitrate (kbps) | | `SOULSEEK_SEARCH_DELAY` | `5000` | Delay between searches (ms) | | `SOULSEEK_DOWNLOAD_DELAY` | `3000` | Delay between downloads (ms) | | `SOULSEEK_MAX_ATTEMPTS` | `10` | Maximum download attempts | | `SOULSEEK_DOWNLOAD_TIMEOUT` | `120000` | Download timeout (ms) | ### Programmatic Configuration ```typescript const config: DownloadConfig = { maxAttempts: 15, // Try up to 15 different sources downloadTimeout: 180000, // 3 minute timeout per download preferSlotsAvailable: true, // Prefer users with open slots minSpeed: 500000, // Minimum 500kb/s connection searchDelay: 3000, // 3 second delay between searches downloadDelay: 2000, // 2 second delay between downloads maxConcurrent: 1, // Always 1 to prevent bans cooldownAfterError: 15000 // 15 second cooldown after errors }; ``` ## ๐Ÿ’ก Examples ### Basic Usage ```typescript import { soulseekDownload, soulseekDisconnect } from 'andrade-soulseek-downloader'; // Download a single track const file1 = await soulseekDownload('The Beatles', 'Hey Jude'); const file2 = await soulseekDownload('Pink Floyd', 'Comfortably Numb'); // Clean up when done await soulseekDisconnect(); ``` ### Batch Downloads ```typescript import { soulseekDownload, soulseekDisconnect } from 'andrade-soulseek-downloader'; const tracks = [ { artist: 'The Beatles', title: 'Hey Jude' }, { artist: 'Pink Floyd', title: 'Comfortably Numb' }, { artist: 'Led Zeppelin', title: 'Stairway to Heaven' } ]; for (const track of tracks) { console.log(`Downloading ${track.artist} - ${track.title}...`); const filePath = await soulseekDownload(track.artist, track.title); if (filePath) { console.log(`โœ… Downloaded: ${filePath}`); } else { console.log(`โŒ Failed: ${track.artist} - ${track.title}`); } } await soulseekDisconnect(); ``` ### Advanced Configuration ```typescript import { SoulseekDownloader, DownloadConfig, SearchOptions } from 'andrade-soulseek-downloader'; const config: DownloadConfig = { maxAttempts: 20, downloadTimeout: 300000, // 5 minutes preferSlotsAvailable: true, minSpeed: 1000000, // 1 MB/s minimum searchDelay: 2000, downloadDelay: 1000 }; const downloader = new SoulseekDownloader(config); try { await downloader.connect(); const searchOptions: SearchOptions = { artist: 'Daft Punk', title: 'One More Time', minBitrate: 320, // Only high quality timeout: 60000, maxResults: 50, strictMatching: true }; const results = await downloader.search(searchOptions); console.log(`Found ${results.length} high-quality results`); if (results.length > 0) { const bestResult = results[0]; // Already sorted by quality const filePath = await downloader.download(bestResult, 'Daft Punk', 'One More Time'); console.log(`Downloaded: ${filePath}`); } } finally { await downloader.disconnect(); } ``` ### CLI Usage ```bash # Global installation pnpm install -g andrade-soulseek-downloader # Command line usage soulseek-download "The Beatles" "Hey Jude" # Or run directly with pnpm pnpm start "The Beatles" "Hey Jude" ``` ## ๐Ÿ“Š Architecture This project demonstrates **professional software architecture** patterns: ```mermaid graph TB subgraph "Presentation Layer" CLI[CLI Handler] API[API Handler] end subgraph "Application Layer" UC[Use Cases] DTO[DTOs] end subgraph "Domain Layer" E[Entities] VO[Value Objects] DS[Domain Services] R[Repository Interfaces] end subgraph "Infrastructure Layer" REPO[Soulseek Repository] LOG[Console Logger] RL[Rate Limiter] DI[DI Container] end CLI --> UC API --> UC UC --> DS UC --> R DS --> E DS --> VO R --> REPO UC --> LOG UC --> RL style CLI fill:#e3f2fd style API fill:#e3f2fd style UC fill:#e8f5e8 style E fill:#fff3e0 style VO fill:#fff3e0 style DS fill:#fff3e0 style REPO fill:#fce4ec ``` ### Directory Structure ``` src/ โ”œโ”€โ”€ presentation/ # User interfaces โ”‚ โ”œโ”€โ”€ api/ # HTTP/Function API โ”‚ โ””โ”€โ”€ cli/ # Command line interface โ”œโ”€โ”€ application/ # Use cases & orchestration โ”‚ โ”œโ”€โ”€ use-cases/ # Business workflows โ”‚ โ””โ”€โ”€ dto/ # Data transfer objects โ”œโ”€โ”€ domain/ # Core business logic โ”‚ โ”œโ”€โ”€ entities/ # Business objects โ”‚ โ”œโ”€โ”€ value-objects/ # Immutable values โ”‚ โ”œโ”€โ”€ services/ # Domain services โ”‚ โ””โ”€โ”€ repositories/ # Repository interfaces (ports) โ”œโ”€โ”€ infrastructure/ # External concerns โ”‚ โ”œโ”€โ”€ repositories/ # Repository implementations (adapters) โ”‚ โ”œโ”€โ”€ services/ # External services โ”‚ โ””โ”€โ”€ container/ # Dependency injection โ”œโ”€โ”€ shared/ # Shared utilities โ”‚ โ”œโ”€โ”€ interfaces/ # Common interfaces โ”‚ โ””โ”€โ”€ types/ # Type definitions โ””โ”€โ”€ core/ # Core components ``` ### Benefits of This Architecture - ๐Ÿงช **Highly Testable**: Easy to mock and test each layer - ๐Ÿ”ง **Maintainable**: Clear separation of concerns - ๐Ÿ”„ **Flexible**: Easy to swap implementations - ๐Ÿ“ˆ **Scalable**: Can grow with your needs - ๐Ÿ›ก๏ธ **Robust**: Handles errors gracefully - ๐Ÿ“ **Self-Documenting**: Clear intent and structure ## ๐Ÿงช Testing The project includes **comprehensive test coverage**: ```bash # Run all tests pnpm test # Run tests with coverage pnpm test:coverage # Run specific test suites pnpm test:unit pnpm test:integration # Watch mode for development pnpm test:watch ``` ### Test Statistics - **42 Tests** across all layers - **4 Test Suites** covering domain, application, and infrastructure - **High Coverage** on critical business logic - **Fast Execution** (< 1 second) ### Test Architecture - **Unit Tests**: Domain entities, value objects, services - **Integration Tests**: Use cases with mocked dependencies - **Mocking**: Comprehensive mocks for external dependencies - **Test Utilities**: Shared test factories and helpers ## ๐Ÿ“ TypeScript Support ### Built-in Type Definitions Full TypeScript support is **included by default**: ```typescript import { soulseekDownload, // Function SoulseekDownloader, // Class DownloadConfig, // Interface SearchOptions, // Interface SoulseekSearchResult, // Interface RateLimiter // Class } from 'andrade-soulseek-downloader'; // All types are automatically available const config: DownloadConfig = { maxAttempts: 10, downloadTimeout: 120000 // TypeScript will provide IntelliSense here }; ``` ### Type Safety Features - **Comprehensive Interfaces**: All public APIs are fully typed - **Generic Support**: Type-safe generic functions where applicable - **Strict Null Checks**: Proper handling of nullable values - **IntelliSense Support**: Full autocomplete in supported editors - **Compile-time Safety**: Catch errors before runtime ### Import Options ```typescript // Barrel imports (recommended) import { Track, Bitrate } from 'andrade-soulseek-downloader/domain'; import { DownloadTrackUseCase } from 'andrade-soulseek-downloader/application'; // Specific imports import { soulseekDownload } from 'andrade-soulseek-downloader/presentation/api'; import { SoulseekDownloader } from 'andrade-soulseek-downloader/core'; // Root imports (simple) import { soulseekDownload, SoulseekDownloader } from 'andrade-soulseek-downloader'; ``` ## ๐Ÿ”ง Advanced Usage ### Custom Rate Limiting ```typescript import { RateLimiter, RateLimitConfig } from 'andrade-soulseek-downloader'; const customConfig: RateLimitConfig = { searchDelay: 2000, // Faster searches (be careful!) downloadDelay: 4000, // Slower downloads (safer) maxConcurrent: 1, // Always 1 for safety cooldownAfterError: 20000 // Longer cooldown }; const rateLimiter = RateLimiter.getInstance(customConfig); // Use with your own functions const results = await rateLimiter.executeSearch(async () => { return await customSearchFunction(); }); ``` ### Extending the Domain ```typescript import { Track, TrackSelectionService } from 'andrade-soulseek-downloader/domain'; class CustomTrackSelectionService extends TrackSelectionService { selectBestTracks(tracks: Track[], maxPerBitrate: number = 5): Track[] { // Your custom selection logic const filtered = tracks.filter(track => track.getBitrate().getValue() >= 256 && track.hasAvailableSlots() ); return super.selectBestTracks(filtered, maxPerBitrate); } } ``` ### Custom Logging ```typescript import { ILogger } from 'andrade-soulseek-downloader/shared'; class CustomLogger implements ILogger { info(message: string): void { // Send to your logging service console.log(`[INFO] ${new Date().toISOString()} ${message}`); } success(message: string): void { // Custom success handling console.log(`[SUCCESS] ${message}`); } // ... implement other methods } // Use with dependency injection container.bind<ILogger>('ILogger').to(CustomLogger); ``` ## โ“ FAQ ### **Q: Is this safe to use? Will I get banned?** A: Yes, it's designed with safety first. The built-in rate limiting prevents bans by enforcing conservative delays between operations. ### **Q: What audio quality can I expect?** A: The library automatically finds the highest quality available, preferring lossless formats (FLAC) when possible, then falling back to 320kbps, 256kbps, etc. ### **Q: Can I use this in production?** A: Yes! The architecture is enterprise-grade with proper error handling, logging, and testing. However, always respect Soulseek's terms of service. ### **Q: Does it work with TypeScript?** A: Absolutely! Full TypeScript support is built-in with comprehensive type definitions. ### **Q: Can I customize the download behavior?** A: Yes, the architecture is designed for extensibility. You can inject custom services, modify selection algorithms, or add your own retry logic. ### **Q: How do I report issues?** A: Please open an issue on GitHub with detailed information about your problem, including logs and environment details. ## ๐Ÿ› Troubleshooting ### Common Issues #### **Connection Problems** ```bash Error: Failed to connect to Soulseek ``` **Solution**: Check your username/password and network connection. #### **No Results Found** ```bash No tracks found ``` **Solutions**: - Try broader search terms - Lower the minimum bitrate requirement - Check if the artist/track exists on Soulseek #### **Download Timeouts** ```bash Download timeout for user X ``` **Solutions**: - Increase `SOULSEEK_DOWNLOAD_TIMEOUT` - The library will automatically try other sources #### **Rate Limit Warnings** ```bash Warning: Multiple SoulseekDownloader instances detected! ``` **Solution**: Use only one instance of SoulseekDownloader, or use the simple API functions. ### Debug Mode Enable debug logging: ```bash DEBUG=true pnpm start "Artist" "Title" ``` Or programmatically: ```typescript process.env.DEBUG = 'true'; import { soulseekDownload } from 'andrade-soulseek-downloader'; ``` ## ๐Ÿค Contributing We welcome contributions! Here's how to get started: ### Development Setup ```bash # Clone the repository git clone https://github.com/andrade/soulseek-downloader.git cd soulseek-downloader # Install dependencies pnpm install # Run tests pnpm test # Build the project pnpm build # Run in development pnpm dev ``` ### Code Standards - **TypeScript**: All code must be in TypeScript - **Architecture**: Follow hexagonal architecture patterns - **Testing**: Maintain high test coverage - **Formatting**: Code is automatically formatted - **Conventions**: Use kebab-case for files, PascalCase for classes ### Submitting Changes 1. Fork the repository 2. Create a feature branch: `git checkout -b feature/amazing-feature` 3. Make your changes following the architecture patterns 4. Add tests for new functionality 5. Ensure all tests pass: `pnpm test` 6. Build successfully: `pnpm build` 7. Commit your changes: `git commit -m 'Add amazing feature'` 8. Push to the branch: `git push origin feature/amazing-feature` 9. Open a Pull Request ### Architecture Guidelines When contributing, please: - Keep domain logic pure (no external dependencies) - Use dependency injection for external concerns - Follow the single responsibility principle - Add comprehensive tests for new features - Update documentation for API changes --- ## ๐Ÿ“„ License MIT ยฉ [andrade](https://github.com/andrade) --- <div align="center"> **โญ Star this repo if you found it helpful!** [Report Bug](https://github.com/andrade/soulseek-downloader/issues) โ€ข [Request Feature](https://github.com/andrade/soulseek-downloader/issues) โ€ข [Contribute](https://github.com/andrade/soulseek-downloader/pulls) </div>