- Add original recipe URL to Mealie description (appended after blank line) - Check for duplicate recipes on scrape (match orgURL or URL in description) - Show warning with link to existing recipe if duplicate found - User can still import anyway (warning only, not blocking) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Recipe Importer
Docker container for importing recipes from Hungarian websites into Mealie (Tandoor support planned).
Problem: Mealie's built-in URL import cannot parse ingredients and instructions from Hungarian recipe sites like mindmegette.hu — it imports the title and image but shows "Could not detect ingredients / instructions".
Solution: This container provides a web UI that scrapes Hungarian recipe pages with site-specific parsers, lets you review and edit the extracted data, then pushes it to Mealie via its REST API.
Architecture
┌─────────────────────────────────────────────────┐
│ recipe-importer container (:8000) │
│ │
│ Flask + Gunicorn │
│ ├── /settings → Configure Mealie connection │
│ ├── /import → Paste URL, scrape, review │
│ ├── /scrape → AJAX: parse recipe HTML │
│ ├── /send → AJAX: push to Mealie API │
│ └── /health → Health check │
│ │
│ Modules: │
│ ├── app/config.py → JSON config persistence │
│ ├── app/scraper.py → Site-specific parsers │
│ └── app/mealie.py → Mealie REST API client │
└───────────────────┬─────────────────────────────┘
│ HTTP
▼
┌──────────────────┐
│ Mealie instance │
│ POST /api/recipes│
│ PATCH /api/... │
│ PUT /api/.../img │
└──────────────────┘
Supported Sites
| Site | Ingredients | Instructions | Image |
|---|---|---|---|
| mindmegette.hu | Yes | Yes | Yes |
| Other sites | Fallback (schema.org JSON-LD) | Fallback (schema.org JSON-LD) | Yes (og:image) |
Mindmegette.hu Parser
Extracts data from the Angular-rendered HTML:
- Title:
og:titlemeta tag, with| Mindmegette.husuffix stripped - Description:
og:descriptionmeta tag - Image:
og:imagemeta tag - Ingredients:
div.ingredients→div.ingredients-metarows, each containing<strong>(qty),<span>(unit),<a class="ingredients-link">(food),<small>(extra) - Ingredient groups: Multiple
div.ingredientscontainers; group title via<strong class="ingredients-group"> - Instructions:
mindmegette-wysiwyg-box→ol > lielements
Generic Fallback Parser
For unsupported sites, attempts extraction via:
- Schema.org JSON-LD
@type: Recipeblocks (recipeIngredient,recipeInstructions) - OpenGraph meta tags for title, description, image
Adding a New Site Parser
- Create a parser function in
app/scraper.pywith the@_register("hostname")decorator - The function receives
(soup: BeautifulSoup, url: str)and returns the standard recipe dict - The hostname substring is matched against the URL — first match wins, unmatched URLs use the generic fallback
Mealie API Integration
The importer uses the Mealie REST API:
- POST
/api/recipes— create a stub recipe (returns slug) - PATCH
/api/recipes/{slug}— populate structured ingredients (with unit/food IDs), instructions, description, orgURL - PUT
/api/recipes/{slug}/image— upload the recipe image
Structured ingredients: The client resolves unit and food names to Mealie database IDs. Missing units/foods are created automatically via the API. Ingredient groups are supported via the title field on the first ingredient of each group.
Authentication uses a long-lived API token (Bearer header), created in Mealie at Profile → API Tokens.
Configuration
All settings are persisted to /data/config.json (mounted as a Docker volume).
| Setting | Description |
|---|---|
mealie_url |
Full URL to Mealie instance (e.g. https://mealie.example.com) |
mealie_api_key |
Mealie API token |
Deployment
Docker Compose
services:
recipe-importer:
image: gitea.dooplex.hu/admin/recipe-importer:0.1.7
container_name: recipe-importer
restart: unless-stopped
ports:
- "8011:8000"
volumes:
- recipe-data:/data
environment:
- SECRET_KEY=change-me-in-production
volumes:
recipe-data:
Environment Variables
| Variable | Default | Description |
|---|---|---|
SECRET_KEY |
recipe-importer-dev-key |
Flask session secret |
DATA_DIR |
/data |
Persistent storage path |
VERSION |
dev |
Shown in the UI navbar |
MEALIE_INTERNAL_URL |
(empty) | Docker-internal Mealie URL (e.g. http://mealie:9000) to avoid Cloudflare hairpin |
Building
On the build server (kisfenyo@192.168.0.180):
cd ~/build/recipe-importer
./build.sh X.X.X --push
Web UI
The UI is in Hungarian and uses a dark theme. The workflow is:
- Settings (
/settings) — Enter Mealie URL and API key, test connection - Import (
/import) — Paste a recipe URL, click "Beolvasás" (Scrape) - Review — Edit structured ingredients (4-column: quantity, unit, food, note), add/remove ingredient groups, edit instructions
- Send — Click "Importálás Mealie-be" to push to Mealie
Tech Stack
- Runtime: Python 3.12 (slim)
- Web framework: Flask 3.1 + Gunicorn
- HTML parsing: BeautifulSoup 4 + lxml
- HTTP client: requests
- Container: ~60 MB image