Files
ReadMeABook/documentation/settings-pages.md
T
kikootwo f23afc1ba2 Add Plex format coercion (.mp4 → .m4b)
Implement Plex-compatible file-extension coercion to avoid Plex silently ignoring .mp4 (and single-file .m4a) audiobooks (issue #166). Adds a DB migration and configuration key (plex_format_coercion_enabled, default true), exposes a toggle in the setup wizard and Admin Paths settings, and persists/reads the setting in the admin/setup APIs.

Introduces src/lib/utils/format-coercion.ts (coerceToPlexCompatible) and related constants in src/lib/constants/audio-formats.ts (PLEX_COMPATIBLE_EXTENSIONS, COERCION_RENAME_MAP, DRM_EXTENSIONS, TRANSCODE_REQUIRED_EXTENSIONS). The organize-files processor now runs coercion after organizing/tagging and before generating the filesHash and triggering scans; coercion is idempotent, never overwrites existing targets, logs warnings on DRM/transcode/permission errors, and is non-fatal.

Adds unit tests for the coercion util and updates processor & setup UI tests. Updates documentation (TABLEOFCONTENTS, file-organization, fixes/file-hash-matching, settings-pages) describing behavior, config, and constraints.
2026-05-15 19:33:59 -04:00

20 KiB

Settings Pages

Status: Implemented | ♻️ Refactored (Jan 2026)

Single tabbed interface for admins to view/modify system configuration post-setup with mandatory validation before saving.

Architecture (Refactored Jan 2026)

Original: Monolithic 2,971-line component Current: Modular architecture with 89% code reduction (2,971 → 325 lines)

Structure:

src/app/admin/settings/
├── page.tsx                      # Shell component (325 lines)
├── lib/
│   ├── types.ts                  # Shared TypeScript interfaces
│   └── helpers.ts                # Business logic (206 lines)
├── hooks/
│   └── useSettings.ts            # Global settings hook
└── tabs/                         # Feature modules
    ├── LibraryTab/               # Plex/Audiobookshelf config
    │   ├── LibraryTab.tsx
    │   ├── useLibrarySettings.ts
    │   ├── PlexSection.tsx
    │   ├── AudiobookshelfSection.tsx
    │   └── index.ts
    ├── AuthTab/                  # Authentication (OIDC + Manual)
    │   ├── AuthTab.tsx
    │   ├── useAuthSettings.ts
    │   ├── OIDCSection.tsx
    │   ├── RegistrationSection.tsx
    │   ├── PendingUsersTable.tsx
    │   └── index.ts
    ├── IndexersTab/              # Prowlarr/indexers
    │   ├── IndexersTab.tsx
    │   ├── useIndexersSettings.ts
    │   └── index.ts
    ├── DownloadTab/              # qBittorrent/Transmission/SABnzbd
    │   ├── DownloadTab.tsx
    │   ├── useDownloadSettings.ts
    │   └── index.ts
    ├── PathsTab/                 # Directory paths
    │   ├── PathsTab.tsx
    │   ├── usePathsSettings.ts
    │   └── index.ts
    ├── EbookTab/                 # E-book sidecar
    │   ├── EbookTab.tsx
    │   ├── useEbookSettings.ts
    │   └── index.ts
    └── BookDateTab/              # AI recommendations
        ├── BookDateTab.tsx
        ├── useBookDateSettings.ts
        └── index.ts

Benefits:

  • Single Responsibility: Each tab manages its own state/logic
  • Testability: Individual tabs can be unit tested
  • Maintainability: Changes to one feature don't affect others
  • Performance: Lazy loading possible (future optimization)
  • Reusability: Custom hooks can be used elsewhere
  • Code Quality: Follows React best practices

Sections

  1. Plex - URL, token (masked), library ID, Audible region, filesystem scan trigger toggle
  2. Audiobookshelf - URL, API token (masked), library ID, Audible region, filesystem scan trigger toggle
  3. Prowlarr - URL, API key (masked), indexer selection with priority, seeding time, RSS monitoring toggle, audiobook/ebook categories per indexer
  4. Download Client - Type (qBittorrent, Transmission, SABnzbd), URL, credentials (masked), custom download path (per-client relative sub-path with live preview)
  5. Paths - Download + media directories, audiobook organization template, metadata tagging toggle, chapter merging toggle, Plex format coercion toggle
  6. E-book Sidecar - Multi-source ebook downloads (Anna's Archive + Indexer Search), preferred format
  7. BookDate - AI provider, API key (encrypted), model selection, library scope, custom prompt, swipe history
  8. Notifications - Multiple backends (Discord, Pushover), event subscriptions, test functionality

E-book Sidecar

Purpose: Configure ebook download sources and preferences to accompany audiobook downloads.

Tab Structure (3 sections):

  1. Anna's Archive Section

    • Enable toggle for Anna's Archive downloads
    • Base URL (default: https://annas-archive.gl)
    • FlareSolverr URL (optional, for Cloudflare bypass)
  2. Indexer Search Section

    • Enable toggle for indexer-based ebook search via Prowlarr
    • Hint directing users to Indexers tab for category configuration
  3. General Settings Section (visible when any source enabled)

    • Preferred format: EPUB (recommended), PDF, MOBI, AZW3, Any
    • Auto-grab toggle: Automatically create ebook requests after audiobook downloads
    • Kindle fix toggle: Apply compatibility fixes to EPUB files (only visible when EPUB format selected)

Configuration Keys:

Key Default Description
ebook_annas_archive_enabled false Enable Anna's Archive
ebook_indexer_search_enabled false Enable Indexer Search via Prowlarr
ebook_sidecar_preferred_format epub Preferred format
ebook_auto_grab_enabled true Auto-create ebook requests after audiobook downloads
ebook_kindle_fix_enabled false Apply Kindle compatibility fixes to EPUB files
ebook_sidecar_base_url https://annas-archive.gl Anna's Archive mirror
ebook_sidecar_flaresolverr_url `` FlareSolverr URL

Behavior:

  • If Anna's Archive enabled → Searches Anna's Archive first
  • If Indexer Search enabled → Falls back to indexer search if Anna's Archive fails/disabled
  • If both disabled → Ebook downloads completely off
  • If auto-grab disabled → Manual "Fetch Ebook" button only (admin buttons still work)
  • If Kindle fix enabled (and EPUB format) → Applies compatibility fixes during organization

Indexer Categories (Tabbed)

Purpose: Configure separate category sets for audiobook and ebook searches per indexer.

UI: Edit Indexer modal has Categories section with two tabs:

  • AudioBook tab - Categories for audiobook searches (default: [3030])
  • EBook tab - Categories for ebook searches (default: [7020])

Storage: prowlarr_indexers JSON config stores:

{
  "id": 1,
  "name": "MyIndexer",
  "audiobookCategories": [3030],
  "ebookCategories": [7020],
  ...
}

Auto-Search Behavior (Indexers tab)

Purpose: Control how ReadMeABook performs automatic indexer searches. Lives on the Indexers tab between the Prowlarr connection block and the IndexerManagement list.

Toggle: Skip unreleased books in automatic searches

  • When ON: auto-search skips books whose release date is in the future. Those requests automatically start searching once the book is released. Manual searches are unaffected.
  • When OFF: auto-search proceeds regardless of release date.

Configuration Key:

Key Default Category Description
indexer.skip_unreleased true (ON) indexer Skip auto-searches for books with future release dates

Read contract (consumed by background workers):

  • value !== 'false' → ON (skip enabled). Missing key OR any non-'false' value → ON.
  • Only the exact string 'false' disables the toggle. Workers MUST match this.

API: Persisted via PUT /api/admin/settings/indexer-options. Saved alongside Prowlarr connection + indexer config when the Indexers tab Save button is clicked.

Audible Region

Purpose: Configure which Audible region to use for metadata and search to ensure accurate ASIN matching with your metadata engine.

Configuration:

  • Key: audible.region (string, default: 'us')
  • Supported regions: US, Canada, UK, Australia, India
  • UI: Dropdown selector in Library tab (both Plex and Audiobookshelf settings)
  • No validation required (immediate save)

Why It Matters:

  • Each Audible region uses different ASINs for the same audiobook
  • Users must match their RMAB region to their Plex/Audiobookshelf metadata engine region
  • Mismatched regions cause poor search results and failed metadata matching

Help Text: "Select the Audible region that matches your metadata engine (Audnexus/Audible Agent) configuration in [Plex/Audiobookshelf]. This ensures accurate book matching and metadata."

Implementation:

  • Affects all Audible API calls (base URL changes per region)
  • Affects all Audnexus API calls (region parameter added)
  • Changes apply immediately on next API call (no restart required)
  • Automatic refresh: Changing region automatically triggers audible_refresh job to fetch popular/new releases for the new region
  • Cache management: ConfigService cache and AudibleService initialization are cleared when region changes
  • Smart re-initialization: Service automatically detects region changes and re-initializes before each request
  • See: documentation/integrations/audible.md for technical details

Audiobook Organization Template

Purpose: Customize how audiobooks are organized within the media directory using variable-based templates.

Configuration:

  • Key: audiobook_path_template (string, default: {author}/{title} {asin})
  • Variables: {author}, {title}, {narrator}, {asin}, {year}
  • Optional variables (narrator, asin, year) removed if not available
  • Template validated on test, shows preview examples

UI (PathsTab):

  • Text input with monospace font
  • Placeholder: {author}/{title} {asin}
  • Variable reference panel showing all available variables
  • Template validation on "Test Paths" with success/error feedback
  • Preview examples showing 2-3 sample paths with actual data

Validation:

  • Must contain at least {author} or {title} (required variables)
  • Cannot be empty or only contain optional variables
  • Invalid templates show error message
  • Valid templates show preview paths

Examples:

  • {author}/{title} {asin}Douglas Adams/The Hitchhiker's Guide to the Galaxy B0009JKV9W/
  • {author}/{title} ({year})Douglas Adams/The Hitchhiker's Guide to the Galaxy (2005)/
  • {author}/{narrator}/{title}Douglas Adams/Stephen Fry/The Hitchhiker's Guide to the Galaxy/

Filesystem Scan Trigger

Purpose: Trigger Plex/Audiobookshelf to scan filesystem after organizing files for users with disabled filesystem watchers.

Configuration:

  • Plex: plex.trigger_scan_after_import (boolean, default: false)
  • Audiobookshelf: audiobookshelf.trigger_scan_after_import (boolean, default: false)

UI:

  • Checkbox toggle in both Plex and Audiobookshelf settings tabs
  • Default: Unchecked (disabled)
  • Help text: "Only enable this if you have [Plex/Audiobookshelf]'s filesystem watcher (automatic scanning) disabled. Most users should leave this disabled and rely on [Plex/Audiobookshelf]'s built-in automatic detection."

Behavior:

  • When enabled: After organize_files job completes, RMAB triggers filesystem scan in media server
  • When disabled: User relies on media server's filesystem watcher or manual scans
  • Error handling: Scan failures logged but don't fail organize job (graceful degradation)

Plex Format Coercion

Purpose: Rename audiobook files to Plex-recognized extensions (.mp4.m4b, single-file .m4a.m4b) before the library scan. Prevents Plex silently ignoring .mp4 audiobooks. Rename-only — no transcoding. See: phase3/file-organization.md.

Configuration:

  • Key: plex_format_coercion_enabled (boolean, default: true)
  • Read contract: value !== 'false' enables (default-on)
  • Configurable in: Setup wizard (Paths step), Admin settings (Paths tab)

UI:

  • Checkbox toggle in PathsTab, between metadata tagging and chapter merging
  • Default: Checked (enabled)
  • Label: "Coerce file formats for Plex compatibility"
  • Sub-text: "Rename .mp4 audiobook files (and single-file .m4a) to .m4b before Plex scans. No re-encoding."

Behavior:

  • When enabled: After organize, rename files per coercion table before scan trigger
  • When disabled: Files left as-is (Plex may silently skip .mp4)
  • Failure isolation: Rename errors logged but don't fail organize job
  • Universal (Plex + ABS) — rename is lossless, no per-backend distinction

Validation Flow

Plex, Download Client, Paths:

  1. User modifies settings (URL, credentials, paths)
  2. User clicks "Test Connection" or "Test Paths"
  3. System validates settings
  4. On success: "Save Changes" button enabled
  5. On failure: Error shown, "Save Changes" remains disabled

Prowlarr (special handling):

  1. On tab load: Current indexer configuration loaded from database automatically
  2. Changing indexer settings (enable/disable, priority, seeding time, RSS):
    • No test required
    • Can save immediately if URL/API key unchanged
  3. Changing URL or API key:
    • Validation required before saving
    • User clicks "Test Connection"
    • On success: Indexers refresh automatically, "Save Changes" enabled
  4. Button text adapts:
    • "Test Connection" when URL/API key changed
    • "Refresh Indexers" when connection info unchanged

BookDate (Admin Settings):

  1. On tab load: Current BookDate global configuration loaded from database automatically
  2. Changing AI provider: Resets model selection
  3. Test connection: Required to fetch available models before saving
  4. Changing API key: Must test connection to verify and fetch models
  5. Saving configuration: Validates all fields (provider, API key, model)
  6. Note: Library scope and custom prompt are now per-user settings (configured in BookDate page)
  7. Clear swipe history: Confirmation dialog, removes ALL users' swipes and cached recommendations
  8. No "Save Changes" button - uses dedicated "Save BookDate Configuration" button
  9. Accessible to admins only

BookDate (User Preferences - in /bookdate page):

  1. Settings icon: Opens modal with per-user preferences
  2. Library scope: Full library or rated books only (default: full)
  3. Custom prompt: Optional text (max 1000 chars, default: blank)
  4. Save: Updates user preferences immediately
  5. Accessible to all authenticated users

Validation state resets when:

  • Plex: URL or token modified
  • Prowlarr: URL or API key modified (NOT indexer config)
  • Download Client: URL, username, or password modified
  • Paths: Directory paths or template modified

API Endpoints

GET /api/admin/settings

  • Returns all config (passwords masked as ••••)
  • Admin auth required

GET /api/admin/settings/prowlarr/indexers

  • Returns current indexer configuration merged with available Prowlarr indexers
  • Loads saved settings (enabled, priority, seeding time, RSS) from database
  • Merges with live indexer list from Prowlarr
  • Admin auth required

PUT /api/admin/settings/plex

  • Updates Plex config
  • Requires prior successful test if URL/token changed

PUT /api/admin/settings/prowlarr

  • Updates Prowlarr URL and API key
  • Requires prior successful test if values changed

PUT /api/admin/settings/audible

  • Updates Audible region
  • Body: { region: string } (one of: us, ca, uk, au, in, es, fr)
  • No validation required

PUT /api/admin/settings/prowlarr/indexers

  • Updates indexer configuration (enabled, priority, seeding time, RSS)
  • No test required if URL/API key unchanged
  • Saves only enabled indexers to database

GET /api/admin/settings/indexer-options

  • Returns { skipUnreleased: boolean }
  • Default ON: missing or non-'false' value resolves to true
  • Admin auth required

PUT /api/admin/settings/indexer-options

  • Updates indexer-wide auto-search options
  • Body: { skipUnreleased: boolean } (strict boolean validation)
  • Persists indexer.skip_unreleased (category: indexer)
  • No connection test required

PUT /api/admin/settings/download-client

  • Updates download client config
  • Requires prior successful test if credentials changed

PUT /api/admin/settings/paths

  • Updates paths and audiobook organization template
  • Requires prior successful test if paths or template changed
  • Body: { downloadDir, mediaDir, audiobookPathTemplate, metadataTaggingEnabled, chapterMergingEnabled }

Test Endpoints (authenticated, handle masked values):

  • POST /api/admin/settings/test-plex - Tests Plex connection, uses stored token if masked, returns libraries
  • POST /api/admin/settings/test-prowlarr - Tests connection, uses stored API key if masked, returns indexers
  • POST /api/admin/settings/test-download-client - Tests qBittorrent/Transmission, uses stored password if masked
  • POST /api/setup/test-paths - Validates paths writable and template format, returns {success, message, templateValidation: {isValid, error?, previewPaths?}}

BookDate Endpoints:

  • GET /api/bookdate/config - Get global BookDate configuration (API key excluded, admin only)
  • POST /api/bookdate/config - Save/update global BookDate configuration (admin only)
  • POST /api/bookdate/test-connection - Test AI provider connection and fetch available models
  • DELETE /api/bookdate/swipes - Clear ALL users' swipe history and cached recommendations (admin only)
  • GET /api/bookdate/preferences - Get user's preferences (libraryScope, customPrompt)
  • PUT /api/bookdate/preferences - Update user's preferences (all authenticated users)

Features

  • Password visibility toggle
  • Mandatory "Test Connection" buttons per tab
  • "Save Changes" disabled until current tab validated
  • Test result display (success/error messages)
  • Toast notifications for save confirmations
  • Form validation with Zod schemas
  • Reuses setup wizard connection test endpoints
  • Visual warning when validation required

Security

  • Admin role required
  • Passwords never returned in GET (masked)
  • Connection tests validate before saving
  • HTTPS required in production

Validation

Plex: Valid HTTP/HTTPS URL, non-empty token, library ID selected Prowlarr: Valid URL, non-empty API key, ≥1 indexer configured, priority 1-25, seedingTimeMinutes ≥0, rssEnabled boolean Download Client: Valid URL, credentials required, type must be 'qbittorrent', 'transmission', or 'sabnzbd' Paths: Absolute paths, exist or creatable, writable, cannot be same directory, template must contain {author} or {title}

Tech Stack

  • React Hook Form
  • Zod validation
  • Tab/sidebar navigation
  • Toast notifications

Fixed Issues

1. Settings Save Without Validation

  • Issue: Users could save invalid/broken settings (wrong URLs, bad credentials, invalid paths)
  • Cause: No validation enforcement before save
  • Fix: Added mandatory "Test Connection"/"Test Paths" buttons per tab, disabled "Save Changes" until validated
  • Behavior: Now matches wizard flow - test first, then save

2. Testing with Masked Credentials

  • Issue: Test connection failed because it was testing with masked •••• values instead of actual credentials
  • Cause: Test endpoints didn't handle masked values, tried to authenticate with literal ••••••••
  • Fix: Created authenticated test endpoints that read actual values from database when masked values detected
  • Endpoints: /api/admin/settings/test-plex, /test-prowlarr, /test-download-client
  • Behavior: Users can test without re-entering unchanged passwords

3. Indexer Configuration Workflow

  • Issue: Indexer settings required re-testing before saving, current settings weren't loading, workflow confusing
  • Cause: Indexers only loaded after test, changing any indexer setting invalidated connection
  • Fix:
    • Load current indexer config from database on tab load (GET /api/admin/settings/prowlarr/indexers)
    • Track which values changed (URL/API key vs indexer config)
    • Only require test if URL/API key changed
    • Allow saving indexer config changes without re-testing connection
    • Button text adapts: "Test Connection" vs "Refresh Indexers"
  • Behavior: Natural workflow - see current settings, modify indexers, save immediately

Notifications

Purpose: Configure notification backends to receive alerts for audiobook request events.

Configuration:

  • Multiple backends per type (Discord, Pushover)
  • Per-backend event subscriptions (4 events)
  • Encrypted sensitive values (webhook URLs, API keys)
  • Enable/disable toggle per backend

UI (NotificationsTab):

  • Type selector cards: Discord (indigo "D"), Pushover (blue "P")
  • Grid layout for configured backends (3 columns)
  • Card shows: type icon, name, enabled status, event count
  • Edit/delete actions per card

Modal (NotificationConfigModal):

  • Type-specific forms (Discord: webhook/username/avatar, Pushover: keys/priority)
  • Event subscription checkboxes (4 events)
  • Enable/disable toggle
  • Test button (sends sample notification)
  • Password masking for sensitive values

Event Types:

  • Request Pending Approval - Admin approval required
  • Request Approved - Approved (manual or auto)
  • Request Available - Available in library
  • Request Error - Failed at any stage

Validation:

  • Name required
  • Discord: webhook URL required
  • Pushover: user key + app token required
  • At least one event selected