Metadata-Version: 2.4
Name: kurmann-kinofilm-manager
Version: 2.1.0
Summary: CLI-Tool zur Konvertierung von DTS/DTS-HD Audiospuren in MKV-Dateien zu AC3
License: MIT License
        
        Copyright (c) 2026 Patrick Kurmann
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/kurmann/kinofilm-manager
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.12
Requires-Dist: rich>=13
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Dynamic: license-file

# kinofilm-manager

Python-CLI-Tool zur Konvertierung von DTS/DTS-HD Audiospuren in MKV-Dateien zu Dolby Digital AC3. Das Ergebnis ist eine neue MKV-Datei, die direkt in **Subler** importiert und zu M4V/MP4 für Apple TV und Infuse weiterverarbeitet werden kann. 

Für Artwork und Fanart wird die **TMDB**-API (The Movie Database) verwendet.

---

## Hintergrund und Motivation

### Das Container-Problem

Der **MP4/M4V-Container** – der native Container für Apple TV, iTunes und Infuse – unterstützt **DTS und DTS-HD nicht**. Dies ist eine Einschränkung des Container-Standards: DTS ist im MP4-Standard nicht als zulässiger Audio-Codec spezifiziert.

Wer eine Archivierungs-Pipeline aufbaut, die auf folgenden Komponenten basiert:

```
MKV (Quellformat) → Subler (Muxing/Metadaten) → M4V (Apple TV / Infuse)
```

steht vor dem Problem, dass Subler DTS-Spuren aus einem MKV **nicht direkt in einen M4V-Container übernehmen** kann. Der Workaround wäre ein manuelles Transcodieren in Subler – was aber keine Batch-Fähigkeit bietet und keinen kontrollierten Workflow erlaubt.

### Die Lösung: Zweistufiger Workflow

Der Workflow wird sauber in zwei unabhängige Schritte getrennt:

```
Schritt 1: kinofilm-manager convert
MKV (mit DTS) → MKV (mit AC3)

Schritt 2: Subler (manuell oder via SublerCLI)
MKV (mit AC3) → M4V (für Apple TV / Infuse)
```

Dadurch bleibt Subler ein reines **Muxing- und Metadaten-Tool** und kinofilm-manager übernimmt die Audiokonvertierung deterministisch und reproduzierbar.

---

## Unterstützte Audio-Codecs im M4V-Container

Zur Übersicht welche Audioformate im MP4/M4V-Container zulässig sind:

| Codec | M4V-kompatibel | Bemerkung |
|---|---|---|
| AAC | ✅ | Nativ |
| AC3 (Dolby Digital) | ✅ | Von Apple für M4V zugelassen |
| E-AC3 (Dolby Digital Plus) | ✅ | Inkl. Atmos-Metadata |
| DTS | ❌ | Nicht im MP4-Standard |
| DTS-HD HRA / MA | ❌ | Nicht im MP4-Standard |
| TrueHD | ❌ | Nicht im MP4-Standard |

---

## Voraussetzungen

- Python 3.11 oder neuer
- `ffmpeg` und `ffprobe` im PATH

**Installation ffmpeg auf macOS:**

```bash
brew install ffmpeg
```

> **Hinweis:** HandBrake nicht via Homebrew installieren – es überschreibt shared Libraries (z.B. `libvpx`) und kann zu `dyld: Library not loaded`-Fehlern bei ffmpeg führen. HandBrake stattdessen als `.app` via DMG installieren.

---

## Installation

```bash
# Via pip
pip install kurmann-kinofilm-manager

# Via uv
uv pip install kurmann-kinofilm-manager

# Für Entwicklung (lokal)
git clone https://github.com/kurmann/kinofilm-manager.git
cd kinofilm-manager
pip install -e ".[dev]"
```

---

## Verwendung

```bash
kinofilm-manager convert <input.mkv> [Optionen]
kinofilm-manager artwork fetch <video-file> [Optionen]
```

### Parameter

| Argument | Kurz | Standard | Beschreibung |
|---|---|---|---|
| `INPUT_FILE` | – | – | Pfad zur Eingabe-MKV (Pflichtfeld) |
| `--output` | `-o` | `<input>_ac3.mkv` | Pfad zur Ausgabe-MKV |
| `--bitrate` | `-b` | automatisch oder `config`-Wert | AC3-Bitrate für alle konvertierten Spuren |
| `--dry-run` / `--no-dry-run` | – | `false` oder `config`-Wert | Zeigt Analyse und FFmpeg-Befehl, erstellt keine Datei |
| `--verbose` / `--no-verbose` | `-v` | `false` oder `config`-Wert | Zusätzliche Diagnoseinformationen auf stderr |

### Beispiele

```bash
# Einfachster Aufruf – Ausgabe landet neben der Quelldatei
kinofilm-manager convert "Film (2024).mkv"

# Ausgabepfad selbst bestimmen
kinofilm-manager convert "Film (2024).mkv" -o "Film (2024)_subler.mkv"

# Erst prüfen was passieren würde (empfohlen beim ersten Test)
kinofilm-manager convert "Film (2024).mkv" --dry-run

# Eigene Bitrate (kleinere Datei, geringfügig weniger Qualität)
kinofilm-manager convert "Film (2024).mkv" --bitrate 448k

# Persistente Defaults für künftige Aufrufe setzen
kinofilm-manager config set bitrate 448k
kinofilm-manager config set verbose true

# Pipeline: Ergebnis-Pfad auf stdout, Diagnose auf stderr
output=$(kinofilm-manager convert "Film (2024).mkv")
echo "Konvertierte Datei: $output"
```

## Artwork / Fanart für Infuse

Für Infuse-kompatibles Fanart kann pro Videodatei ein Backdrop im Format
`<Titel (Jahr)-fanart.jpg>` geladen werden. Die Datei wird immer im gleichen
Verzeichnis wie die Videodatei gespeichert.

```bash
kinofilm-manager artwork fetch <video-file> [Optionen]
```

### Unterstützte Eingaben

- Primär: Dateiname im Schema `<Titel (Jahr)>.m4v`, `.mkv` oder `.mp4`
- Alternativ: `--title` und `--year`, falls der Dateiname nicht parsebar ist

### Optionen

| Option | Standard | Beschreibung |
|---|---|---|
| `--title TEXT` | aus Dateiname | Alternativer Filmtitel |
| `--year INTEGER` | aus Dateiname | Erscheinungsjahr; nur zusammen mit `--title` |
| `--dry-run` / `--no-dry-run` | `false` oder `config`-Wert | Prüft den Workflow ohne Dateiablage |
| `--language TEXT` | – | Suchsprache für TMDB, z.B. `de-DE` oder `en-US` |
| `--provider TEXT` | `tmdb` oder `config`-Wert | Artwork-Provider; aktuell wird nur `tmdb` unterstützt |
| `--tmdb-api-key TEXT` | `config`-Wert | TMDB API Key |
| `--interactive` / `--no-interactive` | `false` | Erlaubt eine manuelle Auswahl bei mehrdeutigen Treffern |

### Beispiele

```bash
# Fanart direkt neben der Videodatei speichern
kinofilm-manager artwork fetch "Frozen (2013).m4v" --tmdb-api-key "$TMDB_API_KEY"

# Parsebaren Dateinamen überschreiben
kinofilm-manager artwork fetch "film-final.mp4" --title "Frozen" --year 2013 --tmdb-api-key "$TMDB_API_KEY"

# Mehrdeutige Treffer bewusst interaktiv auswählen
kinofilm-manager artwork fetch "Frozen (2013).m4v" --interactive --tmdb-api-key "$TMDB_API_KEY"

# Workflow testen ohne Dateiablage
kinofilm-manager artwork fetch "Frozen (2013).m4v" --dry-run --tmdb-api-key "$TMDB_API_KEY"
```

### Ausgabe-Konvention für `artwork fetch`

- **stdout**: Zielpfad der Fanart-Datei
- **stderr**: Parsing-/Such-Events, Auswahlhinweise, Fehlerdiagnosen
- Ohne `--interactive` wird bei mehrdeutigen Treffern mit Exit-Code `1` abgebrochen
- Mit `--dry-run` wird der Zielpfad ausgegeben, aber keine Datei geschrieben

### Verwendete Dienste

| Dienst | Verwendung | Nutzungsform |
|---|---|---|
| [TMDB](https://www.themoviedb.org) | Filmsuche und Backdrop-Download | Kostenlos, API-Key erforderlich |

## Persistente Konfiguration

Persistente CLI-Defaults werden XDG-konform in `~/.config/kinofilm-manager/config.toml`
gespeichert. Falls `XDG_CONFIG_HOME` gesetzt ist, wird stattdessen
`$XDG_CONFIG_HOME/kinofilm-manager/config.toml` verwendet.

### Unterstützte Konfigurationsschlüssel

| Schlüssel | Typ | Beschreibung |
|---|---|---|
| `bitrate` | String | Default für `--bitrate`, z.B. `448k` oder `640k` |
| `dry_run` | Boolean | Default für `--dry-run` / `--no-dry-run` |
| `verbose` | Boolean | Default für `--verbose` / `--no-verbose` |
| `artwork_provider` | String | Default für `artwork fetch --provider` (`tmdb`) |
| `tmdb_api_key` | String | Default für `artwork fetch --tmdb-api-key` |

### CLI-Befehle

```bash
# Alle unterstützten Schlüssel und aktuelle Werte anzeigen
kinofilm-manager config list

# Einzelnen Wert setzen
kinofilm-manager config set bitrate 448k
kinofilm-manager config set dry_run false
kinofilm-manager config set artwork_provider tmdb
kinofilm-manager config set tmdb_api_key "<API_KEY>"

# Einzelnen Wert lesen
kinofilm-manager config get bitrate
```

### Beispiel-Konfiguration

```toml
bitrate = "448k"
dry_run = false
verbose = true
artwork_provider = "tmdb"
tmdb_api_key = "your-key"
```

CLI-Optionen überschreiben persistierte Defaults immer explizit pro Aufruf. Die
Bibliotheks-API liest **keine** globale Konfiguration; technische Laufzeitoptionen werden
explizit als `RuntimeOptions` übergeben.

### Ausgabekonvention

Gemäss den Architekturprinzipien gilt:
- **stdout**: Ergebnis-Pfad der konvertierten Datei (pipeline-freundlich)
- **stderr**: Stream-Analyse, Fortschritt, Warnungen (Rich-formatiert)
- `--verbose`: Zusätzliche Diagnoseinformationen (FFmpeg-Befehl) auf stderr

---

## API-Nutzung

kinofilm-manager kann auch als Python-Bibliothek verwendet werden:

```python
from pathlib import Path
from kinofilm_manager.api import ConvertRequest, RuntimeOptions, convert

request = ConvertRequest(
    input_path=Path("Film.mkv"),
)
runtime_options = RuntimeOptions(
    dry_run=True,
    bitrate="448k",
)
result = convert(request, runtime_options=runtime_options)

if result.success:
    print(f"Konvertiert: {result.converted_count} Spuren")
    for stream in result.streams:
        print(f"  #{stream.index}: {stream.codec_name} → {stream.action}")
```

### Laufzeitoptionen und Konfiguration

`ConvertRequest` beschreibt nur den fachlichen Auftrag (`input_path`, optional `output_path`).
Technische Laufzeitoptionen wie Bitrate oder Dry-Run werden getrennt über
`RuntimeOptions` übergeben. Die CLI liest dafür auf Wunsch persistente Defaults aus
der XDG-Konfiguration und reicht sie explizit an die API weiter.

---

## Fachlogik

### 1. DTS-Erkennung

DTS-Spuren werden über `codec_name` und `profile` aus den ffprobe-Metadaten erkannt:

- `dts` (DTS Core)
- `dts-hd` / `dtshd`
- Profile wie `DTS-HD HRA`, `DTS-HD MA`

### 2. Duplikat-Erkennung: DTS überspringen wenn AC3 mit gleicher Kanalanzahl vorhanden

Viele MKV-Dateien enthalten bereits mehrere Audiospuren pro Sprache. Eine DTS-Spur wird nur dann **übersprungen**, wenn bereits eine **AC3-Spur gleicher Sprache und mindestens gleicher Kanalanzahl** vorhanden ist.

Eine vorhandene AC3 **Stereo** (2.0) Spur ist **kein** Ersatz für eine DTS **5.1** Spur – der Mehrkanal-Surround-Ton würde verloren gehen.

### 3. Kanalanzahl und Bitrate

AC3 (Dolby Digital) unterstützt maximal **5.1 (6 Kanäle)**. Die Bitrate wird automatisch je nach Kanalanzahl gewählt:

| Kanäle | Automatische Bitrate |
|---|---|
| Mono / Stereo | 192k |
| 5.1 | 640k |
| 7.1 (vor Downmix) | 640k |

### 4. 7.1 → 5.1 Downmix via pan-Filter

DTS-HD 7.1 wird via FFmpeg `pan`-Filter auf 5.1 downgemischt. Seitenkanäle (SL/SR) werden auf die Rear-Kanäle (BL/BR) überlagert mit Faktor 0.7 zur Clipping-Vermeidung. Frontkanäle bleiben unverändert.

### 5. Tonversatz (Audio Delay)

MKV-Dateien können pro Spur einen `start_time`-Wert enthalten. FFmpeg erhält `-avoid_negative_ts make_zero` für korrekte Übertragung.

### 6. Stream-Metadaten

Alle Stream-Metadaten (Sprache, Titel) werden explizit pro Stream übertragen. Für konvertierte DTS-Spuren wird der Titel automatisch neu generiert.

### 7. Untertitel-Kompatibilität

Inkompatible Untertitel-Streams (z.B. `dvd_subtitle`) werden erkannt und weggelassen. MKV-kompatible Formate (SRT, ASS, PGS, etc.) werden unverändert kopiert.

**PGS-Untertitel und OCR:** Blu-ray PGS-Untertitel werden als Bitmap ins MKV kopiert. Eine integrierte OCR-Lösung wurde evaluiert, wird aber bewusst nicht umgesetzt. Das Tool bleibt im Scope der **Audio-Konvertierung**. Subler übernimmt im nachgelagerten Schritt die OCR-Konvertierung der PGS-Untertitel beim Muxing zu M4V – mit guter Qualität.

---

## Bekannte Einschränkungen

- **DVD-Untertitel** werden weggelassen. Workaround: In Subler als Bitmap importieren.
- **AC3-Maximum ist 5.1** – 7.1-Material wird immer auf 5.1 downgemischt.
- **Qualitätsverlust** – DTS→AC3 ist verlustbehaftet. Für audiophile Ansprüche bleibt MKV mit DTS-HD das bessere Archivformat.

---

## Weiterführender Workflow

Nach der Konvertierung empfiehlt sich folgender Workflow in **Subler**:

1. Konvertierte MKV in Subler öffnen
2. Gewünschte Spuren auswählen (typisch: eine Audiospur pro Sprache)
3. PGS-Untertitel via Sublers integrierte OCR zu Text konvertieren (falls vorhanden)
4. Metadaten ergänzen (Titel, Jahr, Beschreibung, Artwork)
5. Als M4V exportieren

---

## Änderungsverlauf

### [2.1.0] – 2026-03-18
- Neues Subcommand `kinofilm-manager config` mit `set`, `get` und `list`
- Persistente XDG-Konfiguration unter `~/.config/kinofilm-manager/config.toml`
- `RuntimeOptions` trennt CLI-Laufzeitoptionen von `ConvertRequest`

### [2.0.0] – 2026-03-12
- ⚠️ Breaking Change: Neuer CLI-Aufruf `kinofilm-manager convert` statt `python3 mkv_dts_to_ac3.py`
- Restrukturierung als PyPI-Paket `kurmann-kinofilm-manager`
- CLI mit typer und rich, Ergebnisse auf stdout, Diagnose auf stderr
- Request/Result-API für programmatische Nutzung

### [1.0.0] – 2026-03-12
- Erstveröffentlichung als einzelnes Script

Vollständiger Änderungsverlauf: [CHANGELOG.md](CHANGELOG.md)

---

## Lizenz

MIT
