cbdc9d6664
- #4: LXC/Container Drop-in (lxc-compat.conf) deaktiviert systemd-Hardening; Installer erkennt Container automatisch und bietet Drop-in an - #5: WorkingDirectory=/opt/pdf-ocr-hotfolder in Template-Unit ergänzt - #6: Installer bietet auf Debian 12 bei betroffenen GS-Versionen automatisch bookworm-backports Upgrade an (statt nur Warnung) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
244 lines
9.1 KiB
Markdown
244 lines
9.1 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.
|
||
|
||
## 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)
|
||
- ✅ **PDF/A-Output** (1, 2 oder 3) optional
|
||
- 🛡️ **veraPDF-Validierung** optional
|
||
- ☁️ **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
|
||
|
||
## Schnellstart
|
||
|
||
```bash
|
||
git clone https://gitea.sonith.de/sonith_ug/pdf-ocr-hotfolder.git
|
||
cd pdf-ocr-hotfolder
|
||
sudo ./install.sh
|
||
```
|
||
|
||
Der Installer:
|
||
1. Installiert einmalig Code + venv + systemd-Template-Unit
|
||
2. Fragt nach Instanz-Name, Basis-Pfad, Service-User
|
||
3. Legt so viele Hotfolder-Instanzen an, wie du willst (`Weitere Instanz anlegen? [j/N]`)
|
||
|
||
Bei jedem erneuten Aufruf erkennt der Installer 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.
|
||
|
||
## Multi-Instanz-Betrieb
|
||
|
||
Das Tool arbeitet komplett **instanzbasiert** über eine systemd Template-Unit `pdf-ocr-hotfolder@<name>.service`. Jede Instanz hat:
|
||
|
||
- eigene Config-Datei: `/etc/pdf-ocr-hotfolder/<name>.toml`
|
||
- eigene Datenverzeichnisse: `/var/lib/pdf-ocr-hotfolder/<name>/{incoming,working,outgoing,error}/`
|
||
- eigene systemd-Unit: `pdf-ocr-hotfolder@<name>.service`
|
||
- optional eigenen Service-User (via Drop-in `/etc/systemd/system/pdf-ocr-hotfolder@<name>.service.d/user.conf`)
|
||
|
||
Beispiel für 3 Hotfolder:
|
||
|
||
```bash
|
||
sudo ./install.sh
|
||
# → legt z.B. kunde-a, kunde-b, buchhaltung an
|
||
|
||
systemctl status 'pdf-ocr-hotfolder@*'
|
||
journalctl -u pdf-ocr-hotfolder@kunde-a -f
|
||
```
|
||
|
||
Manuell eine weitere Instanz anlegen geht auch — einfach `install.sh` erneut starten, er fragt wieder nach.
|
||
|
||
## Verzeichnisse
|
||
|
||
| Pfad | Zweck |
|
||
|------|-------|
|
||
| `/opt/pdf-ocr-hotfolder/` | Code + venv (für alle Instanzen gemeinsam) |
|
||
| `/etc/pdf-ocr-hotfolder/<instanz>.toml` | Config pro Instanz |
|
||
| `/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/log/pdf-ocr-hotfolder/` | Logs (zusätzlich zu journald) |
|
||
| `/var/backups/pdf-ocr-hotfolder/` | Update-Backups |
|
||
|
||
## Konfiguration
|
||
|
||
Vollständiges Beispiel: [`config.example.toml`](config.example.toml). Wichtigste Sektionen:
|
||
|
||
### `[ocr]`
|
||
```toml
|
||
languages = "deu+eng" # Tesseract-Sprachen
|
||
jobs = 4 # Threads pro PDF
|
||
skip_text = true # bereits OCR-haltige Seiten überspringen
|
||
pdfa_level = "2" # "1", "2", "3" oder "" für reines PDF
|
||
deskew = true
|
||
max_workers = 2 # parallele PDFs
|
||
timeout = 1800
|
||
```
|
||
|
||
### `[output]`
|
||
```toml
|
||
# Dateiname im outgoing/:
|
||
# "prefix" → OCR_scan.pdf
|
||
# "suffix" → scan_OCR.pdf (vor der Extension)
|
||
# "none" → scan.pdf (unverändert)
|
||
name_mode = "prefix"
|
||
name_tag = "OCR_"
|
||
|
||
# Nach erfolgreichem OCR mit dem Original:
|
||
# "delete" → löschen
|
||
# "archive" → in archive_dir verschieben
|
||
original_on_success = "delete"
|
||
archive_dir = "" # absoluter Pfad, Pflicht bei "archive"
|
||
```
|
||
|
||
### `[upload.nextcloud]`
|
||
```toml
|
||
enabled = true
|
||
url = "https://cloud.example.com"
|
||
username = "scanuser"
|
||
password = "app-password"
|
||
remote_path = "Scans/Inbox"
|
||
```
|
||
|
||
### `[upload.sftp]`
|
||
```toml
|
||
enabled = true
|
||
host = "sftp.example.com"
|
||
username = "scanuser"
|
||
key_file = "/etc/pdf-ocr-hotfolder/sftp_key"
|
||
remote_path = "/uploads"
|
||
```
|
||
|
||
### `[notify.email]`
|
||
```toml
|
||
enabled = true
|
||
smtp_host = "smtp.example.com"
|
||
smtp_port = 587
|
||
smtp_user = "alerts@example.com"
|
||
smtp_password = "secret"
|
||
from_addr = "PDF OCR <alerts@example.com>"
|
||
to_addrs = ["admin@example.com"]
|
||
on = "errors" # always | errors | never
|
||
```
|
||
|
||
## 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@*'
|
||
```
|
||
|
||
## Update
|
||
|
||
```bash
|
||
cd /pfad/zum/repo
|
||
git pull
|
||
sudo ./update.sh
|
||
```
|
||
|
||
`update.sh`:
|
||
1. Stoppt alle laufenden Instanzen
|
||
2. Sichert den alten Code nach `/var/backups/pdf-ocr-hotfolder/`
|
||
3. Aktualisiert Code + venv + systemd-Template-Unit in `/opt/pdf-ocr-hotfolder/`
|
||
4. Startet alle zuvor laufenden Instanzen neu
|
||
|
||
Config-Dateien unter `/etc/pdf-ocr-hotfolder/` werden **nie** überschrieben.
|
||
Das Repo muss bestehen bleiben — `update.sh` kopiert daraus.
|
||
|
||
## Manueller Lauf (One-Shot)
|
||
|
||
Bestehende PDFs einer Instanz einmalig verarbeiten und beenden:
|
||
|
||
```bash
|
||
sudo -u pdfocr /opt/pdf-ocr-hotfolder/venv/bin/python -m pdf_ocr_hotfolder \
|
||
--config /etc/pdf-ocr-hotfolder/kunde-a.toml --once
|
||
```
|
||
|
||
## Troubleshooting
|
||
|
||
### Tesseract findet die Sprache nicht
|
||
```bash
|
||
sudo apt install tesseract-ocr-deu tesseract-ocr-eng
|
||
```
|
||
|
||
### "PriorOcrFoundError"
|
||
ocrmypdf erkennt bereits vorhandenen OCR-Text. `skip_text = true` in der Config setzen.
|
||
|
||
### Berechtigungsprobleme bei AD-User
|
||
Service-User braucht **rw** auf alle vier Verzeichnisse unter `/var/lib/pdf-ocr-hotfolder/`. Bei AD-User mit lokaler UID:
|
||
```bash
|
||
sudo chown -R DOMAIN\\scanuser:DOMAIN\\scangroup /var/lib/pdf-ocr-hotfolder
|
||
```
|
||
|
||
### LXC/Container: Error 226/NAMESPACE
|
||
In LXC-Containern schlagen systemd-Hardening-Optionen fehl. Der Installer erkennt Container automatisch und bietet ein Drop-in an. Manuell:
|
||
```bash
|
||
sudo mkdir -p /etc/systemd/system/pdf-ocr-hotfolder@.service.d/
|
||
sudo cp /opt/pdf-ocr-hotfolder/systemd/lxc-compat.conf \
|
||
/etc/systemd/system/pdf-ocr-hotfolder@.service.d/
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl restart 'pdf-ocr-hotfolder@*'
|
||
```
|
||
|
||
### Ghostscript PDF/A-Bug auf Debian 12
|
||
GS 10.00.0–10.02.0 (Debian 12 Default) zerstört OCR bei `pdfa_level` + `skip_text=true`. Der Installer bietet automatisch bookworm-backports an. Manuell:
|
||
```bash
|
||
echo 'deb http://deb.debian.org/debian bookworm-backports main' | \
|
||
sudo tee /etc/apt/sources.list.d/bookworm-backports.list
|
||
sudo apt update && sudo apt install -t bookworm-backports ghostscript
|
||
```
|
||
|
||
### veraPDF-Validierung schlägt immer fehl
|
||
veraPDF binary prüfen (`[verapdf].binary`). Wenn nicht zwingend gebraucht: `enabled = false`.
|
||
|
||
## Architektur
|
||
|
||
```
|
||
┌──────────┐ watchdog ┌──────────────┐ ocrmypdf ┌──────────┐
|
||
│ Scanner │ ──────────────▶ │ incoming/ │ ─────────────▶ │ working/ │
|
||
└──────────┘ PDF-Datei └──────────────┘ (Library) └────┬─────┘
|
||
│
|
||
optional veraPDF
|
||
│
|
||
▼
|
||
┌──────────────┐
|
||
│ outgoing/ │
|
||
└──────┬───────┘
|
||
│
|
||
┌──────────────────────┼──────────────────────┐
|
||
▼ ▼ ▼
|
||
┌────────────┐ ┌────────────┐ ┌────────────┐
|
||
│ Nextcloud │ │ SFTP │ │ E-Mail │
|
||
│ (WebDAV) │ │ (paramiko) │ │ Notify │
|
||
└────────────┘ └────────────┘ └────────────┘
|
||
```
|
||
|
||
## Lizenz
|
||
|
||
MIT — © Sonith UG
|
||
|
||
---
|
||
|
||
**Version:** 0.3.1
|
||
**Repo:** https://gitea.sonith.de/sonith_ug/pdf-ocr-hotfolder
|