Replace default Anna's Archive base URL from https://annas-archive.li to https://annas-archive.gl across docs, UI components, API routes, processors, services, and tests. Add comprehensive tests for the admin manual-import API route and enhance the manual-import route to fetch missing ASIN details from Audnexus and create audiobook records with proper error handling and logging. Update related test expectations and FlareSolverr test usages to reflect the new default URL.
10 KiB
E-book Support
Status: ✅ Implemented | First-class ebook requests with multi-source support (Anna's Archive + Indexer Search)
Overview
Ebooks are first-class citizens in RMAB, with their own request type, tracking, and UI representation. When an audiobook request completes, an ebook request is automatically created (if a source is enabled). Supports multiple sources: Anna's Archive (direct HTTP) and Indexer Search (via Prowlarr with ebook categories).
Key Details
First-Class Ebook Requests
- Request Type:
type: 'ebook'(vs'audiobook') - Parent Relationship: Ebook requests are children of audiobook requests (
parentRequestId) - Terminal State:
downloaded(ebooks don't have "available" state like audiobooks) - UI Badge: Orange (#f16f19) ebook badge to distinguish from audiobooks
- Separate Tracking: Own progress, status, and error handling
Source Priority
- Anna's Archive (if enabled) - Direct HTTP downloads
- Searched first via ASIN, then title + author
- Uses FlareSolverr if configured (Cloudflare bypass)
- Indexer Search (if enabled, and no Anna's Archive result)
- Searches Prowlarr with ebook categories (default: 7020)
- Ranks using unified ranking algorithm with ebook-specific scoring
- Downloads via qBittorrent (torrents) or SABnzbd (Usenet)
- Both disabled → Ebook downloads disabled entirely
Flow (Anna's Archive)
- Audiobook organization completes
- Ebook request created automatically (if source enabled)
search_ebookjob searches Anna's Archivestart_direct_downloaddownloads via HTTPorganize_filescopies to audiobook folder- Request marked as
downloaded(terminal) - "Available" notification sent
Flow (Indexer Search)
- Audiobook organization completes
- Ebook request created automatically (if source enabled)
search_ebookjob searches indexers (if Anna's Archive failed/disabled)download_torrentjob adds to qBittorrent/SABnzbd (reuses audiobook processor)monitor_downloadtracks progressorganize_filescopies to audiobook folder- Request marked as
downloaded(terminal) - Torrent left to seed (respects seeding limits)
Configuration
Admin Settings → E-book Sidecar tab (3 sections)
Section 1: Anna's Archive
| Key | Default | Description |
|---|---|---|
ebook_annas_archive_enabled |
false |
Enable Anna's Archive downloads |
ebook_sidecar_base_url |
https://annas-archive.gl |
Base URL for mirror |
ebook_sidecar_flaresolverr_url |
`` (empty) | FlareSolverr proxy URL (optional) |
Section 2: Indexer Search
| Key | Default | Description |
|---|---|---|
ebook_indexer_search_enabled |
false |
Enable Indexer Search via Prowlarr |
Note: Ebook categories are configured per-indexer in Settings → Indexers → Edit Indexer → EBook tab
Section 3: General Settings
| Key | Default | Options | Description |
|---|---|---|---|
ebook_sidecar_preferred_format |
epub |
epub, pdf, mobi, azw3, any |
Preferred format |
ebook_auto_grab_enabled |
true |
true, false |
Auto-create ebook requests after audiobook downloads |
ebook_kindle_fix_enabled |
false |
true, false |
Apply Kindle compatibility fixes to EPUB files |
Notes:
- Auto-grab is automatically disabled if no ebook sources are enabled. Manual fetch via admin buttons still works.
- Kindle fix toggle only visible when preferred format is EPUB.
Kindle EPUB Fix
Purpose: Apply compatibility fixes to EPUB files before organizing, ensuring successful Kindle import.
Fixes Applied:
- Encoding declaration - Adds UTF-8 XML declaration to files missing it
- Body ID link fix - Removes
#body/#bodymatterfragments from hyperlinks that break on Kindle - Language validation - Ensures
dc:languageuses Amazon KDP-approved codes (defaults toenif invalid) - Stray IMG removal - Removes
<img>tags withoutsrcattributes
How It Works:
- Enabled via toggle in E-book Sidecar settings (only visible when EPUB format selected)
- Applied during
organize_filesjob, before copying to final location - Creates temp fixed file → organizes temp file → cleans up temp file
- Original download file stays intact (important for seeding torrents)
- Non-blocking: if fix fails, continues with original file
Source: Based on kindle-epub-fix
Database Schema
Request model additions:
type String @default("audiobook") // 'audiobook' | 'ebook'
parentRequestId String? @map("parent_request_id")
parentRequest Request? @relation("EbookParent", fields: [parentRequestId], references: [id])
childRequests Request[] @relation("EbookParent")
Indexes: type, parentRequestId
Job Processors
search_ebook
- Searches Anna's Archive first (if enabled), then indexers (if enabled)
- Anna's Archive: Creates download history with
downloadClient: 'direct', triggersstart_direct_download - Indexer: Triggers
download_torrentjob (reuses audiobook processor)
start_direct_download
- Downloads file via HTTP with progress tracking
- Tries multiple slow download links on failure
- Triggers
organize_fileson success
download_torrent (shared with audiobooks)
- Routes to qBittorrent (torrents) or SABnzbd (Usenet)
- Creates download history with indexer metadata
- Triggers
monitor_downloadjob
Ranking Algorithm (Indexer Results)
Ebook torrent ranking uses unified algorithm with ebook-specific scoring:
| Component | Points | Description |
|---|---|---|
| Title/Author Match | 60 pts | Reuses audiobook matching logic (word coverage, author presence) |
| Format Match | 10 pts | 10 pts if matches preferred format, 0 otherwise |
| Size Quality | 15 pts | Inverted: < 5MB = 15pts, 5-15MB = 10pts, 15-20MB = 5pts |
| Seeder Count | 15 pts | Logarithmic scaling (same as audiobooks) |
Filtering:
- Files > 20 MB are filtered out (too large for ebooks)
- Dual threshold: base score >= 50 AND final score >= 50
Bonus System: Same as audiobooks (indexer priority, flag bonuses)
Delete Behavior
Ebook deletion is different from audiobook deletion:
- Only deletes ebook files (
.epub,.pdf,.mobi, etc.) - Does NOT delete the title folder (audiobook files remain)
- Does NOT delete from backend library (Plex/ABS)
- Does NOT clear audiobook availability linkage
- Soft-deletes the ebook request record
- Torrents left to seed (respects seeding limits)
UI Representation
RequestCard
- Orange ebook badge displayed next to status badge
- Orange book icon for placeholder cover art
- Interactive search disabled (Anna's Archive only)
Status Flow
pending → searching → downloading → processing → downloaded (terminal)
↘ awaiting_search (retry) ↗
FlareSolverr Integration
Anna's Archive uses Cloudflare protection. FlareSolverr bypasses this using a headless browser.
Setup
docker run -d --name flaresolverr -p 8191:8191 ghcr.io/flaresolverr/flaresolverr:latest
Configure URL in Admin Settings → E-book Sidecar: http://localhost:8191
Performance
- First request: ~5-10 seconds
- Subsequent: ~2-5 seconds per page
- Total: ~15-30 seconds per ebook
Scraping Strategy (Anna's Archive)
Method 1: ASIN Search (exact match)
Search: https://annas-archive.gl/search?ext=epub&lang=en&q="asin:B09TWSRMCB"
↓
MD5 Page: https://annas-archive.gl/md5/[md5]
↓
Slow Download: https://annas-archive.gl/slow_download/[md5]/0/5
↓
File Server: http://[server]/path/to/file.epub
Method 2: Title + Author (fallback)
Search: https://annas-archive.gl/search?q=Title+Author&ext=epub&lang=en
↓ (Same flow from MD5 page)
File Naming
Pattern: [Title] - [Author].[format]
Sanitization:
- Remove:
<>:"/\|?* - Collapse spaces, trim, limit to 200 chars
Error Handling
Non-blocking errors:
- No search results → Request goes to
awaiting_searchfor retry - All downloads fail → Same retry behavior
- Audiobook organization never affected
Technical Files
Processors:
src/lib/processors/search-ebook.processor.ts- Multi-source searchsrc/lib/processors/direct-download.processor.ts- Anna's Archive downloadssrc/lib/processors/download-torrent.processor.ts- Indexer downloads (shared)src/lib/processors/organize-files.processor.ts(ebook branch)
Services:
src/lib/services/ebook-scraper.ts- Anna's Archive scrapingsrc/lib/services/job-queue.service.ts(ebook job types)
Utils:
src/lib/utils/file-organizer.ts(organizeEbookmethod)src/lib/utils/ranking-algorithm.ts(rankEbookTorrentsfunction)src/lib/utils/indexer-grouping.ts(supports'ebook'type)src/lib/utils/epub-fixer.ts(Kindle EPUB compatibility fixes)
UI:
src/components/requests/RequestCard.tsx(ebook badge)
Delete:
src/lib/services/request-delete.service.ts(ebook-specific logic)
Format Support
| Format | Extension | Recommended |
|---|---|---|
| EPUB | .epub |
Yes |
.pdf |
Sometimes | |
| MOBI | .mobi |
Legacy |
| AZW3 | .azw3 |
Sometimes |
Indexer Categories
Indexer configuration supports separate category arrays for audiobooks and ebooks:
- Audiobook Categories: Default
[3030](Audio/Audiobook) - Ebook Categories: Default
[7020](Books/EBook)
Categories are configured per-indexer via the tabbed interface in the Edit Indexer modal.
Limitations
- Title search may return wrong book for common titles
- Download speed depends on file server load (Anna's Archive)
- English books only (title search filter for Anna's Archive)
- Format detection from torrent titles may be imprecise
Related
- File Organization - Ebook organization
- Settings Pages - Configuration UI
- Ranking Algorithm - Ebook ranking
- Request Deletion - Delete behavior
- Prowlarr Integration - Indexer search