- Python 99.5%
- Shell 0.4%
- Dockerfile 0.1%
mealie-import auswerten [/Volumes/rezeptimport] [--seit JJJJ-MM-TT] [-o datei] stellt je Rezept den Stand beim Import (Datei im Auftragsordner, ueber mealie_objekt wie beim Hochladen abgebildet) neben den heutigen in Mealie: noch im Posteingang, durchgesehen mit oder ohne Aenderungen, geloescht; dazu die auffaelligen Berichtszeilen. Ersetzte Laeufe desselben Fotos werden nur genannt. Die Aenderungen beim Durchsehen sind die Rueckmeldung, an der sich der lokale Leser verbessern laesst. Am echten Bestand vom 2026-10-13 geprueft: zeigt die Handkorrekturen an fuenf Rezepten richtig. Claude-Session: https://claude.ai/code/session_01TjsuBxX38MCCCm38iBHNP7 |
||
|---|---|---|
| beispiele/erotik-food-kochkurs | ||
| deploy | ||
| src/mealie_import | ||
| tests | ||
| .dockerignore | ||
| .gitignore | ||
| Dockerfile | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
mealie-import
Rezepte aus Webseiten, Fotos, Scans und ganzen Rezeptmappen redigiert nach Mealie bringen. Mit Abschnitten, Tags, Kategorien, Notizen und Bild. Nach dem Hochladen wird zurückgelesen, ob alles angekommen ist.
Entstanden am 2026-10-11 aus der Rezeptmappe eines Kochkurses (PDF, 4 Gänge, 9 Teilrezepte). Nachgemessen gegen Mealie 3.28.0.
Warum, wenn Mealie selbst importieren kann
Mealie hat einen URL-Import und einen KI-Import. Der KI-Import kann aber nur ein Rezept je Vorgang. Sein Prompt sagt wörtlich: „Assume everything you receive describes exactly one recipe.“ Er liest keine PDFs, schneidet das Foto nicht zu und kennt keine Menüs. Auf mealie.motocamp.de ist er außerdem nicht eingerichtet (Stand 2026-10-11: kein KI-Anbieter).
Dieses Werkzeug deckt den Rest ab:
- Dokumente mit mehreren Rezepten, Menüs (ein Rezept je Gang, Teilrezepte als Abschnitte)
- eine Durchsicht vor dem Hochladen: Das Rezept liegt als lesbares Markdown vor, Abweichungen von der Vorlage stehen als Notiz im Rezept
- vorhandene Tags und Kategorien vorab abgleichen, Tippfehler fangen
- aktualisieren statt Dubletten anlegen
- Nachmessen nach jedem Upload
Das Lesen und Redigieren übernimmt Claude Code (Skill rezept-extraktor-fuer-mealie).
Das Werkzeug erledigt den mechanischen Teil und ruft selbst kein Sprachmodell auf.
Einrichten
uv sync
mkdir -p ~/.config/mealie-import && chmod 700 ~/.config/mealie-import
cat > ~/.config/mealie-import/config.toml <<'EOF'
url = "https://mealie.motocamp.de"
token = "…" # Mealie → Profil → API-Tokens
EOF
chmod 600 ~/.config/mealie-import/config.toml
MEALIE_URL und MEALIE_TOKEN in der Umgebung haben Vorrang vor der Datei.
Befehle
| Befehl | Was er tut |
|---|---|
organizer |
vorhandene Kategorien, Tags, Werkzeuge anzeigen |
pruefen DATEIEN|ORDNER |
Form, Typografie, Bilder, Dubletten; mit Mealie: was neu angelegt würde, was schon existiert. --offline ohne Mealie |
hochladen DATEIEN|ORDNER |
zeigt den Plan (Probelauf); mit --ja anlegen bzw. aktualisieren, danach nachmessen |
url URL [-o ORDNER] |
Entwurf aus einer Webseite, mit Bild |
bild FOTO --raster 100 |
Vorschau mit Koordinatengitter zum Ausmessen |
bild FOTO --box x,y,b,h |
zuschneiden, aufrichten, auf 1600 px verkleinern, JPEG |
zip DATEIEN -o X.zip |
Export für die Nextcloud-Migration in der Oberfläche, ohne Token |
Aufruf mit uv run mealie-import ….
Eingangsordner (Dauerdienst auf der DS)
mealie-import eingang ORDNER beobachtet einen Ordner wie den Consume-Ordner von
Paperless. Auf der DS ist das /volume1/rezeptimport, eingebunden als
Container mealie-import (siehe deploy/). Ein Auftrag ist eine Datei oder ein
Unterordner: Foto, Scan, HEIC vom iPhone, PDF, Link-Datei (.webloc, .url,
.txt).
Ablauf je Auftrag:
- Warten, bis die Datei fertig hochgeladen ist (60 s ohne Änderung).
- Das Original vor dem Modellaufruf nach
.in-arbeit/holen. So gibt es nach einem Absturz keinen zweiten Lauf. - Das Modell lesen lassen, Rezept-Markdown und Foto-Box kommen als strukturierte Ausgabe zurück.
- Prüfen, hochladen, nachmessen. Jedes Rezept bekommt den Tag „Posteingang“: in Mealie durchsehen, dann den Tag entfernen.
- Den Auftrag nach
erledigt/oderfehler/legen, jeweils mitbericht.txt. Die Kosten stehen inkosten.csv.
Leser: lokal (Standard) oder Claude
lokal: qwen3.6-35b-a3b-mlx in LM Studio (M1 Max) |
claude: Opus 5.5 | |
|---|---|---|
| Foto-Zuschnitt, IoU gegen Sollposition | 0,86–0,99 (Koordinaten 0–1000) | 1,00 (Pixel) |
| PDF, 2 Seiten der Kochkurs-Mappe | nach den Sicherungen 28/28 Zutatenzeilen; teilt in 2 Rezepte | 1 Rezept mit Abschnitten |
| Dauer | Foto ca. 1,5 min, PDF 3,5–5 min | 20–40 s |
| Kosten | 0 | 0,07–0,12 $ je Auftrag |
| verfügbar | nur, wenn der Mac läuft (sonst warten die Aufträge) | immer |
Gemessen am 2026-10-11/12 mit tests/integration/eingang_echt.py.
Einstellungen:
MEALIE_IMPORT_LESER=lokal|claudewählt den Leser.MEALIE_IMPORT_PDF_LESER=claudeschickt nur PDFs an Claude.LOKAL_URLundLOKAL_MODELLlegen Server und Modell fest.LM_API_KEYist der Schlüssel für LM Studio; am Mac wird er aus~/.hermes/.envgelesen.
Was der lokale Leser falsch machte und wie es abgefangen wird. Jeder Punkt ist in mindestens einem Lauf vorgekommen:
| beobachtet | Gegenmaßnahme |
|---|---|
| erfundene Zeiten, Portionen, Ausbeuten und Menüs, sogar die Beispielwerte der Formatvorlage wörtlich | Er sieht nur kategorien:. Alle anderen Kopfzeilen werden verworfen und im Bericht genannt. Angaben aus der Vorlage schreibt er wörtlich in die Beschreibung. |
| neue Kategorien („Pasta“ neben „Nudelgerichte“) | Nur vorhandene Kategorien kommen durch. |
| Personen-Tag „René“ ohne Beleg | Er setzt keine Tags. |
## Teig statt ### Teig |
Überschriften werden repariert. |
| Flammkuchen-Teil eines PDFs ganz weggelassen, ohne Meldung | Vollständigkeitsprüfung gegen die Textebene: Fehlen mehr als 10 % der Zutatenzeilen, wird nicht hochgeladen. |
| Selbstauskunft behauptet Dinge, die nicht im Ergebnis stehen | Der Hinweis heißt im Bericht „ungeprüft“. |
| Umlaute in Umschrift („Pilzfueellung“), weil der Systemtext so geschrieben war | Der Systemtext steht in richtigem Deutsch. |
strukturierte Ausgabe landet in reasoning_content statt content |
Beide Felder werden gelesen. |
| Schritte umgestellt („in den Ofen“ vor dem Kneten) und Absätze am Ende weggelassen, ohne Meldung (Roggen-Dinkel-Mischbrot) | Erst wörtliche Abschrift der Seite, dann das Rezept (Feld abschrift vorn im Schema). abgleich.py vergleicht über Wortpaare: Schritte zurück in die Reihenfolge der Vorlage, fehlender Text als Schritt angehängt oder als Notiz. Bei PDFs dient die Textebene als Abschrift. |
| Foto nicht gemeldet (Mischbrot) oder mit falscher Box; auf einer Seite mit vier Rezepten und vier Fotos eines gemeldet, links bei 0 statt 147 ‰ | Fotos kommen beim lokalen Leser nur aus einer kleinen zweiten Anfrage, je Rezept. Die große ist Rückfall, falls die kleine scheitert. |
in dieser Nachfrage vier richtige Boxen, aber foto_bild: -1 („keines“); mit Bildern ab 1 hielt er die Zahl für die Buchseite |
Eigenes Feld hat_foto; bei nur einem Bild gilt immer dieses. Fünf Seiten in sieben Läufen richtig, auch die beiden ohne Gericht-Foto. |
| Zutatenzeilen des Nachbarrezepts als Schritt angehängt (Nudelseite) | Der Abgleich hängt nur Fließtext an. Buchsatz wird vorher zerlegt: Trennstriche, umbrochene Zeilen, „·“ zwischen Zutaten. |
| Variationen übersprungen, ihr Text als elf Schritte ans Hauptrezept gehängt (Rouladen) | Mehr als zwei fehlende Absätze je Rezept gehen als Notiz ein, mit dem Hinweis auf übersprungene Rezepte. Im Systemtext: Variationen mit eigenen Zutaten sind eigene Rezepte. |
| Zitat-Tipp beim falschen Rezept (Wurzelpüree-Tipp unter Selleriepüree) | Ein Tipp geht an das Rezept, dessen Name darin steht. |
| Beschreibung wiederholt die Schritte | Sätze, die fast wörtlich in den Schritten stehen, fliegen aus der Beschreibung. |
„Für 4 Personen“ bei allen vier Nudelrezepten weggelassen, Zubereitungszeit und Kalorien behalten; „4 Personen“ als Kopfzeile Portionen: geschrieben, die verworfen wird |
Eine Portionsangabe der Abschrift („Für 4 Personen“, „Zutaten für 4 Personen“, „4 Personen“), die vor den Schritten eines Rezepts steht und ihm fehlt, kommt an den Anfang seiner Beschreibung. |
| Zutatenliste des Buchs in einer Zeile mit Kommas galt als „nicht zugeordnet“ | Die Zutaten eines Abschnitts zählen für den Abgleich auch am Stück. |
| Zutaten in erfundene Abschnitte gegliedert („Nudeln und Gemüse“, „Garnitur“) | Steht keiner der Titel als Zeile in der Abschrift, werden die Abschnitte zusammengelegt. |
| ein Rezept der Seite ausgelassen (Pichelsteiner Eintopf neben dem Wurstsalat; die vier Füllungen neben den Rouladen), der Abgleich hängte dessen Text als Schritte an | Nennt die Abschrift mehr Rezeptköpfe („Für 4 Personen“, „Zutaten für 4 Personen“, „4 Personen“) als Rezepte geliefert sind, fragt das Werkzeug mit denselben Bildern nach den fehlenden (Rezept-Nachfrage, ca. 1 min). Text hinter einem weiteren Rezeptkopf wird nie als Schritt angehängt, sondern als Notiz mit Warnung. Die Zählregel schlägt an neun echten Seiten genau bei den zwei mit ausgelassenem Rezept an. |
Zutaten mit „|“ statt „·“ gereiht; ### Abschnitt und ### Schritt 1 als Titel (aus der Formatvorlage bzw. erfunden) |
„|“ trennt wie „·“. Ein einzelner Abschnitt verliert einen Titel, der nicht in der Vorlage steht; mehrere werden zusammengelegt. |
| Tipp-Kasten mal als Notiz, mal als Schritt („PROFI-TIPP: …“) | Ein Schritt, der mit „Tipp:“ oder „PROFI-TIPP:“ beginnt, wird Notiz „Tipp“. |
gerade Anführungszeichen (Kochbuch "Suppen und Schmankerl") |
Paare in einer Zeile werden „…“. |
| erfundene Beschreibungen („Ein klassischer bayerischer Wurstsalat mit …“) | Ein Satz fällt weg, wenn weniger als 20 % seiner Wortpaare in der Abschrift stehen – nur, wenn die Abschrift mindestens 70 % der Schritte des Rezepts enthält (in einer Abschrift fehlte ein Rezept ganz). Gemessen: erfundene 0,00/0,12, echte 0,94–1,00. |
| „Zutaten für 4 Personen“ als Abschnittstitel | Ein Zutaten-Titel, der nur eine Portionsangabe ist, wird Beschreibung. |
Kategorien: groß geschrieben, still verloren |
Kopfzeilen werden ohne Rücksicht auf Groß- und Kleinschreibung gelesen. |
| Lesefehler in Mengen („1 Msp.“ statt „1 TL“ in kursiver Kleinschrift) | Nicht abgefangen: Der Fehler steht schon in der Abschrift. Beim Durchsehen die Mengen gegen das Buch lesen. Messungen dazu unten. |
Lesefehler in kleiner Schrift: was gemessen ist (IMG_1914, Fleischpflanzerl-Zutaten, 2026-10-12). Je eine Probe, wenn nicht anders angegeben:
| gesendet | Aufgabe | Ergebnis | Dauer |
|---|---|---|---|
| ganze Doppelseite, 2212×1659 (heute) | Hauptanfrage | falsch (2 von 2 Läufen) | ca. 3 min |
| ganze Doppelseite, 2212×1659 / 3200×2400 / 4032×3024 | nur diesen Abschnitt abschreiben | falsch, je eine Zeile fehlt | 128 / 77 / 109 s |
| Halbseite, 1568×2334 | nur diesen Abschnitt abschreiben | richtig (2 von 2) | 60–77 s |
| zwei überlappende Hälften (je 60 %) | Hauptanfrage | falsch, keine doppelten Rezepte | 558 s |
| Halbseite, 1568×2334 | ganze Halbseite abschreiben | richtig | 329 s |
Größere Bilder ändern nichts. Das Bild wird vor dem Modell offenbar auf eine feste Größe gebracht, die genaue Grenze ist nicht gemessen. Hälften helfen nur, wenn das Modell sonst nichts zu tun hat. Ein zweistufiger Ablauf (je Hälfte nur abschreiben, dann aus der Abschrift gliedern) läge hochgerechnet bei 12–15 min je Doppelseite und ist nicht gebaut. | Kategorie „Brot“, vorhanden ist nur „Brot & Backwaren“ | Eindeutig passende vorhandene Kategorie wird übernommen (Teilwort oder sehr ähnlich), sonst verworfen. |
Ein zweiter Lauf desselben Fotos (Datei aus fehler/ oder erledigt/ zurück in
den Eingang) ersetzt ein gleichnamiges Rezept, wenn es noch den Tag „Posteingang“ trägt
und aus demselben Foto stammt (Prüfsumme in den extras), auch unter „Titel (2)“. In jedem anderen Fall
wird nichts überschrieben, das neue Rezept heißt dann „Titel (2)“: ein durchgesehenes
Rezept (Tag entfernt) bleibt so geschützt, und ein gleichnamiges Rezept aus einem
anderen Buch geht nicht verloren. Ändert sich beim zweiten Lauf der Titel, entsteht ein
neues Rezept, und das alte muss von Hand gelöscht werden.
Rückschau: mealie-import auswerten [/Volumes/rezeptimport] [--seit JJJJ-MM-TT] [-o datei.md]
liest alle Aufträge in erledigt/ und fehler/ und stellt je Rezept den Stand beim Import
(die Datei im Auftragsordner) neben den heutigen in Mealie: noch im Posteingang,
durchgesehen mit oder ohne Änderungen, gelöscht. Die Änderungen beim Durchsehen sind die
Rückmeldung, an der sich der Leser verbessern lässt. Dazu die auffälligen Berichtszeilen
(Abgleich, Nachfragen, Reparaturen). Ersetzte Läufe desselben Fotos werden nur genannt.
Bei beiden Lesern gilt: Systemschlüssel (mealie:, bild:, quelle:) aus der
Modellantwort werden immer entfernt. Ein erfundenes mealie: würde sonst ein
bestehendes Rezept überschreiben.
Rezeptformat
Eine Markdown-Datei je Rezept. Ein Menü ist ein Ordner: Rezepte mit
demselben menue: im selben Ordner bilden es, gang: legt die Reihenfolge fest.
Beispiel: beispiele/erotik-food-kochkurs/.
# Titel
kategorien: Vorspeisen, Suppen
tags: Martina, René
portionen: 4
ergibt: 12 Taler
vorbereitung: 20 Minuten
kochzeit: 1 Std. 5 Min.
gesamtzeit: 1 Stunde 25 Minuten
quelle: https://…
bild: titel.jpg
menue: Erotik Food Kochkurs
gang: 1
mealie: titel-slug
Beschreibung als freier Text, auch mehrere Absätze.
## Zutaten
### Abschnitt
- 2 Zwiebeln
## Zubereitung
### Abschnitt
1. Zwiebeln würfeln.
Fortsetzungszeilen gehören zum Schritt davor.
## Notizen
### Quelle
Freier Text.
Alle Kopfzeilen sind optional. Was das Werkzeug daraus macht:
menue:wird automatisch zum Tag. Jedes Rezept des Menüs bekommt eine Notiz „Menüfolge“, gebaut aus allen Dateien des Ordners.mealie:schreibt das Werkzeug nach dem ersten Hochladen selbst in die Datei. Ist der Schlüssel gesetzt, aktualisiert der nächste Upload dieses Rezept. Die Datei ist dann die Quelle der Wahrheit: Was in Mealie von Hand an Titel, Zutaten, Schritten, Notizen, Tags oder Kategorien geändert wurde, wird überschrieben. Bewertung, Kommentare und Werkzeuge bleiben.portionen:ist die Zahl, nach der Mealie skaliert.ergibt:ist eine Ausbeute wie „12 Taler“. Ein Bereich wie „10-12 Taler“ bleibt Text.- Zeiten sind Freitext und gehen so nach Mealie.
- Unicode-Brüche in Zutaten (½) werden zu 1/2, damit Mealies Zutaten-Parser sie versteht.
pruefen warnt bei geraden Anführungszeichen (") und bei unpaarigen „ “.
Typische Abläufe
Foto oder Scan (mit Claude Code):
- Claude liest das Foto und schreibt das Rezept-Markdown.
- Das Gerichtfoto zuschneiden:
bild foto.jpg --raster 100, die Box ablesen, dannbild foto.jpg --box x,y,b,h -o rezept.jpg. Das Ergebnis ansehen. pruefen, durchsehen, dannhochladen(Probelauf) undhochladen --ja.
PDF/Rezeptmappe: den Text mit pdftotext -layout, die Seiten mit
pdftoppm -r 60 -png plus magick montage als Kontaktbogen zum Abgleich. Je Gang
ein Markdown, alle in einen Ordner mit menue: und gang:.
Webseite: url URL -o arbeit/, den Entwurf durchsehen. Beschreibungen von
Rezeptportalen sind oft Werbetext („Über 116 Bewertungen …“), Titel tragen den
Autor („… von uevers“). Kategorien und Tags setzen, dann hochladen.
Wie Mealie die Daten ablegt (nachgemessen)
API (hochladen) |
ZIP (zip, Nextcloud-Migration) |
|
|---|---|---|
| Abschnitte bei Zutaten und Schritten | ja | ja |
| Zeiten | wie geschrieben („20 Minuten“) | englisch („20 minutes“); Freitext, der keine Dauer ist, geht verloren |
| Tag-Schreibweise | wie geschrieben | durch str.title(): „BBQ“ → „Bbq“ |
| erneut hochladen | aktualisiert | Dublette |
| Bild | ja | ja (full.jpg im Rezeptordner) |
| Token nötig | ja | nein, Upload in der Oberfläche |
Weitere Fallen des ZIP-Wegs, die der Export schon berücksichtigt:
keywordsals String; eine Liste ergäbe keine Tags.- Zutaten alle als Dict, nie gemischt.
- JSON mit
ensure_ascii, weil Mealie die Datei ohneencoding=öffnet.
Beim URL-Entwurf liest Mealies Scraper das Rezept
(/api/recipes/test-scrape-url, speichert nichts). Das Werkzeug liest die Seite
zusätzlich selbst, denn manche Seiten (Chefkoch) verweisen für Bild und Autor nur
per @id auf andere Teile des JSON-LD. Als Rückfallebene dient og:image.
Tests
uv run --extra eingang pytest # Format, Prüfungen, ZIP, Entwurf, Eingang
uv run python tests/integration/lokales_mealie.py # gegen ein echtes Mealie, lokal
uv run --extra eingang python tests/integration/eingang_echt.py # Eingang mit lokalem Leser
uv run --extra eingang python tests/integration/eingang_echt.py --leser claude --ja-kostet
Der Integrationstest installiert die Mealie-Version aus GEPRUEFT_GEGEN (nur
Backend, Python 3.14) nach ~/.cache/mealie-import/ und startet sie mit leerer
Datenbank. Dann spielt er durch: anlegen (Bild, Zeiten, Ausbeute), aktualisieren
ohne Dublette, Probelauf ohne Änderung und den ZIP-Weg. Mit --url URL kommt der
Webseiten-Entwurf dazu. Stand 2026-10-11: 21/21 bestanden.
Nach einem Mealie-Update auf dem Server: --version X.Y.Z laufen lassen und
bei Erfolg GEPRUEFT_GEGEN in src/mealie_import/mealie.py nachziehen.
hochladen warnt, solange die Versionen nicht übereinstimmen. Die lokale
Installation meldet sich als develop, das ist harmlos.
Beispiel
beispiele/erotik-food-kochkurs/ enthält die Rezepte aus der Rezeptmappe der
Kochschule KreativKochen (Regina Martensen, Otterfing). Sie liegen hier nur als
privates Testmaterial. Vor einer Veröffentlichung des Repos gehören sie durch
eigene Rezepte ersetzt.