mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 04:40:09 +00:00
d617e26c92
This update enhances audiobook file organization by including the ASIN in folder names and embedding it as a custom metadata tag in audio files (M4B/M4A/MP3). Documentation is updated to reflect the new folder naming convention and metadata tagging. Additionally, local login and registration can now be disabled via an environment variable, and the interactive torrent search modal allows custom search titles for all modes.
161 lines
5.6 KiB
Markdown
161 lines
5.6 KiB
Markdown
# File Organization System
|
|
|
|
**Status:** ✅ Implemented
|
|
|
|
Copies completed downloads to standardized directory structure for Plex. Automatically tags audio files with correct metadata. Originals kept for seeding, cleaned up by scheduled job after requirements met.
|
|
|
|
## Target Structure
|
|
|
|
Target directory read from database config `media_dir` (configurable in setup wizard and settings).
|
|
|
|
```
|
|
[media_dir]/
|
|
└── Author Name/
|
|
└── Book Title (Year) ASIN/
|
|
├── Book Title.m4b
|
|
└── cover.jpg
|
|
```
|
|
|
|
**Folder naming format:**
|
|
- 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)
|
|
|
|
## 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]/`
|
|
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
|
|
8. Originals remain until seeding requirements met
|
|
|
|
## Metadata Tagging
|
|
|
|
**Status:** ✅ Implemented
|
|
|
|
**Purpose:** Automatically writes correct metadata to audio files during file organization to improve Plex matching accuracy.
|
|
|
|
**Supported Formats:**
|
|
- m4b, m4a, mp4 (AAC audiobooks)
|
|
- mp3 (ID3v2 tags)
|
|
|
|
**Metadata Written:**
|
|
- `title` - Book title
|
|
- `album` - Book title (PRIMARY field for Plex matching)
|
|
- `album_artist` - Author (PRIMARY field for Plex matching)
|
|
- `artist` - Author (fallback)
|
|
- `composer` - Narrator (standard audiobook field)
|
|
- `date` - Year
|
|
- `ASIN` - Audible ASIN (custom tag)
|
|
- M4B/M4A/MP4: `----:com.apple.iTunes:ASIN`
|
|
- MP3: Custom ID3v2 tag
|
|
|
|
**Note:** ASIN is a custom metadata tag and may not appear in standard file properties viewers (Windows/macOS/Linux). Use specialized tools to verify:
|
|
```bash
|
|
# Verify ASIN metadata with ffprobe
|
|
ffprobe -v quiet -print_format json -show_format "audiobook.m4b" | grep -i asin
|
|
|
|
# Or use exiftool
|
|
exiftool "audiobook.m4b" | grep -i asin
|
|
```
|
|
|
|
**Configuration:**
|
|
- Key: `metadata_tagging_enabled` (Configuration table)
|
|
- Default: `true`
|
|
- Configurable in: Setup wizard (Paths step), Admin settings (Paths tab)
|
|
|
|
**Implementation:**
|
|
- Uses ffmpeg with `-codec copy` (no re-encoding, metadata only)
|
|
- Fast (no audio transcoding)
|
|
- Lossless (original audio preserved)
|
|
- Runs after file copy, before cover art download
|
|
- Non-blocking (errors don't fail file organization)
|
|
- Logs success/failure per file
|
|
|
|
**Benefits:**
|
|
- Fixes torrents with missing/incorrect metadata
|
|
- Ensures Plex can match audiobooks correctly
|
|
- Writes metadata from Audible/Audnexus (known accurate)
|
|
- Prevents "[Various Albums]" and other metadata issues
|
|
- Embeds ASIN directly in audio files for better identification and matching
|
|
|
|
**Tech Stack:**
|
|
- ffmpeg (system dependency - included in Docker image)
|
|
- `src/lib/utils/metadata-tagger.ts` - Tagging utility
|
|
- Integrated into `src/lib/utils/file-organizer.ts`
|
|
|
|
**Requirements:**
|
|
- ffmpeg must be installed in the container
|
|
- **Multi-container setup** (`Dockerfile`): Added at line 56 via `apk add ffmpeg`
|
|
- **Unified setup** (`dockerfile.unified`): Added at line 16 via `apt-get install ffmpeg`
|
|
- **Verify installation:**
|
|
- Multi-container: `docker exec readmeabook ffmpeg -version`
|
|
- Unified: `docker exec readmeabook-unified ffmpeg -version`
|
|
|
|
## Seeding Support
|
|
|
|
**Config:** `seeding_time_minutes` (0 = unlimited, never cleanup)
|
|
|
|
**Cleanup Job:** `cleanup_seeded_torrents` (every 30 mins)
|
|
1. Check 'available' and 'downloaded' status requests with download history
|
|
2. Query qBittorrent for actual `seeding_time` field
|
|
3. Delete torrent + files only after requirement met
|
|
4. Respects config (0 = never cleanup)
|
|
|
|
## Interface
|
|
|
|
```typescript
|
|
interface OrganizationResult {
|
|
success: boolean;
|
|
targetPath: string;
|
|
filesMovedCount: number;
|
|
errors: string[];
|
|
audioFiles: string[];
|
|
coverArtFile?: string;
|
|
}
|
|
|
|
async function organize(
|
|
downloadPath: string,
|
|
audiobook: {title: string, author: string, year?: number, coverArtUrl?: string, asin?: string}
|
|
): Promise<OrganizationResult>;
|
|
```
|
|
|
|
## Path Sanitization
|
|
|
|
- Remove invalid chars: `<>:"/\|?*`
|
|
- Trim dots/spaces
|
|
- Collapse multiple spaces
|
|
- Limit to 200 chars
|
|
- Example: `Author: The <Best>! Book?` → `Author The Best! Book`
|
|
|
|
## Configuration
|
|
|
|
- **Media directory:** Read from database config key `media_dir` (set in setup wizard or settings)
|
|
- **Fallback:** `/media/audiobooks` if not configured
|
|
- **Temp directory:** `/tmp/readmeabook` (or `TEMP_DIR` env var)
|
|
|
|
## Fixed Issues ✅
|
|
|
|
**1. EPERM errors** - Fixed with `fs.readFile/writeFile` instead of `copyFile`
|
|
**2. Immediate deletion** - Changed to copy-only, scheduled cleanup after seeding
|
|
**3. Files moved not copied** - Now copies to support seeding
|
|
**4. Single file downloads** - Now supports files directly in downloads folder (not just directories)
|
|
**5. Hardcoded media path** - Now reads `media_dir` from database config instead of hardcoded `/media/audiobooks`
|
|
**6. Invalid URL error for cached cover art** - Fixed by detecting local cached thumbnails (`/api/cache/thumbnails/*`) and copying from `/app/cache/thumbnails/` instead of attempting HTTP download
|
|
|
|
## Tech Stack
|
|
|
|
- Node.js `fs/promises`
|
|
- `path` module
|
|
- axios (cover art download)
|