diff --git a/app/scraper.py b/app/scraper.py
index 93578ae..ce6c9e8 100644
--- a/app/scraper.py
+++ b/app/scraper.py
@@ -55,12 +55,19 @@ def scrape(url: str) -> dict:
soup = BeautifulSoup(resp.text, "lxml")
host = _host(url)
+ result = None
for substring, parser in _PARSERS:
if substring in host:
- return parser(soup, url)
+ result = parser(soup, url)
+ break
- # Fallback: try generic schema.org / og-tag extraction
- return _parse_generic(soup, url)
+ if result is None:
+ # Fallback: try generic schema.org / og-tag extraction
+ result = _parse_generic(soup, url)
+
+ # Post-process: extract parenthesized comments from food into extra
+ _extract_ingredient_comments(result)
+ return result
def supported_sites() -> list[str]:
@@ -642,6 +649,20 @@ def _parse_generic(soup: BeautifulSoup, url: str) -> dict:
# ---------------------------------------------------------------------------
+def _extract_ingredient_comments(data: dict):
+ """Move trailing (comment) from food field to extra field for all ingredients."""
+ for ing in data.get("ingredients", []):
+ if "group" in ing:
+ continue
+ food = ing.get("food", "")
+ extra = ing.get("extra", "")
+ if food and not extra:
+ m = re.match(r"^(.+?)\s*\(([^)]+)\)\s*$", food)
+ if m:
+ ing["food"] = m.group(1).strip()
+ ing["extra"] = m.group(2).strip()
+
+
def _host(url: str) -> str:
from urllib.parse import urlparse
return urlparse(url).hostname or ""
diff --git a/app/templates/import.html b/app/templates/import.html
index 80d627d..26fd81c 100644
--- a/app/templates/import.html
+++ b/app/templates/import.html
@@ -467,7 +467,7 @@
-
+
{% if has_mealie %}
{% endif %}
+ {% if has_mealie and has_tandoor %}
+
+ {% endif %}
@@ -775,6 +780,56 @@ async function sendToTandoor() {
btn.disabled = false;
}
+async function sendToBoth() {
+ const recipe = gatherRecipe();
+ if (!recipe.title) { alert('A recept neve kötelező!'); return; }
+
+ const btn = document.getElementById('sendBothBtn');
+ const status = document.getElementById('sendStatus');
+ btn.disabled = true;
+ status.innerHTML = '
Importálás mindkettőbe...';
+
+ const errors = [];
+ try {
+ const mResp = await fetch('/send', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(recipe),
+ });
+ const mData = await mResp.json();
+ if (mData.ok) {
+ importedLinks['Mealie'] = mData.url;
+ } else {
+ errors.push('Mealie: ' + mData.error);
+ }
+ } catch (e) { errors.push('Mealie: ' + e.message); }
+
+ try {
+ const tResp = await fetch('/send-tandoor', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(recipe),
+ });
+ const tData = await tResp.json();
+ if (tData.ok) {
+ importedLinks['Tandoor'] = tData.url;
+ } else {
+ errors.push('Tandoor: ' + tData.error);
+ }
+ } catch (e) { errors.push('Tandoor: ' + e.message); }
+
+ if (errors.length > 0 && Object.keys(importedLinks).length === 0) {
+ status.innerHTML = '
Hiba: ' + errors.join('; ') + '';
+ } else if (errors.length > 0) {
+ status.innerHTML = '
Részben sikeres: ' + errors.join('; ') + '';
+ showResultCard();
+ } else {
+ status.innerHTML = '
✓ Mindkettőbe importálva';
+ showResultCard();
+ }
+ btn.disabled = false;
+}
+
function showResultCard() {
const links = Object.entries(importedLinks).map(([name, url]) =>
'
Megnyitás ' + name + '-ben →'