diff --git a/comfyrecipes/parsing.py b/comfyrecipes/parsing.py
index d702d27..5fb5833 100644
--- a/comfyrecipes/parsing.py
+++ b/comfyrecipes/parsing.py
@@ -6,6 +6,37 @@ import comfyrecipes.settings as settings
import comfyrecipes.issues as issues
import jsonschema
+import lxml.html.clean
+import mistune
+
+
+class SafeHTML:
+ def __init__(self, clean_html: str) -> None:
+ self.html = clean_html
+
+ @classmethod
+ def from_html(cls, dirty_html: str) -> Self:
+ cleaner = lxml.html.clean.Cleaner(
+ allow_tags=["a", "b", "em", "i", "strong"],
+ safe_attrs_only=True,
+ safe_attrs=["href"],
+ )
+
+ html = cleaner.clean_html(dirty_html).strip()
+ assert isinstance(html, str)
+
+ assert html.startswith("
")
+ assert html.endswith("
")
+ html = html[5:-6]
+
+ return cls(html)
+
+ @classmethod
+ def from_markdown(cls, markdowntxt: str) -> Self:
+ dirty_html = mistune.html(markdowntxt)
+ assert isinstance(dirty_html, str)
+ return cls.from_html(dirty_html)
+
class Context:
def __init__(self) -> None:
@@ -219,6 +250,7 @@ class Recipe(Element):
self,
ctx: Context,
title: str,
+ source: Optional[SafeHTML],
ingredients: List[IngredientInstance],
subrecipes: List["Recipe"],
price: Optional["PriceDB"],
@@ -232,6 +264,7 @@ class Recipe(Element):
self.subrecipes = subrecipes
self.price = price
self.stepsections = stepsections
+ self.source = source
@classmethod
def from_dict(cls, ctx: Context, dct: Dict[str, Any]) -> Self:
@@ -287,9 +320,14 @@ class Recipe(Element):
section = StepSection.from_dict(ctx, step)
stepsections.append(section)
+ source = None
+ if "source" in dct:
+ source = SafeHTML.from_markdown(dct["source"])
+
return cls(
ctx=ctx,
title=dct["title"],
+ source=source,
ingredients=ingredients,
subrecipes=subrecipes,
price=price,
diff --git a/comfyrecipes/schemas/recipe.json b/comfyrecipes/schemas/recipe.json
index a8f9876..e2220b0 100644
--- a/comfyrecipes/schemas/recipe.json
+++ b/comfyrecipes/schemas/recipe.json
@@ -8,6 +8,7 @@
"additionalProperties": false,
"properties": {
"title": { "type": "string" },
+ "source": { "type": "string" },
"ingredients": {
"type": "array",
"items": {
diff --git a/comfyrecipes/templates/recipe.html b/comfyrecipes/templates/recipe.html
index 552920d..0b1bde7 100644
--- a/comfyrecipes/templates/recipe.html
+++ b/comfyrecipes/templates/recipe.html
@@ -17,6 +17,7 @@
{{getrecipe(subrecipe)}}
{% endfor %}
{{rec.title}}
+{% if rec.source != None %}source: {{rec.source.html|safe}}{%endif%}
{% if rec.ingredients|length != 0 %}
Ingredients
{% for ing in rec.ingredients %}
diff --git a/pyproject.toml b/pyproject.toml
index 9cac692..12f1a3f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,7 +9,7 @@ description = "Smart recipe static site generator"
authors = [{ name = "Emi Vasilek", email = "emi.vasilek@gmail.com" }]
keywords = ["recipes", "recipe", "static site generator"]
requires-python = ">= 3.8"
-dependencies = ["jsonschema", "jinja2", "PyYAML"]
+dependencies = ["jsonschema", "jinja2", "PyYAML", "mistune", "lxml"]
classifiers = [
"Development Status :: 3 - Alpha",
"Programming Language :: Python",