🎵 Soulseek Downloader
🚀 Enterprise-grade Soulseek downloader with automatic quality selection, ban protection, and clean architecture
Installation • Quick Start • Features • API • Architecture • Examples
📖 Table of Contents
- ⚡ Key Features
- 🏗️ Architecture Highlights
- 🛡️ Ban Protection
- 📦 Installation
- 🚀 Quick Start
- 🎯 How It Works
- 📚 API Reference
- ⚙️ Configuration
- 💡 Examples
- 🎨 Quality Selection
- 📊 Architecture
- 🧪 Testing
- 🔧 Advanced Usage
- 📝 TypeScript Support
- ❓ FAQ
- 🐛 Troubleshooting
- 🤝 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
- Single Responsibility: Each class has one clear purpose
- Open/Closed: Extensible through interfaces
- Liskov Substitution: Proper inheritance and polymorphism
- Interface Segregation: Focused, minimal interfaces
- Dependency 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:
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:#ffcc99Protection 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
# pnpm (recommended)
pnpm add andrade-soulseek-downloader
# npm
npm install andrade-soulseek-downloader
# yarn
yarn add andrade-soulseek-downloaderTypeScript 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:
# 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 ms2️⃣ Use the Simple API
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
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
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:#fce4ecQuality Selection Algorithm
- 🔍 Search Phase: Finds all available files
- 📊 Quality Grouping: Groups files by bitrate (FLAC, 320kbps, 256kbps, etc.)
- ⭐ Smart Scoring: Scores each file based on:
- Bitrate quality (50 points max)
- Slot availability (25 points)
- Connection speed (15 points)
- Filename match (10 points)
- 🎯 Progressive Download: Tries best options first, falls back gracefully
📚 API Reference
Simple API (Recommended)
// Single function for most use cases
soulseekDownload(artist: string, title: string): Promise<string | null>
// Clean up when done
soulseekDisconnect(): Promise<void>Advanced API
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
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
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
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
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
# 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:
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:#fce4ecDirectory 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 componentsBenefits 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:
# 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:watchTest 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:
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
// 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
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
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
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
Error: Failed to connect to SoulseekSolution: Check your username/password and network connection.
No Results Found
No tracks foundSolutions:
- Try broader search terms
- Lower the minimum bitrate requirement
- Check if the artist/track exists on Soulseek
Download Timeouts
Download timeout for user XSolutions:
- Increase
SOULSEEK_DOWNLOAD_TIMEOUT - The library will automatically try other sources
Rate Limit Warnings
Warning: Multiple SoulseekDownloader instances detected!Solution: Use only one instance of SoulseekDownloader, or use the simple API functions.
Debug Mode
Enable debug logging:
DEBUG=true pnpm start "Artist" "Title"Or programmatically:
process.env.DEBUG = 'true';
import { soulseekDownload } from 'andrade-soulseek-downloader';🤝 Contributing
We welcome contributions! Here's how to get started:
Development Setup
# 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 devCode 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
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes following the architecture patterns
- Add tests for new functionality
- Ensure all tests pass:
pnpm test - Build successfully:
pnpm build - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - 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
⭐ Star this repo if you found it helpful!