smileface

Untitled

May 18th, 2026
30
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JSON 18.03 KB | None | 0 0
  1. {
  2.   "name": "Banner Generator",
  3.   "nodes": [
  4.     {
  5.       "parameters": {
  6.         "fileSelector": "/data/apps/n8n/banners/week */*.json",
  7.         "options": {}
  8.       },
  9.       "type": "n8n-nodes-base.readWriteFile",
  10.       "typeVersion": 1,
  11.       "position": [
  12.         -1060,
  13.         -760
  14.       ],
  15.       "id": "66ac86b3-f784-4aa6-8cca-b732968b5db1",
  16.       "name": "Read/Write Files from Disk"
  17.     },
  18.     {
  19.       "parameters": {
  20.         "operation": "fromJson",
  21.         "binaryPropertyName": "=data",
  22.         "options": {}
  23.       },
  24.       "type": "n8n-nodes-base.extractFromFile",
  25.       "typeVersion": 1,
  26.       "position": [
  27.         -840,
  28.         -760
  29.       ],
  30.       "id": "ce8c3e02-6a94-470a-9201-d4497480d22b",
  31.       "name": "Extract from File"
  32.     },
  33.     {
  34.       "parameters": {
  35.         "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"
  36.       },
  37.       "type": "n8n-nodes-base.code",
  38.       "typeVersion": 2,
  39.       "position": [
  40.         -540,
  41.         -760
  42.       ],
  43.       "id": "7f3740e1-2223-460a-acdb-c92e64cbb55d",
  44.       "name": "Code"
  45.     },
  46.     {
  47.       "parameters": {
  48.         "method": "POST",
  49.         "url": "http://192.168.1.28:8188/prompt",
  50.         "sendBody": true,
  51.         "specifyBody": "json",
  52.         "jsonBody": "={{JSON.stringify({prompt: $json.prompt})}}\n",
  53.         "options": {}
  54.       },
  55.       "type": "n8n-nodes-base.httpRequest",
  56.       "typeVersion": 4.2,
  57.       "position": [
  58.         -180,
  59.         -260
  60.       ],
  61.       "id": "539c751e-10bc-40b5-a545-4955f4eefb97",
  62.       "name": "HTTP Request"
  63.     },
  64.     {
  65.       "parameters": {
  66.         "url": "=http://192.168.1.28:8188/history/{{$json.prompt_id}}\n",
  67.         "options": {}
  68.       },
  69.       "type": "n8n-nodes-base.httpRequest",
  70.       "typeVersion": 4.2,
  71.       "position": [
  72.         140,
  73.         -140
  74.       ],
  75.       "id": "4ce5e242-f559-409b-a005-c689b2cddc86",
  76.       "name": "HTTP Request1"
  77.     },
  78.     {
  79.       "parameters": {
  80.         "mode": "runOnceForEachItem",
  81.         "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"
  82.       },
  83.       "type": "n8n-nodes-base.code",
  84.       "typeVersion": 2,
  85.       "position": [
  86.         220,
  87.         -520
  88.       ],
  89.       "id": "0f7ea871-a505-4e45-ad47-39dffe3cd8bd",
  90.       "name": "Code1"
  91.     },
  92.     {
  93.       "parameters": {
  94.         "mode": "raw",
  95.         "jsonOutput": "={{$json}}\n",
  96.         "options": {}
  97.       },
  98.       "type": "n8n-nodes-base.set",
  99.       "typeVersion": 3.4,
  100.       "position": [
  101.         260,
  102.         -280
  103.       ],
  104.       "id": "2a692a11-72e3-4f86-a2d4-877de3e53682",
  105.       "name": "Edit Fields"
  106.     },
  107.     {
  108.       "parameters": {
  109.         "mode": "runOnceForEachItem",
  110.         "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"
  111.       },
  112.       "type": "n8n-nodes-base.code",
  113.       "typeVersion": 2,
  114.       "position": [
  115.         -360,
  116.         -260
  117.       ],
  118.       "id": "b01b9d2e-3e5e-4b0f-b267-f72550b3e61d",
  119.       "name": "Build Workflow"
  120.     },
  121.     {
  122.       "parameters": {
  123.         "rule": {
  124.           "interval": [
  125.             {
  126.               "field": "hours",
  127.               "hoursInterval": 12
  128.             }
  129.           ]
  130.         }
  131.       },
  132.       "type": "n8n-nodes-base.scheduleTrigger",
  133.       "typeVersion": 1.2,
  134.       "position": [
  135.         -1240,
  136.         -760
  137.       ],
  138.       "id": "87a9f39d-6e70-4045-95e3-7256d4319336",
  139.       "name": "Schedule Trigger"
  140.     },
  141.     {
  142.       "parameters": {
  143.         "conditions": {
  144.           "options": {
  145.             "caseSensitive": true,
  146.             "leftValue": "",
  147.             "typeValidation": "strict",
  148.             "version": 2
  149.           },
  150.           "conditions": [
  151.             {
  152.               "id": "d1dacde8-0a58-462c-a7f0-53fed53166e4",
  153.               "leftValue": "={{$json.stdout}}",
  154.               "rightValue": "=EXISTS",
  155.               "operator": {
  156.                 "type": "string",
  157.                 "operation": "contains"
  158.               }
  159.             }
  160.           ],
  161.           "combinator": "and"
  162.         },
  163.         "options": {}
  164.       },
  165.       "type": "n8n-nodes-base.if",
  166.       "typeVersion": 2.2,
  167.       "position": [
  168.         -460,
  169.         -520
  170.       ],
  171.       "id": "1d868edf-1a60-4ad4-9543-a8a5ce14a270",
  172.       "name": "If"
  173.     },
  174.     {
  175.       "parameters": {
  176.         "executeOnce": false,
  177.         "command": "=test -f \"/data/apps/comfyui/output/{{$json.filename}}.png\" && echo \"EXISTS\" || echo \"MISSING\"\n"
  178.       },
  179.       "type": "n8n-nodes-base.executeCommand",
  180.       "typeVersion": 1,
  181.       "position": [
  182.         -640,
  183.         -520
  184.       ],
  185.       "id": "05656f8f-7103-44e0-9270-262cb4f8de94",
  186.       "name": "Execute Command",
  187.       "alwaysOutputData": false
  188.     },
  189.     {
  190.       "parameters": {
  191.         "mode": "chooseBranch"
  192.       },
  193.       "type": "n8n-nodes-base.merge",
  194.       "typeVersion": 3,
  195.       "position": [
  196.         -1040,
  197.         -520
  198.       ],
  199.       "id": "2bb231b1-e135-4dbc-b1ee-435d4b7f26a6",
  200.       "name": "Merge"
  201.     },
  202.     {
  203.       "parameters": {
  204.         "mode": "combine",
  205.         "combineBy": "combineByPosition",
  206.         "options": {}
  207.       },
  208.       "type": "n8n-nodes-base.merge",
  209.       "typeVersion": 3,
  210.       "position": [
  211.         -520,
  212.         -260
  213.       ],
  214.       "id": "1c22c223-9cf8-4929-a2bb-3b006cf8645a",
  215.       "name": "Merge1"
  216.     },
  217.     {
  218.       "parameters": {
  219.         "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"
  220.       },
  221.       "type": "n8n-nodes-base.code",
  222.       "typeVersion": 2,
  223.       "position": [
  224.         -680,
  225.         -760
  226.       ],
  227.       "id": "deb3548b-5da2-4c78-8fa0-dd0a4bbadefc",
  228.       "name": "Code2"
  229.     }
  230.   ],
  231.   "pinData": {},
  232.   "connections": {
  233.     "Read/Write Files from Disk": {
  234.       "main": [
  235.         [
  236.           {
  237.             "node": "Extract from File",
  238.             "type": "main",
  239.             "index": 0
  240.           }
  241.         ]
  242.       ]
  243.     },
  244.     "Extract from File": {
  245.       "main": [
  246.         [
  247.           {
  248.             "node": "Code2",
  249.             "type": "main",
  250.             "index": 0
  251.           }
  252.         ]
  253.       ]
  254.     },
  255.     "Code": {
  256.       "main": [
  257.         [
  258.           {
  259.             "node": "Merge",
  260.             "type": "main",
  261.             "index": 0
  262.           }
  263.         ]
  264.       ]
  265.     },
  266.     "HTTP Request": {
  267.       "main": [
  268.         []
  269.       ]
  270.     },
  271.     "HTTP Request1": {
  272.       "main": [
  273.         [
  274.           {
  275.             "node": "Edit Fields",
  276.             "type": "main",
  277.             "index": 0
  278.           }
  279.         ]
  280.       ]
  281.     },
  282.     "Code1": {
  283.       "main": [
  284.         []
  285.       ]
  286.     },
  287.     "Build Workflow": {
  288.       "main": [
  289.         [
  290.           {
  291.             "node": "HTTP Request",
  292.             "type": "main",
  293.             "index": 0
  294.           }
  295.         ]
  296.       ]
  297.     },
  298.     "Schedule Trigger": {
  299.       "main": [
  300.         [
  301.           {
  302.             "node": "Read/Write Files from Disk",
  303.             "type": "main",
  304.             "index": 0
  305.           }
  306.         ]
  307.       ]
  308.     },
  309.     "If": {
  310.       "main": [
  311.         [],
  312.         [
  313.           {
  314.             "node": "Merge1",
  315.             "type": "main",
  316.             "index": 0
  317.           }
  318.         ]
  319.       ]
  320.     },
  321.     "Execute Command": {
  322.       "main": [
  323.         [
  324.           {
  325.             "node": "If",
  326.             "type": "main",
  327.             "index": 0
  328.           }
  329.         ]
  330.       ]
  331.     },
  332.     "Merge": {
  333.       "main": [
  334.         [
  335.           {
  336.             "node": "Execute Command",
  337.             "type": "main",
  338.             "index": 0
  339.           },
  340.           {
  341.             "node": "Merge1",
  342.             "type": "main",
  343.             "index": 1
  344.           }
  345.         ]
  346.       ]
  347.     },
  348.     "Merge1": {
  349.       "main": [
  350.         [
  351.           {
  352.             "node": "Build Workflow",
  353.             "type": "main",
  354.             "index": 0
  355.           }
  356.         ]
  357.       ]
  358.     },
  359.     "Code2": {
  360.       "main": [
  361.         [
  362.           {
  363.             "node": "Code",
  364.             "type": "main",
  365.             "index": 0
  366.           }
  367.         ]
  368.       ]
  369.     }
  370.   },
  371.   "active": true,
  372.   "settings": {
  373.     "executionOrder": "v1"
  374.   },
  375.   "versionId": "6afe8585-432b-4897-b881-a758d911bef4",
  376.   "meta": {
  377.     "instanceId": "557d3f2d8cae4e50174e58ffa38fd9a74c273e6b1d52ed0d0cfe604b3c6667e6"
  378.   },
  379.   "id": "apRqP9nh9xbLY8LX",
  380.   "tags": []
  381. }
Add Comment
Please, Sign In to add comment