985a33d3f9
- pdf-ocr-hotfolder@<name>.service mit Config pro Instanz - install.sh als Instanz-Manager: erkennt bestehende, fragt nach weiteren - Optional eigener Service-User pro Instanz (systemd drop-in) - update.sh stoppt/startet alle aktiven Instanzen automatisch Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.0 KiB
6.0 KiB
AI Agent Briefing — PDF OCR Hotfolder
Zuletzt aktualisiert: 2026-04-08 Version: 0.2.0 Status: Multi-Instanz-Support, nicht produktiv getestet
🎯 Projektziel
Eingehende gescannte PDFs werden automatisch durch OCR (ocrmypdf + Tesseract) in durchsuchbare PDFs (optional PDF/A) umgewandelt und nach Wahl in einen Ordner / Nextcloud / per SFTP weitergegeben. Ersetzt das alte Bash-Tool pdf-tool (im Workspace).
📁 Projekt-Struktur
pdf-ocr-hotfolder/
├── pdf_ocr_hotfolder/
│ ├── __init__.py # Versionsstring
│ ├── __main__.py # CLI-Entrypoint (argparse, --once, --config)
│ ├── config.py # TOML-Loader, Dataclasses
│ ├── service.py # Hauptservice (watchdog + ThreadPool)
│ ├── processor.py # ocrmypdf + veraPDF
│ └── uploaders.py # folder, nextcloud (WebDAV), sftp, email
├── systemd/
│ └── pdf-ocr-hotfolder@.service # systemd Template-Unit (Instanz = %i)
├── config.example.toml
├── install.sh # Interaktiver Installer
├── update.sh # Update aus Repo
├── requirements.txt
├── VERSION
├── CHANGELOG.md
└── README.md
🔧 Stack
| Komponente | Technologie |
|---|---|
| Sprache | Python 3.11+ (für tomllib aus stdlib) |
| OCR | ocrmypdf (als Library, nicht via Subprozess) |
| Engine | Tesseract |
| Watcher | watchdog |
| HTTP | requests (Nextcloud WebDAV) |
| SFTP | paramiko |
smtplib (stdlib) |
|
| Service | systemd |
🖥️ Installations-Layout (Multi-Instanz)
| Pfad | Inhalt |
|---|---|
/opt/pdf-ocr-hotfolder/ |
Code + venv (für alle Instanzen gemeinsam) |
/etc/pdf-ocr-hotfolder/<instanz>.toml |
Config pro Instanz (mode 640, root:) |
/etc/systemd/system/pdf-ocr-hotfolder@.service |
Template-Unit |
/etc/systemd/system/pdf-ocr-hotfolder@<instanz>.service.d/user.conf |
Drop-in für abweichenden User (optional) |
/var/lib/pdf-ocr-hotfolder/<instanz>/{incoming,working,outgoing,error}/ |
Daten pro Instanz |
/var/log/pdf-ocr-hotfolder/ |
Logs |
/var/backups/pdf-ocr-hotfolder/ |
Update-Backups |
👤 Service-User
- Basis-Install legt Default-User
pdfocran (als System-User, falls nicht schon vorhanden) - Beim Anlegen einer Instanz fragt der Installer nach dem Service-User (default
pdfocr) - Wird ein abweichender User gewählt, wird ein systemd-Drop-in erstellt (
pdf-ocr-hotfolder@<instanz>.service.d/user.conf) mitUser=/Group=Override - Existierende User (lokal oder AD via SSSD/Winbind) werden übernommen, primäre Gruppe via
id -gnermittelt - Bei AD-Usern mit lokaler UID werden Datei-Berechtigungen über die UID gesetzt — transparent
🗂️ Instanz-Management
install.sh ist gleichzeitig Installer und Instanz-Manager:
- Erster Lauf: Basis-Install + erste Instanz anlegen (Pflicht)
- Folgender Lauf: Basis-Install wird übersprungen, bestehende Instanzen werden gelistet, weitere Instanzen können ergänzt werden
- Eingaben pro Instanz: Name (
[a-z0-9-]+), Basis-Pfad (default/var/lib/pdf-ocr-hotfolder/<name>), Service-User config.tomlwird ausconfig.example.tomlmit sed-substituierten Pfaden generiert- Instanz wird sofort
enable --nowgestartet
Manuelles Löschen einer Instanz:
systemctl disable --now pdf-ocr-hotfolder@<name>
rm /etc/pdf-ocr-hotfolder/<name>.toml
rm -rf /etc/systemd/system/pdf-ocr-hotfolder@<name>.service.d
systemctl daemon-reload
# Datenverzeichnis /var/lib/pdf-ocr-hotfolder/<name> manuell aufräumen
🔄 Update-Verhalten
update.sh:
- Ermittelt alle aktiven
pdf-ocr-hotfolder@*.serviceUnits - Stoppt diese
- Backup nach
/var/backups/pdf-ocr-hotfolder/ - Kopiert Code + requirements + VERSION + config.example aus dem Repo
pip install --upgradeim venv- Aktualisiert Template-Unit +
daemon-reload - Startet alle zuvor aktiven Instanzen wieder
- Exit 1 wenn eine Instanz nicht mehr hochkommt
Config-Dateien werden nie überschrieben.
🔄 Verarbeitungs-Flow
watchdogtriggert auf Datei-Event inincoming/_wait_until_stable()wartet, bis Datei nicht mehr wächst (Scanner schreibt mehrmals)- Move nach
working/ ocrmypdf.ocr()als Library-Call (kein Subprozess-Start pro PDF — schneller)- Optional: veraPDF-Validierung (CLI-Subprozess)
- Move nach
outgoing/alsOCR_<originalname>.pdf - Aktive Upload-Targets ausführen (folder/nextcloud/sftp)
- Optional E-Mail-Notify
Fehler → Move nach error/, Service läuft weiter (kein exit 1 wie im alten Bash-Tool).
🧠 Performance-Entscheidungen
- ocrmypdf als Library statt
subprocess: spart Python-Interpreter-Start pro PDF - ThreadPool mit
max_workers(default 2) — selbst wenn selten >1 PDF gleichzeitig kommt, blockiert ein langsamer Scan keinen schnellen --jobsan ocrmypdf: Tesseract parallelisiert Seiten innerhalb eines PDFsskip_text=True: bereits OCR-haltige Seiten werden nicht neu verarbeitet- Stabilitäts-Check statt magic-file
new(alte Bash-Krücke) - veraPDF nur wenn
enabled=true(JVM-Start ist teuer)
🛠️ Entwicklung
Lokaler Test ohne Installation:
cd ~/dev/gitea.sonith.de/pdf-ocr-hotfolder
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
cp config.example.toml /tmp/config.toml
# Pfade in /tmp/config.toml auf Test-Verzeichnisse anpassen
python -m pdf_ocr_hotfolder --config /tmp/config.toml
📋 Roadmap / TODO
- Tests (
pytest) fürprocessorunduploaders - Prometheus-Metriken (verarbeitete PDFs, Fehlerquote, Laufzeit)
- CLI-Subkommandos:
pdf-ocr-hotfolder reprocess <error-file> - Optional: S3/MinIO Upload-Target
- Docker-Image für Setups ohne systemd
🔑 Repo
- Repo: https://gitea.sonith.de/sonith_ug/pdf-ocr-hotfolder
- Owner: sonith_ug
- Versionierung: Semver (PATCH bei jedem Build, MINOR bei Features, MAJOR manuell)
- Tags:
v{VERSION}, automatischer Push nach Commit
📞 Kontakt
Maintainer: Dominik Höfling (Sonith GmbH)