Open-source project for Schniddzl.de, fetching and displaying restaurant menus automatically.
Deploy using Docker Compose. It pulls the latest image and exposes the app on the specified port.
Open the app locally: http://localhost:8156
services:
mittagskarte:
image: ghcr.io/flohoss/mittagskarte:latest
container_name: mittagskarte
restart: always
volumes:
- ./config:/app/config
ports:
- "8156:8156"Mittagskarte fetches restaurant menus and converts them into fast-loading webp images.
Key features:
- Downloads menus in PDF or image formats
- Scrapes menus from HTML pages
- Converts menus to webp, reducing images wider than 1920px
- Updates menus automatically based on a cron schedule
See config/config.yaml for a full example.
| Key | Description |
|---|---|
api_token |
Required if a restaurant has no parse section. Used to upload a menu image manually. |
impressum |
Show legal info on the page (enabled: true) with responsible name and email. |
log_level |
Logging verbosity (debug, info, warn, error) |
meta.title |
Website title |
meta.description |
Suffix for the HTML description. Full HTML <meta> description = {meta.title} - {meta.description} |
meta.social |
Array of social media links (optional) |
restaurants |
Dictionary of restaurants |
server.address |
Host to bind (0.0.0.0 for all interfaces) |
server.port |
Port to serve the app |
time_zone |
Used for scheduling updates |
umami_analytics |
Optional analytics integration (enabled, domain, websiteid) |
Notes:
- Only
nameandurlare strictly required for each restaurant. - If
parseis empty,api_tokenmust be set. - Menus are automatically resized to a maximum width of 1920px.
Direct PDF download
parse:
update_cron: "30 9,10 * * 1,3"
direct_download: "https://davvero-stuttgart.de/download/mittagskarte.pdf"
file_type: "pdf"Image download via CSS selector
parse:
update_cron: "30 9,10 * * 1,2"
navigate:
- locator: ".et_pb_image_1 > span:nth-child(1) > img:nth-child(1)"
attribute: "src"
file_type: "image"HTML scraping
parse:
update_cron: "30 9,10 * * 1,4"
navigate:
- locator: "p.paragraph-mittagstisch-right-corona"
style: ".w-nav { display: none !important; }"PDF link via XPath
parse:
update_cron: "30 9,10 1-3 * *"
navigate:
- locator: "//a[contains(text(), 'Mittagstisch')]"
file_type: "pdf"All navigate steps use:
- locator: "<CSS or XPath selector>"
attribute: "<optional HTML attribute to fetch>"
style: "<optional CSS to hide unwanted elements>"Use {{date(...)}} to match menus dynamically.
| Argument | Description |
|---|---|
format |
Go time format (e.g., 02.01.2006, Jan) |
lang |
Language (en, de). Defaults to en |
day |
Weekday to adjust to (monday, tuesday, etc.) |
offset |
Number of weeks to shift (-1 last week, 0 this week, 1 next week) |
upper |
Convert output to uppercase |
Example:
"locator": "//div[@class='calendar']//span[text()='{{date(format=02.01.2006, day=fr, offset=-1)}}']"You can add custom thumbnails for restaurants to be displayed in the app.
-
Folder: Place your thumbnails inside the
config/thumbnailsdirectory. -
File Name: Name the thumbnail file exactly like the restaurant key in your
restaurantssection, with a.webpextension.-
Example: For the restaurant key
sw34, the thumbnail must be:config/thumbnails/sw34.webp
-
-
Format: Only WebP format is supported.
The app automatically uses the thumbnail for a restaurant:
background-image: url(/thumbnails/<restaurant_key>.webp);Example with sw34:
background-image: url(/thumbnails/sw34.webp);Note: WebP is required. You can convert images online using https://mazanoke.y8o.de/.
docker compose up --build --force-recreate- Auto-creates
config.yamlif missing - Detects changes automatically
# Node packages
docker compose run --rm node yarn upgrade
# Go packages
docker compose run --rm backend go get -u && go mod tidy