{"id":45634498,"url":"https://github.com/rodaddy/audiobook-pipeline","last_synced_at":"2026-02-24T01:35:41.375Z","repository":{"id":339812867,"uuid":"1163421258","full_name":"rodaddy/audiobook-pipeline","owner":"rodaddy","description":"Convert audio files to chaptered M4B audiobooks with rich metadata from the Audible catalog","archived":false,"fork":false,"pushed_at":"2026-02-21T17:54:36.000Z","size":321,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-21T23:25:57.703Z","etag":null,"topics":["audible","audiobook","audiobook-converter","audiobookshelf","bash","ffmpeg","m4b","metadata","plex","tone"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rodaddy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"rodaddy","buy_me_a_coffee":"rodaddy"}},"created_at":"2026-02-21T15:57:54.000Z","updated_at":"2026-02-21T17:54:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rodaddy/audiobook-pipeline","commit_stats":null,"previous_names":["rodaddy/audiobook-pipeline"],"tags_count":1,"template":true,"template_full_name":null,"purl":"pkg:github/rodaddy/audiobook-pipeline","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodaddy%2Faudiobook-pipeline","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodaddy%2Faudiobook-pipeline/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodaddy%2Faudiobook-pipeline/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodaddy%2Faudiobook-pipeline/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rodaddy","download_url":"https://codeload.github.com/rodaddy/audiobook-pipeline/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodaddy%2Faudiobook-pipeline/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29766919,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-24T01:28:30.166Z","status":"ssl_error","status_checked_at":"2026-02-24T01:28:27.518Z","response_time":90,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audible","audiobook","audiobook-converter","audiobookshelf","bash","ffmpeg","m4b","metadata","plex","tone"],"created_at":"2026-02-24T01:35:40.781Z","updated_at":"2026-02-24T01:35:41.367Z","avatar_url":"https://github.com/rodaddy.png","language":"Shell","funding_links":["https://github.com/sponsors/rodaddy","https://buymeacoffee.com/rodaddy"],"categories":[],"sub_categories":[],"readme":"# audiobook-pipeline\n\nConvert audio files to chaptered M4B audiobooks with rich metadata from the Audible catalog.\n\n## Features\n\n- **Multi-format input** -- MP3, FLAC, OGG, M4A, WMA\n- **Chaptered M4B output** -- one file per book with chapter markers from source files\n- **Dual metadata sources** -- Audible catalog API (primary) with Audnexus fallback\n- **Rich metadata** -- cover art (up to 2400px), author/narrator, series info, subtitle, copyright, publisher, rating, genre taxonomy, ISBN\n- **Accurate chapters** -- Audible API provides official chapter markers with exact timestamps\n- **Plex-ready organization** -- `Author/Book (Year)/Book.m4b` folder structure\n- **M4B enrichment** -- fix metadata and organize existing M4B files (skip conversion)\n- **Idempotent processing** -- SQLite-based state tracking with automatic resume\n- **Automation ready** -- Readarr webhook, cron scanner, batch processing with `--no-lock`\n- **Error recovery** -- categorized failures, automatic retries, failed/ directory quarantine\n- **Hardware-accelerated encoding** -- AudioToolbox (macOS) when available, software AAC fallback\n\n## Quick Start\n\n```bash\n# Clone and configure\ngit clone https://github.com/rodaddy/audiobook-pipeline.git\ncd audiobook-pipeline\ncp config.env.example config.env\n# Edit config.env -- set your paths\n\n# Install dependencies (see Installation section)\n\n# Convert a directory of MP3s to M4B\nuv run audiobook-convert /path/to/audiobook-mp3s/\n\n# Batch convert multiple books (CPU-aware parallel processing)\nuv run audiobook-convert --mode convert /path/to/incoming/\n```\n\n## Pipeline Levels\n\nThe pipeline supports four intelligence tiers, configured via `PIPELINE_LEVEL` in `.env` or `--level` on the CLI:\n\n| Level | Convert | Metadata | Organize | AI | Use case |\n|-------|---------|----------|----------|----|----------|\n| `simple` | Yes | Audible/Audnexus API | No -- m4b stays in source dir | None | \"Just give me a tagged m4b\" |\n| `normal` | Yes | Audible/Audnexus API | Best-effort, fallback `_unsorted/` | None | \"Try to file it, don't overthink\" |\n| `ai` | Yes | API + LLM disambiguation | Full library placement | LLM resolves conflicts | Current `--ai-all` behavior |\n| `full` | Yes | API + LLM | Interactive agent-guided | Agent walks user through issues | See `docs/install.md` |\n\n```bash\n# Set in .env\nPIPELINE_LEVEL=normal\n\n# Or override per-run\nuv run audiobook-convert --level simple /path/to/book/\n```\n\nNotes:\n- `--reorganize` and `--ai-all` force level to `ai` minimum (with a warning if lower)\n- `simple` and `normal` levels never call the LLM, even if `PIPELINE_LLM_BASE_URL` is configured\n- `full` level behaves identically to `ai` in the pipeline -- the difference is the interactive agent guide (`.claude/agents/audiobook-guide.md`)\n\n## Installation\n\n### Dependencies\n\n| Tool | Purpose | macOS Install | Linux Install |\n|------|---------|---------------|---------------|\n| `ffmpeg` | Audio concat + AAC encoding + metadata tagging | `brew install ffmpeg` | `apt install ffmpeg` |\n\n### Setup\n\n```bash\ngit clone https://github.com/rodaddy/audiobook-pipeline.git\ncd audiobook-pipeline\ncp config.env.example config.env\n```\n\nEdit `config.env` to configure paths for your system. At minimum:\n- `WORK_DIR` -- temporary processing space\n- `NFS_OUTPUT_DIR` -- your Plex/Audiobookshelf library root\n\n## Configuration\n\nCopy `config.env.example` to `config.env` and customize for your environment.\n\n### Configuration Variables\n\n**Directories**\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `WORK_DIR` | `/var/lib/audiobook-pipeline/work` | Temporary processing workspace |\n| `OUTPUT_DIR` | `/var/lib/audiobook-pipeline/output` | Local output before NFS move |\n| `LOG_DIR` | `/var/log/audiobook-pipeline` | Pipeline logs |\n| `NFS_OUTPUT_DIR` | `/mnt/media/AudioBooks` | Library root for organized output (Plex/Audiobookshelf) |\n| `ARCHIVE_DIR` | `/var/lib/audiobook-pipeline/archive` | Archive original source files after processing |\n\n**Encoding**\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MAX_BITRATE` | `128` | Cap output bitrate (kbps). Source bitrate matched up to this limit. |\n| `CHANNELS` | `1` | Audio channels: 1=mono (recommended for speech), 2=stereo |\n\n**Metadata**\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `METADATA_SOURCE` | `audible` | Primary metadata source: `audible` or `audnexus` (see below) |\n| `AUDIBLE_REGION` | `com` | Audible API region (see Region Configuration below) |\n| `AUDNEXUS_REGION` | `us` | Audnexus fallback region: `us`, `uk`, `au`, `ca`, `de`, `fr`, `jp`, `in`, `it`, `es` |\n| `AUDNEXUS_CACHE_DIR` | `$WORK_DIR` | Metadata cache directory (defaults to work dir) |\n| `AUDNEXUS_CACHE_DAYS` | `30` | Cache metadata responses for N days |\n| `CHAPTER_DURATION_TOLERANCE` | `5` | Percent tolerance for chapter duration matching |\n| `METADATA_SKIP` | `false` | Set `true` to skip metadata enrichment entirely |\n| `FORCE_METADATA` | `false` | Set `true` to re-fetch metadata even if cached |\n\n**Automation**\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `INCOMING_DIR` | `/mnt/media/AudioBooks/_incoming` | Cron scanner watches this directory for new books |\n| `QUEUE_DIR` | `/var/lib/audiobook-pipeline/queue` | Queue directory for automation webhooks |\n| `PROCESSING_DIR` | `/var/lib/audiobook-pipeline/processing` | Active processing marker directory |\n| `COMPLETED_DIR` | `/var/lib/audiobook-pipeline/completed` | Completed book tracking |\n| `FAILED_DIR` | `/var/lib/audiobook-pipeline/failed` | Quarantine directory for permanent failures |\n| `PIPELINE_BIN` | `/opt/audiobook-pipeline/bin/audiobook-convert` | Path to conversion script for automation |\n| `STABILITY_THRESHOLD` | `120` | Seconds -- cron scanner skips recently modified books |\n\n**Error Recovery**\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MAX_RETRIES` | `3` | Retry attempts before quarantine to `failed/` |\n| `FAILURE_WEBHOOK_URL` | _(empty)_ | Slack/Discord webhook for failure notifications |\n\n**Permissions**\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `FILE_OWNER` | _(empty)_ | chown target for output files (e.g., `1000:1000`). Leave empty to skip. |\n| `FILE_MODE` | `644` | File permissions for output M4B files |\n| `DIR_MODE` | `755` | Directory permissions for organized folders |\n\n**Behavior**\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `DRY_RUN` | `false` | Preview mode -- show what would happen without making changes |\n| `FORCE` | `false` | Re-process even if already completed |\n| `VERBOSE` | `false` | Enable debug-level logging |\n| `CLEANUP_WORK_DIR` | `true` | Delete work directory after successful completion |\n| `LOG_LEVEL` | `INFO` | Log verbosity: `DEBUG`, `INFO`, `WARN`, `ERROR` |\n\n### Metadata Source\n\nThe pipeline supports two metadata sources with automatic fallback:\n\n#### `METADATA_SOURCE=audible` (default)\n\nFetches metadata from the **Audible catalog API** and normalizes to Audnexus-compatible format. Provides richer metadata:\n\n- **Subtitle** -- book subtitle (not available in Audnexus)\n- **Copyright** -- copyright statement with year\n- **Publisher** -- publishing house name\n- **ISBN** -- 10-digit ISBN (when available)\n- **Rating** -- Audible customer rating (0.0-5.0)\n- **Genre path** -- full category taxonomy (e.g., \"Fiction / Fantasy / Epic\")\n- **Cover art** -- up to 2400x2400px (vs 500px from Audnexus)\n- **Official chapters** -- chapter markers with exact timestamps from Audible's production data\n\nFalls back to Audnexus if Audible API fails or returns no results.\n\n#### `METADATA_SOURCE=audnexus`\n\nUses the **Audnexus API** directly (community-maintained Audible metadata mirror). Good for:\n\n- **Plex users** with the Audnexus metadata agent installed\n- **Rate limit avoidance** if processing large batches\n- **Older books** that may have been removed from active Audible catalog\n\nFalls back to Audible API if Audnexus returns no results.\n\n#### Inline Override\n\nOverride metadata source for a single run without editing `config.env`:\n\n```bash\nMETADATA_SOURCE=audnexus bin/audiobook-convert /path/to/book/\n```\n\n### Region Configuration\n\nThe `AUDIBLE_REGION` variable controls which Audible marketplace to query. Use the domain suffix for your region:\n\n| Region | AUDIBLE_REGION | Audible URL |\n|--------|----------------|-------------|\n| United States | `com` | audible.com |\n| United Kingdom | `co.uk` | audible.co.uk |\n| Australia | `com.au` | audible.com.au |\n| Canada | `ca` | audible.ca |\n| Germany | `de` | audible.de |\n| France | `fr` | audible.fr |\n| Japan | `co.jp` | audible.co.jp |\n| India | `in` | audible.in |\n| Italy | `it` | audible.it |\n| Spain | `es` | audible.es |\n\n**Important:** The ASIN must exist in the target region's catalog. A book sold on audible.com may not be available on audible.co.uk with the same ASIN.\n\n### Metadata Fields\n\nThe pipeline writes the following metadata tags to M4B files using `ffmpeg`:\n\n| M4B Tag | ffmpeg key | Source |\n|---------|------------|--------|\n| Title | `title` | Audible/Audnexus |\n| Artist | `artist` | Author + Narrator |\n| Album Artist | `album_artist` | Author |\n| Album | `album` | Book title |\n| Composer | `composer` | Narrator |\n| Genre | `genre` | Audible categories |\n| Date | `date` | Release year |\n| Description | `description` | Publisher summary |\n| Comment | `comment` | Publisher summary |\n| Sort Album | `sort_album` | Series sort key |\n| Copyright | `copyright` | From Audible |\n| Publisher | `publisher` | From Audible |\n| Show | `show` | Series name |\n| Grouping | `grouping` | Series + Book # |\n| ASIN | `ASIN` | Audible ASIN |\n| Media Type | `media_type` | `2` (audiobook) |\n| Cover Art | embedded | Up to 2400x2400px |\n\n**Custom fields** (stored but not displayed by most players):\n\n| Field Name | Source |\n|------------|--------|\n| `AUDIBLE_ASIN` | ASIN for future re-runs |\n| `AUDIBLE_URL` | Direct link to Audible product page |\n\n## Usage\n\nEntry point: `uv run audiobook-convert`\n\n### Convert a directory of audio files\n\n```bash\n# Auto-detects directory input -\u003e convert mode\nuv run audiobook-convert /mnt/downloads/MyBook/\n\n# Batch convert with CPU-aware parallel processing\nuv run audiobook-convert --mode convert /mnt/downloads/incoming/\n\n# With options\nuv run audiobook-convert --verbose --force /mnt/downloads/MyBook/\n```\n\nPipeline stages: `validate -\u003e concat -\u003e convert -\u003e asin -\u003e metadata -\u003e organize -\u003e archive -\u003e cleanup`\n\n### Enrich an existing M4B\n\n```bash\n# Auto-detects .m4b input -\u003e enrich mode\nuv run audiobook-convert /mnt/media/untagged-book.m4b\n```\n\nSkips conversion stages. Fetches metadata from configured source and organizes into your library.\n\n### Metadata-only mode\n\n```bash\nuv run audiobook-convert --mode metadata /path/to/book.m4b\n```\n\nFetches ASIN and applies metadata (cover art, author, narrator, series) without moving the file.\n\n### Organize-only mode\n\n```bash\nuv run audiobook-convert --mode organize /path/to/book.m4b\n```\n\nMoves the file into the `Author/Book (Year)/Book.m4b` folder structure without touching metadata.\n\n### Region-specific processing\n\n```bash\n# German audiobook\nAUDIBLE_REGION=de uv run audiobook-convert /path/to/german-book/\n\n# UK audiobook\nAUDIBLE_REGION=co.uk uv run audiobook-convert /path/to/uk-book/\n```\n\n### Override metadata source\n\n```bash\n# Use Audnexus instead of Audible for this run\nMETADATA_SOURCE=audnexus uv run audiobook-convert /path/to/book.m4b\n\n# Use Audible API for UK marketplace\nAUDIBLE_REGION=co.uk METADATA_SOURCE=audible uv run audiobook-convert /path/to/book/\n```\n\n### Large library processing\n\nFor large batches (hundreds or thousands of books), the pipeline builds an in-memory index of your library once at startup, replacing per-file directory scans with O(1) dict lookups.\n\n**Add new books to an existing library:**\n\n```bash\n# Organize a staging directory into your library\nuv run audiobook-convert /path/to/new/books --mode organize --dry-run\n\n# Verify the dry-run output, then run for real\nuv run audiobook-convert /path/to/new/books --mode organize\n```\n\n**Reorganize an existing library in-place:**\n\n```bash\n# Dry-run first -- see what would move\nuv run audiobook-convert /Volumes/media_files/AudioBooks --reorganize --dry-run\n\n# Verify moves look correct, then run\nuv run audiobook-convert /Volumes/media_files/AudioBooks --reorganize\n```\n\nThe `--reorganize` flag:\n- Implies `--mode organize` and `--ai-all` (every book gets AI metadata verification)\n- Moves files instead of copying (avoids doubling library size)\n- Detects books already in the correct location and skips them\n- Cleans up empty directories left behind after moves\n- Deduplicates across source directories within a batch\n\n**Recommended workflow:**\n1. Always start with `--dry-run` to verify decisions\n2. Review the output for any unexpected moves\n3. Run without `--dry-run` when satisfied\n4. Test on a known subset before processing a full library\n\n### Batch processing\n\n```bash\n# Automatic CPU-aware parallel processing (recommended)\nuv run audiobook-convert --mode convert /mnt/downloads/batch/\n\n# Manual parallel processing (legacy)\nfor dir in /mnt/downloads/*/; do\n  uv run audiobook-convert --no-lock \"$dir\" \u0026\ndone\nwait\n```\n\n### Dry-run mode\n\n```bash\n# Preview what would happen without making changes\nuv run audiobook-convert --dry-run --verbose /mnt/downloads/MyBook/\n```\n\n### CLI flags reference\n\n```\n-m, --mode {convert,enrich,metadata,organize}  Pipeline mode (auto-detected if omitted)\n--level {simple,normal,ai,full}                Override PIPELINE_LEVEL from config\n--dry-run                                      Preview without making changes\n--force                                        Re-process even if completed\n-v, --verbose                                  Enable DEBUG logging\n-c, --config PATH                              Path to .env file\n--ai-all                                       Run AI validation on all books\n--reorganize                                   Move misplaced books (implies --ai-all)\n--verify                                       Run data quality checks after processing\n--no-lock                                      Skip file locking (manual batch mode)\n--asin TEXT                                    Override ASIN discovery\n```\n\n## Architecture\n\n```\nSOURCE_PATH (directory or .m4b file)\n    |\n    v\n┌─────────────────────────────────────────────────────────┐\n│ 01-validate  Find audio files, detect bitrate, check    │\n│              disk space, write sorted file list          │\n│                          |                               │\n│ 02-concat    Generate ffmpeg concat list + FFMETADATA1  │\n│              chapter file from per-file durations        │\n│                          |                               │\n│ 03-convert   Single-pass ffmpeg: concat + AAC encode +  │\n│              chapter inject + faststart                  │\n│                          |                               │\n│ 05-asin      Discover ASIN via folder name, Readarr     │  Stages 01-03\n│              API, or Audnexus/Audible search             │  skipped for\n│                          |                               │  M4B input\n│ 06-metadata  Fetch from Audible API (or Audnexus):      │  (enrich mode)\n│              cover art (2400px), author, narrator,       │\n│              series, subtitle, copyright, publisher,     │\n│              rating, genre path, official chapters       │\n│                          |                               │\n│ 07-organize  Create Author/Book (Year)/ structure,      │\n│              move M4B + companion files to library       │\n│                          |                               │\n│ 08-archive   Archive original source to archive/        │\n│                          |                               │\n│ 09-cleanup   Remove work directory, release locks        │\n└─────────────────────────────────────────────────────────┘\n    |\n    v\nNFS_OUTPUT_DIR/Author/Book (Year)/Book.m4b\n```\n\n### ASIN Discovery\n\nThe pipeline tries multiple sources to find an Audible ASIN (in priority order):\n\n1. **Folder name pattern match** -- `{ASIN}` or `[ASIN]` in directory name\n2. **Readarr API lookup** -- if configured, queries Readarr for the book's ASIN\n3. **Audible/Audnexus search** -- searches by title/author extracted from folder name\n4. **Manual entry prompt** -- interactive mode asks user to provide ASIN\n\n**ASIN format:** Must be the **Audible ASIN** from the audible.com (or regional) URL, NOT the Amazon product ASIN. Example:\n\n- Audible URL: `https://www.audible.com/pd/B084QHXYFP` -\u003e ASIN: `B084QHXYFP` ✅\n- Amazon URL: `https://www.amazon.com/dp/198009036X` -\u003e Product ASIN: `198009036X` ❌\n\n## Automation\n\n### Readarr Webhook\n\nTriggered when Readarr imports a new audiobook. Queues the book for processing.\n\n```bash\n# Readarr custom script (Settings -\u003e Connect -\u003e Custom Script)\n/opt/audiobook-pipeline/bin/readarr-hook.sh\n```\n\n### Cron Scanner\n\nWatches `INCOMING_DIR` for new audiobook directories. Skips recently modified books (based on `STABILITY_THRESHOLD`).\n\n```bash\n# Crontab example: run every 5 minutes\n*/5 * * * * /opt/audiobook-pipeline/bin/cron-scanner.sh\n```\n\n### Queue Processor\n\nProcesses queued books sequentially (one at a time). Run as a systemd service or cron job.\n\n```bash\n# Systemd service (recommended)\n# /etc/systemd/system/audiobook-queue.service\n[Unit]\nDescription=Audiobook Pipeline Queue Processor\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=/opt/audiobook-pipeline/bin/queue-processor.sh\nRestart=always\nUser=audiobook\nEnvironment=\"PATH=/usr/local/bin:/usr/bin:/bin\"\n\n[Install]\nWantedBy=multi-user.target\n```\n\n## Troubleshooting\n\n### No metadata found\n\n**Symptoms:** Pipeline logs `\"No metadata found for ASIN XXX\"` or `\"Audible API returned invalid or empty response\"`\n\n**Causes:**\n1. Wrong ASIN format -- used Amazon product ASIN instead of Audible ASIN\n2. Book not available in the configured region\n3. ASIN is invalid or has been removed from Audible catalog\n\n**Solutions:**\n- Verify ASIN is from the Audible URL (not Amazon). Check the URL: `https://www.audible.com/pd/[ASIN]`\n- Check if the book exists in your configured `AUDIBLE_REGION` marketplace\n- Try switching metadata source: `METADATA_SOURCE=audnexus bin/audiobook-convert ...`\n- Try a different region if the book was purchased from a different marketplace: `AUDIBLE_REGION=co.uk bin/audiobook-convert ...`\n\n### Cover art download failed\n\n**Symptoms:** `\"Failed to download cover art from Audible\"` or `\"Downloaded cover art is not a valid JPEG\"`\n\n**Causes:**\n1. Audible API rate limiting\n2. Network timeout or connection issue\n3. Invalid or missing image URL in API response\n\n**Solutions:**\n- Pipeline automatically falls back to Audnexus for cover art if Audible fails\n- Check network connectivity: `curl -I https://api.audible.com`\n- Wait a few minutes and retry -- rate limits are usually temporary\n- Verify the ASIN is correct and the book has cover art on audible.com\n\n### Chapter duration mismatch\n\n**Symptoms:** `\"Chapter duration mismatch: expected XXXms, got YYYms\"`\n\n**Causes:**\n1. Audible's official chapter markers don't match actual file duration (intro/outro credits, regional differences)\n2. Source files were trimmed or edited\n\n**Solutions:**\n- Adjust `CHAPTER_DURATION_TOLERANCE` in `config.env` (default: 5%). Try `10` or `15` for books with significant intro/outro content\n- Check source file integrity -- re-download if files were corrupted\n- Use `--verbose` to see detailed chapter timestamp comparison\n- If Audible chapters are consistently wrong for a book, fallback to file-based chapters by skipping the metadata stage: `bin/audiobook-convert --mode organize /path/to/book.m4b`\n\n### Region mismatch\n\n**Symptoms:** Metadata is incorrect, wrong narrator, or cover art doesn't match\n\n**Causes:**\n1. Book was purchased from a different regional Audible marketplace\n2. Different editions exist across regions (US vs UK narrators, abridged vs unabridged)\n\n**Solutions:**\n- Check which Audible marketplace the book was purchased from\n- Set `AUDIBLE_REGION` to match the purchase region: `AUDIBLE_REGION=co.uk bin/audiobook-convert ...`\n- Search the book on multiple regional Audible sites to find the matching ASIN\n- For UK books, use: `AUDIBLE_REGION=co.uk`\n- For German books, use: `AUDIBLE_REGION=de`\n\n### Pipeline stalls or hangs\n\n**Symptoms:** Pipeline stops responding during processing, no log output\n\n**Causes:**\n1. ffmpeg encoding stalled (rare, usually hardware codec issue)\n2. NFS mount is unresponsive\n3. Disk full\n\n**Solutions:**\n- Check disk space: `df -h $WORK_DIR $NFS_OUTPUT_DIR`\n- Verify NFS mount is accessible: `ls -la $NFS_OUTPUT_DIR`\n- Kill hung ffmpeg processes: `pkill -9 ffmpeg`\n- Check work directory for partial files: `ls -lah $WORK_DIR`\n- Enable verbose logging and retry: `uv run audiobook-convert --verbose --force /path/to/book/`\n\n### Permission denied errors\n\n**Symptoms:** `\"Permission denied\"` when writing to output directory or setting file ownership\n\n**Causes:**\n1. Pipeline user doesn't have write access to `NFS_OUTPUT_DIR`\n2. `FILE_OWNER` is set but pipeline user can't chown files\n\n**Solutions:**\n- Verify write permissions: `touch $NFS_OUTPUT_DIR/test.txt \u0026\u0026 rm $NFS_OUTPUT_DIR/test.txt`\n- If using NFS, check export options (no_root_squash, user mapping)\n- If `FILE_OWNER` is set, ensure pipeline runs as root or the target user\n- For non-root setups, set `FILE_OWNER=\"\"` in `config.env` to skip chown\n\n## License\n\nMIT -- see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frodaddy%2Faudiobook-pipeline","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frodaddy%2Faudiobook-pipeline","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frodaddy%2Faudiobook-pipeline/lists"}