diff --git a/components/Emoji.tsx b/components/Emoji.tsx index f21371e..8bb7f43 100644 --- a/components/Emoji.tsx +++ b/components/Emoji.tsx @@ -10,5 +10,5 @@ export const Emoji = (props: { class?: string; name: string }) => { /> ) : {props.name} - : <>; + : null; }; diff --git a/lib/recipeSchema.ts b/lib/recipeSchema.ts index d5dcbfa..cfa63de 100644 --- a/lib/recipeSchema.ts +++ b/lib/recipeSchema.ts @@ -18,8 +18,9 @@ export const IngredientGroupSchema = z.object({ export type IngredientGroup = z.infer; const recipeSchema = z.object({ + _type: z.literal("Recipe"), name: z.string().describe( - "Title of the Recipe, without the name of the website or author", + "Name of the Recipe, without the name of the website or author", ), description: z.string().describe( "Optional, short description of the recipe", @@ -41,11 +42,7 @@ const recipeSchema = z.object({ export type Recipe = z.infer; -const noRecipeSchema = z.object({ - errorMessages: z.array(z.string()).describe( - "List of error messages, if no recipe was found", - ), -}); +const noRecipeSchema = z.literal("none").describe("No Recipe found"); export const recipeResponseSchema = z.union([recipeSchema, noRecipeSchema]); diff --git a/lib/resources.ts b/lib/resources.ts index d7ced83..75bd9a2 100644 --- a/lib/resources.ts +++ b/lib/resources.ts @@ -1,26 +1,26 @@ export const resources = { "home": { - emoji: "House with Garden.png", + emoji: "home_icon.png", name: "Home", link: "/", }, "recipe": { - emoji: "Fork and Knife with Plate.png", + emoji: "recipes_icon.png", name: "Recipes", link: "/recipes", }, "movie": { - emoji: "Popcorn.png", + emoji: "movies_icon.png", name: "Movies", link: "/movies", }, "article": { - emoji: "Writing Hand Medium-Light Skin Tone.png", + emoji: "articles_icon.png", name: "Articles", link: "/articles", }, "series": { - emoji: "Television.png", + emoji: "tv_series_icon.png", name: "Series", link: "/series", }, diff --git a/lib/string.ts b/lib/string.ts index cd64b87..13ed976 100644 --- a/lib/string.ts +++ b/lib/string.ts @@ -17,6 +17,7 @@ export function safeFileName(input: string): string { .normalize("NFKD") .replace(/[\u0300-\u036f]/g, "") .replace(/[\s-]+/g, "_") + .replace(/-+/g, "-") .replace(/[^A-Za-z0-9_]+/g, "") .replace(/_+/g, "_") // Trim underscores/dots from ends and prevent leading dots diff --git a/lib/webScraper.ts b/lib/webScraper.ts index a49e513..5be403a 100644 --- a/lib/webScraper.ts +++ b/lib/webScraper.ts @@ -181,7 +181,7 @@ export async function webScrape( return { ...result, - dom, + dom: dom.window.document, markdown: turndownService.turndown(result.content), }; } diff --git a/routes/api/recipes/create/index.ts b/routes/api/recipes/create/index.ts index 5a5c518..19e5371 100644 --- a/routes/api/recipes/create/index.ts +++ b/routes/api/recipes/create/index.ts @@ -37,17 +37,17 @@ async function processCreateRecipeFromUrl( let recipe: z.infer | undefined = undefined; if (jsonLds.length > 0) { for (const jsonLd of jsonLds) { - recipe = parseJsonLdToRecipeSchema(jsonLd.textContent || ""); - if (recipe) break; + if (jsonLd.textContent) { + recipe = parseJsonLdToRecipeSchema(jsonLd.textContent); + if (recipe) break; + } } } if (!recipe) { const res = await openai.extractRecipe(result.markdown); - if (!res || "errorMessages" in res) { - const errorMessage = res?.errorMessages?.[0] || - "could not extract recipe"; - streamResponse.error(`failed to extract recipe: ${errorMessage}`); + if (!res || res === "none") { + streamResponse.error(`failed to extract recipe: ${res}`); return; } recipe = res; @@ -72,9 +72,7 @@ async function processCreateRecipeFromUrl( if (newRecipe?.image && newRecipe.image.length > 5) { const extension = fileExtension(newRecipe.image); - const finalPath = `resources/recipes/images/${ - safeFileName(id) - }_cover.${extension}`; + const finalPath = `recipes/images/${safeFileName(id)}_cover.${extension}`; streamResponse.info("downloading image"); try { streamResponse.info("downloading image"); @@ -82,7 +80,7 @@ async function processCreateRecipeFromUrl( streamResponse.info("saving image"); const buffer = await res.arrayBuffer(); await createResource(finalPath, buffer); - newRecipe.image = finalPath; + newRecipe.image = `resources/${finalPath}`; } catch (err) { console.log("Failed to save image", err); } diff --git a/routes/api/recipes/create/parseJsonLd.ts b/routes/api/recipes/create/parseJsonLd.ts index c45a8f4..5433b85 100644 --- a/routes/api/recipes/create/parseJsonLd.ts +++ b/routes/api/recipes/create/parseJsonLd.ts @@ -1,5 +1,4 @@ -import recipeSchema from "@lib/recipeSchema.ts"; -import { parseIngredients } from "@lib/parseIngredient.ts"; +import recipeSchema, { Recipe } from "@lib/recipeSchema.ts"; export function parseJsonLdToRecipeSchema(jsonLdContent: string) { try { @@ -19,12 +18,7 @@ export function parseJsonLdToRecipeSchema(jsonLdContent: string) { return; } - // Map and parse ingredients into the new schema - const ingredients = parseIngredients( - data?.recipeIngredient?.join("\n") || "", - ); - - const instructions = Array.isArray(data.recipeInstructions) + const recipeInstructions = Array.isArray(data.recipeInstructions) ? data.recipeInstructions.map((instr: unknown) => { if (!instr) return ""; if (typeof instr === "string") return instr; @@ -36,43 +30,41 @@ export function parseJsonLdToRecipeSchema(jsonLdContent: string) { : []; // Parse servings - const servings = parseServings(data.recipeYield); + const recipeYield = parseServings(data.recipeYield); // Parse times - const prepTime = parseDuration(data.prepTime); - const cookTime = parseDuration(data.cookTime); const totalTime = parseDuration(data.totalTime); // Extract tags - const tags = data.keywords + const keywords = data.keywords ? Array.isArray(data.keywords) ? data.keywords : data.keywords.split(",").map((tag: string) => tag.trim()) : []; // Build the recipe object - const recipe = { + const recipe: Recipe = { _type: "Recipe", - title: data.name || "Unnamed Recipe", + name: data.name || "Unnamed Recipe", image: pickImage(image || data.image || ""), - author: Array.isArray(data.author) - ? data.author.map((a: { name: string }) => a.name).join(", ") - : data.author?.name || "", + author: { + "_type": "Person", + name: Array.isArray(data.author) + ? data.author.map((a: { name: string }) => a.name).join(", ") + : data.author?.name || "", + }, description: data.description || "", - ingredients, - instructions, - servings, - prepTime, - cookTime, + recipeIngredient: data.recipeIngredient, + recipeInstructions, + recipeYield, totalTime, - tags, - notes: data.notes || [], + keywords, }; // Validate against the schema return recipeSchema.parse(recipe); } catch (error) { - console.error("Invalid JSON-LD content or parsing error:", error); + console.log("Invalid JSON-LD content or parsing error:", error); return undefined; } } diff --git a/routes/articles/index.tsx b/routes/articles/index.tsx index 24f3745..e579c67 100644 --- a/routes/articles/index.tsx +++ b/routes/articles/index.tsx @@ -49,8 +49,9 @@ export default function Greet( - {articles?.map((doc) => ( + {articles?.map((doc, i) => ( diff --git a/routes/index.tsx b/routes/index.tsx index 9e37396..3dbfdb9 100644 --- a/routes/index.tsx +++ b/routes/index.tsx @@ -12,14 +12,12 @@ export default function Home(props: PageProps) { -

- - Resources -

{Object.values(resources).filter((v) => v.link !== "/").map((m) => { return ( {movies?.map((doc, i) => { - return ; + return ; })} diff --git a/routes/series/index.tsx b/routes/series/index.tsx index 0b72f49..11d2c89 100644 --- a/routes/series/index.tsx +++ b/routes/series/index.tsx @@ -50,7 +50,9 @@ export default function Greet( {series?.map((doc, i) => { - return ; + return ( + + ); })} diff --git a/static/emojis/articles_icon.png b/static/emojis/articles_icon.png new file mode 100644 index 0000000..1f3ed1f Binary files /dev/null and b/static/emojis/articles_icon.png differ diff --git a/static/emojis/home_icon.png b/static/emojis/home_icon.png new file mode 100644 index 0000000..7d1dd7e Binary files /dev/null and b/static/emojis/home_icon.png differ diff --git a/static/emojis/movies_icon.png b/static/emojis/movies_icon.png new file mode 100644 index 0000000..5dcf9f9 Binary files /dev/null and b/static/emojis/movies_icon.png differ diff --git a/static/emojis/recipes_icon.png b/static/emojis/recipes_icon.png new file mode 100644 index 0000000..399c7fa Binary files /dev/null and b/static/emojis/recipes_icon.png differ diff --git a/static/emojis/tv_series_icon.png b/static/emojis/tv_series_icon.png new file mode 100644 index 0000000..c3f0d21 Binary files /dev/null and b/static/emojis/tv_series_icon.png differ diff --git a/static/splotch_1.png b/static/splotch_1.png new file mode 100644 index 0000000..c2dd759 Binary files /dev/null and b/static/splotch_1.png differ diff --git a/static/splotch_2.png b/static/splotch_2.png new file mode 100644 index 0000000..23ffd07 Binary files /dev/null and b/static/splotch_2.png differ diff --git a/static/splotch_3.png b/static/splotch_3.png new file mode 100644 index 0000000..305c216 Binary files /dev/null and b/static/splotch_3.png differ