mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 21:00:09 +00:00
682836237b
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
194 lines
8.4 KiB
Markdown
194 lines
8.4 KiB
Markdown
# 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:**
|
|
1. Extract `info_hash` from magnet URI (deterministic)
|
|
2. Upload via `urls` parameter
|
|
3. Return extracted hash immediately
|
|
|
|
**Torrent Files:**
|
|
1. Download .torrent file to memory
|
|
2. Parse with `parse-torrent` (bencode decoder)
|
|
3. Extract `info_hash` (SHA-1 of info dict)
|
|
4. Upload file content via `torrents` parameter (multipart/form-data)
|
|
5. 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 username
|
|
- `download_client_password` - qBittorrent password
|
|
- `download_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 qBittorrent
|
|
- `download_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_dir` config
|
|
- Handles config changes: if user changes `download_dir`, category updates automatically
|
|
- Uses both `createCategory` and `editCategory` APIs for reliability
|
|
|
|
**Why Both Create and Edit:**
|
|
1. Create: Ensures category exists (idempotent, won't fail if exists)
|
|
2. 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:**
|
|
1. Admin Settings → Download Client → Enable Remote Path Mapping
|
|
2. Enter remote path (as reported by qBittorrent)
|
|
3. Enter local path (accessible to ReadMeABook)
|
|
4. Test connection validates local path exists
|
|
5. Save settings
|
|
|
|
**Implementation:**
|
|
- `PathMapper` utility (`src/lib/utils/path-mapper.ts`) handles transformation
|
|
- Applied in `monitor-download.processor.ts` when download completes
|
|
- Applied in `retry-failed-imports.processor.ts` for failed imports
|
|
- Uses simple prefix replacement with path normalization
|
|
- Graceful fallback: if path doesn't match remote prefix, returns unchanged
|
|
|
|
**Path Transformation:**
|
|
```typescript
|
|
// 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_path` and constructed `save_path + name`
|
|
|
|
## Data Models
|
|
|
|
```typescript
|
|
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: false` when 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/qbittorrent` fully 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 `Referer` and `Origin` headers to all login requests
|
|
- Headers set to qBittorrent base URL (e.g., `https://seedbox.example.com:443/qbittorrent`)
|
|
- Applied to both `login()` and `testConnectionWithCredentials()` 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](./file-organization.md) for seeding support
|