mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
1cefa437b7
Introduces `asin` and `isbn` fields to the PlexLibrary schema and database, with migration and indexing for fast lookups. Updates scan and recently-added processors to persist ASIN/ISBN from both Plex and Audiobookshelf backends. Enhances matching logic to prioritize exact ASIN matches using the new fields, improving match accuracy for Audiobookshelf users. Also includes minor improvements: fixes cover art handling for cached thumbnails, adds download URL validation in Prowlarr and qBittorrent integrations, and updates documentation to reflect these changes.
6.4 KiB
6.4 KiB
Database Schema
Status: ✅ Implemented
PostgreSQL database storing users, audiobooks, requests, downloads, configuration, and jobs.
Setup: Automatically created on container startup via prisma db push (syncs schema directly to DB without migration files).
Tables
Users
id(UUID PK),plex_id(unique),plex_username,plex_email,role('user'|'admin')is_setup_admin(bool, default false) - First admin created during setup, role protected from changesavatar_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)
- BookDate per-user preferences:
bookdate_library_scope('full'|'rated', default 'full') - Library scope for recommendationsbookdate_custom_prompt(text, optional, max 1000 chars) - Custom preferences for AIbookdate_onboarding_complete(bool, default false) - Whether user has completed BookDate onboarding
- Indexes:
plex_id,role
Audible_Cache
id(UUID PK),asin(unique, Audible ID),title,author,narrator,descriptioncover_art_url,cached_cover_path(local thumbnail path),duration_minutes,release_date,rating,genres(JSONB)- Discovery:
is_popular(bool),is_new_release(bool),popular_rank,new_release_rank last_synced_at,created_at,updated_at- Indexes:
asin,title,author,is_popular,is_new_release,popular_rank,new_release_rank - Purpose: Cached Audible metadata (popular/new releases), thumbnails stored locally in
/app/cache/thumbnails
Plex_Library (Library Cache)
id(UUID PK),plex_guid(unique, external ID from Plex or Audiobookshelf),plex_rating_keytitle,author,narrator,summary,duration(milliseconds),year,user_rating(0-10 scale)- Universal identifiers:
asin(Audible ASIN),isbn(ISBN-10 or ISBN-13) file_path,thumb_url,plex_library_id,added_atlast_scanned_at,created_at,updated_at- Indexes:
plex_guid,title,author,plex_library_id,asin,isbn - Purpose: Universal library cache for both Plex and Audiobookshelf backends
- ASIN/ISBN fields: Enable accurate matching across backends
- Plex: ASIN extracted from Plex GUID (e.g.,
com.plexapp.agents.audible://B00ABC123) + stored in dedicated field - Audiobookshelf: ASIN/ISBN retrieved directly from ABS metadata + stored in dedicated fields
- Matching: Prioritizes exact ASIN/ISBN matches (100% confidence) before fuzzy title/author matching
- Plex: ASIN extracted from Plex GUID (e.g.,
Audiobooks
id(UUID PK),audible_asin(nullable),title,author,narrator,descriptioncover_art_url,file_path,file_format,file_size_bytesplex_guid(nullable),plex_library_id(nullable)status('requested'|'downloading'|'processing'|'completed'|'failed')created_at,updated_at,completed_at- Indexes:
audible_asin,plex_guid,title,author,status - Purpose: User-requested audiobooks only (created on request)
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)
progress(0-100),priority,error_messagesearch_attempts,download_attempts,import_attempts,max_import_retries(default 5)last_search_at,last_import_at,created_at,updated_at,completed_at- Unique:
(user_id, audiobook_id) - Indexes:
user_id,audiobook_id,status,created_at DESC
Download_History
id(UUID PK),request_id(FK),indexer_name,torrent_name,torrent_hashtorrent_size_bytes,magnet_link,torrent_url,seeders,leechersquality_score,selected(bool),download_client,download_client_iddownload_status('queued'|'downloading'|'completed'|'failed'|'stalled')download_error,started_at,completed_at,created_at- Indexes:
request_id,selected,created_at DESC
Configuration
id(UUID PK),key(unique),value,encrypted(bool),category,descriptioncreated_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
Jobs
id(UUID PK),bull_job_id,request_id(FK nullable)type('search_indexers'|'monitor_download'|'organize_files'|'scan_plex'|'match_plex'|'plex_library_scan'|'plex_recently_added_check'|'audible_refresh'|'retry_missing_torrents'|'retry_failed_imports'|'cleanup_seeded_torrents'|'monitor_rss_feeds')status('pending'|'active'|'completed'|'failed'|'delayed'|'stuck')priority,attempts,max_attempts(default 3)payload(JSONB),result(JSONB),error_message,stack_tracestarted_at,completed_at,created_at,updated_at- Indexes:
request_id,type,status,created_at DESC
Job_Events
id(UUID PK),job_id(FK → Jobs, CASCADE delete)level('info'|'warn'|'error')context(processor name: OrganizeFiles, FileOrganizer, MonitorDownload, etc.)message(event description)metadata(JSONB, optional structured data)created_at(timestamp)- Indexes:
job_id,created_at - Purpose: Store detailed event logs for job operations (shown in admin logs UI)
Relationships
- User → Requests (1:many)
- Audiobook → Requests (1:many)
- Request → Download History (1:many)
- Request → Jobs (1:many, nullable)
- Job → Job Events (1:many, CASCADE delete)
Setup Strategy
Approach: Schema sync via prisma db push
- Prisma schema is source of truth
- On startup: sync schema → database
- Idempotent (safe to run multiple times)
- No migration files needed
- Generates Prisma client after sync
ORM: Prisma 6.x
- Type-safe queries
- Auto-generated types
- Connection pooling
- Client output:
src/generated/prisma
Security
Encryption at Rest (AES-256):
- User auth tokens
- API keys/passwords in Configuration
- Download client credentials
SQL Injection: Parameterized queries only via ORM
Access Control: Row-level (users see only their requests), admins have full access
Tech Stack
- PostgreSQL 16+
- Prisma 6.x
prisma db push(schema sync)- Node.js crypto (encryption)