cd803a3dfe
Vor dem Rollout durchgesehen und die verbliebenen Stellen geschlossen, an denen etwas schiefgehen konnte, ohne dass es irgendwo sichtbar wurde. Datenverlust: - veraPDF: das in [verapdf].binary konfigurierte Programm wird im Preflight geprueft. Bisher galt bei falschem Pfad JEDE Datei als "nicht konform" — Ergebnis nach error/, Original geloescht (Default delete). run_verapdf() trennt jetzt ausserdem ein echtes FAIL-Urteil von einer Stoerung (VeraPdfUnavailable: nicht startbar, abgestuerzt, kein PASS/FAIL in der Ausgabe). Bei Stoerung wandern Original UND Ergebnis nach error/, das Original wird nicht entsorgt. - Gleichnamige Dateien wurden in outgoing/, error/ und beim Ordner-Upload mit abweichendem target kommentarlos ueberschrieben. Jetzt Zeitstempel daneben, mit Warnung; ProcessResult.output traegt den echten Pfad. Robustheit: - Kaputtes oder nicht lesbares TOML beim Start: Exit 2 statt Traceback. - RestartPreventExitStatus=2 in der Unit — Exit 2 (Config/Preflight) laeuft nicht mehr endlos neu, die Instanz bleibt sichtbar failed stehen. - Toter watchdog-Observer wird erkannt: Exit 3, systemd setzt den Watch neu auf. Vorher blieb die Unit "active" und verarbeitete nichts mehr. - Relative Pfade in [paths]/archive_dir/target sind ein Config-Fehler statt still unter /opt zu landen. - Fehler beim Archivieren entwertet den Durchlauf nicht mehr: Upload und Mail laufen, Sichtbarkeit ueber log.error + "OK mit Warnung"-Mail. - Nicht-PDFs in incoming/ werden beim Start-Scan gesammelt gemeldet. - Logging explizit nach stdout (die Doku versprach das schon). Struktur: - Neue lib/common.sh, von install.sh und update.sh gesourct. Die doppelte venv_is_healthy() gibt es nur noch einmal, in der gruendlichen Fassung — die schlanke in install.sh haette eine nach einem Distro-Sprung kaputte venv als gesund durchgewunken (nachgewiesen). - install.sh warnt in Containern, wenn systemd-journald nicht laeuft. Doku: Dateisystem-Festlegung (ext4/xfs/zfs, kein CIFS/NFS wegen inotify), Debian 13 in LXC auf Proxmox scheitert an journald (243/CREDENTIALS, AppArmor blockiert sd-mkdcreds) inkl. Abhilfe, echte Speicher-Messwerte, Exit-Code-Tabelle. 254 Tests gruen (vorher 152). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
185 lines
9.2 KiB
Markdown
185 lines
9.2 KiB
Markdown
# PDF OCR Hotfolder
|
|
|
|
Verwandelt eingehende gescannte PDFs automatisch in **durchsuchbare PDFs** (PDF/A optional) per OCR. Hauptanwendung: Kunden-Scanner schiebt PDF in einen Ordner — Sekunden später liegt die OCR-Version im Ausgang oder wird in Nextcloud / per SFTP weitergeleitet.
|
|
|
|
## Dokumentation
|
|
|
|
| Dokument | Inhalt |
|
|
|----------|--------|
|
|
| **[docs/INSTALLATION.md](docs/INSTALLATION.md)** | Erstinstallation, Instanzen anlegen, LXC, Ghostscript, **Konfigurationsreferenz**, Troubleshooting |
|
|
| **[docs/UPDATE.md](docs/UPDATE.md)** | Update mit `update.sh`: Ablauf, Backup, Rollback, `--check-config`, Config-Drift |
|
|
| **[docs/OS-UPGRADE.md](docs/OS-UPGRADE.md)** | Debian-Major-Upgrade (12 → 13): venv neu bauen, Pins anheben |
|
|
|
|
Weiter: [CHANGELOG.md](CHANGELOG.md) · [AI_AGENT_BRIEFING.md](AI_AGENT_BRIEFING.md) · [config.example.toml](config.example.toml)
|
|
|
|
## Features
|
|
|
|
- 🔍 **OCR via ocrmypdf + Tesseract** (Library-Call, kein Subprozess-Overhead)
|
|
- 📂 **Hotfolder via watchdog** — reagiert auf `created`, `moved`, `closed` Events
|
|
- 🧠 **Stabilitäts-Erkennung**: wartet bis Scanner fertig geschrieben hat
|
|
- 🔁 **Parallelverarbeitung** mehrerer PDFs (ThreadPool, konfigurierbar)
|
|
- ♻️ **Wiederaufnahme aus `working/`** nach einem harten Stopp — keine Datei bleibt liegen
|
|
- ✅ **PDF/A-Output** (1, 2 oder 3) optional
|
|
- 🛡️ **veraPDF-Validierung** optional — Binary wird im Preflight geprüft, eine Störung gilt nicht als FAIL
|
|
- 🚫 **Überschreibt nie eine gleichnamige Datei** — in `outgoing/`, `error/`, Archiv und Ordner-Upload weicht sie mit Zeitstempel aus
|
|
- ☁️ **Upload-Ziele**: lokaler Ordner, Nextcloud (WebDAV via Python), SFTP
|
|
- 📧 **E-Mail-Notify** (immer / nur Fehler / nie)
|
|
- 🔐 **Service-User-Support** für lokale **und AD-User mit lokaler UID** (SSSD/Winbind)
|
|
- ⚙️ Saubere systemd-Integration mit auto-Restart, **Multi-Instanz** über eine Template-Unit
|
|
- 👁️ **Toter Verzeichnis-Watch wird erkannt** — der Dienst beendet sich (Exit 3), systemd setzt den Watch neu auf
|
|
- 🩺 **`--check-config`** prüft eine Instanz-Config ohne etwas zu verarbeiten
|
|
|
|
## Schnellstart
|
|
|
|
**Voraussetzungen:** Debian 12 oder 13, Python 3.11+, root — und **mindestens
|
|
2 GB RAM** (512 MB reichen für 300-dpi-Scans nachweislich nicht, siehe
|
|
[Systemanforderungen](docs/INSTALLATION.md#systemanforderungen)).
|
|
Dateisystem **ext4, xfs oder zfs**; `incoming/` muss **lokal** liegen — auf
|
|
einem CIFS/NFS-Mount liefert inotify keine Events und der Hotfolder bemerkt
|
|
neue Dateien nur noch beim Start
|
|
([warum](docs/INSTALLATION.md#dateisystem-ext4-xfs-oder-zfs)).
|
|
|
|
```bash
|
|
git clone gitea@gitea.sonith.de:sonith_ug/pdf-ocr-hotfolder.git
|
|
cd pdf-ocr-hotfolder
|
|
sudo ./install.sh
|
|
```
|
|
|
|
Der Installer legt einmalig Code, venv und die systemd-Template-Unit an und
|
|
fragt danach **pro Instanz** Name, Basis-Pfad, Service-User, OCR-Sprachen und
|
|
die Original-Behandlung ab. Bei jedem erneuten Aufruf erkennt er bestehende
|
|
Instanzen und fragt nur nach neuen.
|
|
|
|
Test:
|
|
|
|
```bash
|
|
cp irgendein-scan.pdf /var/lib/pdf-ocr-hotfolder/<instanz>/incoming/
|
|
journalctl -u pdf-ocr-hotfolder@<instanz> -f
|
|
```
|
|
|
|
Nach wenigen Sekunden liegt das OCR-PDF im `outgoing/`-Ordner der Instanz.
|
|
|
|
Alle Details zu den Abfragen, zum Multi-Instanz-Betrieb und zu den Fallstricken
|
|
(LXC, Ghostscript): **[docs/INSTALLATION.md](docs/INSTALLATION.md)**.
|
|
|
|
Update: `git pull && sudo ./update.sh` — siehe **[docs/UPDATE.md](docs/UPDATE.md)**.
|
|
Nach einem Debian-Major-Upgrade: **[docs/OS-UPGRADE.md](docs/OS-UPGRADE.md)**.
|
|
|
|
## Verzeichnisse
|
|
|
|
| Pfad | Zweck |
|
|
|------|-------|
|
|
| `/opt/pdf-ocr-hotfolder/` | Code + venv (für alle Instanzen gemeinsam) |
|
|
| `/opt/pdf-ocr-hotfolder/lib/common.sh` | gemeinsame Shell-Funktionen für `install.sh`/`update.sh` (mitkopiert) |
|
|
| `/etc/pdf-ocr-hotfolder/<instanz>.toml` | Config pro Instanz (640, root:\<service-gruppe\>) |
|
|
| `/etc/systemd/system/pdf-ocr-hotfolder@.service` | systemd Template-Unit |
|
|
| `/var/lib/pdf-ocr-hotfolder/<instanz>/incoming` | Eingang (Scanner schreibt hier rein) |
|
|
| `/var/lib/pdf-ocr-hotfolder/<instanz>/working` | Arbeitsverzeichnis während OCR |
|
|
| `/var/lib/pdf-ocr-hotfolder/<instanz>/outgoing` | Ausgang (fertige PDFs) |
|
|
| `/var/lib/pdf-ocr-hotfolder/<instanz>/error` | Fehlgeschlagene PDFs |
|
|
| `/var/backups/pdf-ocr-hotfolder/` | Update-Backups (0600, letzte 5) |
|
|
|
|
Ein eigenes Logverzeichnis gibt es nicht — der Dienst loggt nach stdout und
|
|
damit ins journal.
|
|
|
|
## Konfiguration im Überblick
|
|
|
|
Jede Instanz hat ihre eigene TOML unter `/etc/pdf-ocr-hotfolder/<instanz>.toml`.
|
|
Vollständiges, kommentiertes Beispiel: [`config.example.toml`](config.example.toml).
|
|
Key-für-Key-Referenz: [docs/INSTALLATION.md](docs/INSTALLATION.md#konfigurationsreferenz).
|
|
|
|
| Sektion | Zweck |
|
|
|---------|-------|
|
|
| `[paths]` | `incoming`, `outgoing`, `working`, `error` — **Pflicht**, alle **absolut** |
|
|
| `[ocr]` | Sprachen, `jobs`, `skip_text`, `pdfa_level`, `deskew`, `max_workers`, `timeout` (Sekunden **pro Seite**) |
|
|
| `[output]` | Dateibenennung (`name_mode`/`name_tag`) und Original-Behandlung (`delete`/`archive`) |
|
|
| `[verapdf]` | optionale PDF/A-Validierung per CLI |
|
|
| `[upload.folder]` / `[upload.nextcloud]` / `[upload.sftp]` | Upload-Ziele, beliebig viele gleichzeitig |
|
|
| `[notify.email]` | SMTP-Benachrichtigung: `always` \| `errors` \| `never` |
|
|
| `[logging]` | `level` = DEBUG/INFO/WARNING/ERROR |
|
|
|
|
Die Instanz-Configs enthalten **Klartext-Passwörter** (SMTP, Nextcloud, SFTP) —
|
|
deshalb `640 root:<service-gruppe>` und beim Debuggen nicht in Tickets kopieren.
|
|
|
|
Config prüfen, ohne etwas zu verarbeiten:
|
|
|
|
```bash
|
|
cd /opt/pdf-ocr-hotfolder && sudo ./venv/bin/python -m pdf_ocr_hotfolder \
|
|
--check-config --config /etc/pdf-ocr-hotfolder/<instanz>.toml
|
|
```
|
|
|
|
Exit 0 = sauber, 1 = Warnungen, 2 = Fehler. Details:
|
|
[docs/UPDATE.md](docs/UPDATE.md#config-prüfung-per---check-config).
|
|
|
|
### Exit-Codes des Dienstes
|
|
|
|
| Exit | Bedeutung | Neustart durch systemd |
|
|
|------|-----------|------------------------|
|
|
| `0` | regulärer Stopp | — |
|
|
| `1` | nur bei `--once`: mindestens eine PDF fehlgeschlagen | — |
|
|
| `2` | Config- oder Preflight-Fehler | **nein** (`RestartPreventExitStatus=2`) — die Instanz bleibt sichtbar `failed` |
|
|
| `3` | Verzeichnis-Watch gestorben, es würden keine Dateien mehr erkannt | **ja**, genau dafür |
|
|
|
|
Vollständig: [docs/INSTALLATION.md](docs/INSTALLATION.md#exit-codes).
|
|
|
|
## Service-Verwaltung
|
|
|
|
```bash
|
|
# Eine bestimmte Instanz
|
|
sudo systemctl status pdf-ocr-hotfolder@kunde-a
|
|
sudo systemctl restart pdf-ocr-hotfolder@kunde-a
|
|
journalctl -u pdf-ocr-hotfolder@kunde-a -f
|
|
|
|
# Alle Instanzen
|
|
sudo systemctl status 'pdf-ocr-hotfolder@*'
|
|
sudo systemctl restart 'pdf-ocr-hotfolder@*'
|
|
journalctl -u 'pdf-ocr-hotfolder@*' --since today
|
|
```
|
|
|
|
Ein laufendes OCR darf beim Stoppen zu Ende laufen (`TimeoutStopSec=300`) — ein
|
|
`stop` kann deshalb pro Instanz bis zu 5 Minuten dauern.
|
|
|
|
## Architektur
|
|
|
|
```
|
|
┌──────────┐ watchdog ┌──────────────┐ ocrmypdf ┌──────────┐
|
|
│ Scanner │ ──────────────▶ │ incoming/ │ ─────────────▶ │ working/ │
|
|
└──────────┘ PDF-Datei └──────────────┘ (Library) └────┬─────┘
|
|
│
|
|
optional veraPDF
|
|
│
|
|
▼
|
|
┌──────────────┐
|
|
│ outgoing/ │
|
|
└──────┬───────┘
|
|
│
|
|
┌──────────────────────┼──────────────────────┐
|
|
▼ ▼ ▼
|
|
┌────────────┐ ┌────────────┐ ┌────────────┐
|
|
│ Nextcloud │ │ SFTP │ │ E-Mail │
|
|
│ (WebDAV) │ │ (paramiko) │ │ Notify │
|
|
└────────────┘ └────────────┘ └────────────┘
|
|
```
|
|
|
|
Beim Start wird `working/` zuerst durchsucht: was ein harter Stopp dort liegen
|
|
ließ, wird wiederaufgenommen; unvollständige OCR-Fragmente (`__ocr_*`) werden
|
|
gelöscht.
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
pytest # 254 Tests
|
|
```
|
|
|
|
`ocrmypdf` muss dafür nicht installiert sein — der Import ist lazy und wird in
|
|
den Tests gemockt.
|
|
|
|
## Lizenz
|
|
|
|
MIT — © Sonith UG
|
|
|
|
---
|
|
|
|
**Version:** 0.7.0
|
|
**Repo:** https://gitea.sonith.de/sonith_ug/pdf-ocr-hotfolder
|