mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 04:40:09 +00:00
Add request approval system and audiobook path template
Implements admin approval workflow for user requests with global and per-user auto-approve controls. Adds new request statuses ('awaiting_approval', 'denied'), related API endpoints, and UI for pending approvals. Introduces configurable audiobook organization path template with validation and preview in settings, updates database schema and migrations for new fields.
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
- **Settings management, encryption** → [backend/services/config.md](backend/services/config.md)
|
||||
- **Settings UI (modular architecture, all tabs)** → [settings-pages.md](settings-pages.md)
|
||||
- **Settings architecture refactoring (Jan 2026)** → [settings-pages.md](settings-pages.md#architecture-refactored-jan-2026)
|
||||
- **Audiobook organization templates** → [settings-pages.md](settings-pages.md#audiobook-organization-template), [phase3/file-organization.md](phase3/file-organization.md#target-structure)
|
||||
- **Setup middleware & status check** → [backend/middleware.md](backend/middleware.md)
|
||||
- **Environment variables, PUBLIC_URL, OAuth configuration** → [backend/services/environment.md](backend/services/environment.md)
|
||||
|
||||
@@ -75,6 +76,7 @@
|
||||
- **Dashboard (metrics, downloads, requests)** → [admin-dashboard.md](admin-dashboard.md)
|
||||
- **Jobs management UI** → [backend/services/scheduler.md](backend/services/scheduler.md)
|
||||
- **Request deletion (soft delete, seeding awareness)** → [admin-features/request-deletion.md](admin-features/request-deletion.md)
|
||||
- **Request approval system, auto-approve settings** → [admin-features/request-approval.md](admin-features/request-approval.md)
|
||||
|
||||
## Deployment
|
||||
- **Docker Compose setup (multi-container)** → [deployment/docker.md](deployment/docker.md)
|
||||
@@ -98,6 +100,9 @@
|
||||
**"How does authentication work?"** → [backend/services/auth.md](backend/services/auth.md)
|
||||
**"How do I change my password?"** → [backend/services/auth.md](backend/services/auth.md) (local users only - accessed via user menu in header)
|
||||
**"How do I delete requests?"** → [admin-features/request-deletion.md](admin-features/request-deletion.md)
|
||||
**"How do I approve/deny user requests?"** → [admin-features/request-approval.md](admin-features/request-approval.md)
|
||||
**"How do I enable auto-approve for requests?"** → [admin-features/request-approval.md](admin-features/request-approval.md)
|
||||
**"How do I customize audiobook folder organization?"** → [settings-pages.md](settings-pages.md#audiobook-organization-template), [phase3/file-organization.md](phase3/file-organization.md#target-structure)
|
||||
**"How do I deploy?"** → [deployment/docker.md](deployment/docker.md) (multi-container), [deployment/unified.md](deployment/unified.md) (all-in-one)
|
||||
**"How do I use the unified container?"** → [deployment/unified.md](deployment/unified.md)
|
||||
**"OAuth redirects to localhost / PUBLIC_URL not working"** → [backend/services/environment.md](backend/services/environment.md)
|
||||
|
||||
@@ -7,6 +7,7 @@ Comprehensive overview of system metrics, active requests, download monitoring,
|
||||
## Sections
|
||||
|
||||
- **Metrics:** Total requests, active downloads, completed/failed requests, total users, system health
|
||||
- **Requests Awaiting Approval:** Grid of requests pending admin approval (approve/deny buttons, auto-refresh)
|
||||
- **Active Downloads:** Real-time table with title, progress, speed, ETA
|
||||
- **Recent Requests:** Last 50 with status and timestamps
|
||||
- **Quick Actions:** Links to settings, users, scheduled jobs, system logs
|
||||
@@ -27,13 +28,27 @@ Comprehensive overview of system metrics, active requests, download monitoring,
|
||||
**GET /api/admin/requests/recent**
|
||||
- Request ID, title, user, status, created/completed dates
|
||||
|
||||
**GET /api/admin/requests/pending-approval**
|
||||
- Requests with status 'awaiting_approval', includes audiobook + user details
|
||||
- Returns: requests array, count
|
||||
|
||||
**POST /api/admin/requests/[id]/approve**
|
||||
- Action: 'approve' (set status to 'pending', trigger search) or 'deny' (set status to 'denied')
|
||||
- Validates request is in 'awaiting_approval' status
|
||||
|
||||
**GET /api/admin/users**
|
||||
- User ID, Plex ID, username, email, role, avatar, created/updated dates, last login, request count
|
||||
- User ID, Plex ID, username, email, role, avatar, created/updated dates, last login, request count, autoApproveRequests
|
||||
|
||||
**PUT /api/admin/users/[id]**
|
||||
- Update user role (user/admin)
|
||||
- Update user role (user/admin), autoApproveRequests (true/false/null)
|
||||
- Prevents self-demotion
|
||||
|
||||
**GET /api/admin/settings/auto-approve**
|
||||
- Get global auto-approve setting (boolean)
|
||||
|
||||
**PATCH /api/admin/settings/auto-approve**
|
||||
- Update global auto-approve setting (boolean)
|
||||
|
||||
**GET /api/admin/logs**
|
||||
- Query params: page, limit, status, type
|
||||
- Returns: Job logs with request/audiobook/user details, pagination info
|
||||
@@ -45,6 +60,14 @@ Comprehensive overview of system metrics, active requests, download monitoring,
|
||||
- Back to Home button in header
|
||||
- Admin role required
|
||||
- Real-time progress updates
|
||||
- **Requests Awaiting Approval Section:**
|
||||
- Only visible when pending approval requests exist
|
||||
- Grid layout (3 columns on desktop)
|
||||
- Book cards with cover, title, author, user info, timestamp
|
||||
- Approve (green) and Deny (red) buttons
|
||||
- Loading states during approval/denial actions
|
||||
- Toast notifications for success/errors
|
||||
- Mutates pending-approval, recent requests, metrics caches on action
|
||||
|
||||
## Navigation
|
||||
|
||||
@@ -55,11 +78,18 @@ Comprehensive overview of system metrics, active requests, download monitoring,
|
||||
|
||||
## User Management Features
|
||||
|
||||
- List all users with avatar, email, role, request count, last login
|
||||
- List all users with avatar, email, role, request count, last login, autoApproveRequests
|
||||
- Edit user roles (user/admin)
|
||||
- Cannot change own role (security)
|
||||
- Shows request count per user
|
||||
- Role badges (purple for admin, gray for user)
|
||||
- **Global Auto-Approve Toggle:**
|
||||
- Checkbox at top: "Auto-approve all requests by default"
|
||||
- Updates Configuration.auto_approve_requests
|
||||
- **Per-User Auto-Approve Control:**
|
||||
- Dropdown: Use Global (null), Always Auto-Approve (true), Always Require Approval (false)
|
||||
- Updates User.autoApproveRequests
|
||||
- Shows effective setting (considers global + per-user)
|
||||
|
||||
## System Logs Features
|
||||
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
# Request Approval System
|
||||
|
||||
**Status:** ✅ Implemented | Admin approval workflow for user requests with global & per-user auto-approve controls
|
||||
|
||||
## Overview
|
||||
Allows admins to review and approve/deny user requests before they are processed. Supports global auto-approve toggle and per-user auto-approve overrides.
|
||||
|
||||
## Key Details
|
||||
|
||||
### Request Statuses
|
||||
- **awaiting_approval** - New status for requests pending admin approval
|
||||
- **denied** - New status for requests rejected by admin
|
||||
- **pending** - Status after approval (triggers search job)
|
||||
- Applies to all existing statuses: pending, searching, downloading, processing, downloaded, available, failed, cancelled, awaiting_search, awaiting_import, warn
|
||||
|
||||
### Configuration Keys
|
||||
- `auto_approve_requests` (Configuration table) - Global setting (true/false string)
|
||||
- `User.autoApproveRequests` (User table) - Per-user override (boolean, nullable)
|
||||
- `null` = Use global setting
|
||||
- `true` = Always auto-approve for this user
|
||||
- `false` = Always require approval for this user
|
||||
|
||||
### Approval Logic
|
||||
**When user creates request:**
|
||||
1. Check `User.autoApproveRequests`:
|
||||
- If `true` → Set status to 'pending', trigger search job
|
||||
- If `false` → Set status to 'awaiting_approval', wait for admin
|
||||
- If `null` → Check global `auto_approve_requests` setting
|
||||
- If 'true' → Auto-approve (status: 'pending')
|
||||
- Otherwise → Require approval (status: 'awaiting_approval')
|
||||
|
||||
**Admin approval actions:**
|
||||
- **Approve** → Change status to 'pending', trigger search job
|
||||
- **Deny** → Change status to 'denied', no further processing
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### GET /api/admin/requests/pending-approval
|
||||
Fetch all requests with status 'awaiting_approval'
|
||||
|
||||
**Auth:** Admin only
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"requests": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"createdAt": "2026-01-15T12:00:00Z",
|
||||
"audiobook": {
|
||||
"title": "Book Title",
|
||||
"author": "Author Name",
|
||||
"coverArtUrl": "https://..."
|
||||
},
|
||||
"user": {
|
||||
"id": "uuid",
|
||||
"plexUsername": "username",
|
||||
"avatarUrl": "https://..."
|
||||
}
|
||||
}
|
||||
],
|
||||
"count": 5
|
||||
}
|
||||
```
|
||||
|
||||
### POST /api/admin/requests/[id]/approve
|
||||
Approve or deny a specific request
|
||||
|
||||
**Auth:** Admin only
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"action": "approve" | "deny"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (approve):**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Request approved and search job triggered",
|
||||
"request": { /* full request object */ }
|
||||
}
|
||||
```
|
||||
|
||||
**Response (deny):**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Request denied",
|
||||
"request": { /* full request object */ }
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `404` - Request not found
|
||||
- `400` - Request not in 'awaiting_approval' status
|
||||
- `400` - Invalid action (must be 'approve' or 'deny')
|
||||
|
||||
### GET /api/admin/settings/auto-approve
|
||||
Get global auto-approve setting
|
||||
|
||||
**Auth:** Admin only
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"autoApproveRequests": true
|
||||
}
|
||||
```
|
||||
|
||||
### PATCH /api/admin/settings/auto-approve
|
||||
Update global auto-approve setting
|
||||
|
||||
**Auth:** Admin only
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"autoApproveRequests": true
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"autoApproveRequests": true
|
||||
}
|
||||
```
|
||||
|
||||
### PUT /api/admin/users/[id]
|
||||
Update user (includes autoApproveRequests field)
|
||||
|
||||
**Auth:** Admin only
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"autoApproveRequests": true | false | null
|
||||
}
|
||||
```
|
||||
|
||||
## UI Features
|
||||
|
||||
### Admin Dashboard (/admin)
|
||||
**Requests Awaiting Approval Section:**
|
||||
- Shows only when pending approval requests exist
|
||||
- Grid layout with book cards (3 columns on desktop)
|
||||
- Each card displays:
|
||||
- Book cover image
|
||||
- Title and author
|
||||
- User avatar and username
|
||||
- Request timestamp (relative: "2 hours ago")
|
||||
- Approve button (green, checkmark icon)
|
||||
- Deny button (red, X icon)
|
||||
- Auto-refreshes every 10 seconds (SWR)
|
||||
- Loading states on buttons during approval/denial
|
||||
- Success/error toast notifications
|
||||
- Mutates multiple caches on action: pending-approval, recent requests, metrics
|
||||
|
||||
### Admin Users Page (/admin/users)
|
||||
**Global Auto-Approve Toggle:**
|
||||
- Checkbox at top of page
|
||||
- Label: "Auto-approve all requests by default"
|
||||
- Updates `auto_approve_requests` configuration
|
||||
- Optimistic UI update with revert on error
|
||||
- Toast notification on success/error
|
||||
|
||||
**Per-User Auto-Approve Control:**
|
||||
- Each user row has toggle dropdown:
|
||||
- "Use Global Setting" (null, default)
|
||||
- "Always Auto-Approve" (true)
|
||||
- "Always Require Approval" (false)
|
||||
- Updates `User.autoApproveRequests` field
|
||||
- Shows current effective setting (considers global + per-user)
|
||||
- Optimistic UI update
|
||||
|
||||
### User Request Flow
|
||||
**When creating request (POST /api/requests):**
|
||||
- System checks approval logic (see above)
|
||||
- If awaiting approval → User sees status "Awaiting Approval" on request card
|
||||
- If auto-approved → User sees status "Pending" and processing begins
|
||||
|
||||
### Request Status Badges
|
||||
- **awaiting_approval** → Amber badge with warning icon
|
||||
- **denied** → Red badge with X icon
|
||||
- All other statuses → Existing badge colors
|
||||
|
||||
## Database Schema
|
||||
|
||||
### User Table
|
||||
```
|
||||
autoApproveRequests: Boolean (nullable, default null)
|
||||
- null: Use global setting
|
||||
- true: Always auto-approve
|
||||
- false: Always require approval
|
||||
```
|
||||
|
||||
### Request Table
|
||||
```
|
||||
status: Enum (includes 'awaiting_approval', 'denied')
|
||||
```
|
||||
|
||||
### Configuration Table
|
||||
```
|
||||
key: 'auto_approve_requests'
|
||||
value: 'true' | 'false' (string)
|
||||
```
|
||||
|
||||
## Related
|
||||
- [Admin Dashboard](../admin-dashboard.md) - Dashboard UI features
|
||||
- [Database Schema](../backend/database.md) - User and Request tables
|
||||
- [Settings Pages](../settings-pages.md) - Global settings management
|
||||
@@ -92,7 +92,13 @@ model Request {
|
||||
- **ONLY deletes title folder** (not author folder)
|
||||
- Handles missing folders gracefully
|
||||
|
||||
4. **Soft Delete Request**
|
||||
4. **Delete from Library Backend**
|
||||
- **Audiobookshelf Mode:** Delete library item via API if `absItemId` exists
|
||||
- Prevents "ghost" entries in Audiobookshelf library
|
||||
- Only removes from ABS database, not files (already deleted in step 3)
|
||||
- **Plex Mode:** Clear plex_library cache records
|
||||
|
||||
5. **Soft Delete Request**
|
||||
- UPDATE: `deletedAt = NOW(), deletedBy = adminUserId`
|
||||
- Preserves for audit trail and orphaned download tracking
|
||||
|
||||
@@ -186,6 +192,8 @@ where: {
|
||||
6. ✅ **Media folder not found** - Log and continue (already deleted)
|
||||
7. ✅ **Multiple delete clicks** - Button disabled during deletion
|
||||
8. ✅ **Network error** - Alert shown, request remains
|
||||
9. ✅ **ABS library item deletion fails** - Log error, continue with soft delete
|
||||
10. ✅ **No absItemId present** - Skip ABS deletion (not yet in library)
|
||||
|
||||
## File Structure
|
||||
|
||||
|
||||
@@ -14,6 +14,11 @@ PostgreSQL database storing users, audiobooks, requests, downloads, configuratio
|
||||
- `avatar_url`, `auth_token` (encrypted), `created_at`, `updated_at`, `last_login_at`
|
||||
- **Plex Home profile tracking:**
|
||||
- `plex_home_user_id` (string, nullable) - Profile ID from Plex Home (null = main account, set = home profile)
|
||||
- **Request approval control:**
|
||||
- `auto_approve_requests` (bool, nullable, default null) - Per-user override for request approval
|
||||
- `null` = Use global setting (Configuration.auto_approve_requests)
|
||||
- `true` = Always auto-approve this user's requests
|
||||
- `false` = Always require admin approval for this user's requests
|
||||
- **BookDate per-user preferences:**
|
||||
- `bookdate_library_scope` ('full'|'rated', default 'full') - Library scope for recommendations
|
||||
- `bookdate_custom_prompt` (text, optional, max 1000 chars) - Custom preferences for AI
|
||||
@@ -52,8 +57,12 @@ PostgreSQL database storing users, audiobooks, requests, downloads, configuratio
|
||||
|
||||
### Requests
|
||||
- `id` (UUID PK), `user_id` (FK), `audiobook_id` (FK)
|
||||
- `status` ('pending'|'searching'|'downloading'|'processing'|'downloaded'|'available'|'failed'|'cancelled'|'awaiting_search'|'awaiting_import'|'warn')
|
||||
- Flow: pending → searching → downloading → processing → downloaded → available (when matched in Plex)
|
||||
- `status` ('pending'|'searching'|'downloading'|'processing'|'downloaded'|'available'|'failed'|'cancelled'|'awaiting_search'|'awaiting_import'|'warn'|'awaiting_approval'|'denied')
|
||||
- **Approval flow:** awaiting_approval → (approve) → pending → searching → downloading → processing → downloaded → available
|
||||
- **Denial flow:** awaiting_approval → (deny) → denied
|
||||
- **awaiting_approval** - Request pending admin approval (only if auto-approve disabled)
|
||||
- **denied** - Request rejected by admin (terminal state)
|
||||
- **pending** - Request approved and queued for processing
|
||||
- `progress` (0-100), `priority`, `error_message`
|
||||
- `search_attempts`, `download_attempts`, `import_attempts`, `max_import_retries` (default 5)
|
||||
- `last_search_at`, `last_import_at`, `created_at`, `updated_at`, `completed_at`
|
||||
@@ -72,7 +81,11 @@ PostgreSQL database storing users, audiobooks, requests, downloads, configuratio
|
||||
- `id` (UUID PK), `key` (unique), `value`, `encrypted` (bool), `category`, `description`
|
||||
- `created_at`, `updated_at`
|
||||
- Indexes: `key`, `category`
|
||||
- Example keys: `plex.server_url`, `plex.auth_token`, `indexer.prowlarr_url`, `download_client.qbittorrent_password`, `paths.downloads`, `setup.completed`
|
||||
- Example keys: `plex.server_url`, `plex.auth_token`, `indexer.prowlarr_url`, `download_client.qbittorrent_password`, `paths.downloads`, `setup.completed`, `auto_approve_requests`
|
||||
- **Request approval:**
|
||||
- `auto_approve_requests` (value: 'true'|'false') - Global setting for auto-approving requests
|
||||
- If 'true' and User.autoApproveRequests is null, requests auto-approved
|
||||
- If not 'true' and User.autoApproveRequests is null, requests require admin approval
|
||||
|
||||
### Jobs
|
||||
- `id` (UUID PK), `bull_job_id`, `request_id` (FK nullable)
|
||||
|
||||
@@ -42,6 +42,7 @@ download_client.qbittorrent.url
|
||||
download_client.qbittorrent.password (encrypted)
|
||||
paths.downloads
|
||||
paths.media_library
|
||||
paths.audiobook_path_template
|
||||
automation.check_interval_seconds
|
||||
system.setup_completed
|
||||
```
|
||||
@@ -99,7 +100,8 @@ const CONFIG_DEFAULTS = {
|
||||
'system.setup_completed': 'false',
|
||||
'system.log_level': 'info',
|
||||
'paths.downloads': '/downloads',
|
||||
'paths.media_library': '/media'
|
||||
'paths.media_library': '/media',
|
||||
'audiobook_path_template': '{author}/{title} {asin}'
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
@@ -8,32 +8,38 @@ Copies completed downloads to standardized directory structure for Plex. Automat
|
||||
|
||||
Target directory read from database config `media_dir` (configurable in setup wizard and settings).
|
||||
|
||||
**Template-based organization:**
|
||||
- Config key: `audiobook_path_template`
|
||||
- Default: `{author}/{title} {asin}`
|
||||
- Variables: `{author}`, `{title}`, `{narrator}`, `{asin}`, `{year}`
|
||||
- Optional variables (narrator, asin, year) are removed if not available
|
||||
|
||||
**Examples:**
|
||||
```
|
||||
[media_dir]/
|
||||
└── Author Name/
|
||||
└── Book Title (Year) ASIN/
|
||||
├── Book Title.m4b
|
||||
└── cover.jpg
|
||||
Template: {author}/{title} {asin}
|
||||
Result: Douglas Adams/The Hitchhiker's Guide to the Galaxy B0009JKV9W/
|
||||
|
||||
Template: {author}/{title} ({year})
|
||||
Result: Douglas Adams/The Hitchhiker's Guide to the Galaxy (2005)/
|
||||
|
||||
Template: {author}/{narrator}/{title}
|
||||
Result: Douglas Adams/Stephen Fry/The Hitchhiker's Guide to the Galaxy/
|
||||
```
|
||||
|
||||
**Folder naming format:**
|
||||
**Legacy behavior (hardcoded):**
|
||||
- With year and ASIN: `Book Title (Year) ASIN`
|
||||
- With ASIN only: `Book Title ASIN`
|
||||
- With year only: `Book Title (Year)`
|
||||
- Fallback: `Book Title`
|
||||
|
||||
**Example:** `Douglas Adams/The Hitchhiker's Guide to the Galaxy (2005) B0009JKV9W/`
|
||||
|
||||
**Rationale:** Including ASIN in folder name improves Plex/Audnexus agent matching accuracy.
|
||||
|
||||
Default: `/media/audiobooks/` (if not configured)
|
||||
**Rationale:** Template system allows customization for different metadata agent configurations and user preferences while maintaining backward compatibility.
|
||||
|
||||
## Process
|
||||
|
||||
1. Download completes in `/downloads/[torrent-name]/` or `/downloads/[filename]` (single file)
|
||||
2. Identify audiobook files (.m4b, .m4a, .mp3) - supports both directories and single files
|
||||
3. Read media directory from database config `media_dir`
|
||||
4. Create `[media_dir]/[Author]/[Title (Year) ASIN]/`
|
||||
3. Read media directory and path template from database config (`media_dir`, `audiobook_path_template`)
|
||||
4. Apply template to create target path: `[media_dir]/[template result]/`
|
||||
5. **Copy** files (not move - originals stay for seeding)
|
||||
6. **Tag metadata** (if enabled) - writes correct title, author, narrator, ASIN to audio files
|
||||
7. Copy cover art if found, else download from Audible
|
||||
@@ -191,7 +197,10 @@ async function organize(
|
||||
## Configuration
|
||||
|
||||
- **Media directory:** Read from database config key `media_dir` (set in setup wizard or settings)
|
||||
- **Fallback:** `/media/audiobooks` if not configured
|
||||
- **Path template:** Read from database config key `audiobook_path_template` (default: `{author}/{title} {asin}`)
|
||||
- **Metadata tagging:** `metadata_tagging_enabled` (boolean, default: true)
|
||||
- **Chapter merging:** `chapter_merging_enabled` (boolean, default: false)
|
||||
- **Fallback:** `/media/audiobooks` if media_dir not configured
|
||||
- **Temp directory:** `/tmp/readmeabook` (or `TEMP_DIR` env var)
|
||||
|
||||
## Fixed Issues ✅
|
||||
|
||||
@@ -68,7 +68,7 @@ src/app/admin/settings/
|
||||
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
|
||||
4. **Download Client** - Type, URL, credentials (masked)
|
||||
5. **Paths** - Download + media directories
|
||||
5. **Paths** - Download + media directories, audiobook organization template, metadata tagging toggle, chapter merging toggle
|
||||
6. **BookDate** - AI provider, API key (encrypted), model selection, library scope, custom prompt, swipe history
|
||||
|
||||
## Audible Region
|
||||
@@ -98,6 +98,34 @@ src/app/admin/settings/
|
||||
- **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.
|
||||
@@ -160,7 +188,7 @@ src/app/admin/settings/
|
||||
- Plex: URL or token modified
|
||||
- Prowlarr: URL or API key modified (NOT indexer config)
|
||||
- Download Client: URL, username, or password modified
|
||||
- Paths: Directory paths modified
|
||||
- Paths: Directory paths or template modified
|
||||
|
||||
## API Endpoints
|
||||
|
||||
@@ -197,14 +225,15 @@ src/app/admin/settings/
|
||||
- Requires prior successful test if credentials changed
|
||||
|
||||
**PUT /api/admin/settings/paths**
|
||||
- Updates paths
|
||||
- Requires prior successful test if paths changed
|
||||
- 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 (no sensitive data, reuses wizard endpoint)
|
||||
- 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)
|
||||
@@ -237,7 +266,7 @@ src/app/admin/settings/
|
||||
**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' or 'transmission'
|
||||
**Paths:** Absolute paths, exist or creatable, writable, cannot be same directory
|
||||
**Paths:** Absolute paths, exist or creatable, writable, cannot be same directory, template must contain `{author}` or `{title}`
|
||||
|
||||
## Tech Stack
|
||||
|
||||
|
||||
Reference in New Issue
Block a user