# Audible API — Metadata Retrieval for Audiobooks

Use the Audible catalog API to fetch metadata for audiobooks when known (Audible ASIN) or unknown (title search). Then embed cover art and tags with mutagen.

## Finding an ASIN

### By Audible.de product URL
The ASIN is the last path segment: `audible.de/pd/.../B0DQPGQ5VN` → ASIN = `B0DQPGQ5VN`.

### By title search
```bash
curl -s "https://api.audible.de/1.0/catalog/products?title=Karl+Marx+Fluch&num_results=5&response_groups=product_desc,contributors,media,product_attrs,rating" \
  -H "User-Agent: Audible/3.50.0 (iPhone; iOS 16.0)"
```
The response returns matching products with their ASINs.

**PITFALL (June 2026):** The search API is degraded — returns irrelevant results regardless of query. **Workaround:** scrape the search page HTML instead:
```python
import urllib.request, re
query = "In einem Zug Daniel Glattauer Hörbuch"
url = f"https://www.audible.de/search?keywords={urllib.parse.quote(query)}"
html = urllib.request.urlopen(urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})).read().decode()
asins = re.findall(r'asin["\s:=]+["\']([A-Z0-9]{10})["\']', html)
# First match is usually the correct product
```

## API Endpoints

### Product detail (by ASIN)
```
GET https://api.audible.de/1.0/catalog/products/{ASIN}?response_groups=product_desc,contributors,media,product_attrs,product_extended_attrs,category_ladders,rating
```

### Required headers
```
User-Agent: Audible/3.50.0 (iPhone; iOS 16.0)
```
Without this User-Agent, the API returns minimal data or errors.

### Marketplace selection
- `.de` for German audiobooks (Audible.de)
- `.com` for English/international (returns different catalog)

The `.com` endpoint returns English-only results. For German books, **always use `api.audible.de`**.

## Response Fields (key ones)

| Field | Path | Notes |
|-------|------|-------|
| ASIN | `product.asin` | Unique ID |
| Title | `product.title` | e.g. "Verbrechen und Strafe" |
| Authors | `product.authors[].name` | Can include translator as co-author |
| Narrators | `product.narrators[].name` | Multiple narrators possible |
| Publisher | `product.publisher_name` | |
| Runtime | `product.runtime_length_min` | Minutes (integer) |
| Release date | `product.release_date` | YYYY-MM-DD |
| Language | `product.language` | "german" / "english" |
| Description | `product.publisher_summary` | HTML in `<p>` tags |
| Cover URL | `product.product_images["500"]` | 500px JPEG |
| Genre | `product.category_ladders[].ladder[].name` | Hierarchical |
| Rating | `product.rating.overall_distribution.average_rating` | 1-5 float |
| Format type | `product.format_type` | "unabridged" / "abridged" |

## Cover Image Download

```bash
curl -sL "https://m.media-amazon.com/images/I/51Kh0hWkYgL._SL500_.jpg" -o cover.jpg
```

Replace `_SL500_` with `_SL1500_` in the URL for higher resolution if needed.

## Mutagen ID3 Tag Mapping

| ID3 Frame | Mutagen Class | Our Field |
|-----------|---------------|-----------|
| TIT2 | `TIT2` | Title → Album |
| TPE1 | `TPE1` | Author |
| TPE2 | `TPE2` | Author (Album Artist) |
| TALB | `TALB` | Title |
| TCOM | `TCOM` | Narrator(s) |
| COMM | `COMM` | Description |
| TPUB | `TPUB` | Publisher |
| TDRC | `TDRC` | Release Year |
| TCON | `TCON` | Genre |
| APIC | `APIC` | Cover Image |

### Cover embedding
```python
from mutagen.id3 import APIC
with open("cover.jpg", "rb") as f:
    cover_data = f.read()
audio.tags.add(APIC(encoding=3, mime="image/jpeg", type=3, desc="Cover", data=cover_data))
```

## Pitfalls

- **`api.audible.de` with wrong User-Agent** returns only `{"product": {"asin": "...", "response_groups": ["always-returned"]}}` — no real data. Always use the iPhone User-Agent.
- **Audible API degraded (June 2026):** The search endpoint (`/catalog/products?q=...`) returns irrelevant results regardless of query. The product detail endpoint (`/catalog/products/{ASIN}`) returns empty `contributors` arrays. **Workaround:** scrape the product page HTML at `https://www.audible.de/pd/{ASIN}` for author, narrator, and description using regex on embedded JSON/HTML.
- **Files on `/media/HDD_1TB/` are root-owned** — mutagen needs write access. Run `sudo chown az-a:samba *.mp3 && sudo chmod 664 *.mp3` before tagging.
- **`sudo python3` can't find mutagen:** mutagen is installed at `/DATA/.local/lib/python3.12/site-packages/` but sudo drops the user's Python path. Use `sudo PYTHONPATH=/DATA/.local/lib/python3.12/site-packages python3 script.py`.
- **Cover download to `/media/HDD_1TB/` needs sudo** — either `sudo mkdir` the target dir first, or download to `/tmp/` and move.
- **`ffprobe` reads tags** — after tagging, verify with `ffprobe -v quiet -print_format json -show_format file.mp3`.
- **`Schuld und Sühne` = `Verbrechen und Strafe`** — same book, Audible uses the newer Geier translation title. Search by ASIN not title to disambiguate.
