Metadata-Version: 2.4
Name: kurmann-kamera-einleser
Version: 1.4.0
Summary: Einfacher & solider Medien-Importeur. Archiviert Videoaufnahmen von SSD/iPhone/NAS in ISO-Dateien.
Project-URL: Homepage, https://github.com/kurmann/kamera-einleser
Project-URL: Repository, https://github.com/kurmann/kamera-einleser
Project-URL: Changelog, https://github.com/kurmann/kamera-einleser/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/kurmann/kamera-einleser/issues
Author: Patrick Kurmann
License-Expression: MIT
License-File: LICENSE
Keywords: archive,backup,import,iso,media,nas,rclone,synology
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Archiving :: Backup
Requires-Python: >=3.10
Requires-Dist: inquirerpy>=0.3.4
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.0.0
Requires-Dist: tomli-w>=1.0
Requires-Dist: tomli>=2.0; python_version < '3.11'
Requires-Dist: typer>=0.9.0
Provides-Extra: dev
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# 🎬 Kamera-Einleser

Einfacher & solider Medien-Importeur. Archiviert Videoaufnahmen von SSD/iPhone in ISO-Dateien (organisiert nach Tag) und synchronisiert sie auf Cloud-Speicher. Stellt Archive bei Bedarf wieder her.

## ✨ Features

- **💿 ISO-Archivierung**: Erstellt zuverlässige ISO-Dateien mit UDF-Format (hdiutil makehybrid)
- **📅 Tägliche Gruppierung**: Dateien werden automatisch nach Änderungsdatum in tägliche Archive gruppiert
- **🔄 Idempotente Archivierung**: Prüft vor Erstellung, ob Dateien bereits im ISO vorhanden sind
- **📋 ditto-Staging**: Metadaten-erhaltendes Kopieren via macOS `ditto` ins Staging
- **☁️ rclone-Integration**: Synchronisiert ISOs zu beliebig vielen Cloud-Zielen (NAS, Mega.nz, Google Drive, etc.)
- **🔄 Idempotente Uploads**: rclone läuft auch ohne neue ISOs, um fehlgeschlagene Uploads zu wiederholen
- **🔗 SMB-Mount**: Mounte Archive direkt via SMB ohne Download (schneller, schreibgeschützter Zugriff)
- **📥 Archiv-Wiederherstellung**: Lädt ISO vom NAS herunter und mountet es automatisch
- **🔍 Intelligente Suche**: Findet Archive nach Namen mit Pagination und Filterung
- **🤖 Headless-Modus**: Automatische Wiederherstellung/Mount per Kommandozeile
- **⚙️ Interaktive Konfiguration**: Benutzerfreundliche Einstellungsverwaltung
- **📂 Mehrere Quellen**: Sequenzielle Verarbeitung mehrerer Quellverzeichnisse
- **🗑️ Optionale Löschung**: Fragt am Ende nach dem Löschen der Originaldateien

## 📋 Voraussetzungen

- macOS (für ISO-Erstellung via hdiutil)
- Python 3.10 oder höher
- [rclone](https://rclone.org/) (für Cloud-Synchronisation)

## 🚀 Installation

### Empfohlen: Mit uv

```bash
# uv installieren (falls noch nicht vorhanden)
curl -LsSf https://astral.sh/uv/install.sh | sh

# oder mit brew
brew install uv

# kamera-einleser installieren
uv tool install git+https://github.com/kurmann/kamera-einleser.git
```

### Alternative: Mit pip / pipx / uv

Von PyPI (empfohlen ab 1.4.0):

```bash
uv tool install kurmann-kamera-einleser
# oder
pipx install kurmann-kamera-einleser
# oder
pip install kurmann-kamera-einleser
```

Direkt vom Git-Repository (für Entwicklung):

```bash
pip install git+https://github.com/kurmann/kamera-einleser.git
```

### rclone installieren

```bash
# macOS
brew install rclone
```

## 🎯 Schnellstart

### 1. Einstellungen konfigurieren

```bash
kamera-einleser settings
```

Hier konfigurierst du:
- **Quellverzeichnisse**: Wo liegen deine Kamera-Aufnahmen?
- **rclone-Ziele**: Wohin sollen die Backups synchronisiert werden?
- **Archiv-Verzeichnisse**: Wo sind deine Archive gespeichert? (für Wiederherstellung)
- **Arbeitsverzeichnis**: Wo werden ISO-Dateien lokal erstellt/gespeichert?
- **Ausschluss-Muster**: Dateien/Ordner, die nicht archiviert werden sollen

### 2. Archivierung starten

```bash
# Archiviert alle konfigurierten Quellverzeichnisse
kamera-einleser archive

# Oder direkt ein bestimmtes Verzeichnis angeben
kamera-einleser archive /Volumes/SSD/Videos

# Mit optionaler Löschung der Originale am Ende
kamera-einleser archive --delete-source
```

### 2a. Arbeitsverzeichnisse (primär + Fallback)

Ab **1.4.0** kann man zwei Arbeitsverzeichnisse konfigurieren. Das Tool nimmt automatisch das passende:

```bash
# Primär: interne SSD — schnell, ideal für kleine/mittlere Volumina
kamera-einleser config set working_directory ~/Movies/Kamera-Einleser

# Fallback: externe SSD — gross, ideal für 100 GB+ Pulls vom NAS
kamera-einleser config set working_directory_fallback /Volumes/Samsung2TB/Kamera-Einleser
```

Logik pro Quelle:

1. Primär nutzen, wenn gemountet **und** genug freier Platz (Quell-Grösse × 1.1 bei rclone, × 2.5 bei lokalen Quellen).
2. Sonst Fallback versuchen (gleiche Prüfung).
3. Beide nicht geeignet: bei lokalen Quellen System-Temp, bei rclone-Quellen **harter Abbruch** mit klarer Meldung — Temp kann auf macOS im RAM liegen und einen 200-GB-Pull sprengen.

### 2b. Netzlaufwerke als Quellen (rclone, z.B. Synology NAS)

Ab **1.4.0** dürfen Quellen auch rclone-Remotes sein — zum Beispiel ein
Synology NAS. Die Dateien werden beim Archive-Lauf zunächst temporär in das
Arbeitsverzeichnis gezogen (Staging) und von dort ganz normal in ISO-Dateien
pro Tag verpackt.

```bash
# Einmalig rclone für das NAS einrichten (interaktiv)
rclone config

# Quellen können ab jetzt lokale Pfade *oder* rclone-Specs sein:
kamera-einleser config get source_directories
# → ["/Volumes/Card", "synology:/volume1/Fotos/Eingang"]
```

Das Parsing erfolgt über das Muster `remote:pfad`. Lokale Pfade beginnen mit
`/` oder `~`. Auf NAS-Quellen verhält sich die CLI identisch zu lokalen
Quellen, mit Ausnahme von `--delete-source`: das wirkt nur auf lokale Quellen.

### 3. Archive mounten (ohne Download)

Mounte Archive direkt via SMB für schnellen, schreibgeschützten Zugriff:

```bash
# Interaktiver Modus: Wähle Archiv aus Liste
kamera-einleser mount

# Headless-Modus: Mounte direkt
kamera-einleser mount Originalmedien-2026-01-15.iso

# Oder mit Alias:
kamera-einleser load Originalmedien-2026-01-15.iso

# Zeige gemountete Archive
kamera-einleser mount --list-mounted
kamera-einleser status

# Unmounten
kamera-einleser unmount Originalmedien-2026-01-15.iso
# Oder alle:
kamera-einleser unmount --all
```

**Vorteile:**
- ✅ Kein lokaler Speicherplatz nötig
- ✅ Sofortiger Zugriff (kein Download)
- ✅ Ideal für Durchsuchen, Ansehen, Videoschnitt mit Referenzen

**Einrichtung:**
Füge `smb_url` zu deinen rclone-Zielen in der Config hinzu:

```bash
kamera-einleser settings  # → rclone-Ziele bearbeiten
```

In der Config-Datei (`~/.config/kamera-einleser/config.yaml`):
```yaml
rclone_targets:
  - name: "NAS"
    path: "nas:/Archiv"
    smb_url: "smb://user@nas.local/Archiv"  # Diese Zeile hinzufügen
```

Beim ersten Mount fragt macOS nach Zugangsdaten und bietet an,
diese im Schlüsselbund zu speichern. Danach erfolgen Mounts automatisch.

### 4. Archiv wiederherstellen

```bash
# Interaktiver Modus: Durchsuche und wähle Archive aus
kamera-einleser restore

# Headless-Modus: Automatische Wiederherstellung nach Dateiname
kamera-einleser restore Originalmedien-2026-01-15.iso

# Mit eigenem Zielverzeichnis
kamera-einleser restore --restore-dir /custom/path Originalmedien-2026-01-15
```

### 5. Optionale Parameter für Archivierung

```bash
# Nur Archiv erstellen, ohne Upload
kamera-einleser archive --no-upload

# Originaldateien nach erfolgreichem Backup löschen (fragt nach)
kamera-einleser archive --delete-source
```

## 📖 Befehle

### `settings`
Öffnet das interaktive Einstellungsmenü.

```bash
kamera-einleser settings
```

### `config`
Zeigt die aktuelle Konfiguration an.

```bash
kamera-einleser config
```

### `archive`
Erstellt tägliche ISO-Archive und synchronisiert sie zu Cloud-Zielen.

```bash
kamera-einleser archive [QUELLVERZEICHNIS] [OPTIONEN]
```

**Optionen:**
- `--no-upload`: Nur Archiv erstellen, nicht synchronisieren
- `--delete-source`: Originaldateien nach erfolgreichem Backup löschen

### `restore`
Stellt ein ISO-Archiv aus Cloud-Speicher wieder her (mit Download).

```bash
# Interaktiver Modus
kamera-einleser restore

# Headless-Modus mit Dateiname
kamera-einleser restore [DATEINAME] [OPTIONEN]
```

**Optionen:**
- `--restore-dir, -r PATH`: Zielverzeichnis für Wiederherstellung (optional)

### `mount` / `load`
Mountet ein Archiv direkt via SMB ohne Download (schneller, schreibgeschützter Zugriff).

```bash
# Interaktiver Modus
kamera-einleser mount
kamera-einleser load  # Alias

# Headless-Modus mit Dateiname
kamera-einleser mount [ARCHIVNAME]

# Zeige gemountete Archive
kamera-einleser mount --list-mounted

# Zeige Mount-Pfad (für Scripting)
kamera-einleser mount --print-path [ARCHIVNAME]
```

**Voraussetzungen:**
- SMB-URL in `config.yaml` konfiguriert (siehe "Archive mounten" oben)
- Netzwerkzugriff auf das NAS

### `unmount`
Unmountet gemountete Archive.

```bash
# Interaktiver Modus (Auswahl aus Liste)
kamera-einleser unmount

# Headless-Modus mit Archivname
kamera-einleser unmount [ARCHIVNAME]

# Alle Archive unmounten
kamera-einleser unmount --all
```

### `status`
Zeigt alle aktuell gemounteten Archive an.

```bash
kamera-einleser status
```

### `version`
Zeigt die installierte Version an.

```bash
kamera-einleser --version
```

## ⚙️ Konfiguration

Die Konfigurationsdatei liegt unter:
```
~/.config/kamera-einleser/config.yaml
```

Beispiel-Konfiguration:

```yaml
source_directories:
  - /Volumes/SSD/BlackmagicCamera
  - /Volumes/iPhone/DCIM
rclone_targets:
  - name: NAS
    path: nas:/Backups/Videos/2026
  - name: Mega.nz
    path: mega:Backups/Videos
archive_directories:
  - nas:/Backups/Videos/2026
  - nas:/Backups/Videos/2025
  - mega:Backups/Videos
current_archive_directory: nas:/Backups/Videos/2026
working_directory: /Users/username/Movies/Kamera-Einleser
restore_directory: /Users/username/Movies/Kamera-Restore
excludes:
  - ._*
  - .DS_Store
```

**Konfigurationsoptionen:**
- `source_directories`: Liste von Quellverzeichnissen zum Archivieren
- `rclone_targets`: Liste von rclone-Zielen für Synchronisation
- `archive_directories`: Liste von rclone-Pfaden, die nach Archiven durchsucht werden
- `current_archive_directory`: Das aktuell verwendete Verzeichnis für neue Archive
- `working_directory`: Lokales Verzeichnis für ISO-Dateien (nicht temporär!)
- `restore_directory`: Standard-Verzeichnis für wiederhergestellte ISOs
- `excludes`: Muster für auszuschließende Dateien (z.B. `.DS_Store`, `._*`)

💡 **Tipp**: Das Arbeitsverzeichnis ist der lokale Speicher für ISO-Dateien. Diese werden mit rclone zu den Zielen synchronisiert und lokal behalten für Videoschnitt etc.

## 🔧 rclone einrichten

Bevor du Cloud-Ziele nutzen kannst, musst du rclone konfigurieren:

```bash
# Interaktive rclone-Konfiguration
rclone config

# Teste die Verbindung
rclone lsd nas:
```

Weitere Infos: [rclone.org/docs](https://rclone.org/docs/)

## 🎬 Workflow-Beispiele

### Archivierung

1. Kamera-SSD am Mac anschließen
2. `kamera-einleser archive` ausführen
3. Programm analysiert Dateien und gruppiert sie nach Tag (Änderungsdatum)
4. Prüft bestehende ISOs im Arbeitsverzeichnis und auf allen rclone-Zielen
5. Prüft Idempotenz: Überspringt Tage, deren Dateien bereits archiviert sind
6. Erstellt ditto-Staging für neue/geänderte Tage (Metadaten-erhaltend)
7. Erstellt ISO-Dateien mit UDF-Format (z.B. "Originalmedien-2026-01-15.iso")
8. Verifiziert die Integrität mit hdiutil
9. Synchronisiert zu allen konfigurierten Cloud-Zielen (organisiert in Jahresordner)
10. Optional: Löscht Originaldateien nach Bestätigung am Ende

### Wiederherstellung

**Interaktiv:**
1. `kamera-einleser restore` ausführen
2. Optional: Nach Dateinamen suchen
3. Durch Archive blättern (10 pro Seite)
4. Archiv auswählen
5. Bestätigen
6. Programm lädt ISO herunter und mountet es

**Headless (Automatisiert):**
```bash
# In Skript oder Cronjob
kamera-einleser restore Originalmedien-2026-01-15 --restore-dir /restore/path
```

## 🎯 Archivierungs-Konzept

Das Programm verwendet tägliche ISO-Archive:

1. **Datei-Analyse**: Scannt alle Dateien und liest deren Änderungsdatum
2. **Tägliche Gruppierung**: Gruppiert Dateien nach Tag (YYYY-MM-DD)
3. **ISO pro Tag**: 
   - Dateiname: `Originalmedien-YYYY-MM-DD.iso`
   - Volume-Name: `Originalmedien-YYYY-MM-DD`
   - Format: UDF (Universal Disk Format)
4. **Idempotenz**: 
   - Prüft vor Erstellung, ob alle Dateien bereits im bestehenden ISO vorhanden sind
   - Prüft bestehende ISOs auf allen konfigurierten rclone-Zielen (Namenskollision)
   - Überspringt Tag wenn bereits vollständig archiviert
   - Bei neuen Dateien wird neue ISO mit Suffix erstellt (`-Teil-2`, `-Teil-3`, etc.)
5. **ditto-Staging**:
   - Kopiert Dateien via macOS `ditto` ins temporäre Staging-Verzeichnis
   - Erhält Resource Forks, Extended Attributes und HFS+/APFS-Metadaten
   - Erzeugt ein eigenständiges, vollständiges Archiv
6. **Verzeichnisstruktur**: Unterverzeichnisse werden im ISO übernommen
7. **rclone-Organisation**: ISOs werden in Jahresordner auf Remote-Zielen organisiert
8. **Idempotente Uploads**: rclone läuft auch ohne neue ISOs, um fehlgeschlagene Uploads zu wiederholen

**Beispiel:**
```
Quellverzeichnis/
├── 2026-01-15_Video1.mp4   → Originalmedien-2026-01-15.iso
├── 2026-01-15_Video2.mp4   → Originalmedien-2026-01-15.iso
├── 2026-02-05_Video3.mp4   → Originalmedien-2026-02-05.iso
└── Projekt/
    ├── 2026-01-15_Clip.mp4 → Originalmedien-2026-01-15.iso (Projekt/...)
    └── 2026-02-10_Raw.mov  → Originalmedien-2026-02-10.iso (Projekt/...)
```

**Remote-Struktur:**
```
nas:/Backups/Videos/
└── 2026/
    ├── Originalmedien-2026-01-15.iso
    ├── Originalmedien-2026-02-05.iso
    └── Originalmedien-2026-02-10.iso
```

**Vorteile:**
- ✅ Tägliche Organisation für präzise Übersicht
- ✅ Idempotenz vermeidet Duplikate und unnötige I/O
- ✅ Unveränderliche ISOs: Einmal erstellt, nie überschrieben
- ✅ UDF-Format (universell lesbar, nicht nur macOS)
- ✅ Verzeichnisstruktur bleibt erhalten
- ✅ Natives macOS-Format via hdiutil, mountbar im Finder
- ✅ Effiziente rclone-Synchronisation (nur neue ISOs werden hochgeladen)

## 📝 ISO-Format & Technische Details

### ISO-Erstellung mit hdiutil makehybrid

Das Programm nutzt `hdiutil makehybrid` zur ISO-Erstellung:

- **Format**: UDF (Universal Disk Format)
- **Kompatibilität**: Lesbar auf macOS, Windows, Linux
- **Kommando**: `hdiutil makehybrid -o output.iso source_dir -udf -default-volume-name NAME`
- **Mount**: Automatisch via macOS Finder oder `hdiutil attach`

### 📋 ditto-Staging

1. Erstelle temporäres Staging-Verzeichnis
2. Kopiere Dateien via macOS `ditto` (erhält Metadaten, Resource Forks, Extended Attributes)
3. Erstelle ISO aus dem Staging-Verzeichnis mit `hdiutil makehybrid`
4. Lösche Staging-Verzeichnis

### 🔒 Unveränderlichkeit (Immutability)

ISO-Dateien sind nach der Erstellung unveränderlich. Das wird auf drei Ebenen sichergestellt:

**Lokal (Arbeitsverzeichnis):**
- Bestehende ISOs werden nie überschrieben
- Wenn `Originalmedien-2026-01-15.iso` bereits existiert, wird automatisch `-Teil-2`, `-Teil-3`, etc. angehängt
- Neue Quelldateien für denselben Tag erzeugen eine zusätzliche ISO

**Remote-Abgleich (rclone-Ziele):**
- Vor der ISO-Erstellung werden alle konfigurierten rclone-Ziele auf bestehende ISO-Dateien geprüft
- Wenn ein Ziel bereits eine ISO-Datei mit demselben Namen hat, wird eine neue ISO mit Suffix erstellt (`-Teil-2`, `-Teil-3`, etc.)
- Dies gilt auch wenn die ISO im Arbeitsverzeichnis nicht vorhanden ist (z.B. bei Einlesen an verschiedenen Computern)
- In der Konsole wird ausgegeben, welche Ziele geprüft werden und ob Namenskollisionen gefunden wurden

**Remote (rclone-Ziele):**
- `rclone copyto --immutable` verhindert jedes Überschreiben auf den Zielen
- Identische Dateien werden automatisch übersprungen (kein Fehler)
- Unterschiedliche Dateien mit gleichem Namen erzeugen einen Abbruch — der Anwender muss manuell eingreifen

**Warum?**
- ✅ Schutz vor versehentlichem Datenverlust
- ✅ Dreischichtiger Schutz: Lokal (Suffix) + Remote-Abgleich (Namenskollision) + Remote (rclone --immutable)
- ✅ Inkonsistenzen werden sofort erkannt statt stillschweigend überschrieben
- ✅ Funktioniert auch bei Einlesen an mehreren Computern mit unterschiedlichen Arbeitsverzeichnissen

### 🔄 Idempotenz: Keine Duplikate

**Archivierungs-Idempotenz:**

Vor der ISO-Erstellung für einen Tag prüft das Programm:
1. Existiert bereits ISO für diesen Tag im Arbeitsverzeichnis?
2. Falls ja: Mount ISO readonly, vergleiche alle Dateien (Name + Größe)
3. Falls alle Dateien bereits vorhanden → Tag überspringen
4. Falls neue/geänderte Dateien → Erstelle neue ISO mit Suffix (`-Teil-2`, `-Teil-3`)
5. Zusätzlich: Prüfe alle konfigurierten rclone-Ziele auf bestehende ISO-Dateinamen
6. Falls ein Ziel bereits eine ISO mit demselben Namen hat → Verwende Suffix, auch wenn im Arbeitsverzeichnis keine ISO vorhanden ist

**Upload-Idempotenz:**

Selbst wenn keine neuen ISOs erstellt wurden:
1. Sammle alle existierenden ISOs aus Arbeitsverzeichnis
2. Führe Integritätsprüfung durch
3. Führe rclone-Synchronisation durch

**Warum ist das wichtig?**
- ✅ Wiederholt fehlgeschlagene Uploads automatisch
- ✅ Stellt gelöschte Remote-Dateien wieder her
- ✅ rclone selbst ist idempotent (vergleicht Größe/Checksum)
- ✅ Keine unnötige Arbeit bei bereits archivierten Tagen

## 🤝 Beitragen

Issues und Pull Requests sind willkommen! [GitHub Repository](https://github.com/kurmann/kamera-einleser)

## 📄 Lizenz

MIT License - siehe [LICENSE](LICENSE)

## 👨‍💻 Autor

Patrick Kurmann
