Replaces scattered console statements with a unified RMABLogger across backend API routes and services. Adds LOG_LEVEL-based filtering, job-aware database persistence, and context-based logging. Updates documentation to describe the new logging system and usage patterns. Also documents qBittorrent CSRF header fix
8.4 KiB
qBittorrent Integration
Status: ✅ Implemented
Free, open-source BitTorrent client with comprehensive Web API.
Enterprise Torrent Addition
Challenge: /api/v2/torrents/add returns only "Ok." without torrent hash.
Solution (Professional):
Magnet Links:
- Extract
info_hashfrom magnet URI (deterministic) - Upload via
urlsparameter - Return extracted hash immediately
Torrent Files:
- Download .torrent file to memory
- Parse with
parse-torrent(bencode decoder) - Extract
info_hash(SHA-1 of info dict) - Upload file content via
torrentsparameter (multipart/form-data) - Return extracted hash immediately
Benefits: Deterministic, no race conditions, works with Docker networking, handles expired URLs
API Endpoints
Base: http://qbittorrent:8080/api/v2
Auth: Cookie-based (login required)
POST /auth/login - Get session cookie
POST /torrents/add - Add torrent (supports urls and torrents params, savepath override)
GET /torrents/info?hashes={hash} - Get status/progress
POST /torrents/pause - Pause torrent
POST /torrents/resume - Resume
POST /torrents/delete - Delete torrent
GET /torrents/files - Get file list
POST /torrents/createCategory - Create category with save path
POST /torrents/editCategory - Update category save path
POST /torrents/setCategory - Set category for torrent
Config
Required (database only, no env fallbacks):
download_client_url- qBittorrent Web UI URL (supports HTTP and HTTPS)download_client_username- qBittorrent usernamedownload_client_password- qBittorrent passworddownload_dir- Download save path (passed to qBittorrent for all torrents)
Optional (SSL/TLS):
download_client_disable_ssl_verify- Disable SSL certificate verification for HTTPS (boolean as string "true"/"false", default: "false")- Use when connecting to qBittorrent with self-signed certificates
- ⚠️ Security warning: Only use on trusted private networks
- Enhanced error messages guide users when SSL issues detected
Optional (Remote Path Mapping):
download_client_remote_path_mapping_enabled- Enable path mapping (boolean as string "true"/"false")download_client_remote_path- Remote path prefix from qBittorrentdownload_client_local_path- Local path prefix for ReadMeABook
Validation: All required fields checked before service initialization. Path mapping fields validated when enabled.
Singleton Invalidation: Service uses singleton pattern for performance. When settings change (via admin settings page), singleton is invalidated to force reload:
invalidateQBittorrentService()called after updating paths or download client settings- Forces service to re-read database config on next torrent addition
- Ensures category save path and credentials are always current
Category Management
Category: readmeabook (auto-created for all torrents)
Save Path Synchronization:
- Category created/updated on every torrent addition
- Category save path always synced with
download_dirconfig - Handles config changes: if user changes
download_dir, category updates automatically - Uses both
createCategoryandeditCategoryAPIs for reliability
Why Both Create and Edit:
- Create: Ensures category exists (idempotent, won't fail if exists)
- Edit: Updates save path to match current config (handles user changing settings)
This prevents issues where category retains old save path after user changes download_dir setting.
Remote Path Mapping
Use Case: qBittorrent runs on different machine/container with different filesystem perspective.
Example Scenario:
- qBittorrent reports:
/remote/mnt/d/done/Audiobook.Name - ReadMeABook needs:
/downloads/Audiobook.Name - Mapping: Remote
/remote/mnt/d/done→ Local/downloads
Configuration:
- Admin Settings → Download Client → Enable Remote Path Mapping
- Enter remote path (as reported by qBittorrent)
- Enter local path (accessible to ReadMeABook)
- Test connection validates local path exists
- Save settings
Implementation:
PathMapperutility (src/lib/utils/path-mapper.ts) handles transformation- Applied in
monitor-download.processor.tswhen download completes - Applied in
retry-failed-imports.processor.tsfor failed imports - Uses simple prefix replacement with path normalization
- Graceful fallback: if path doesn't match remote prefix, returns unchanged
Path Transformation:
// Input from qBittorrent
qbPath = "/remote/mnt/d/done/Audiobook.Name"
// Config
remotePath = "/remote/mnt/d/done"
localPath = "/downloads"
// Output (used for file organization)
organizePath = "/downloads/Audiobook.Name"
Validation:
- Local path accessibility checked during test connection
- Prevents misconfiguration before save
- Warning shown for existing downloads (mapping only affects new downloads)
Behavior:
- Mapping only applies when enabled
- If path doesn't start with remote prefix, returns original (logs warning)
- Path normalization handles trailing slashes, backslashes, redundant separators
- Works with both
content_pathand constructedsave_path + name
Data Models
interface TorrentInfo {
hash: string;
name: string;
size: number;
progress: number; // 0.0-1.0
dlspeed: number; // bytes/s
upspeed: number;
eta: number; // seconds
state: TorrentState;
category: string;
savePath: string;
completionDate: number;
}
type TorrentState = 'downloading' | 'uploading' | 'stalledDL' |
'pausedDL' | 'queuedDL' | 'checkingDL' | 'error' | 'missingFiles';
Fixed Issues ✅
1. Naive torrent identification - Fixed with deterministic hash extraction
2. Docker networking issues - Fixed by downloading .torrent ourselves
3. Duplicate detection - Check if hash exists before adding
4. Config fallbacks to env - Removed, database only
5. Unclear error messages - List missing fields explicitly
6. Race condition on torrent availability - Fixed with 3s initial delay + exponential backoff retry (500ms, 1s, 2s)
7. Error logging during duplicate check - Removed console.error in getTorrent() during expected "not found" cases (duplicate checking)
8. Prowlarr magnet link redirects - Some indexers return HTTP URLs that redirect to magnet: links. Fixed by intercepting 3xx redirects before axios follows them, extracting the Location header, and routing to magnet flow if target is a magnet: link
9. Category save path not updating - When user changes download_dir setting, category keeps old path. Fixed by:
- Checking existing categories before create/edit (avoid unnecessary 409 errors)
- Invalidating service singleton when settings change (forces config reload)
- Settings API calls
invalidateQBittorrentService()after updating paths or credentials 10. Remote seedbox path mismatch - qBittorrent on remote machine reports different filesystem paths. Fixed by: - Remote path mapping feature with toggle in admin settings and setup wizard
- PathMapper utility for prefix replacement transformation
- Local path validation during test connection
- Applied in download completion and import retry processors 11. HTTPS SSL certificate errors - Users with seedboxes using self-signed certificates or Let's Encrypt couldn't connect. Fixed by:
- Optional SSL verification disable toggle in setup wizard and admin settings
- Custom HTTPS agent with
rejectUnauthorized: falsewhen enabled - Enhanced error messages identifying SSL/TLS certificate issues with actionable guidance
- Secure by default (SSL verification enabled), with clear security warnings when disabled
- URL format:
https://qbt.domain.com:443/qbittorrentfully supported 12. CSRF protection HTTP 401 errors - qBittorrent v4.1.0+ has CSRF protection enabled by default, causing authentication failures (HTTP 401) when Referer/Origin headers missing. Browsers work because they auto-send these headers. Fixed by: - Adding
RefererandOriginheaders to all login requests - Headers set to qBittorrent base URL (e.g.,
https://seedbox.example.com:443/qbittorrent) - Applied to both
login()andtestConnectionWithCredentials()methods - Works with all qBittorrent versions and configurations
Tech Stack
- axios (HTTP + cookie mgmt)
- parse-torrent (bencode + hash extraction)
- form-data (multipart uploads)
Related
- See File Organization for seeding support