Files
ReadMeABook/documentation/features/bulk-import.md
T

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

  1. Select Folder — Browse base folders (Downloads, Media Library, Book Drop), pick scan root
  2. Scan & Match — Recursively discover audiobook folders (max 10 levels), read metadata via ffprobe, search Audible per book (1.5s rate limit)
  3. 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_EXTENSIONS from src/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 album tag):
    1. 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
    2. Folder name — cleaned (strips bracketed ASIN/year, underscores→spaces); skipped if generic (CD1, Disc 2, Part 3, Vol 1, etc.); shows "Low Confidence" badge
    3. First file name — last resort; shows "Low Confidence" badge
  • 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_files job 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_matched event 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 metadata
  • src/app/api/admin/bulk-import/scan/route.ts — SSE scan endpoint
  • src/app/api/admin/bulk-import/execute/route.ts — Batch import endpoint

Frontend:

  • src/components/admin/BulkImportWizard.tsx — Modal orchestrator
  • src/components/admin/bulk-import/types.ts — Shared types
  • src/components/admin/bulk-import/ScanFolderStep.tsx — Folder browser
  • src/components/admin/bulk-import/ScanProgressStep.tsx — Progress display
  • src/components/admin/bulk-import/MatchReviewStep.tsx — Review list + import

Modified:

  • src/app/admin/page.tsx — Added Bulk Import quick action + modal