diff --git a/Alain-Ducase-Bread.cook b/Alain-Ducase-Bread.cook index c0a7b0b..9eb7c4f 100644 --- a/Alain-Ducase-Bread.cook +++ b/Alain-Ducase-Bread.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/alain-ducase-bread.webp --- title: Alain Ducasse's Gougères diff --git a/Arugula Pesto Chicken with Pecans & Roasted Butternut.cook b/Arugula Pesto Chicken with Pecans & Roasted Butternut.cook index e30a247..1ac4545 100644 --- a/Arugula Pesto Chicken with Pecans & Roasted Butternut.cook +++ b/Arugula Pesto Chicken with Pecans & Roasted Butternut.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/arugula-pesto-chicken-with-pecans-roasted-butternut.webp --- title: Arugula Pesto Chicken with Pecans & Roasted Butternut diff --git a/Arugula Pesto Wheat Berries Recipe.cook b/Arugula Pesto Wheat Berries Recipe.cook index 59fbc9f..a62f1dc 100644 --- a/Arugula Pesto Wheat Berries Recipe.cook +++ b/Arugula Pesto Wheat Berries Recipe.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/arugula-pesto-wheat-berries-recipe.webp --- title: Arugula Pesto Wheat Berries Recipe servings: 1 diff --git a/Asparagus Salad.cook b/Asparagus Salad.cook index 7db0ac3..ec69b63 100644 --- a/Asparagus Salad.cook +++ b/Asparagus Salad.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/asparagus-salad.webp --- title: Asparagus Salad prep_time: 20 minutes diff --git a/Authentic Cacio e Pepe Recipe.cook b/Authentic Cacio e Pepe Recipe.cook index c283a85..929f314 100644 --- a/Authentic Cacio e Pepe Recipe.cook +++ b/Authentic Cacio e Pepe Recipe.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/authentic-cacio-e-pepe-recipe.webp --- title: Authentic Cacio e Pepe Recipe servings: 2 diff --git a/BUTTERNUT & TURKEY CHILI with sweet peppers, spiced pepitas & queso fresco.cook b/BUTTERNUT & TURKEY CHILI with sweet peppers, spiced pepitas & queso fresco.cook index eabe936..bfad132 100644 --- a/BUTTERNUT & TURKEY CHILI with sweet peppers, spiced pepitas & queso fresco.cook +++ b/BUTTERNUT & TURKEY CHILI with sweet peppers, spiced pepitas & queso fresco.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/butternut-turkey-chili-with-sweet-peppers-spiced-pepitas-queso-fresco.webp --- title: BUTTERNUT & TURKEY CHILI with sweet peppers, spiced pepitas & queso fresco servings: 1 diff --git a/Baked Chile Relleno with Black Beans.cook b/Baked Chile Relleno with Black Beans.cook index a59af0f..515e826 100644 --- a/Baked Chile Relleno with Black Beans.cook +++ b/Baked Chile Relleno with Black Beans.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/baked-chile-relleno-with-black-beans.webp --- title: Baked Chile Relleno with Black Beans servings: 2 diff --git a/Bang Bang Tofu.cook b/Bang Bang Tofu.cook index de717c8..59db20e 100644 --- a/Bang Bang Tofu.cook +++ b/Bang Bang Tofu.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/bang-bang-tofu.webp --- title: Bang Bang Tofu servings: 2 diff --git a/Barbecue Chicken with Crispy Pan-Fried Okra & Cheddar Biscuits.cook b/Barbecue Chicken with Crispy Pan-Fried Okra & Cheddar Biscuits.cook index dfbf019..7afa63a 100644 --- a/Barbecue Chicken with Crispy Pan-Fried Okra & Cheddar Biscuits.cook +++ b/Barbecue Chicken with Crispy Pan-Fried Okra & Cheddar Biscuits.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/barbecue-chicken-with-crispy-pan-fried-okra-cheddar-biscuits.webp --- title: Barbecue Chicken with Crispy Pan-Fried Okra & Cheddar Biscuits servings: 2 diff --git a/Black Bean and Corn Salad.cook b/Black Bean and Corn Salad.cook index 613119f..76e3a42 100644 --- a/Black Bean and Corn Salad.cook +++ b/Black Bean and Corn Salad.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/black-bean-and-corn-salad.webp --- title: Black Bean and Corn Salad servings: 4 diff --git a/Blackened Shrimp and Gouda Grits.cook b/Blackened Shrimp and Gouda Grits.cook index 7b94d84..07bc4b7 100644 --- a/Blackened Shrimp and Gouda Grits.cook +++ b/Blackened Shrimp and Gouda Grits.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/blackened-shrimp-and-gouda-grits.webp --- title: Blackened Shrimp & Gouda Grits servings: 2 diff --git a/Butternut Turkey Chili.cook b/Butternut Turkey Chili.cook index 49528c8..f01b6c1 100644 --- a/Butternut Turkey Chili.cook +++ b/Butternut Turkey Chili.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/butternut-turkey-chili-2.webp --- title: Butternut and Turkey Chili servings: 1 diff --git a/Cabbage_Salad.cook b/Cabbage_Salad.cook index 079ad5f..9be374c 100644 --- a/Cabbage_Salad.cook +++ b/Cabbage_Salad.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/cabbage-salad.webp --- title: Cabbage Salad servings: 6 diff --git a/Chana Chickpea Salad.cook b/Chana Chickpea Salad.cook index d3dba57..3e10e4d 100644 --- a/Chana Chickpea Salad.cook +++ b/Chana Chickpea Salad.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/chana-chickpea-salad.webp --- title: Chana Salad description: I think my mom lied to me about this chana salaad. She said it was junk food so I would eat it, but now that I'm older I know better. After all, it's chickpeas and vegetables! But I'm glad my mom said whatever it took at the time to get me to eat it, because I love this salad. It is perfect for summer and is easy to make. Serve it as a side salad or as a main dish for a vegan meal. Adjust the cayenne as desired for your heat preference. diff --git a/Chicken and Pumpkin Gnocchi with Ricotta.cook b/Chicken and Pumpkin Gnocchi with Ricotta.cook index de39af8..80601d7 100644 --- a/Chicken and Pumpkin Gnocchi with Ricotta.cook +++ b/Chicken and Pumpkin Gnocchi with Ricotta.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/chicken-and-pumpkin-gnocchi-with-ricotta.webp --- title: Chicken & Pumpkin Gnocchi with Ricotta servings: 2 diff --git a/Chickpea Butter Curry.cook b/Chickpea Butter Curry.cook index 12e6a04..88093db 100644 --- a/Chickpea Butter Curry.cook +++ b/Chickpea Butter Curry.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/chickpea-butter-curry.webp --- title: Chickpea Butter Curry servings: 2 diff --git a/Chickpea_Butter_Curry.cook b/Chickpea_Butter_Curry.cook index 6aac1a0..ac9d516 100644 --- a/Chickpea_Butter_Curry.cook +++ b/Chickpea_Butter_Curry.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/chickpea-butter-curry-2.webp --- title: Chickpea Butter Curry servings: 2 diff --git a/Chinese Orange Shrimp.cook b/Chinese Orange Shrimp.cook index 47471e1..80e0c86 100644 --- a/Chinese Orange Shrimp.cook +++ b/Chinese Orange Shrimp.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/chinese-orange-shrimp.webp --- title: Chinese Orange Shrimp servings: 2 diff --git a/Chocolate_Mousse_Cake_with_Raspberries.cook b/Chocolate_Mousse_Cake_with_Raspberries.cook index ba077f9..301dea1 100644 --- a/Chocolate_Mousse_Cake_with_Raspberries.cook +++ b/Chocolate_Mousse_Cake_with_Raspberries.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/chocolate-mousse-cake-with-raspberries.webp --- title: Chocolate Mousse Cake with Raspberries servings: 24 diff --git a/Cilantro Pesto Shrimp Over Orzo.cook b/Cilantro Pesto Shrimp Over Orzo.cook index fa4ca42..dbecc11 100644 --- a/Cilantro Pesto Shrimp Over Orzo.cook +++ b/Cilantro Pesto Shrimp Over Orzo.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/cilantro-pesto-shrimp-over-orzo.webp --- title: Cilantro Pesto Shrimp Over Orzo servings: 2 diff --git a/Crispy Roasted Chickpeas (Garbanzo Beans).cook b/Crispy Roasted Chickpeas (Garbanzo Beans).cook index d44c934..9e37391 100644 --- a/Crispy Roasted Chickpeas (Garbanzo Beans).cook +++ b/Crispy Roasted Chickpeas (Garbanzo Beans).cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/crispy-roasted-chickpeas-garbanzo-beans.webp --- title: Crispy Roasted Chickpeas (Garbanzo Beans) servings: 2 diff --git a/Crispy_Roasted_Chickpeas_(Garbanzo_Beans).cook b/Crispy_Roasted_Chickpeas_(Garbanzo_Beans).cook index d44c934..95f4a14 100644 --- a/Crispy_Roasted_Chickpeas_(Garbanzo_Beans).cook +++ b/Crispy_Roasted_Chickpeas_(Garbanzo_Beans).cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/crispy-roasted-chickpeas-garbanzo-beans-2.webp --- title: Crispy Roasted Chickpeas (Garbanzo Beans) servings: 2 diff --git a/Dried Fava Soup.cook b/Dried Fava Soup.cook index 9eaf184..6c06a5e 100644 --- a/Dried Fava Soup.cook +++ b/Dried Fava Soup.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/dried-fava-soup.webp --- title: Dried Fava Soup with Mint and Guajillo Chiles prep_time: 10 minutes diff --git a/Easy Lemon Rosemary White Bean Soup.cook b/Easy Lemon Rosemary White Bean Soup.cook index 6b08f49..193bd73 100644 --- a/Easy Lemon Rosemary White Bean Soup.cook +++ b/Easy Lemon Rosemary White Bean Soup.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/easy-lemon-rosemary-white-bean-soup.webp --- title: EASY LEMON ROSEMARY WHITE BEAN SOUP servings: 4 diff --git a/Easy_Lemon_Rosemary_White_Bean_Soup.cook b/Easy_Lemon_Rosemary_White_Bean_Soup.cook index 6b08f49..ec81771 100644 --- a/Easy_Lemon_Rosemary_White_Bean_Soup.cook +++ b/Easy_Lemon_Rosemary_White_Bean_Soup.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/easy-lemon-rosemary-white-bean-soup-2.webp --- title: EASY LEMON ROSEMARY WHITE BEAN SOUP servings: 4 diff --git a/Gaja's Bulgogi Lettuce Wraps.cook b/Gaja's Bulgogi Lettuce Wraps.cook index 6605da7..2973ee2 100644 --- a/Gaja's Bulgogi Lettuce Wraps.cook +++ b/Gaja's Bulgogi Lettuce Wraps.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/gaja-s-bulgogi-lettuce-wraps.webp --- title: Gaja's Bulgogi Lettuce Wraps servings: 2 diff --git a/Gochugaru Honey Salmon.cook b/Gochugaru Honey Salmon.cook index 729e285..cf7c49e 100644 --- a/Gochugaru Honey Salmon.cook +++ b/Gochugaru Honey Salmon.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/gochugaru-honey-salmon.webp --- title: Gochugaru Honey Salmon servings: 2 diff --git a/Indian Spiced Chicken.cook b/Indian Spiced Chicken.cook index baecd53..9fea45e 100644 --- a/Indian Spiced Chicken.cook +++ b/Indian Spiced Chicken.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/indian-spiced-chicken-2.webp --- title: Indian Spiced Chicken with Creamy Tomato Sauce & Spinach servings: 2 diff --git a/Kale and Chestnut Stew.cook b/Kale and Chestnut Stew.cook index 0203904..6752b57 100644 --- a/Kale and Chestnut Stew.cook +++ b/Kale and Chestnut Stew.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/kale-and-chestnut-stew.webp --- title: Kale & Chestnut Stew with Butternut Squash, Wild Rice & Parsley–Walnut Pesto servings: 2 diff --git a/Kkanpoong Tofu.cook b/Kkanpoong Tofu.cook index 08cae9a..66bd082 100644 --- a/Kkanpoong Tofu.cook +++ b/Kkanpoong Tofu.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/kkanpoong-tofu.webp --- title: Spicy & Crunchy Garlic Tofu (Kkanpoong Tofu) servings: 2 diff --git a/Korean Shrimp Tacos.cook b/Korean Shrimp Tacos.cook index be597ba..517c6a9 100644 --- a/Korean Shrimp Tacos.cook +++ b/Korean Shrimp Tacos.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/korean-shrimp-tacos.webp --- title: Korean Shrimp Tacos servings: 2 diff --git a/Kung Pao Shrimp with Sichuan Roasted Green Beans.cook b/Kung Pao Shrimp with Sichuan Roasted Green Beans.cook index e62919e..dcb654e 100644 --- a/Kung Pao Shrimp with Sichuan Roasted Green Beans.cook +++ b/Kung Pao Shrimp with Sichuan Roasted Green Beans.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/kung-pao-shrimp-with-sichuan-roasted-green-beans.webp --- title: Kung Pao Shrimp with Sichuan Roasted Green Beans servings: 2 diff --git a/Lactation cookies.cook b/Lactation cookies.cook index 1a452ab..ce45145 100644 --- a/Lactation cookies.cook +++ b/Lactation cookies.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/lactation-cookies.webp --- title: Lactation cookies servings: 1 diff --git a/Lactation_cookies.cook b/Lactation_cookies.cook index 3ae21f5..ae72e5f 100644 --- a/Lactation_cookies.cook +++ b/Lactation_cookies.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/lactation-cookies-2.webp --- title: Lactation Cookies servings: 12 diff --git a/Moroccan Spiced Lentil & Chickpea Soup.cook b/Moroccan Spiced Lentil & Chickpea Soup.cook index 0eb040d..cfdd4e7 100644 --- a/Moroccan Spiced Lentil & Chickpea Soup.cook +++ b/Moroccan Spiced Lentil & Chickpea Soup.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/moroccan-spiced-lentil-chickpea-soup.webp --- title: Moroccan Spiced Lentil & Chickpea Soup servings: 1 diff --git a/Moroccan_Spiced_Lentil_and_Chickpea_Soup.cook b/Moroccan_Spiced_Lentil_and_Chickpea_Soup.cook index 0eb040d..30cf9de 100644 --- a/Moroccan_Spiced_Lentil_and_Chickpea_Soup.cook +++ b/Moroccan_Spiced_Lentil_and_Chickpea_Soup.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/moroccan-spiced-lentil-and-chickpea-soup.webp --- title: Moroccan Spiced Lentil & Chickpea Soup servings: 1 diff --git a/Orange Rolls.cook b/Orange Rolls.cook index 466b20f..f907342 100644 --- a/Orange Rolls.cook +++ b/Orange Rolls.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/orange-rolls.webp --- title: Orange Rolls servings: 8 diff --git a/Palak Paneer.cook b/Palak Paneer.cook index 9c605a6..f7907dc 100644 --- a/Palak Paneer.cook +++ b/Palak Paneer.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/palak-paneer.webp --- title: Palak Paneer servings: 4 diff --git a/Pecan-Brown Butter Trout.cook b/Pecan-Brown Butter Trout.cook index 285f630..268495a 100644 --- a/Pecan-Brown Butter Trout.cook +++ b/Pecan-Brown Butter Trout.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/pecan-brown-butter-trout.webp --- title: Pecan-Brown Butter Trout servings: 2 diff --git a/Roasted Pumpkin Sage.cook b/Roasted Pumpkin Sage.cook index 8f1fc5e..e55b619 100644 --- a/Roasted Pumpkin Sage.cook +++ b/Roasted Pumpkin Sage.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/roasted-pumpkin-sage.webp --- title: Roasted Pumpkin Sage Soup description: Pumpkin season is in full swing, and this pumpkin sage soup is the perfect first course for any meal. Totally optional, but I love using hollowed out pumpkins as a bowl for a beautiful presentation. diff --git a/Saag Paneer.cook b/Saag Paneer.cook index 4ee9ed5..38e5f7c 100644 --- a/Saag Paneer.cook +++ b/Saag Paneer.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/saag-paneer.webp --- title: Saag Paneer servings: 1 diff --git a/Spaghetti Carbonara.cook b/Spaghetti Carbonara.cook index 3a3a469..8056a09 100644 --- a/Spaghetti Carbonara.cook +++ b/Spaghetti Carbonara.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/spaghetti-carbonara.webp --- title: Spaghetti Carbonara with Bacon & Crisp Lettuce Salad servings: 2 diff --git a/Spaghetti_Squash_w_Chickpeas_and_Kale.cook b/Spaghetti_Squash_w_Chickpeas_and_Kale.cook index bc2b397..45a3778 100644 --- a/Spaghetti_Squash_w_Chickpeas_and_Kale.cook +++ b/Spaghetti_Squash_w_Chickpeas_and_Kale.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/spaghetti-squash-w-chickpeas-and-kale.webp --- title: Spaghetti Squash w/ Chickpeas & Kale servings: 3 diff --git a/Spanakopita.cook b/Spanakopita.cook index 3b2bb26..72baa7c 100644 --- a/Spanakopita.cook +++ b/Spanakopita.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/spanakopita.webp --- title: Skillet Spanakopita (Greek Spinach Feta Pie) servings: 8 diff --git a/Spicy Shrimp Paella.cook b/Spicy Shrimp Paella.cook index 1f9b295..5354b1e 100644 --- a/Spicy Shrimp Paella.cook +++ b/Spicy Shrimp Paella.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/spicy-shrimp-paella.webp --- title: Spicy Shrimp Paella servings: 2 diff --git a/Thai_Veggie_Pad_See_Ew.cook b/Thai_Veggie_Pad_See_Ew.cook index 78e7a05..3001cd5 100644 --- a/Thai_Veggie_Pad_See_Ew.cook +++ b/Thai_Veggie_Pad_See_Ew.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/thai-veggie-pad-see-ew.webp --- title: Thai Veggie Pad See Ew servings: 2 diff --git a/Warm Up With a Quick Lemony Chicken Soup.cook b/Warm Up With a Quick Lemony Chicken Soup.cook index 65d5d9d..3ff4c04 100644 --- a/Warm Up With a Quick Lemony Chicken Soup.cook +++ b/Warm Up With a Quick Lemony Chicken Soup.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/warm-up-with-a-quick-lemony-chicken-soup.webp --- title: Warm Up With a Quick Lemony Chicken Soup servings: 6 diff --git a/West African Peanut Chicken.cook b/West African Peanut Chicken.cook index 3cafd5a..97ec73a 100644 --- a/West African Peanut Chicken.cook +++ b/West African Peanut Chicken.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/west-african-peanut-chicken.webp --- title: West African Peanut Chicken servings: 2 diff --git a/bin/fetch-recipe-image.sh b/bin/fetch-recipe-image.sh new file mode 100755 index 0000000..2554253 --- /dev/null +++ b/bin/fetch-recipe-image.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -euo pipefail + +slug="$1" +url="$2" + +echo "[$slug] Fetching $url" >&2 +html=$(curl -sL --max-time 15 "$url" 2>/dev/null) || { echo "[$slug] FETCH FAILED" >&2; echo ""; exit 0; } + +# Try multiple og:image patterns +img_url=$(echo "$html" | grep -oP 'property="og:image"\s+content="[^"]*"' | sed 's/.*content="//;s/"//' | head -1) + +if [ -z "$img_url" ]; then + img_url=$(echo "$html" | grep -oP 'name="og:image"\s+content="[^"]*"' | sed 's/.*content="//;s/"//' | head -1) +fi + +if [ -z "$img_url" ]; then + img_url=$(echo "$html" | grep -oP 'content="[^"]*"[^>]*property="og:image"' | sed 's/.*content="//;s/".*//' | head -1) +fi + +if [ -n "$img_url" ]; then + echo "[$slug] Found: $img_url" >&2 + echo "$img_url" +else + echo "[$slug] No og:image found" >&2 +fi diff --git a/bin/final-scrape.py b/bin/final-scrape.py new file mode 100644 index 0000000..3c3fd53 --- /dev/null +++ b/bin/final-scrape.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +"""Final attempt: search DDG text, scrape recipe pages for actual images.""" + +import csv +import json +import re +import sys +import time +from pathlib import Path + +import requests +from bs4 import BeautifulSoup +from ddgs import DDGS + +WORKDIR = Path("/home/jbrechtel/recipes/.worktrees/recipe-images") +HEADERS = { + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", +} +SKIP_DOMAINS = {"pinterest", "facebook", "instagram", "twitter", "youtube", "tiktok"} +MIN_IMAGE_SIZE = 10000 # 10KB minimum for a recipe photo + + +def search_url(query: str) -> str | None: + """Search and return first relevant recipe page URL.""" + try: + results = list(DDGS().text(query, max_results=8)) + except Exception as e: + print(f" Search error: {e}", file=sys.stderr) + return None + + for r in results: + url = r.get("href", "") + domain = url.split("/")[2] if "//" in url else "" + if any(s in domain for s in SKIP_DOMAINS): + continue + # Prefer recipe sites + if any(kw in domain for kw in ["recipe", "cook", "food", "kitchen", "nytimes", "loveandlemons"]): + return url + + # Return first non-wikipedia result + for r in results: + url = r.get("href", "") + if "wikipedia.org" not in url: + return url + return None + + +def find_recipe_images(url: str) -> list[str]: + """Fetch a page and find actual recipe/food images.""" + try: + resp = requests.get(url, headers=HEADERS, timeout=15, allow_redirects=True) + resp.raise_for_status() + except requests.RequestException: + return [] + + soup = BeautifulSoup(resp.text, "html.parser") + images = [] + + # 1. Check JSON-LD for recipe image + for script in soup.find_all("script", type="application/ld+json"): + try: + data = json.loads(script.string) + except (json.JSONDecodeError, TypeError): + continue + # Handle list or single + items = data if isinstance(data, list) else [data] + for item in items: + if not isinstance(item, dict): + continue + if item.get("@type") == "Recipe": + img = item.get("image") + if isinstance(img, str): + images.append(img) + elif isinstance(img, list): + for i in img: + if isinstance(i, str): + images.append(i) + elif isinstance(i, dict): + u = i.get("contentUrl") or i.get("url", "") + if u: + images.append(u) + elif isinstance(img, dict): + u = img.get("contentUrl") or img.get("url", "") + if u: + images.append(u) + + # 2. Check og:image + for meta in soup.find_all("meta"): + content = meta.get("content", "") + if (meta.get("property") == "og:image" or meta.get("name") == "og:image" or meta.get("name") == "twitter:image"): + if content and "logo" not in content.lower(): + images.append(content) + + # 3. Check all img tags for large, relevant images + for img in soup.find_all("img"): + src = img.get("src") or img.get("data-src") or "" + if not src.startswith("http"): + continue + src_lower = src.lower() + if any(kw in src_lower for kw in ["logo", "avatar", "icon", "banner", "pixel", "spacer", "thumb"]): + continue + # Check for recipe-related class/id + parent_classes = "" + parent = img.parent + if parent: + parent_classes = " ".join(parent.get("class", []) or []) + " " + (parent.get("id") or "") + if any(kw in parent_classes.lower() for kw in ["logo", "nav", "header", "footer", "menu", "icon"]): + continue + images.append(src) + + # Deduplicate + seen = set() + unique = [] + for img in images: + if img not in seen: + seen.add(img) + unique.append(img) + + return unique + + +def download_image(url: str, dest: Path) -> bool: + """Download image, validate it's a real image.""" + try: + resp = requests.get(url, headers=HEADERS, timeout=30, stream=True) + resp.raise_for_status() + ct = resp.headers.get("content-type", "") + if not ct.startswith("image/"): + return False + content = resp.content + if len(content) < MIN_IMAGE_SIZE: + return False + dest.write_bytes(content) + return True + except requests.RequestException: + return False + + +def get_title(filepath: Path) -> str: + content = filepath.read_text(encoding="utf-8") + m = re.search(r"^title:\s*(.+)$", content, re.MULTILINE) + if m: + return m.group(1).strip() + return filepath.stem.replace("_", " ").replace("-", " ") + + +def add_image_field(filepath: Path, slug: str): + url = f"https://roux.roo.lol/recipe-images/{slug}.webp" + content = filepath.read_text(encoding="utf-8") + if re.search(r"^image:", content, re.MULTILINE): + content = re.sub(r"^image:.*$", f"image: {url}", count=1, flags=re.MULTILINE, string=content) + else: + content = re.sub(r"^---$", f"image: {url}\n---", count=1, flags=re.MULTILINE, string=content) + filepath.write_text(content, encoding="utf-8") + print(f" Updated {filepath.name}", file=sys.stderr) + + +def get_source_url(filepath: Path) -> str | None: + content = filepath.read_text(encoding="utf-8") + m = re.search(r"^source:\s*(.+)$", content, re.MULTILINE) + if m: + val = m.group(1).strip() + if val.startswith("http"): + return val + return None + + +def main(): + csv_file = WORKDIR / "docs" / "no-image-has-fm.csv" + + recipes = [] + with open(csv_file) as f: + reader = csv.reader(f, delimiter="|") + for row in reader: + if len(row) >= 2: + recipes.append((row[0].strip(), row[1].strip())) + + success = [] + failed = [] + + for filename, slug in recipes: + filepath = WORKDIR / filename + if not filepath.exists(): + failed.append(slug) + continue + + dest = WORKDIR / "recipe-images" / f"{slug}.webp" + + # Skip if already has image AND file exists + content = filepath.read_text(encoding="utf-8") + has_field = bool(re.search(r"^image:", content, re.MULTILINE)) + has_file = dest.exists() + if has_field and has_file and dest.stat().st_size > 10000: + print(f"[{slug}] Already done, skipping", file=sys.stderr) + success.append(slug) + continue + + title = get_title(filepath) + query = f"{title} recipe" + + print(f"[{slug}] Searching: {query}", file=sys.stderr) + page_url = search_url(query) + if not page_url: + print(f" No search results", file=sys.stderr) + failed.append(slug) + continue + + print(f" Page: {page_url}", file=sys.stderr) + time.sleep(0.3) + + img_urls = find_recipe_images(page_url) + if not img_urls: + print(f" No images found on page", file=sys.stderr) + failed.append(slug) + continue + + downloaded = False + for img_url in img_urls[:5]: + print(f" Trying: {img_url[:80]}...", file=sys.stderr) + if download_image(img_url, dest): + print(f" Downloaded ({dest.stat().st_size} bytes)", file=sys.stderr) + add_image_field(filepath, slug) + downloaded = True + break + time.sleep(0.2) + + if downloaded: + success.append(slug) + else: + failed.append(slug) + + time.sleep(0.5) + + print(f"\n=== Results ===", file=sys.stderr) + print(f"Success: {len(success)}", file=sys.stderr) + print(f"Failed: {len(failed)}", file=sys.stderr) + if failed: + print(f"Failed: {', '.join(failed)}", file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/bin/find-remaining.py b/bin/find-remaining.py new file mode 100644 index 0000000..ccec575 --- /dev/null +++ b/bin/find-remaining.py @@ -0,0 +1,95 @@ +#!/tmp/recipe-venv/bin/python3 +"""Find images for remaining 6 recipes.""" +import re, sys, time +from pathlib import Path +import requests +from bs4 import BeautifulSoup +from ddgs import DDGS + +WORKDIR = Path("/home/jbrechtel/recipes/.worktrees/recipe-images") +HEADERS = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"} + +recipes = [ + ("Arugula Pesto Chicken with Pecans & Roasted Butternut.cook", "arugula-pesto-chicken-with-pecans-roasted-butternut", "Arugula Pesto Chicken Pecans Roasted Butternut recipe garnishandgather"), + ("Bang Bang Tofu.cook", "bang-bang-tofu", "Bang Bang Tofu recipe"), + ("Chana Chickpea Salad.cook", "chana-chickpea-salad", "Chana Chickpea Salad recipe"), + ("Gaja's Bulgogi Lettuce Wraps.cook", "gaja-s-bulgogi-lettuce-wraps", "Gaja Bulgogi Lettuce Wraps recipe garnishandgather"), + ("Pecan-Brown Butter Trout.cook", "pecan-brown-butter-trout", "Pecan Brown Butter Trout recipe"), + ("West African Peanut Chicken.cook", "west-african-peanut-chicken", "West African Peanut Chicken recipe"), +] + +for file, slug, query in recipes: + fp = WORKDIR / file + dest = WORKDIR / "recipe-images" / f"{slug}.webp" + if dest.exists() and dest.stat().st_size > 10000: + print(f"[{slug}] Already done") + continue + + print(f"[{slug}] Searching: {query}") + try: + results = list(DDGS().text(query, max_results=5)) + except Exception as e: + print(f" Search error: {e}") + continue + + found = False + for r in results: + url = r.get("href", "") + if not url or any(s in url for s in ["pinterest","facebook","instagram"]): + continue + print(f" Fetching: {url}") + try: + resp = requests.get(url, headers=HEADERS, timeout=15) + resp.raise_for_status() + except Exception as e: + print(f" Failed: {e}") + continue + + soup = BeautifulSoup(resp.text, "html.parser") + img_urls = [] + + # og:image + for meta in soup.find_all("meta"): + c = meta.get("content", "") + p = (meta.get("property") or "").lower() + n = (meta.get("name") or "").lower() + if ("og:image" in p or "og:image" in n or "twitter:image" in n) and c and "logo" not in c.lower(): + img_urls.append(c) + + # img tags + for img in soup.find_all("img"): + src = img.get("src") or img.get("data-src") or "" + if src.startswith("http") and "logo" not in src.lower() and "icon" not in src.lower(): + img_urls.append(src) + + for img_url in img_urls[:5]: + try: + r2 = requests.get(img_url, headers=HEADERS, timeout=15) + r2.raise_for_status() + ct = r2.headers.get("content-type", "") + if ct.startswith("image/") and len(r2.content) > 10000: + dest.write_bytes(r2.content) + print(f" Downloaded ({len(r2.content)} bytes): {img_url[:60]}") + found = True + break + except: + pass + if found: + break + time.sleep(0.3) + + if found: + # Add image field + url_val = f"https://roux.roo.lol/recipe-images/{slug}.webp" + content = fp.read_text(encoding="utf-8") + if re.search(r"^image:", content, re.MULTILINE): + content = re.sub(r"^image:.*$", f"image: {url_val}", count=1, flags=re.MULTILINE, string=content) + else: + content = re.sub(r"^---$", f"image: {url_val}\n---", count=1, flags=re.MULTILINE, string=content) + fp.write_text(content, encoding="utf-8") + print(f" Updated image field") + else: + print(f" FAILED") + time.sleep(0.5) + +print("\nDone!") diff --git a/bin/scrape-images.py b/bin/scrape-images.py new file mode 100644 index 0000000..0ed3f33 --- /dev/null +++ b/bin/scrape-images.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +"""Scrape og:image from recipe source URLs, download images, update .cook files.""" + +import csv +import os +import re +import sys +import time +from pathlib import Path + +import requests +from bs4 import BeautifulSoup + +WORKDIR = Path("/home/jbrechtel/recipes/.worktrees/recipe-images") +HEADERS = { + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", +} + + +def get_source_url(filepath: Path) -> str | None: + """Extract the source URL from a .cook file's YAML frontmatter.""" + content = filepath.read_text(encoding="utf-8") + match = re.search(r"^source:\s*(.+)$", content, re.MULTILINE) + if not match: + return None + val = match.group(1).strip() + # Only return actual URLs + if val.startswith("http://") or val.startswith("https://"): + return val + return None + + +def fetch_og_image(url: str, timeout: int = 20) -> str | None: + """Fetch a page and extract og:image URL.""" + try: + resp = requests.get(url, headers=HEADERS, timeout=timeout, allow_redirects=True) + resp.raise_for_status() + except requests.RequestException as e: + print(f" FETCH FAILED: {e}", file=sys.stderr) + return None + + soup = BeautifulSoup(resp.text, "html.parser") + + # Try og:image meta tag + for meta in soup.find_all("meta"): + if meta.get("property") == "og:image" and meta.get("content"): + return meta["content"] + if meta.get("name") == "og:image" and meta.get("content"): + return meta["content"] + + # Try twitter:image + for meta in soup.find_all("meta"): + if meta.get("name") == "twitter:image" and meta.get("content"): + return meta["content"] + + # Try finding the first large image (common in recipe sites) + for img in soup.find_all("img"): + src = img.get("src") or "" + if "logo" in src.lower() or "avatar" in src.lower() or "icon" in src.lower(): + continue + if src.startswith("http") and any(kw in src.lower() for kw in ["upload", "wp-content", "images"]): + return src + + return None + + +def download_image(url: str, dest: Path) -> bool: + """Download an image from URL to destination path.""" + try: + resp = requests.get(url, headers=HEADERS, timeout=30, stream=True) + resp.raise_for_status() + dest.write_bytes(resp.content) + return True + except requests.RequestException as e: + print(f" DOWNLOAD FAILED: {e}", file=sys.stderr) + return False + + +def add_image_field(filepath: Path, slug: str): + """Add or update the image field in a .cook file's YAML frontmatter.""" + url = f"https://roux.roo.lol/recipe-images/{slug}.webp" + content = filepath.read_text(encoding="utf-8") + + # If image field already exists, replace it + if re.search(r"^image:", content, re.MULTILINE): + content = re.sub(r"^image:.*$", f"image: {url}", content, count=1, flags=re.MULTILINE) + else: + # Insert before closing --- + content = re.sub(r"^---$", f"image: {url}\n---", content, count=1, flags=re.MULTILINE) + + filepath.write_text(content, encoding="utf-8") + print(f" Updated image field: {filepath.name}", file=sys.stderr) + + +def process_recipe(filepath: Path, slug: str) -> bool: + """Process a single recipe: find image, download, update frontmatter.""" + dest = WORKDIR / "recipe-images" / f"{slug}.webp" + + # Skip if already downloaded + if dest.exists(): + print(f"[{slug}] Image already exists, skipping download", file=sys.stderr) + add_image_field(filepath, slug) + return True + + # Try to get source URL and scrape image + src_url = get_source_url(filepath) + if src_url: + print(f"[{slug}] Scraping {src_url}", file=sys.stderr) + img_url = fetch_og_image(src_url) + if img_url: + print(f" Found: {img_url}", file=sys.stderr) + if download_image(img_url, dest): + print(f" Downloaded to {dest.name}", file=sys.stderr) + add_image_field(filepath, slug) + return True + else: + print(f" Download failed", file=sys.stderr) + + return False + + +def main(): + csv_file = WORKDIR / "docs" / "no-image-has-fm.csv" + if not csv_file.exists(): + print(f"CSV not found: {csv_file}", file=sys.stderr) + sys.exit(1) + + recipes = [] + with open(csv_file) as f: + reader = csv.reader(f, delimiter="|") + for row in reader: + if len(row) >= 2: + recipes.append((row[0].strip(), row[1].strip())) + + print(f"Processing {len(recipes)} recipes...", file=sys.stderr) + + success = [] + failed = [] + + for filename, slug in recipes: + filepath = WORKDIR / filename + if not filepath.exists(): + print(f"[{slug}] File not found: {filepath}", file=sys.stderr) + failed.append(slug) + continue + + ok = process_recipe(filepath, slug) + if ok: + success.append(slug) + else: + failed.append(slug) + + # Be polite to servers + time.sleep(0.5) + + print(f"\n=== Results ===", file=sys.stderr) + print(f"Success: {len(success)}", file=sys.stderr) + print(f"Failed: {len(failed)}", file=sys.stderr) + if failed: + print(f"Failed slugs: {', '.join(failed)}", file=sys.stderr) + + # Write failed slugs to file for retry + if failed: + with open(WORKDIR / "docs" / "failed-images.csv", "w") as f: + for slug in failed: + f.write(f"{slug}\n") + + +if __name__ == "__main__": + main() diff --git a/bin/search-and-download.py b/bin/search-and-download.py new file mode 100644 index 0000000..96a101d --- /dev/null +++ b/bin/search-and-download.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +"""Search the web for recipe images, download them, and update .cook files. + +Uses DuckDuckGo search to find recipe pages, then scrapes og:image. +""" + +import csv +import os +import re +import sys +import time +from pathlib import Path + +import requests +from bs4 import BeautifulSoup +from duckduckgo_search import DDGS + +WORKDIR = Path("/home/jbrechtel/recipes/.worktrees/recipe-images") +HEADERS = { + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", +} + +IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".webp", ".gif"} + + +def fetch_og_image(url: str) -> str | None: + """Fetch a page and extract og:image URL.""" + try: + resp = requests.get(url, headers=HEADERS, timeout=15, allow_redirects=True) + resp.raise_for_status() + except requests.RequestException: + return None + + soup = BeautifulSoup(resp.text, "html.parser") + + # Try og:image meta tag (property or name) + for meta in soup.find_all("meta"): + prop = (meta.get("property") or "").lower() + name = (meta.get("name") or "").lower() + content = meta.get("content", "") + if "og:image" in prop and content: + return content + if "og:image" in name and content: + return content + if "twitter:image" in name and content: + return content + + # Try schema.org JSON-LD + for script in soup.find_all("script", type="application/ld+json"): + import json + try: + data = json.loads(script.string) + if isinstance(data, dict): + img = data.get("image") + if isinstance(img, str) and img.startswith("http"): + return img + if isinstance(img, dict): + url = img.get("contentUrl") or img.get("url", "") + if url.startswith("http"): + return url + if isinstance(img, list) and img: + first = img[0] + if isinstance(first, str): + return first + if isinstance(first, dict): + url = first.get("contentUrl") or first.get("url", "") + if url.startswith("http"): + return url + except (json.JSONDecodeError, AttributeError): + pass + + return None + + +def search_and_find_image(query: str) -> str | None: + """Search DuckDuckGo for a recipe and find og:image.""" + try: + results = list(DDGS().text(query, max_results=5)) + except Exception as e: + print(f" Search error: {e}", file=sys.stderr) + return None + + for r in results: + url = r.get("href", "") + if not url or "pinterest" in url or "facebook" in url or "instagram" in url: + continue + print(f" Trying: {url}", file=sys.stderr) + img = fetch_og_image(url) + if img: + print(f" Found image: {img}", file=sys.stderr) + return img + time.sleep(0.3) + + return None + + +def download_image(url: str, dest: Path) -> bool: + """Download an image from URL to destination path.""" + try: + resp = requests.get(url, headers=HEADERS, timeout=30, stream=True) + resp.raise_for_status() + dest.write_bytes(resp.content) + return True + except requests.RequestException as e: + print(f" Download failed: {e}", file=sys.stderr) + return False + + +def add_image_field(filepath: Path, slug: str): + """Add or update the image field in a .cook file's YAML frontmatter.""" + url = f"https://roux.roo.lol/recipe-images/{slug}.webp" + content = filepath.read_text(encoding="utf-8") + + if re.search(r"^image:", content, re.MULTILINE): + content = re.sub(r"^image:.*$", f"image: {url}", count=1, flags=re.MULTILINE, string=content) + else: + content = re.sub(r"^---$", f"image: {url}\n---", count=1, flags=re.MULTILINE, string=content) + + filepath.write_text(content, encoding="utf-8") + print(f" Updated {filepath.name}", file=sys.stderr) + + +def get_source_url(filepath: Path) -> str | None: + """Extract source URL from .cook frontmatter.""" + content = filepath.read_text(encoding="utf-8") + match = re.search(r"^source:\s*(.+)$", content, re.MULTILINE) + if match: + val = match.group(1).strip() + if val.startswith("http://") or val.startswith("https://"): + return val + return None + + +def process_recipe(filename: str, slug: str) -> bool: + """Try to find and download an image for a recipe.""" + filepath = WORKDIR / filename + dest = WORKDIR / "recipe-images" / f"{slug}.webp" + + if dest.exists(): + print(f"[{slug}] Already exists", file=sys.stderr) + add_image_field(filepath, slug) + return True + + # Strategy 1: Try source URL if available + src_url = get_source_url(filepath) + if src_url: + print(f"[{slug}] Trying source URL: {src_url}", file=sys.stderr) + img = fetch_og_image(src_url) + if img: + if download_image(img, dest): + print(f" Downloaded from source", file=sys.stderr) + add_image_field(filepath, slug) + return True + + # Strategy 2: Search by recipe name + # Get the title from frontmatter + content = filepath.read_text(encoding="utf-8") + title_match = re.search(r"^title:\s*(.+)$", content, re.MULTILINE) + if title_match: + query = title_match.group(1).strip() + " recipe" + else: + query = filename.replace(".cook", "").replace("_", " ").replace("-", " ") + " recipe" + + print(f"[{slug}] Searching: {query}", file=sys.stderr) + img = search_and_find_image(query) + if img and download_image(img, dest): + print(f" Downloaded from search", file=sys.stderr) + add_image_field(filepath, slug) + return True + + # Strategy 3: Try with "og:image" or "recipe image" variations + query2 = query + " image" + print(f"[{slug}] Searching (alt): {query2}", file=sys.stderr) + img = search_and_find_image(query2) + if img and download_image(img, dest): + print(f" Downloaded from alt search", file=sys.stderr) + add_image_field(filepath, slug) + return True + + return False + + +def main(): + # First process the no-image-has-fm list + csv_file = WORKDIR / "docs" / "no-image-has-fm.csv" + if not csv_file.exists(): + print(f"CSV not found: {csv_file}", file=sys.stderr) + sys.exit(1) + + recipes = [] + with open(csv_file) as f: + reader = csv.reader(f, delimiter="|") + for row in reader: + if len(row) >= 2: + recipes.append((row[0].strip(), row[1].strip())) + + success = [] + failed = [] + + for filename, slug in recipes: + filepath = WORKDIR / filename + if not filepath.exists(): + failed.append(slug) + continue + + dest = WORKDIR / "recipe-images" / f"{slug}.webp" + if dest.exists() and re.search(r"^image:", filepath.read_text(), re.MULTILINE): + print(f"[{slug}] Already processed, skipping", file=sys.stderr) + success.append(slug) + continue + + ok = process_recipe(filename, slug) + if ok: + success.append(slug) + else: + failed.append(slug) + + # Be polite + time.sleep(0.5) + + print(f"\n=== Results ===", file=sys.stderr) + print(f"Success: {len(success)}", file=sys.stderr) + print(f"Failed: {len(failed)}", file=sys.stderr) + + if failed: + with open(WORKDIR / "docs" / "failed-images.csv", "w") as f: + for s in failed: + f.write(f"{s}\n") + print(f"Failed: {', '.join(failed)}", file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/bin/search-images-ddgs.py b/bin/search-images-ddgs.py new file mode 100644 index 0000000..e171779 --- /dev/null +++ b/bin/search-images-ddgs.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +"""Search DuckDuckGo Images for recipe photos, download, update .cook files.""" + +import csv +import re +import sys +import time +from pathlib import Path + +import requests +from duckduckgo_search import DDGS + +WORKDIR = Path("/home/jbrechtel/recipes/.worktrees/recipe-images") +HEADERS = { + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36", +} + + +def download_image(url: str, dest: Path) -> bool: + """Download an image from URL to destination path.""" + try: + resp = requests.get(url, headers=HEADERS, timeout=30, stream=True) + resp.raise_for_status() + # Validate it's actually an image + content_type = resp.headers.get("content-type", "") + if not content_type.startswith("image/"): + print(f" Not an image (content-type: {content_type})", file=sys.stderr) + return False + dest.write_bytes(resp.content) + return True + except requests.RequestException as e: + print(f" Download failed: {e}", file=sys.stderr) + return False + + +def add_image_field(filepath: Path, slug: str): + """Add or update the image field in a .cook file.""" + url = f"https://roux.roo.lol/recipe-images/{slug}.webp" + content = filepath.read_text(encoding="utf-8") + + if re.search(r"^image:", content, re.MULTILINE): + content = re.sub(r"^image:.*$", f"image: {url}", count=1, flags=re.MULTILINE, string=content) + else: + content = re.sub(r"^---$", f"image: {url}\n---", count=1, flags=re.MULTILINE, string=content) + + filepath.write_text(content, encoding="utf-8") + print(f" Updated {filepath.name}", file=sys.stderr) + + +def search_images(query: str, max_results: int = 5): + """Search DuckDuckGo Images for a recipe and return image URLs.""" + try: + results = list(DDGS().images(query, max_results=max_results)) + return [r["image"] for r in results if r.get("image")] + except Exception as e: + print(f" Image search error: {e}", file=sys.stderr) + return [] + + +def get_title(filepath: Path) -> str: + """Extract title from .cook frontmatter.""" + content = filepath.read_text(encoding="utf-8") + match = re.search(r"^title:\s*(.+)$", content, re.MULTILINE) + if match: + return match.group(1).strip() + # Fall back to filename + name = filepath.stem + return name.replace("_", " ").replace("-", " ") + + +def main(): + csv_file = WORKDIR / "docs" / "failed-images.csv" + # Also check which are actually missing + existing_csv = WORKDIR / "docs" / "no-image-has-fm.csv" + + recipes = [] + with open(existing_csv) as f: + reader = csv.reader(f, delimiter="|") + for row in reader: + if len(row) >= 2: + recipes.append((row[0].strip(), row[1].strip())) + + # Filter to those without an image file or without image field + to_process = [] + for filename, slug in recipes: + filepath = WORKDIR / filename + dest = WORKDIR / "recipe-images" / f"{slug}.webp" + has_image_file = dest.exists() + has_image_field = False + if filepath.exists(): + content = filepath.read_text(encoding="utf-8") + has_image_field = bool(re.search(r"^image:", content, re.MULTILINE)) + + if not has_image_file or not has_image_field: + to_process.append((filename, slug)) + + print(f"Processing {len(to_process)} remaining recipes...", file=sys.stderr) + + success = [] + failed = [] + + for filename, slug in to_process: + filepath = WORKDIR / filename + dest = WORKDIR / "recipe-images" / f"{slug}.webp" + + if not filepath.exists(): + failed.append(slug) + continue + + title = get_title(filepath) + queries = [ + f"{title} recipe", + title, + ] + + found = False + for query in queries: + print(f"[{slug}] Searching images: {query}", file=sys.stderr) + urls = search_images(query) + for img_url in urls: + # Skip tiny icons, logos + if any(kw in img_url.lower() for kw in ["logo", "icon", "avatar", "merriam", "wikipedia"]): + continue + print(f" Trying: {img_url[:80]}...", file=sys.stderr) + if download_image(img_url, dest): + print(f" Downloaded!", file=sys.stderr) + add_image_field(filepath, slug) + found = True + break + time.sleep(0.2) + if found: + break + time.sleep(0.5) + + if found: + success.append(slug) + else: + failed.append(slug) + + print(f"\n=== Results ===", file=sys.stderr) + print(f"Success: {len(success)}", file=sys.stderr) + print(f"Failed: {len(failed)}", file=sys.stderr) + if failed: + print(f"Failed: {', '.join(failed)}", file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/cauliflower-milanese.cook b/cauliflower-milanese.cook index 3b923d7..9104517 100644 --- a/cauliflower-milanese.cook +++ b/cauliflower-milanese.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/cauliflower-milanese.webp --- title: Cauliflower Milanese source: https://cooking.nytimes.com/recipes/1026145-cauliflower-milanese diff --git a/docs/batch-no-image-aa b/docs/batch-no-image-aa new file mode 100644 index 0000000..71af7e3 --- /dev/null +++ b/docs/batch-no-image-aa @@ -0,0 +1,10 @@ +Alain-Ducase-Bread.cook|alain-ducase-bread +Arugula Pesto Chicken with Pecans & Roasted Butternut.cook|arugula-pesto-chicken-with-pecans-roasted-butternut +Arugula Pesto Wheat Berries Recipe.cook|arugula-pesto-wheat-berries-recipe +Asparagus Salad.cook|asparagus-salad +Authentic Cacio e Pepe Recipe.cook|authentic-cacio-e-pepe-recipe +Baked Chile Relleno with Black Beans.cook|baked-chile-relleno-with-black-beans +Bang Bang Tofu.cook|bang-bang-tofu +Barbecue Chicken with Crispy Pan-Fried Okra & Cheddar Biscuits.cook|barbecue-chicken-with-crispy-pan-fried-okra-cheddar-biscuits +Black Bean and Corn Salad.cook|black-bean-and-corn-salad +Blackened Shrimp and Gouda Grits.cook|blackened-shrimp-and-gouda-grits diff --git a/docs/batch-no-image-ab b/docs/batch-no-image-ab new file mode 100644 index 0000000..e55790b --- /dev/null +++ b/docs/batch-no-image-ab @@ -0,0 +1,10 @@ +Butternut Turkey Chili.cook|butternut-turkey-chili-2 +BUTTERNUT & TURKEY CHILI with sweet peppers, spiced pepitas & queso fresco.cook|butternut-turkey-chili-with-sweet-peppers-spiced-pepitas-queso-fresco +Cabbage_Salad.cook|cabbage-salad +cauliflower-milanese.cook|cauliflower-milanese +Chana Chickpea Salad.cook|chana-chickpea-salad +Chicken and Pumpkin Gnocchi with Ricotta.cook|chicken-and-pumpkin-gnocchi-with-ricotta +Chickpea Butter Curry.cook|chickpea-butter-curry +Chickpea_Butter_Curry.cook|chickpea-butter-curry-2 +Chinese Orange Shrimp.cook|chinese-orange-shrimp +Chocolate_Mousse_Cake_with_Raspberries.cook|chocolate-mousse-cake-with-raspberries diff --git a/docs/batch-no-image-ac b/docs/batch-no-image-ac new file mode 100644 index 0000000..62bcb9c --- /dev/null +++ b/docs/batch-no-image-ac @@ -0,0 +1,10 @@ +Cilantro Pesto Shrimp Over Orzo.cook|cilantro-pesto-shrimp-over-orzo +Crispy Roasted Chickpeas (Garbanzo Beans).cook|crispy-roasted-chickpeas-garbanzo-beans +Crispy_Roasted_Chickpeas_(Garbanzo_Beans).cook|crispy-roasted-chickpeas-garbanzo-beans-2 +Dried Fava Soup.cook|dried-fava-soup +Easy Lemon Rosemary White Bean Soup.cook|easy-lemon-rosemary-white-bean-soup +Easy_Lemon_Rosemary_White_Bean_Soup.cook|easy-lemon-rosemary-white-bean-soup-2 +Gaja's Bulgogi Lettuce Wraps.cook|gaja-s-bulgogi-lettuce-wraps +Gochugaru Honey Salmon.cook|gochugaru-honey-salmon +Indian Spiced Chicken.cook|indian-spiced-chicken-2 +Kale and Chestnut Stew.cook|kale-and-chestnut-stew diff --git a/docs/batch-no-image-ad b/docs/batch-no-image-ad new file mode 100644 index 0000000..a62f0d7 --- /dev/null +++ b/docs/batch-no-image-ad @@ -0,0 +1,10 @@ +Kkanpoong Tofu.cook|kkanpoong-tofu +Korean Shrimp Tacos.cook|korean-shrimp-tacos +Kung Pao Shrimp with Sichuan Roasted Green Beans.cook|kung-pao-shrimp-with-sichuan-roasted-green-beans +Lactation cookies.cook|lactation-cookies +Lactation_cookies.cook|lactation-cookies-2 +Moroccan_Spiced_Lentil_and_Chickpea_Soup.cook|moroccan-spiced-lentil-and-chickpea-soup +Moroccan Spiced Lentil & Chickpea Soup.cook|moroccan-spiced-lentil-chickpea-soup +Orange Rolls.cook|orange-rolls +Palak Paneer.cook|palak-paneer +Pecan-Brown Butter Trout.cook|pecan-brown-butter-trout diff --git a/docs/batch-no-image-ae b/docs/batch-no-image-ae new file mode 100644 index 0000000..62cbe46 --- /dev/null +++ b/docs/batch-no-image-ae @@ -0,0 +1,10 @@ +Roasted Pumpkin Sage.cook|roasted-pumpkin-sage +Saag Paneer.cook|saag-paneer +sheet-pan-miso-chicken-with-radishes-and-lime.cook|sheet-pan-miso-chicken-with-radishes-and-lime +Spaghetti Carbonara.cook|spaghetti-carbonara +Spaghetti_Squash_w_Chickpeas_and_Kale.cook|spaghetti-squash-w-chickpeas-and-kale +Spanakopita.cook|spanakopita +Spicy Shrimp Paella.cook|spicy-shrimp-paella +Thai_Veggie_Pad_See_Ew.cook|thai-veggie-pad-see-ew +Warm Up With a Quick Lemony Chicken Soup.cook|warm-up-with-a-quick-lemony-chicken-soup +West African Peanut Chicken.cook|west-african-peanut-chicken diff --git a/docs/batch-search-aa b/docs/batch-search-aa new file mode 100644 index 0000000..7126ed7 --- /dev/null +++ b/docs/batch-search-aa @@ -0,0 +1,5 @@ +arugula-pesto-chicken-with-pecans-roasted-butternut|Arugula Pesto Chicken with Pecans & Roasted Butternut +asparagus-salad|Asparagus Salad loveandlemons asparagus salad +baked-chile-relleno-with-black-beans|Baked Chile Relleno with Black Beans +bang-bang-tofu|Bang Bang Tofu +barbecue-chicken-with-crispy-pan-fried-okra-cheddar-biscuits|Barbecue Chicken with Crispy Pan-Fried Okra & Cheddar Biscuits diff --git a/docs/batch-search-ab b/docs/batch-search-ab new file mode 100644 index 0000000..149bde6 --- /dev/null +++ b/docs/batch-search-ab @@ -0,0 +1,5 @@ +blackened-shrimp-and-gouda-grits|Blackened Shrimp and Gouda Grits +butternut-turkey-chili-2|Butternut Turkey Chili +butternut-turkey-chili-with-sweet-peppers-spiced-pepitas-queso-fresco|BUTTERNUT & TURKEY CHILI with sweet peppers, spiced pepitas & queso fresco +chana-chickpea-salad|Chana Chickpea Salad +chicken-and-pumpkin-gnocchi-with-ricotta|Chicken and Pumpkin Gnocchi with Ricotta diff --git a/docs/batch-search-ac b/docs/batch-search-ac new file mode 100644 index 0000000..1ce9dde --- /dev/null +++ b/docs/batch-search-ac @@ -0,0 +1,5 @@ +chickpea-butter-curry|Chickpea Butter Curry +chickpea-butter-curry-2|Chickpea Butter Curry +chinese-orange-shrimp|Chinese Orange Shrimp +chocolate-mousse-cake-with-raspberries|Chocolate Mousse Cake with Raspberries +cilantro-pesto-shrimp-over-orzo|Cilantro Pesto Shrimp Over Orzo diff --git a/docs/batch-search-ad b/docs/batch-search-ad new file mode 100644 index 0000000..a2140cd --- /dev/null +++ b/docs/batch-search-ad @@ -0,0 +1,5 @@ +crispy-roasted-chickpeas-garbanzo-beans|Crispy Roasted Chickpeas (Garbanzo Beans) +crispy-roasted-chickpeas-garbanzo-beans-2|Crispy Roasted Chickpeas (Garbanzo Beans) +dried-fava-soup|Dried Fava Soup 101cookbooks +easy-lemon-rosemary-white-bean-soup|Easy Lemon Rosemary White Bean Soup +easy-lemon-rosemary-white-bean-soup-2|Easy Lemon Rosemary White Bean Soup diff --git a/docs/batch-search-ae b/docs/batch-search-ae new file mode 100644 index 0000000..8b51ab5 --- /dev/null +++ b/docs/batch-search-ae @@ -0,0 +1,5 @@ +gaja-s-bulgogi-lettuce-wraps|Gaja's Bulgogi Lettuce Wraps +gochugaru-honey-salmon|Gochugaru Honey Salmon +indian-spiced-chicken-2|Indian Spiced Chicken +kale-and-chestnut-stew|Kale and Chestnut Stew garnishandgather +korean-shrimp-tacos|Korean Shrimp Tacos diff --git a/docs/batch-search-af b/docs/batch-search-af new file mode 100644 index 0000000..3edd5ea --- /dev/null +++ b/docs/batch-search-af @@ -0,0 +1,5 @@ +kung-pao-shrimp-with-sichuan-roasted-green-beans|Kung Pao Shrimp with Sichuan Roasted Green Beans +lactation-cookies|Lactation cookies +lactation-cookies-2|Lactation cookies +moroccan-spiced-lentil-and-chickpea-soup|Moroccan Spiced Lentil and Chickpea Soup +moroccan-spiced-lentil-chickpea-soup|Moroccan Spiced Lentil and Chickpea Soup diff --git a/docs/batch-search-ag b/docs/batch-search-ag new file mode 100644 index 0000000..d977b67 --- /dev/null +++ b/docs/batch-search-ag @@ -0,0 +1,5 @@ +palak-paneer|Palak Paneer +pecan-brown-butter-trout|Pecan Brown Butter Trout +saag-paneer|Saag Paneer +spaghetti-carbonara|Spaghetti Carbonara +spaghetti-squash-w-chickpeas-and-kale|Spaghetti Squash with Chickpeas and Kale loveandlemons diff --git a/docs/batch-search-ah b/docs/batch-search-ah new file mode 100644 index 0000000..38fcc60 --- /dev/null +++ b/docs/batch-search-ah @@ -0,0 +1,5 @@ +spanakopita|Spanakopita +spicy-shrimp-paella|Spicy Shrimp Paella +thai-veggie-pad-see-ew|Thai Veggie Pad See Ew +warm-up-with-a-quick-lemony-chicken-soup|Lemony Chicken Soup +west-african-peanut-chicken|West African Peanut Chicken diff --git a/docs/failed-images.csv b/docs/failed-images.csv new file mode 100644 index 0000000..a8fc8b9 --- /dev/null +++ b/docs/failed-images.csv @@ -0,0 +1,27 @@ +arugula-pesto-chicken-with-pecans-roasted-butternut +asparagus-salad +baked-chile-relleno-with-black-beans +bang-bang-tofu +barbecue-chicken-with-crispy-pan-fried-okra-cheddar-biscuits +blackened-shrimp-and-gouda-grits +butternut-turkey-chili-2 +chana-chickpea-salad +chicken-and-pumpkin-gnocchi-with-ricotta +chickpea-butter-curry +chickpea-butter-curry-2 +chocolate-mousse-cake-with-raspberries +cilantro-pesto-shrimp-over-orzo +crispy-roasted-chickpeas-garbanzo-beans-2 +easy-lemon-rosemary-white-bean-soup +gaja-s-bulgogi-lettuce-wraps +kale-and-chestnut-stew +korean-shrimp-tacos +kung-pao-shrimp-with-sichuan-roasted-green-beans +moroccan-spiced-lentil-chickpea-soup +palak-paneer +pecan-brown-butter-trout +spaghetti-squash-w-chickpeas-and-kale +spanakopita +spicy-shrimp-paella +thai-veggie-pad-see-ew +west-african-peanut-chicken diff --git a/recipe-images/alain-ducase-bread.webp b/recipe-images/alain-ducase-bread.webp new file mode 100644 index 0000000..a2915eb Binary files /dev/null and b/recipe-images/alain-ducase-bread.webp differ diff --git a/recipe-images/arugula-pesto-chicken-with-pecans-roasted-butternut.webp b/recipe-images/arugula-pesto-chicken-with-pecans-roasted-butternut.webp new file mode 100644 index 0000000..316b123 Binary files /dev/null and b/recipe-images/arugula-pesto-chicken-with-pecans-roasted-butternut.webp differ diff --git a/recipe-images/arugula-pesto-wheat-berries-recipe.webp b/recipe-images/arugula-pesto-wheat-berries-recipe.webp new file mode 100644 index 0000000..48f7d32 Binary files /dev/null and b/recipe-images/arugula-pesto-wheat-berries-recipe.webp differ diff --git a/recipe-images/asparagus-salad.webp b/recipe-images/asparagus-salad.webp new file mode 100644 index 0000000..71cce47 Binary files /dev/null and b/recipe-images/asparagus-salad.webp differ diff --git a/recipe-images/authentic-cacio-e-pepe-recipe.webp b/recipe-images/authentic-cacio-e-pepe-recipe.webp new file mode 100644 index 0000000..41f625f Binary files /dev/null and b/recipe-images/authentic-cacio-e-pepe-recipe.webp differ diff --git a/recipe-images/baked-chile-relleno-with-black-beans.webp b/recipe-images/baked-chile-relleno-with-black-beans.webp new file mode 100644 index 0000000..8071938 Binary files /dev/null and b/recipe-images/baked-chile-relleno-with-black-beans.webp differ diff --git a/recipe-images/bang-bang-tofu.webp b/recipe-images/bang-bang-tofu.webp new file mode 100644 index 0000000..0e86479 Binary files /dev/null and b/recipe-images/bang-bang-tofu.webp differ diff --git a/recipe-images/barbecue-chicken-with-crispy-pan-fried-okra-cheddar-biscuits.webp b/recipe-images/barbecue-chicken-with-crispy-pan-fried-okra-cheddar-biscuits.webp new file mode 100644 index 0000000..bb2af04 Binary files /dev/null and b/recipe-images/barbecue-chicken-with-crispy-pan-fried-okra-cheddar-biscuits.webp differ diff --git a/recipe-images/black-bean-and-corn-salad.webp b/recipe-images/black-bean-and-corn-salad.webp new file mode 100644 index 0000000..9664d61 Binary files /dev/null and b/recipe-images/black-bean-and-corn-salad.webp differ diff --git a/recipe-images/blackened-shrimp-and-gouda-grits.webp b/recipe-images/blackened-shrimp-and-gouda-grits.webp new file mode 100644 index 0000000..c44b98d Binary files /dev/null and b/recipe-images/blackened-shrimp-and-gouda-grits.webp differ diff --git a/recipe-images/butternut-turkey-chili-2.webp b/recipe-images/butternut-turkey-chili-2.webp new file mode 100644 index 0000000..7c2419e Binary files /dev/null and b/recipe-images/butternut-turkey-chili-2.webp differ diff --git a/recipe-images/butternut-turkey-chili-with-sweet-peppers-spiced-pepitas-queso-fresco.webp b/recipe-images/butternut-turkey-chili-with-sweet-peppers-spiced-pepitas-queso-fresco.webp new file mode 100644 index 0000000..f45836b Binary files /dev/null and b/recipe-images/butternut-turkey-chili-with-sweet-peppers-spiced-pepitas-queso-fresco.webp differ diff --git a/recipe-images/cabbage-salad.webp b/recipe-images/cabbage-salad.webp new file mode 100644 index 0000000..cba7066 Binary files /dev/null and b/recipe-images/cabbage-salad.webp differ diff --git a/recipe-images/cauliflower-milanese.webp b/recipe-images/cauliflower-milanese.webp new file mode 100644 index 0000000..7616a83 Binary files /dev/null and b/recipe-images/cauliflower-milanese.webp differ diff --git a/recipe-images/chana-chickpea-salad.webp b/recipe-images/chana-chickpea-salad.webp new file mode 100644 index 0000000..c06fb6c Binary files /dev/null and b/recipe-images/chana-chickpea-salad.webp differ diff --git a/recipe-images/chicken-and-pumpkin-gnocchi-with-ricotta.webp b/recipe-images/chicken-and-pumpkin-gnocchi-with-ricotta.webp new file mode 100644 index 0000000..01f98ea Binary files /dev/null and b/recipe-images/chicken-and-pumpkin-gnocchi-with-ricotta.webp differ diff --git a/recipe-images/chickpea-butter-curry-2.webp b/recipe-images/chickpea-butter-curry-2.webp new file mode 100644 index 0000000..3a25af4 Binary files /dev/null and b/recipe-images/chickpea-butter-curry-2.webp differ diff --git a/recipe-images/chickpea-butter-curry.webp b/recipe-images/chickpea-butter-curry.webp new file mode 100644 index 0000000..355523c Binary files /dev/null and b/recipe-images/chickpea-butter-curry.webp differ diff --git a/recipe-images/chinese-orange-shrimp.webp b/recipe-images/chinese-orange-shrimp.webp new file mode 100644 index 0000000..7016253 Binary files /dev/null and b/recipe-images/chinese-orange-shrimp.webp differ diff --git a/recipe-images/chocolate-mousse-cake-with-raspberries.webp b/recipe-images/chocolate-mousse-cake-with-raspberries.webp new file mode 100644 index 0000000..f13a543 Binary files /dev/null and b/recipe-images/chocolate-mousse-cake-with-raspberries.webp differ diff --git a/recipe-images/cilantro-pesto-shrimp-over-orzo.webp b/recipe-images/cilantro-pesto-shrimp-over-orzo.webp new file mode 100644 index 0000000..902b4e9 Binary files /dev/null and b/recipe-images/cilantro-pesto-shrimp-over-orzo.webp differ diff --git a/recipe-images/crispy-roasted-chickpeas-garbanzo-beans-2.webp b/recipe-images/crispy-roasted-chickpeas-garbanzo-beans-2.webp new file mode 100644 index 0000000..3046330 Binary files /dev/null and b/recipe-images/crispy-roasted-chickpeas-garbanzo-beans-2.webp differ diff --git a/recipe-images/crispy-roasted-chickpeas-garbanzo-beans.webp b/recipe-images/crispy-roasted-chickpeas-garbanzo-beans.webp new file mode 100644 index 0000000..a94213d Binary files /dev/null and b/recipe-images/crispy-roasted-chickpeas-garbanzo-beans.webp differ diff --git a/recipe-images/dried-fava-soup.webp b/recipe-images/dried-fava-soup.webp new file mode 100644 index 0000000..a94213d Binary files /dev/null and b/recipe-images/dried-fava-soup.webp differ diff --git a/recipe-images/easy-lemon-rosemary-white-bean-soup-2.webp b/recipe-images/easy-lemon-rosemary-white-bean-soup-2.webp new file mode 100644 index 0000000..c80fcec Binary files /dev/null and b/recipe-images/easy-lemon-rosemary-white-bean-soup-2.webp differ diff --git a/recipe-images/easy-lemon-rosemary-white-bean-soup.webp b/recipe-images/easy-lemon-rosemary-white-bean-soup.webp new file mode 100644 index 0000000..471241c Binary files /dev/null and b/recipe-images/easy-lemon-rosemary-white-bean-soup.webp differ diff --git a/recipe-images/gaja-s-bulgogi-lettuce-wraps.webp b/recipe-images/gaja-s-bulgogi-lettuce-wraps.webp new file mode 100644 index 0000000..ba70cdc Binary files /dev/null and b/recipe-images/gaja-s-bulgogi-lettuce-wraps.webp differ diff --git a/recipe-images/gochugaru-honey-salmon.webp b/recipe-images/gochugaru-honey-salmon.webp new file mode 100644 index 0000000..5286977 Binary files /dev/null and b/recipe-images/gochugaru-honey-salmon.webp differ diff --git a/recipe-images/indian-spiced-chicken-2.webp b/recipe-images/indian-spiced-chicken-2.webp new file mode 100644 index 0000000..9b09c5d Binary files /dev/null and b/recipe-images/indian-spiced-chicken-2.webp differ diff --git a/recipe-images/kale-and-chestnut-stew.webp b/recipe-images/kale-and-chestnut-stew.webp new file mode 100644 index 0000000..d45cfe9 Binary files /dev/null and b/recipe-images/kale-and-chestnut-stew.webp differ diff --git a/recipe-images/kkanpoong-tofu.webp b/recipe-images/kkanpoong-tofu.webp new file mode 100644 index 0000000..56099a3 Binary files /dev/null and b/recipe-images/kkanpoong-tofu.webp differ diff --git a/recipe-images/korean-shrimp-tacos.webp b/recipe-images/korean-shrimp-tacos.webp new file mode 100644 index 0000000..7d84260 Binary files /dev/null and b/recipe-images/korean-shrimp-tacos.webp differ diff --git a/recipe-images/kung-pao-shrimp-with-sichuan-roasted-green-beans.webp b/recipe-images/kung-pao-shrimp-with-sichuan-roasted-green-beans.webp new file mode 100644 index 0000000..9202910 Binary files /dev/null and b/recipe-images/kung-pao-shrimp-with-sichuan-roasted-green-beans.webp differ diff --git a/recipe-images/lactation-cookies-2.webp b/recipe-images/lactation-cookies-2.webp new file mode 100644 index 0000000..3297f76 Binary files /dev/null and b/recipe-images/lactation-cookies-2.webp differ diff --git a/recipe-images/lactation-cookies.webp b/recipe-images/lactation-cookies.webp new file mode 100644 index 0000000..0e64c40 Binary files /dev/null and b/recipe-images/lactation-cookies.webp differ diff --git a/recipe-images/moroccan-spiced-lentil-and-chickpea-soup.webp b/recipe-images/moroccan-spiced-lentil-and-chickpea-soup.webp new file mode 100644 index 0000000..719202f Binary files /dev/null and b/recipe-images/moroccan-spiced-lentil-and-chickpea-soup.webp differ diff --git a/recipe-images/moroccan-spiced-lentil-chickpea-soup.webp b/recipe-images/moroccan-spiced-lentil-chickpea-soup.webp new file mode 100644 index 0000000..b2a4eba Binary files /dev/null and b/recipe-images/moroccan-spiced-lentil-chickpea-soup.webp differ diff --git a/recipe-images/orange-rolls.webp b/recipe-images/orange-rolls.webp new file mode 100644 index 0000000..64a37d5 Binary files /dev/null and b/recipe-images/orange-rolls.webp differ diff --git a/recipe-images/palak-paneer.webp b/recipe-images/palak-paneer.webp new file mode 100644 index 0000000..785e8cb Binary files /dev/null and b/recipe-images/palak-paneer.webp differ diff --git a/recipe-images/pecan-brown-butter-trout.webp b/recipe-images/pecan-brown-butter-trout.webp new file mode 100644 index 0000000..8880189 Binary files /dev/null and b/recipe-images/pecan-brown-butter-trout.webp differ diff --git a/recipe-images/roasted-pumpkin-sage.webp b/recipe-images/roasted-pumpkin-sage.webp new file mode 100644 index 0000000..06a69e7 Binary files /dev/null and b/recipe-images/roasted-pumpkin-sage.webp differ diff --git a/recipe-images/saag-paneer.webp b/recipe-images/saag-paneer.webp new file mode 100644 index 0000000..99b506f Binary files /dev/null and b/recipe-images/saag-paneer.webp differ diff --git a/recipe-images/sheet-pan-miso-chicken-with-radishes-and-lime.webp b/recipe-images/sheet-pan-miso-chicken-with-radishes-and-lime.webp new file mode 100644 index 0000000..625d3bf Binary files /dev/null and b/recipe-images/sheet-pan-miso-chicken-with-radishes-and-lime.webp differ diff --git a/recipe-images/spaghetti-carbonara.webp b/recipe-images/spaghetti-carbonara.webp new file mode 100644 index 0000000..d6166e7 Binary files /dev/null and b/recipe-images/spaghetti-carbonara.webp differ diff --git a/recipe-images/spaghetti-squash-w-chickpeas-and-kale.webp b/recipe-images/spaghetti-squash-w-chickpeas-and-kale.webp new file mode 100644 index 0000000..0ee7b9c Binary files /dev/null and b/recipe-images/spaghetti-squash-w-chickpeas-and-kale.webp differ diff --git a/recipe-images/spanakopita.webp b/recipe-images/spanakopita.webp new file mode 100644 index 0000000..eb700dc Binary files /dev/null and b/recipe-images/spanakopita.webp differ diff --git a/recipe-images/spicy-shrimp-paella.webp b/recipe-images/spicy-shrimp-paella.webp new file mode 100644 index 0000000..804952b Binary files /dev/null and b/recipe-images/spicy-shrimp-paella.webp differ diff --git a/recipe-images/thai-veggie-pad-see-ew.webp b/recipe-images/thai-veggie-pad-see-ew.webp new file mode 100644 index 0000000..bcafd07 Binary files /dev/null and b/recipe-images/thai-veggie-pad-see-ew.webp differ diff --git a/recipe-images/warm-up-with-a-quick-lemony-chicken-soup.webp b/recipe-images/warm-up-with-a-quick-lemony-chicken-soup.webp new file mode 100644 index 0000000..a94213d Binary files /dev/null and b/recipe-images/warm-up-with-a-quick-lemony-chicken-soup.webp differ diff --git a/recipe-images/west-african-peanut-chicken.webp b/recipe-images/west-african-peanut-chicken.webp new file mode 100644 index 0000000..9851cda Binary files /dev/null and b/recipe-images/west-african-peanut-chicken.webp differ diff --git a/sheet-pan-miso-chicken-with-radishes-and-lime.cook b/sheet-pan-miso-chicken-with-radishes-and-lime.cook index ad0fcc3..4684272 100644 --- a/sheet-pan-miso-chicken-with-radishes-and-lime.cook +++ b/sheet-pan-miso-chicken-with-radishes-and-lime.cook @@ -1,3 +1,4 @@ +image: https://roux.roo.lol/recipe-images/sheet-pan-miso-chicken-with-radishes-and-lime.webp --- title: Sheet-Pan Miso Chicken With Radishes and Lime source: https://cooking.nytimes.com/recipes/1020988-sheet-pan-miso-chicken-with-radishes-and-lime