diff --git a/app/main.py b/app/main.py
index 3915719..09f8e60 100644
--- a/app/main.py
+++ b/app/main.py
@@ -81,7 +81,19 @@ def scrape_url():
return jsonify({"ok": False, "error": "Nincs URL megadva."})
try:
data = scrape(url)
- return jsonify({"ok": True, "data": data})
+
+ # Check for duplicate in Mealie
+ duplicate = None
+ cfg = config.load()
+ if cfg.get("mealie_url") and cfg.get("mealie_api_key"):
+ try:
+ client = MealieClient(cfg["mealie_url"], cfg["mealie_api_key"],
+ api_url=config.MEALIE_INTERNAL_URL)
+ duplicate = client.find_duplicate(url, data.get("title", ""))
+ except Exception:
+ pass # non-fatal
+
+ return jsonify({"ok": True, "data": data, "duplicate": duplicate})
except Exception as exc:
return jsonify({"ok": False, "error": str(exc), "trace": traceback.format_exc()})
diff --git a/app/mealie.py b/app/mealie.py
index 90094e7..f31419c 100644
--- a/app/mealie.py
+++ b/app/mealie.py
@@ -29,6 +29,40 @@ class MealieClient:
r.raise_for_status()
return r.json()
+ def find_duplicate(self, url: str, title: str = "") -> dict | None:
+ """Check if a recipe with this original URL already exists.
+
+ Searches by URL in orgURL and description fields.
+ Returns {"slug": ..., "name": ..., "url": ...} or None.
+ """
+ if not url:
+ return None
+ # Search by title to find candidates (Mealie search matches name)
+ search_term = title or url.split("/")[-1].replace("-", " ")
+ r = self.session.get(
+ f"{self.api_url}/api/recipes",
+ params={"search": search_term, "perPage": 50},
+ timeout=10,
+ )
+ if not r.ok:
+ return None
+ for item in r.json().get("items", []):
+ detail = self.session.get(
+ f"{self.api_url}/api/recipes/{item['slug']}", timeout=10
+ )
+ if not detail.ok:
+ continue
+ data = detail.json()
+ org = data.get("orgURL", "") or ""
+ desc = data.get("description", "") or ""
+ if org == url or url in desc:
+ return {
+ "slug": data["slug"],
+ "name": data.get("name", data["slug"]),
+ "url": f"{self.base_url}/g/home/r/{data['slug']}",
+ }
+ return None
+
def create_recipe(self, recipe: dict) -> str:
"""Create a recipe in Mealie from a scraper result dict.
@@ -175,12 +209,17 @@ class MealieClient:
"ingredientReferences": [],
})
+ description = recipe.get("description", "")
+ original_url = recipe.get("original_url", "")
+ if original_url and original_url not in description:
+ description = f"{description}\n\n{original_url}".strip()
+
return {
"name": recipe["title"],
- "description": recipe.get("description", ""),
+ "description": description,
"recipeIngredient": ingredients,
"recipeInstructions": instructions,
- "orgURL": recipe.get("original_url", ""),
+ "orgURL": original_url,
"recipeYield": "",
}
diff --git a/app/templates/base.html b/app/templates/base.html
index 4457b81..bebf582 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -162,6 +162,7 @@
.text-dim { color: var(--text-dim); }
.text-success { color: var(--success); }
.text-danger { color: var(--danger); }
+ .text-warning { color: var(--warning); }
.flex { display: flex; gap: 0.75rem; align-items: center; }
.flex-wrap { flex-wrap: wrap; }
.grow { flex: 1; }
diff --git a/app/templates/import.html b/app/templates/import.html
index d89efb6..fd44881 100644
--- a/app/templates/import.html
+++ b/app/templates/import.html
@@ -232,7 +232,14 @@ async function scrapeRecipe() {
currentRecipe = data.data;
populatePreview(currentRecipe);
document.getElementById('previewCard').classList.add('visible');
- status.innerHTML = '✓ Beolvasva';
+
+ if (data.duplicate) {
+ status.innerHTML = '⚠ Ez a recept már létezik Mealie-ben: '
+ + ''
+ + escHtml(data.duplicate.name) + '';
+ } else {
+ status.innerHTML = '✓ Beolvasva';
+ }
} catch (e) {
status.innerHTML = 'Hálózati hiba: ' + e.message + '';
}