138 lines
4.1 KiB
Markdown
138 lines
4.1 KiB
Markdown
# KitchenOwl Recipe Import (Doris-managed)
|
|
|
|
## Why this exists
|
|
|
|
KitchenOwl's built-in import/scrape flow is unreliable for the way John and Leanne actually send recipe links around.
|
|
|
|
This repo-side helper gives Doris a safer path:
|
|
|
|
1. try KitchenOwl's native `/recipe/scrape` endpoint for sites it understands
|
|
2. if that fails, fetch the page directly
|
|
3. extract schema.org `Recipe` JSON-LD
|
|
4. normalize ingredients/tags/timings into KitchenOwl's create-recipe payload
|
|
5. optionally create the recipe through the KitchenOwl API
|
|
|
|
That means Doris can ingest recipe links from chat without depending on KitchenOwl's flaky native importer.
|
|
|
|
## Script
|
|
|
|
```bash
|
|
automation/bin/kitchenowl_recipe_import.py
|
|
```
|
|
|
|
Default mode is **dry-run**. It prints the normalized KitchenOwl payload and does **not** create anything unless `--create` is passed.
|
|
|
|
## Required env vars
|
|
|
|
Put these in a live `.env` file or export them ad hoc. Do **not** commit live secrets.
|
|
|
|
```env
|
|
KITCHENOWL_BASE_URL=https://owl.paccoco.com
|
|
KITCHENOWL_API_TOKEN=CHANGE_ME
|
|
KITCHENOWL_HOUSEHOLD_ID=1
|
|
```
|
|
|
|
Notes:
|
|
- `KITCHENOWL_API_TOKEN` should be a long-lived token for a user who can create recipes in the target household.
|
|
- `KITCHENOWL_HOUSEHOLD_ID` for the current live instance is `1` (`Stafford's`) unless that changes later.
|
|
|
|
## Usage
|
|
|
|
### Dry-run a link
|
|
|
|
```bash
|
|
cd /home/fizzlepoof/repos/truenas-stacks
|
|
export KITCHENOWL_BASE_URL=https://owl.paccoco.com
|
|
export KITCHENOWL_API_TOKEN=...redacted...
|
|
export KITCHENOWL_HOUSEHOLD_ID=1
|
|
|
|
automation/bin/kitchenowl_recipe_import.py --pretty \
|
|
"https://example.com/my-recipe"
|
|
```
|
|
|
|
### Actually create the recipe
|
|
|
|
```bash
|
|
automation/bin/kitchenowl_recipe_import.py --pretty --create \
|
|
"https://example.com/my-recipe"
|
|
```
|
|
|
|
### Import several links at once
|
|
|
|
```bash
|
|
automation/bin/kitchenowl_recipe_import.py --create --pretty \
|
|
"https://example.com/recipe-1" \
|
|
"https://example.com/recipe-2"
|
|
```
|
|
|
|
## Behavior details
|
|
|
|
### Strategy order
|
|
|
|
By default the helper tries:
|
|
|
|
1. `GET /api/household/<id>/recipe/scrape?url=...`
|
|
2. if KitchenOwl returns `Unsupported website` or otherwise fails, Doris falls back to direct HTML fetch + JSON-LD parse
|
|
|
|
If you want to skip the built-in scrape and go straight to Doris mode:
|
|
|
|
```bash
|
|
automation/bin/kitchenowl_recipe_import.py --skip-kitchenowl-scrape --pretty URL
|
|
```
|
|
|
|
### Duplicate protection
|
|
|
|
Before creating a recipe, the helper loads existing household recipes and skips creation if it finds:
|
|
- an exact matching `source` URL, or
|
|
- an exact matching recipe `name`
|
|
|
|
To force duplicates anyway:
|
|
|
|
```bash
|
|
automation/bin/kitchenowl_recipe_import.py --create --allow-duplicates URL
|
|
```
|
|
|
|
### Visibility
|
|
|
|
KitchenOwl visibility enum:
|
|
- `0` = private (default)
|
|
- `1` = link
|
|
- `2` = public
|
|
|
|
Example:
|
|
|
|
```bash
|
|
automation/bin/kitchenowl_recipe_import.py --create --visibility 0 URL
|
|
```
|
|
|
|
## What parses well
|
|
|
|
The fallback parser works best on recipe pages that expose proper schema.org `Recipe` JSON-LD, which many decent recipe sites do.
|
|
|
|
It pulls:
|
|
- name
|
|
- description
|
|
- instructions
|
|
- ingredient list
|
|
- total/prep/cook time
|
|
- yield/servings
|
|
- categories/cuisine/keywords → KitchenOwl tags
|
|
|
|
## Known limitations
|
|
|
|
- Some sites block scraping aggressively; Doris first tries normal fetch, then falls back to `curl` headers for better odds.
|
|
- Ingredient normalization is heuristic, not magic. It aims for a sane KitchenOwl item name plus a description/amount, but some weird ingredients may still need manual cleanup.
|
|
- Image upload/import is not wired yet in this helper.
|
|
- The helper expects structured JSON-LD. If a site hides the recipe entirely in client-side blobs or anti-bot nonsense, manual copy/paste may still be needed.
|
|
- `GET /api/settings` on the live KitchenOwl instance currently returns `500`, but that does not block recipe import.
|
|
|
|
## Doris workflow expectation
|
|
|
|
Preferred operator flow when John or Leanne sends recipe links:
|
|
|
|
1. Doris runs a dry-run first when the site is unfamiliar or suspicious.
|
|
2. If the normalized payload looks sane, Doris reruns with `--create`.
|
|
3. If the page is too messy, Doris asks for the raw ingredients/steps and imports it manually.
|
|
|
|
That keeps KitchenOwl as the recipe store while making Doris the reliable intake layer.
|