[{"parameters": {"jsCode": "const response = $input.first().json.body || $input.first().json;\nconst results = response.result || [];\n\nif (!results.length) return [];\n\nconst msg = results[0];\nconst newOffset = msg.update_id + 1;\n\nconst staticData = $getWorkflowStaticData('global');\nstaticData.reelsLastUpdateId = newOffset;\n\nif (msg.message?.from?.is_bot) return [];\n\nconst chatId = String(msg.message?.chat?.id || '');\nif (chatId !== '7769073261') return [];\n\nconst text = (msg.message?.text || '').trim();\n\n// Detectar comando: reels <URL>\nconst reelsMatch = text.match(/^reels\\s+(https?:\\/\\/[^\\s]+)/i);\nif (!reelsMatch) return [];\n\nconst url = reelsMatch[1];\nreturn [{ json: { url, messageId: msg.message.message_id, newOffset } }];"}, "id": "node-extrair", "name": "Extrair Comando Reels", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [688, 304]}, {"parameters": {"url": "https://api.telegram.org/bot8684225036:AAHPUgR6b1y7oN7y5q1FMQ7vcoi3Io9nysE/sendMessage", "sendQuery": true, "queryParameters": {"parameters": [{"name": "chat_id", "value": "7769073261"}, {"name": "text", "value": "⏳ Gerando Reels... aguarde."}]}, "options": {}}, "id": "node-telegram-recebido", "name": "Telegram - Recebido", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1168, 304]}, {"parameters": {"url": "=https://r.jina.ai/{{ $('Extrair Comando Reels').item.json.url }}", "options": {}}, "id": "node-buscar", "name": "Buscar Matéria", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1408, 304]}, {"parameters": {"jsCode": "// Input principal: Jina reader\nconst jina = $input.first().json;\nconst body = jina.data || jina.body || '';\n\n// HTML cru: acessar nó diretamente (inputs paralelos não funcionam via $input.all())\nlet htmlRaw = '';\ntry {\n  const h = $('Buscar HTML Direto').first().json;\n  htmlRaw = h.data || h.body || h.text || '';\n} catch(e) {}\n\nconst titleMatch = body.match(/Title:\\s*(.+)/i);\nconst descMatch  = body.match(/Description:\\s*(.+)/i);\nconst ogJina     = (body.match(/Image:\\s*(https?:\\/\\/[^\\s]+)/i) || [])[1] || '';\n\nconst title = titleMatch ? titleMatch[1].trim() : '';\nconst desc  = descMatch  ? descMatch[1].trim()  : '';\n\nconst skipPatterns = /logo|icon|thumb|avatar|banner|placeholder|pixel|1x1|spacer|ads|sponsor|\\/assets\\//i;\n\n// og:image do HTML cru (CANONICAL — imagem principal declarada pelo portal)\nconst ogHtml = (htmlRaw.match(/property=[\"\\']og:image[\"\\'][^>]*content=[\"\\'](https?:\\/\\/[^\"\\'\\s]+)/i)\n             || htmlRaw.match(/content=[\"\\'](https?:\\/\\/[^\"\\'\\s]+)[\"\\'][^>]*property=[\"\\']og:image[\"\\']/i)\n             || [])[1] || '';\n\n// Imagens inline markdown no Jina (fallback — pode pegar \"leia também\")\nconst mdImgs = [...body.matchAll(/!\\[[^\\]]*\\]\\((https?:\\/\\/[^)\\s]+\\.(?:jpg|jpeg|png|webp)[^)\\s]*)\\)/gi)].map(m => m[1]);\n\n// URLs de imagem soltas no Jina\nconst rawImgs = [...body.matchAll(/(https?:\\/\\/[^\\s\"\\')]+\\.(?:jpg|jpeg|png|webp)[^\\s\"\\'\\)]*)/gi)].map(m => m[1]);\n\nconst allImgs = [...new Set([...mdImgs, ...rawImgs])].filter(u => !skipPatterns.test(u));\n\n// PRIORIDADE CORRIGIDA: og:image sempre primeiro (é a imagem canônica da matéria).\n// Inline só como último recurso — Jina às vezes coloca \"leia também\" no topo do body.\n// URLs vindas do HTML cru chegam com entidades escapadas (&amp;) — sem decodificar,\n// o servidor ignora parâmetros como og=1 e serve thumbnail minúscula (64px)\nconst decodeEntities = s => s\n  .replace(/&amp;/gi, '&')\n  .replace(/&#0*38;/g, '&')\n  .replace(/&quot;/gi, '\"')\n  .replace(/&#0*39;/g, \"'\");\n\nconst image = decodeEntities(ogHtml || ogJina || allImgs[0] || '');\n\nconst paraMatch = body.match(/(?:^|\\n)([A-ZÁÉÍÓÚÃÕÇ].{80,})/gm) || [];\nconst bodyText = paraMatch.slice(0, 5).join(' ').replace(/\\s+/g,' ').trim();\n\nreturn [{ json: { title, desc, image, bodyText, url: $('Extrair Comando Reels').item.json.url } }];"}, "id": "node-extrair-dados", "name": "Extrair Dados da Matéria", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1648, 304]}, {"parameters": {"method": "POST", "url": "https://api.openai.com/v1/chat/completions", "sendHeaders": true, "headerParameters": {"parameters": [{"name": "Content-Type", "value": "application/json"}]}, "sendBody": true, "specifyBody": "json", "jsonBody": "={{ ({ model: 'gpt-4o', max_tokens: 1200, temperature: 0.2, response_format: { type: 'json_object' }, messages: [{ role: 'system', content: 'Você é editor do portal O Oeirense (Oeiras, Piauí). Gere textos jornalísticos fiéis ao fonte.\\n\\nREGRAS INVIOLÁVEIS:\\n1. NUNCA invente datas, dias, horários, nomes, cidades ou números. Copie EXATAMENTE do texto.\\n2. Se não constar no texto, NÃO mencione.\\n3. TÍTULO e DESCRIÇÃO são fontes mais confiáveis que o bodyText.\\n4. Retorne JSON com: chapeu, titulo, script_narracao, legenda.' }, { role: 'user', content: `Gere:\\n- chapeu: máximo 2 palavras\\n- titulo: base no original, máx 80 chars, sem ponto final\\n- script_narracao: narração voz over 30-40s (~85 palavras), linguagem oral jornalística. Gancho → desenvolvimento → fechamento. Sem abreviações; números, horas e siglas por extenso.\\n- legenda: 4 parágrafos (fato, detalhes, impactos, observação). 2-3 frases cada. Sem links/CTA. Termine com pergunta para engajamento.\\n\\nLEMBRETE: NÃO invente nenhum dado.\\n\\nTítulo: ${$json.title}\\nDescrição: ${$json.desc}\\nTexto: ${($json.bodyText || '').substring(0, 3000)}` }] }) }}", "options": {}, "authentication": "predefinedCredentialType", "nodeCredentialType": "openAiApi"}, "id": "node-openai", "name": "OpenAI - Gerar Textos", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1888, 304], "credentials": {"openAiApi": {"id": "kzx5weRZep0izFEn", "name": "OpenAI account"}}}, {"parameters": {"jsCode": "// Correções para o material gerado pelo OpenAI\n//\n// GRAFIA_FIX — aplica em todos os campos visíveis (chapéu/título/script/legenda).\n// Corrige erros recorrentes do modelo que afetariam a exibição.\nconst GRAFIA_FIX = {\n  'Hamilton': 'Amilton',  // Amilton de Zé Moura (pres. Câmara de Oeiras) — grafia sem H\n};\n\n// TTS_FIX — aplica SÓ no script que vai pro ElevenLabs. Reescreve foneticamente\n// nomes que o TTS multilíngue lê errado na grafia correta: H mudo virando H\n// soprado do inglês, \"y\" final lido como ípsilon.\n// Espelho OBRIGATÓRIO: LEGENDA_FIX no gerar-reels.py. A legenda queimada nasce do\n// alignment desta fala, então sem o espelho a grafia fonética vai pra tela.\nconst TTS_FIX = {\n  'Hailton': 'Ailton',    // Dr. Hailton Alves Filho (prefeito) — pronúncia \"Aílton\"\n  'Tapety': 'Tapetí',     // família Tapety, da Fazenda Lagoa do Tabuleiro — pronúncia \"Tapetí\"\n};\n\nfunction aplicarMapa(texto, mapa) {\n  if (!texto) return texto;\n  let out = texto;\n  for (const [de, para] of Object.entries(mapa)) {\n    out = out.replace(new RegExp('\\\\b' + de + '\\\\b', 'g'), para);\n  }\n  return out;\n}\n\nconst content = $input.first().json.choices[0].message.content.trim();\nconst clean = content.replace(/```json|```/g,'').trim();\nconst parsed = JSON.parse(clean);\n\nconst chapeu = aplicarMapa(parsed.chapeu, GRAFIA_FIX);\nconst titulo = aplicarMapa(parsed.titulo, GRAFIA_FIX);\nconst legenda = aplicarMapa(parsed.legenda || '', GRAFIA_FIX);\nconst scriptGrafia = aplicarMapa(parsed.script_narracao, GRAFIA_FIX);\nconst script = aplicarMapa(scriptGrafia, TTS_FIX);\n\nreturn [{ json: {\n  chapeu,\n  titulo,\n  script,\n  legenda,\n  image: $('Extrair Dados da Matéria').item.json.image\n}}];"}, "id": "node-parsear", "name": "Parsear OpenAI", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [2128, 304]}, {"parameters": {"method": "POST", "url": "https://api.elevenlabs.io/v1/text-to-speech/onwK4e9ZLuTAKqWW03F9/with-timestamps", "sendHeaders": true, "headerParameters": {"parameters": [{"name": "xi-api-key", "value": "sk_17b648b211092dff78874555e1b21eded7db792734229a29"}, {"name": "Content-Type", "value": "application/json"}]}, "sendBody": true, "specifyBody": "json", "jsonBody": "={{ ({ text: $json.script, model_id: 'eleven_multilingual_v2', voice_settings: { stability: 0.5, similarity_boost: 0.75, style: 0.3, use_speaker_boost: true } }) }}", "options": {}}, "id": "node-elevenlabs", "name": "ElevenLabs - Narração", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [2368, 304], "onError": "continueErrorOutput"}, {"parameters": {"jsCode": "const fs = require('fs');\nconst data = $input.first().json;\n\nconst jobId = Date.now();\nconst audioPath = `/tmp/reels_audio_${jobId}.mp3`;\nconst tsPath    = `/tmp/reels_ts_${jobId}.json`;\nconst outputPath = `/tmp/reels_output_${jobId}.mp4`;\n\n// Salvar MP3\nconst audioBuffer = Buffer.from(data.audio_base64, 'base64');\nfs.writeFileSync(audioPath, audioBuffer);\n\n// Salvar timestamps\nfs.writeFileSync(tsPath, JSON.stringify(data.alignment));\n\nreturn [{ json: {\n  jobId,\n  audioPath,\n  tsPath,\n  outputPath,\n  chapeu:  $('Parsear OpenAI').item.json.chapeu,\n  titulo:  $('Parsear OpenAI').item.json.titulo,\n  image:   $('Parsear OpenAI').item.json.image\n}}];"}, "id": "node-salvar-arquivos", "name": "Salvar Áudio e Timestamps", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [2608, 304]}, {"parameters": {"url": "={{ $json.image }}", "options": {}}, "id": "node-baixar-img", "name": "Baixar Imagem", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [2848, 304]}, {"parameters": {"jsCode": "const fs = require('fs');\nconst prev = $('Salvar Áudio e Timestamps').item.json;\n\nconst binaryKey = Object.keys($input.first().binary || {})[0];\nconst binaryMeta = $input.first().binary?.[binaryKey];\n\nlet imgBuffer;\nif (binaryMeta?.data && binaryMeta.data.length > 500) {\n  imgBuffer = Buffer.from(binaryMeta.data, 'base64');\n} else {\n  imgBuffer = await this.helpers.getBinaryDataBuffer(0, binaryKey);\n}\n\nconst fotoPath = `/tmp/reels_foto_${prev.jobId}.jpg`;\nfs.writeFileSync(fotoPath, imgBuffer);\n\nreturn [{ json: { ...prev, fotoPath } }];"}, "id": "node-salvar-img", "name": "Salvar Imagem", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [3088, 304]}, {"parameters": {"jsCode": "const { spawn } = require('child_process');\nconst data = $input.first().json;\n\nconst args = [\n  '/Users/mac/ARQUIVOS CLAUDE/gerar-reels.py',\n  '--foto',       data.fotoPath,\n  '--audio',      data.audioPath,\n  '--timestamps', data.tsPath,\n  '--chapeu',     data.chapeu,\n  '--titulo',     data.titulo,\n  '--output',     data.outputPath\n];\n\n// Arquivo de status — quando existir, o vídeo está pronto\nconst statusFile = data.outputPath + '.done';\nconst logFile    = data.outputPath + '.log';\n\nconst fs = require('fs');\n\n// Lançar processo desanexado (não bloqueia o n8n)\nconst child = spawn('/Users/mac/.venv-reels/bin/python', args, {\n  detached: true,\n  stdio: ['ignore',\n    fs.openSync(logFile, 'w'),\n    fs.openSync(logFile, 'a')]\n});\nchild.unref();\n\nreturn [{ json: { ...data, statusFile, logFile, pid: child.pid } }];"}, "id": "node-gerar-reels", "name": "Gerar Reels (ffmpeg)", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [3328, 304], "onError": "continueErrorOutput"}, {"parameters": {"jsCode": "const fs = require('fs');\nconst { execFile, spawn } = require('child_process');\n\nconst data = $('Verificar Vídeo Pronto').first().json;\n\nconst IG_USER_ID = '17841477402712235';\nconst ACCESS_TOKEN = 'EAAVw81fCrxUBRLOnGwCYAexfTBk6BLWezW627TNjp4bEQMGBcaCmhWV0TTuNzLl0N9TqDSXniD3ZBSOiwevn9YTXKE7bGBDZBwqAGzRRpeOTmSTEjYvqDZBqV3nv8tLMEKlyCo6Xl23AkX7ZBoJ8t3hjUmh0HIDnIicoFEoEFbOeddWkpxQHHRUnMG0PgQZDZD';\n\nconst caption = String($('Parsear OpenAI').first().json.legenda || '');\nconst videoPath = String(data.outputPath || '');\n\nif (!videoPath) throw new Error('outputPath ausente');\nif (!fs.existsSync(videoPath)) throw new Error('Vídeo não existe: ' + videoPath);\nconst fileSize = fs.statSync(videoPath).size;\n\n// curl assíncrono — execFileSync bloqueava o event loop e o task runner\n// era morto por \"unresponsive\" quando o upload passava de ~40s\nfunction curlAsync(args) {\n  return new Promise((resolve, reject) => {\n    execFile('/usr/bin/curl', args, { maxBuffer: 10 * 1024 * 1024, encoding: 'utf8' },\n      (err, stdout, stderr) => {\n        if (err) reject(new Error('curl falhou: ' + err.message + ' | stderr: ' + String(stderr || '').slice(0, 400)));\n        else resolve(stdout);\n      });\n  });\n}\n\nfunction sleepAsync(secs) {\n  return new Promise(resolve => execFile('/bin/sleep', [String(secs)], () => resolve()));\n}\n\n// Step 1: Init resumable container (POST pequeno e rápido)\nconst initBody = JSON.stringify({\n  media_type: 'REELS',\n  upload_type: 'resumable',\n  caption: caption,\n  access_token: ACCESS_TOKEN,\n});\n\nlet init = null;\nlet lastError = '';\nfor (let attempt = 1; attempt <= 2 && !init; attempt++) {\n  try {\n    const raw = await curlAsync(['-s', '-S', '--max-time', '20', '-X', 'POST',\n      `https://graph.facebook.com/v25.0/${IG_USER_ID}/media`,\n      '-H', 'Content-Type: application/json', '--data-raw', initBody]);\n    let parsed;\n    try { parsed = JSON.parse(raw); } catch (e) {\n      throw new Error('STEP1_INIT JSON invalido: ' + raw.slice(0, 600));\n    }\n    if (parsed.error) {\n      throw new Error('STEP1_INIT erro Meta: ' + (parsed.error.message || JSON.stringify(parsed.error)).slice(0, 400));\n    }\n    if (!parsed.id || !parsed.uri) {\n      throw new Error('STEP1_INIT sem id/uri: ' + raw.slice(0, 600));\n    }\n    init = parsed;\n  } catch (e) {\n    lastError = e.message;\n    if (attempt < 2) await sleepAsync(5);\n  }\n}\nif (!init) throw new Error('Init do container falhou: ' + lastError);\n\nconst containerId = String(init.id);\nconst uploadUri = String(init.uri);\n\n// Step 2: upload binário desanexado (mesmo padrão do nó ffmpeg) — roda fora\n// do task runner; o Wait antes do publish dá tempo de terminar\nconst resultFile = `/tmp/reels_upload_${data.jobId}.json`;\nconst scriptPath = `/tmp/reels_upload_${data.jobId}.sh`;\nconst uploadLog  = `/tmp/reels_upload_${data.jobId}.log`;\n\nconst script = [\n  '#!/bin/bash',\n  'for i in 1 2 3; do',\n  `  /usr/bin/curl -s -S -X POST '${uploadUri}' \\\\`,\n  `    -H 'Authorization: OAuth ${ACCESS_TOKEN}' \\\\`,\n  \"    -H 'offset: 0' \\\\\",\n  `    -H 'file_size: ${fileSize}' \\\\`,\n  \"    -H 'Content-Type: application/octet-stream' \\\\\",\n  `    --data-binary @'${videoPath}' --max-time 150 -o '${resultFile}'`,\n  `  if grep -Eq '\"success\"[[:space:]]*:[[:space:]]*true' '${resultFile}' 2>/dev/null; then exit 0; fi`,\n  '  sleep 20',\n  'done',\n  'exit 1',\n].join('\\n');\n\nfs.writeFileSync(scriptPath, script, { mode: 0o755 });\n\nconst child = spawn('/bin/bash', [scriptPath], {\n  detached: true,\n  stdio: ['ignore', fs.openSync(uploadLog, 'w'), fs.openSync(uploadLog, 'a')]\n});\nchild.unref();\n\nreturn [{ json: {\n  jobId: data.jobId,\n  outputPath: videoPath,\n  chapeu: data.chapeu,\n  titulo: data.titulo,\n  id: containerId,\n  fileSize: fileSize,\n  uploadResultFile: resultFile,\n  uploadPid: child.pid,\n} }];"}, "id": "node-upload-reels", "name": "Instagram - Criar Container Reels", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [3568, 304], "onError": "continueErrorOutput"}, {"parameters": {"unit": "seconds", "amount": 60}, "id": "node-aguardar", "name": "Aguardar Geração (10min)", "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [4048, 304], "webhookId": "d8de132e-c89a-40a9-bda4-8adb18a02eb5", "notes": "Loop de verificacao: espera 60s e volta pro \"Verificar Video Pronto\". Teto ~25 tentativas (~25min).", "notesInFlow": true}, {"parameters": {"method": "POST", "url": "https://graph.facebook.com/v25.0/17841477402712235/media_publish", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ ({ creation_id: $input.first().json.id, access_token: 'EAAVw81fCrxUBRLOnGwCYAexfTBk6BLWezW627TNjp4bEQMGBcaCmhWV0TTuNzLl0N9TqDSXniD3ZBSOiwevn9YTXKE7bGBDZBwqAGzRRpeOTmSTEjYvqDZBqV3nv8tLMEKlyCo6Xl23AkX7ZBoJ8t3hjUmh0HIDnIicoFEoEFbOeddWkpxQHHRUnMG0PgQZDZD' }) }}", "options": {}}, "id": "node-publicar", "name": "Instagram - Publicar Reels", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [4768, 304], "onError": "continueErrorOutput"}, {"parameters": {"url": "https://api.telegram.org/bot8684225036:AAHPUgR6b1y7oN7y5q1FMQ7vcoi3Io9nysE/sendMessage", "sendQuery": true, "queryParameters": {"parameters": [{"name": "chat_id", "value": "7769073261"}, {"name": "text", "value": "=✅ Reels publicado no @ooeirense!\n\n📰 {{ $('Parsear OpenAI').item.json.titulo }}"}]}, "options": {}}, "id": "node-confirmar", "name": "Telegram - Confirmar Reels", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [5008, 304]}, {"parameters": {"jsCode": "const fs = require('fs');\nconst prev = $input.first().json;\n\nconst MAX_ATTEMPTS = 25;              // teto ~25 min (60s por tentativa)\nconst attempt = (prev.attempt || 0) + 1;\n\n// O script so cria o .done (\"ok\") ao terminar com sucesso.\nif (fs.existsSync(prev.statusFile) && fs.existsSync(prev.outputPath)) {\n  try { fs.unlinkSync(prev.statusFile); } catch (e) {}\n  return [{ json: { ...prev, ready: true, attempt } }];\n}\n\nlet log = '';\ntry { log = fs.readFileSync(prev.logFile, 'utf8'); } catch (e) {}\n\n// Falha explicita: traceback no log -> aborta na hora (nao espera o teto)\nif (log.includes('Traceback (most recent call last)')) {\n  throw new Error('Falha ao gerar o video (Python quebrou). Fim do log:\\n' + log.slice(-800));\n}\n\n// Estourou o teto de tempo\nif (attempt >= MAX_ATTEMPTS) {\n  throw new Error('Video nao ficou pronto apos ' + attempt + ' min. Fim do log:\\n' + (log ? log.slice(-800) : 'sem log'));\n}\n\n// Ainda renderizando -> segue no loop\nreturn [{ json: { ...prev, ready: false, attempt } }];"}, "id": "node-check-done", "name": "Verificar Vídeo Pronto", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [4048, 640], "onError": "continueErrorOutput"}, {"parameters": {"amount": 240}, "id": "node-wait-insta", "name": "Aguardar Instagram (3min)", "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [4528, 304], "webhookId": "445ffba6-a412-40d9-a3e8-130a44fb4c5d"}, {"parameters": {"url": "=https://r.jina.ai/{{ $('Extrair Comando Reels').item.json.url }}", "sendHeaders": true, "headerParameters": {"parameters": [{"name": "X-Return-Format", "value": "html"}]}, "options": {"response": {"response": {"responseFormat": "text"}}, "timeout": 30000}}, "id": "buscar-html-direto", "name": "Buscar HTML Direto", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1408, 528]}, {"id": "tg-reels-erro-001", "name": "Telegram - Erro Reels", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [4288, 560], "parameters": {"method": "POST", "url": "https://api.telegram.org/bot8684225036:AAHPUgR6b1y7oN7y5q1FMQ7vcoi3Io9nysE/sendMessage", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ JSON.stringify({ chat_id: '7769073261', text: '❌ <b>Erro ao gerar Reels no @ooeirense</b>\\n\\n' + ($json.error?.message || $json.message || JSON.stringify($json)).substring(0, 300), parse_mode: 'HTML' }) }}", "options": {}}}, {"parameters": {"path": "dispatch-reels", "httpMethod": "POST", "responseMode": "onReceived", "options": {}}, "id": "wh-dispatch-reels", "name": "Webhook Dispatcher", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "position": [208, 304], "webhookId": "wh-dispatch-reels"}, {"parameters": {"conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "cond-video-pronto", "operator": {"type": "boolean", "operation": "true", "singleValue": true}, "leftValue": "={{ $json.ready }}", "rightValue": ""}]}, "options": {}}, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [4320, 640], "id": "if-video-pronto-2026", "name": "Vídeo Pronto?"}]