mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
5.0 KiB
5.0 KiB
Bulk Import Feature
Status: ✅ Implemented | Admin-only | Multi-step wizard modal
Overview
Lets admins scan a server folder recursively, discover audiobook subfolders, match against Audible, review matches, and import selected books via the existing manual import pipeline.
Flow
- Select Folder — Browse base folders (Downloads, Media Library, Book Drop), pick scan root
- Scan & Match — Recursively discover audiobook folders (max 10 levels), read metadata via ffprobe, search Audible per book (1.5s rate limit)
- Review & Import — Scrollable list with skip toggles, library status, confidence badges; Start Import queues organize_files jobs
Key Details
- Access: Admin-only, modal opened from admin dashboard Quick Actions
- Audio detection: Uses
AUDIO_EXTENSIONSfromsrc/lib/constants/audio-formats.ts - Audiobook boundary: A folder containing audio files = one audiobook. Files with matching metadata tags are grouped by title+author+narrator. Files with no metadata title tag are all grouped together per folder (one entry, not one per file).
- Metadata extraction: ffprobe reads
album(title),album_artist(author),composer(narrator) from all audio files in folder - Search term fallback chain (when no
albumtag):- ASIN in folder name — scans folder name for pattern
B[A-Z0-9]{9}bounded by bracket/paren/space; if found, uses direct ASIN lookup instead of text search; no badge shown - Folder name — cleaned (strips bracketed ASIN/year, underscores→spaces); skipped if generic (CD1, Disc 2, Part 3, Vol 1, etc.); shows "Low Confidence" badge
- First file name — last resort; shows "Low Confidence" badge
- ASIN in folder name — scans folder name for pattern
- Generic folder detection:
/^(cd|disc|disk|part|vol(ume)?)\s*\d+$/i— these names are skipped as search terms - Author/narrator dedup: Splits on
,;&delimiters, removes names appearing in both fields - Scan depth: Max 10 levels recursion
- Rate limiting: 1.5s delay between Audible searches (same as existing scraping rate limit)
- Library check: Uses
findPlexMatch()for ASIN-based availability detection - Import: Reuses existing
organize_filesjob queue (same as manual import) - No new database tables — all state is ephemeral during wizard session
API Endpoints
POST /api/admin/bulk-import/scan (SSE stream)
- Body:
{ rootPath: string } - Path validation: must be within download_dir, media_dir, or /bookdrop
- Streams events:
progress,discovery_complete,matching,book_matched,complete,error - Each
book_matchedevent includes: folderPath, match (Audible data), inLibrary, hasActiveRequest, metadataSource
POST /api/admin/bulk-import/execute
- Body:
{ imports: Array<{ folderPath: string, asin: string }> } - Creates audiobook records + requests, queues organize_files jobs
- Returns:
{ success, results[], summary: { total, succeeded, failed } }
SSE Event Types
| Event | Data | When |
|---|---|---|
progress |
{ phase, foldersScanned, audiobooksFound, currentFolder } |
During folder discovery |
discovery_complete |
{ totalFound, message } |
All folders scanned |
matching |
{ current, total, folderName, searchTerm } |
Before each Audible search |
book_matched |
Full book result with match data | After each Audible search |
complete |
{ audiobooks[], totalFound, matched, inLibrary } |
All matching done |
error |
{ message } |
On failure |
UI States
| State | Visual |
|---|---|
| Normal (will import) | Full opacity, blue toggle ON |
| Skipped by user | 40% opacity, gray toggle OFF |
| Already in library | 40% opacity, green "In Library" badge, toggle disabled |
| Active request exists | 40% opacity, purple "Requested" badge, toggle disabled |
| No Audible match | Red "No Match" badge, folder name shown, pre-skipped |
| ASIN extracted from folder name | No badge (high confidence — direct ASIN lookup) |
| Low confidence (folder name or file name fallback, no ASIN) | Amber "Low Confidence" badge |
Files
Backend:
src/lib/utils/bulk-import-scanner.ts— Folder discovery + ffprobe metadatasrc/app/api/admin/bulk-import/scan/route.ts— SSE scan endpointsrc/app/api/admin/bulk-import/execute/route.ts— Batch import endpoint
Frontend:
src/components/admin/BulkImportWizard.tsx— Modal orchestratorsrc/components/admin/bulk-import/types.ts— Shared typessrc/components/admin/bulk-import/ScanFolderStep.tsx— Folder browsersrc/components/admin/bulk-import/ScanProgressStep.tsx— Progress displaysrc/components/admin/bulk-import/MatchReviewStep.tsx— Review list + import
Modified:
src/app/admin/page.tsx— Added Bulk Import quick action + modal
Related
- Manual Import — Single-book import (reused pipeline)
- File Organization — organize_files job
- Audible Integration — Search/scraping
- Background Jobs — Job queue system