Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- {
- "name": "Banner Generator",
- "nodes": [
- {
- "parameters": {
- "fileSelector": "/data/apps/n8n/banners/week */*.json",
- "options": {}
- },
- "type": "n8n-nodes-base.readWriteFile",
- "typeVersion": 1,
- "position": [
- -1060,
- -760
- ],
- "id": "66ac86b3-f784-4aa6-8cca-b732968b5db1",
- "name": "Read/Write Files from Disk"
- },
- {
- "parameters": {
- "operation": "fromJson",
- "binaryPropertyName": "=data",
- "options": {}
- },
- "type": "n8n-nodes-base.extractFromFile",
- "typeVersion": 1,
- "position": [
- -840,
- -760
- ],
- "id": "ce8c3e02-6a94-470a-9201-d4497480d22b",
- "name": "Extract from File"
- },
- {
- "parameters": {
- "jsCode": "// ---------------------------------------------------------\n// SANITIZE — definitieve versie\n// ---------------------------------------------------------\nfunction sanitize(str) {\n if (!str) return \"\";\n\n return String(str)\n .replace(/^\"+|\"+$/g, \"\")\n .replace(/\\\\\"/g, \"\")\n .replace(/\"/g, \"\")\n .replace(/<[^>]*>/g, \"\")\n .replace(/[\\u0000-\\u001F]/g, \"\")\n .replace(/\\u200B/g, \"\")\n .replace(/\\u00AD/g, \"\")\n .replace(/\\uFEFF/g, \"\")\n .replace(/\\uFFFD/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nconst newItems = [];\n\nfor (const item of items) {\n const data = item.json.data;\n\n // ---------------------------------------------------------\n // BASISGEGEVENS\n // ---------------------------------------------------------\n const sku = sanitize(data.SKU);\n const week = sanitize(data.Week);\n const name = sanitize(data.Name);\n\n // ⭐ FIX 1 — valideer productnaam\n if (!name || name.length < 2) {\n console.log(\"SKIPPED ITEM — Ongeldige productnaam:\", name);\n continue;\n }\n\n // ⭐ FIX 2 — veilige prompt\n const promptText = `Een scherpe, professionele productfoto van de ${name}.`;\n\n if (!promptText || promptText.includes(\"undefined\")) {\n console.log(\"SKIPPED ITEM — Ongeldige prompt:\", promptText);\n continue;\n }\n\n // ---------------------------------------------------------\n // AFBEELDING — 100% veilig\n // ---------------------------------------------------------\n let rawImage = data.MainImage || data[\"Main Product Image\"] || \"\";\n let image_url = sanitize(rawImage);\n\n if (!image_url || !image_url.startsWith(\"http\")) {\n console.log(\"SKIPPED ITEM — Ongeldige image_url:\", image_url);\n continue;\n }\n\n // ---------------------------------------------------------\n // PRIJZEN\n // ---------------------------------------------------------\n const priceRaw = sanitize(data.Price);\n const specialRaw = sanitize(data.Special_Price);\n\n const price = priceRaw ? parseFloat(priceRaw.replace(\",\", \".\")) : null;\n const special = specialRaw ? parseFloat(specialRaw.replace(\",\", \".\")) : null;\n\n let discount = null;\n let percentage = null;\n\n if (price && special && price > 0) {\n discount = price - special;\n percentage = (discount / price * 100);\n }\n\n // ---------------------------------------------------------\n // TEKSTEN\n // ---------------------------------------------------------\n const weekText = `Week ${week} aanbieding`;\n\n const priceLine =\n price && special\n ? `Van €${price.toFixed(2)} voor €${special.toFixed(2)}`\n : null;\n\n const saveLine =\n discount !== null\n ? `Je bespaart €${discount.toFixed(2)}`\n : null;\n\n const percentLine =\n percentage !== null\n ? `${percentage.toFixed(2)}% korting`\n : null;\n\n const storyTextParts = [weekText, priceLine, saveLine, percentLine].filter(Boolean);\n const storyText = storyTextParts.join(\" — \");\n\n // ---------------------------------------------------------\n // 1) PRODUCT\n // ---------------------------------------------------------\n newItems.push({\n json: {\n type: \"product\",\n sku,\n week,\n filename: `product_${sku}`,\n output: `/data/apps/n8n/banners/week ${week}/product/${sku}.jpg`,\n prompt: promptText,\n product: name, // ⭐ FIX 3\n image_url,\n price,\n special_price: special,\n discount,\n discount_percentage: percentage\n }\n });\n\n // ---------------------------------------------------------\n // 2) ADS\n // ---------------------------------------------------------\n newItems.push({\n json: {\n type: \"ads\",\n sku,\n week,\n filename: `ads_${sku}`,\n output: `/data/apps/n8n/banners/week ${week}/ads/${sku}.jpg`,\n prompt: promptText,\n product: name, // ⭐ FIX 3\n image_url,\n price,\n special_price: special,\n discount,\n discount_percentage: percentage\n }\n });\n\n // ---------------------------------------------------------\n // 3) STORIES\n // ---------------------------------------------------------\n newItems.push({\n json: {\n type: \"stories\",\n sku,\n week,\n filename: `stories_${sku}`,\n output: `/data/apps/n8n/banners/week ${week}/stories/${sku}.jpg`,\n prompt: promptText,\n product: name, // ⭐ FIX 3\n image_url,\n price,\n special_price: special,\n discount,\n discount_percentage: percentage,\n story_text: storyText,\n week_text: weekText,\n price_line: priceLine,\n save_line: saveLine,\n percent_line: percentLine\n }\n });\n\n // ---------------------------------------------------------\n // 4) CARROUSEL\n // ---------------------------------------------------------\n newItems.push({\n json: {\n type: \"carrousel\",\n sku,\n week,\n filename: `carrousel_${sku}`,\n output: `/data/apps/n8n/banners/week ${week}/carrousel/${sku}.jpg`,\n prompt: promptText,\n product: name, // ⭐ FIX 3\n image_url,\n price,\n special_price: special,\n discount,\n discount_percentage: percentage\n }\n });\n}\n\nreturn newItems;\n"
- },
- "type": "n8n-nodes-base.code",
- "typeVersion": 2,
- "position": [
- -540,
- -760
- ],
- "id": "7f3740e1-2223-460a-acdb-c92e64cbb55d",
- "name": "Code"
- },
- {
- "parameters": {
- "method": "POST",
- "url": "http://192.168.1.28:8188/prompt",
- "sendBody": true,
- "specifyBody": "json",
- "jsonBody": "={{JSON.stringify({prompt: $json.prompt})}}\n",
- "options": {}
- },
- "type": "n8n-nodes-base.httpRequest",
- "typeVersion": 4.2,
- "position": [
- -180,
- -260
- ],
- "id": "539c751e-10bc-40b5-a545-4955f4eefb97",
- "name": "HTTP Request"
- },
- {
- "parameters": {
- "url": "=http://192.168.1.28:8188/history/{{$json.prompt_id}}\n",
- "options": {}
- },
- "type": "n8n-nodes-base.httpRequest",
- "typeVersion": 4.2,
- "position": [
- 140,
- -140
- ],
- "id": "4ce5e242-f559-409b-a005-c689b2cddc86",
- "name": "HTTP Request1"
- },
- {
- "parameters": {
- "mode": "runOnceForEachItem",
- "jsCode": "// Haal de base64 PNG op uit de ComfyUI history response\nconst base64 = $json.outputs[\"13\"].images[0].image;\n\n// Zet base64 om naar binary zodat n8n het als bestand kan opslaan\nreturn {\n binary: {\n data: {\n data: Buffer.from(base64, 'base64'),\n mimeType: 'image/png',\n fileExtension: 'png'\n }\n },\n json: {\n // Geef de originele JSON door zodat Move Binary File nog weet waar het heen moet\n type: $json.type,\n sku: $json.sku,\n week: $json.week\n }\n};\n"
- },
- "type": "n8n-nodes-base.code",
- "typeVersion": 2,
- "position": [
- 220,
- -520
- ],
- "id": "0f7ea871-a505-4e45-ad47-39dffe3cd8bd",
- "name": "Code1"
- },
- {
- "parameters": {
- "mode": "raw",
- "jsonOutput": "={{$json}}\n",
- "options": {}
- },
- "type": "n8n-nodes-base.set",
- "typeVersion": 3.4,
- "position": [
- 260,
- -280
- ],
- "id": "2a692a11-72e3-4f86-a2d4-877de3e53682",
- "name": "Edit Fields"
- },
- {
- "parameters": {
- "mode": "runOnceForEachItem",
- "jsCode": "// -------------------------\n// DEBUG + VALIDATIE\n// -------------------------\nconsole.log(\"DEBUG URL:\", $json.image_url);\n\nif (!$json.image_url || String($json.image_url).trim() === \"\") {\n throw new Error(\"image_url ontbreekt of is ongeldig.\");\n}\n\n// -------------------------\n// DYNAMISCHE PADEN\n// -------------------------\n\n// Weekmap (bijv. \"week 2619\")\nconst weekFolder = $json.week || \"week 2619\";\n\n// Advertentietype (ads, product, stories, carrousel)\nconst adType = $json.type || \"product\";\n\n// Volledig outputpad\nconst outputPath = `/data/apps/n8n/banners/${weekFolder}/${adType}/`;\n\n\n// -------------------------\n// 1. AUTOMATISCHE LAYOUT-DETECTIE\n// -------------------------\nlet layout = \"left\";\n\nif ($json.filename.includes(\"_middle\")) layout = \"middle\";\nelse if ($json.filename.includes(\"_right\")) layout = \"right\";\nelse if ($json.filename.includes(\"_studio\")) layout = \"studio\";\n\n// -------------------------\n// 2. AUTOMATISCHE RESOLUTIE\n// -------------------------\nlet width = 1080;\nlet height = 1080;\n\nif (layout === \"middle\") { width = 1200; height = 628; }\nif (layout === \"right\") { width = 1080; height = 1350; }\nif (layout === \"studio\") { width = 1080; height = 1080; }\n\n// -------------------------\n// 3. AUTOMATISCHE STIJL\n// -------------------------\nlet style = \"\";\n\nif (layout === \"left\")\n style = \"clean product photo, studio lighting, premium look, glossy highlights\";\nif (layout === \"middle\")\n style = \"promo banner style, bold colors, high contrast, advertisement energy\";\nif (layout === \"right\")\n style = \"lifestyle desk setup, ambient lighting, realistic environment\";\nif (layout === \"studio\")\n style = \"neutral packshot, softbox lighting, clean white background\";\n\n// -------------------------\n// 4. COMFYUI WORKFLOW — CORRECTE IPADAPTER\n// -------------------------\nconst workflow = {\n \"3\": {\n class_type: \"KSampler\",\n inputs: {\n seed: Math.floor(Math.random() * 999999999),\n steps: 34,\n cfg: 5.8,\n sampler_name: \"dpmpp_2m\",\n scheduler: \"karras\",\n denoise: 1,\n model: [\"4\", 0],\n positive: [\"6\", 0],\n negative: [\"7\", 0],\n latent_image: [\"10\", 0],\n ipadapter: [\"12\", 0]\n }\n },\n\n \"4\": {\n class_type: \"CheckpointLoaderSimple\",\n inputs: {\n ckpt_name: \"v1-5-pruned-emaonly.safetensors\"\n }\n },\n\n \"11\": {\n class_type: \"LoadImageFromHttpURL\",\n inputs: {\n image_url: $json.image_url\n }\n },\n\n \"13\": {\n class_type: \"IPAdapterModelLoader\",\n inputs: {\n ipadapter_file: \"ip-adapter_sd15.bin\"\n }\n },\n\n \"12\": {\n class_type: \"IPAdapterAdvanced\",\n inputs: {\n image: [\"11\", 0],\n model: [\"13\", 0],\n weight: 0.85\n }\n },\n\n \"5\": {\n class_type: \"VAEDecode\",\n inputs: {\n samples: [\"3\", 0],\n vae: [\"4\", 2]\n }\n },\n\n \"6\": {\n class_type: \"CLIPTextEncode\",\n inputs: {\n text:\n ($json.prompt || \"\") + \", \" +\n style + \", product photo of \" +\n ($json.product || \"\"),\n clip: [\"4\", 1]\n }\n },\n\n \"7\": {\n class_type: \"CLIPTextEncode\",\n inputs: {\n text:\n \"blurry, low quality, distorted, deformed, noisy, grainy, washed out colors, flat lighting, overexposed, underexposed, watermark, text artifacts, jpeg artifacts, AI look, unrealistic reflections, clutter, low detail\",\n clip: [\"4\", 1]\n }\n },\n\n // ⭐ GECORRIGEERDE SAVEIMAGE NODE\n \"9\": {\n class_type: \"SaveImage\",\n inputs: {\n filename_prefix: $json.filename,\n output_path: outputPath,\n images: [\"5\", 0]\n }\n },\n\n \"10\": {\n class_type: \"EmptyLatentImage\",\n inputs: {\n width: width,\n height: height,\n batch_size: 1\n }\n }\n};\n\nreturn {\n json: {\n prompt: workflow\n }\n};\n"
- },
- "type": "n8n-nodes-base.code",
- "typeVersion": 2,
- "position": [
- -360,
- -260
- ],
- "id": "b01b9d2e-3e5e-4b0f-b267-f72550b3e61d",
- "name": "Build Workflow"
- },
- {
- "parameters": {
- "rule": {
- "interval": [
- {
- "field": "hours",
- "hoursInterval": 12
- }
- ]
- }
- },
- "type": "n8n-nodes-base.scheduleTrigger",
- "typeVersion": 1.2,
- "position": [
- -1240,
- -760
- ],
- "id": "87a9f39d-6e70-4045-95e3-7256d4319336",
- "name": "Schedule Trigger"
- },
- {
- "parameters": {
- "conditions": {
- "options": {
- "caseSensitive": true,
- "leftValue": "",
- "typeValidation": "strict",
- "version": 2
- },
- "conditions": [
- {
- "id": "d1dacde8-0a58-462c-a7f0-53fed53166e4",
- "leftValue": "={{$json.stdout}}",
- "rightValue": "=EXISTS",
- "operator": {
- "type": "string",
- "operation": "contains"
- }
- }
- ],
- "combinator": "and"
- },
- "options": {}
- },
- "type": "n8n-nodes-base.if",
- "typeVersion": 2.2,
- "position": [
- -460,
- -520
- ],
- "id": "1d868edf-1a60-4ad4-9543-a8a5ce14a270",
- "name": "If"
- },
- {
- "parameters": {
- "executeOnce": false,
- "command": "=test -f \"/data/apps/comfyui/output/{{$json.filename}}.png\" && echo \"EXISTS\" || echo \"MISSING\"\n"
- },
- "type": "n8n-nodes-base.executeCommand",
- "typeVersion": 1,
- "position": [
- -640,
- -520
- ],
- "id": "05656f8f-7103-44e0-9270-262cb4f8de94",
- "name": "Execute Command",
- "alwaysOutputData": false
- },
- {
- "parameters": {
- "mode": "chooseBranch"
- },
- "type": "n8n-nodes-base.merge",
- "typeVersion": 3,
- "position": [
- -1040,
- -520
- ],
- "id": "2bb231b1-e135-4dbc-b1ee-435d4b7f26a6",
- "name": "Merge"
- },
- {
- "parameters": {
- "mode": "combine",
- "combineBy": "combineByPosition",
- "options": {}
- },
- "type": "n8n-nodes-base.merge",
- "typeVersion": 3,
- "position": [
- -520,
- -260
- ],
- "id": "1c22c223-9cf8-4929-a2bb-3b006cf8645a",
- "name": "Merge1"
- },
- {
- "parameters": {
- "jsCode": "for (const item of items) {\n for (const key of Object.keys(item.json.data)) {\n const value = item.json.data[key];\n\n if (typeof value === \"string\") {\n item.json.data[key] = value\n .replace(/^\"+|\"+$/g, \"\") // verwijder quotes aan begin/eind\n .replace(/\\\\\"/g, \"\") // verwijder escaped quotes\n .trim();\n }\n }\n}\nreturn items;\n"
- },
- "type": "n8n-nodes-base.code",
- "typeVersion": 2,
- "position": [
- -680,
- -760
- ],
- "id": "deb3548b-5da2-4c78-8fa0-dd0a4bbadefc",
- "name": "Code2"
- }
- ],
- "pinData": {},
- "connections": {
- "Read/Write Files from Disk": {
- "main": [
- [
- {
- "node": "Extract from File",
- "type": "main",
- "index": 0
- }
- ]
- ]
- },
- "Extract from File": {
- "main": [
- [
- {
- "node": "Code2",
- "type": "main",
- "index": 0
- }
- ]
- ]
- },
- "Code": {
- "main": [
- [
- {
- "node": "Merge",
- "type": "main",
- "index": 0
- }
- ]
- ]
- },
- "HTTP Request": {
- "main": [
- []
- ]
- },
- "HTTP Request1": {
- "main": [
- [
- {
- "node": "Edit Fields",
- "type": "main",
- "index": 0
- }
- ]
- ]
- },
- "Code1": {
- "main": [
- []
- ]
- },
- "Build Workflow": {
- "main": [
- [
- {
- "node": "HTTP Request",
- "type": "main",
- "index": 0
- }
- ]
- ]
- },
- "Schedule Trigger": {
- "main": [
- [
- {
- "node": "Read/Write Files from Disk",
- "type": "main",
- "index": 0
- }
- ]
- ]
- },
- "If": {
- "main": [
- [],
- [
- {
- "node": "Merge1",
- "type": "main",
- "index": 0
- }
- ]
- ]
- },
- "Execute Command": {
- "main": [
- [
- {
- "node": "If",
- "type": "main",
- "index": 0
- }
- ]
- ]
- },
- "Merge": {
- "main": [
- [
- {
- "node": "Execute Command",
- "type": "main",
- "index": 0
- },
- {
- "node": "Merge1",
- "type": "main",
- "index": 1
- }
- ]
- ]
- },
- "Merge1": {
- "main": [
- [
- {
- "node": "Build Workflow",
- "type": "main",
- "index": 0
- }
- ]
- ]
- },
- "Code2": {
- "main": [
- [
- {
- "node": "Code",
- "type": "main",
- "index": 0
- }
- ]
- ]
- }
- },
- "active": true,
- "settings": {
- "executionOrder": "v1"
- },
- "versionId": "6afe8585-432b-4897-b881-a758d911bef4",
- "meta": {
- "instanceId": "557d3f2d8cae4e50174e58ffa38fd9a74c273e6b1d52ed0d0cfe604b3c6667e6"
- },
- "id": "apRqP9nh9xbLY8LX",
- "tags": []
- }
Add Comment
Please, Sign In to add comment