{
  "id": "GY6idqkiNIk6iXL6",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI Documentation to Training Video Creator",
  "tags": [],
  "nodes": [
    {
      "id": "a8dfa0fd-04c9-46fd-a610-65f432b4bb41",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "notes": "\ud83c\udfac TRIGGER: Accepts POST requests with documentation URL to start video generation process",
      "position": [
        -768,
        176
      ],
      "parameters": {
        "path": "create-video",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "notesInFlow": true,
      "typeVersion": 1.1
    },
    {
      "id": "365f774e-bb3e-4b4e-99cf-122c7853c136",
      "name": "Fetch Documentation",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "\ud83d\udcc4 Fetches the complete HTML content from the provided documentation URL",
      "position": [
        -544,
        176
      ],
      "parameters": {
        "url": "={{ $json.body.documentationUrl }}",
        "options": {
          "response": {
            "response": {}
          }
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "17952071-9411-4aa2-abd3-da32191d697f",
      "name": "Parse Documentation Content",
      "type": "n8n-nodes-base.code",
      "notes": "\ud83d\udd0d Extracts text, code blocks, and structure from HTML documentation",
      "position": [
        -320,
        176
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Extract and clean documentation content\nconst html = $input.item.json.data;\n\n// Remove script tags, style tags, navigation, and other non-content elements\nconst cleanHtml = html\n  .replace(/<script[^>]*>.*?<\\/script>/gis, '')\n  .replace(/<style[^>]*>.*?<\\/style>/gis, '')\n  .replace(/<nav[^>]*>.*?<\\/nav>/gis, '')\n  .replace(/<header[^>]*>.*?<\\/header>/gis, '')\n  .replace(/<footer[^>]*>.*?<\\/footer>/gis, '');\n\n// Extract text content, code blocks, and headings\nconst textContent = cleanHtml\n  .replace(/<[^>]+>/g, ' ')\n  .replace(/\\s+/g, ' ')\n  .trim();\n\n// Extract code blocks with language hints\nconst codeBlockRegex = /<pre[^>]*><code[^>]*class=[\"']language-(\\w+)[\"'][^>]*>(.*?)<\\/code><\\/pre>/gis;\nconst codeBlocks = [];\nlet match;\n\nwhile ((match = codeBlockRegex.exec(html)) !== null) {\n  codeBlocks.push({\n    language: match[1],\n    code: match[2].replace(/<[^>]+>/g, '').trim()\n  });\n}\n\n// Extract headings for structure\nconst headingRegex = /<h([1-6])[^>]*>(.*?)<\\/h[1-6]>/gi;\nconst headings = [];\n\nwhile ((match = headingRegex.exec(html)) !== null) {\n  headings.push({\n    level: parseInt(match[1]),\n    text: match[2].replace(/<[^>]+>/g, '').trim()\n  });\n}\n\nreturn {\n  json: {\n    documentationUrl: $input.item.json.url || $json.body.documentationUrl,\n    textContent: textContent.substring(0, 8000), // Limit for AI processing\n    codeBlocks: codeBlocks,\n    headings: headings,\n    rawHtml: html.substring(0, 5000) // Keep sample for reference\n  }\n};"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "719343d9-3179-4624-8360-5813bcf3de41",
      "name": "Structure Tutorial Outline",
      "type": "n8n-nodes-base.code",
      "notes": "\ud83d\udccb Validates and structures the AI-generated tutorial outline with metadata",
      "position": [
        256,
        176
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Parse AI response and validate structure\nconst aiResponse = $input.item.json.response || $input.item.json.content?.[0]?.text;\n\nif (!aiResponse) {\n  throw new Error('No AI response received');\n}\n\n// Remove markdown code fences if present\nconst cleanedResponse = aiResponse\n  .replace(/```json\\n?/g, '')\n  .replace(/```\\n?/g, '')\n  .trim();\n\nlet tutorialOutline;\ntry {\n  tutorialOutline = JSON.parse(cleanedResponse);\n} catch (error) {\n  throw new Error(`Failed to parse AI response: ${error.message}`);\n}\n\n// Validate required fields\nif (!tutorialOutline.sections || !Array.isArray(tutorialOutline.sections)) {\n  throw new Error('Invalid tutorial outline: missing sections array');\n}\n\n// Add metadata\ntutorialOutline.createdAt = new Date().toISOString();\ntutorialOutline.sourceUrl = $('Parse Documentation Content').item.json.documentationUrl;\ntutorialOutline.totalSections = tutorialOutline.sections.length;\n\n// Calculate total estimated duration\nconst totalSeconds = tutorialOutline.sections.reduce((sum, section) => {\n  return sum + (parseInt(section.duration) || 30);\n}, 0);\ntutorialOutline.totalDurationSeconds = totalSeconds;\ntutorialOutline.totalDurationMinutes = Math.ceil(totalSeconds / 60);\n\nreturn {\n  json: tutorialOutline\n};"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "66097e77-91a1-4278-85d1-8d88ed5d3340",
      "name": "Generate Narration Script",
      "type": "n8n-nodes-base.code",
      "notes": "\ud83c\udf99\ufe0f Creates timestamped narration script with voice settings for text-to-speech",
      "position": [
        480,
        176
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Generate narration audio script with timing markers\nconst outline = $input.item.json;\nconst sections = outline.sections;\n\nconst narrationScript = sections.map((section, index) => {\n  const timestamp = sections.slice(0, index).reduce((sum, s) => sum + parseInt(s.duration || 30), 0);\n  \n  return {\n    sectionIndex: index,\n    sectionTitle: section.sectionTitle,\n    startTime: timestamp,\n    duration: parseInt(section.duration || 30),\n    narrationText: section.narration,\n    voiceSettings: {\n      speed: 1.0,\n      pitch: 1.0,\n      voice: 'en-US-Neural2-J' // Male voice for tech tutorials\n    }\n  };\n});\n\n// Create intro narration\nconst intro = {\n  sectionIndex: -1,\n  sectionTitle: 'Introduction',\n  startTime: 0,\n  duration: 10,\n  narrationText: `Welcome to this tutorial on ${outline.videoTitle}. ${outline.videoDescription}`,\n  voiceSettings: {\n    speed: 1.0,\n    pitch: 1.0,\n    voice: 'en-US-Neural2-J'\n  }\n};\n\n// Create outro narration\nconst outro = {\n  sectionIndex: 999,\n  sectionTitle: 'Conclusion',\n  startTime: outline.totalDurationSeconds,\n  duration: 8,\n  narrationText: 'Thank you for watching this tutorial. Don\\'t forget to subscribe for more developer content, and check the description for links and resources.',\n  voiceSettings: {\n    speed: 1.0,\n    pitch: 1.0,\n    voice: 'en-US-Neural2-J'\n  }\n};\n\nreturn {\n  json: {\n    videoTitle: outline.videoTitle,\n    narrationSegments: [intro, ...narrationScript, outro],\n    totalSegments: narrationScript.length + 2\n  }\n};"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "526e312a-e230-4b63-8c79-88d5ba93a34b",
      "name": "Create Visual Scenes",
      "type": "n8n-nodes-base.code",
      "notes": "\ud83c\udfa8 Generates visual scene configurations for code highlights, terminal animations, and diagrams",
      "position": [
        1056,
        176
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Create visual scene instructions for video generation\nconst outline = $('Structure Tutorial Outline').item.json;\nconst sections = outline.sections;\n\nconst videoScenes = sections.map((section, index) => {\n  const scene = {\n    sceneIndex: index,\n    sectionTitle: section.sectionTitle,\n    duration: parseInt(section.duration || 30),\n    visualType: section.visualType,\n    transitions: {\n      in: 'fade',\n      out: 'fade'\n    }\n  };\n\n  // Configure scene based on visual type\n  switch (section.visualType) {\n    case 'code':\n      scene.content = {\n        type: 'code-editor',\n        code: section.codeSnippet || section.terminalCommands?.join('\\n') || '',\n        language: detectLanguage(section.codeSnippet || ''),\n        theme: 'dracula',\n        highlightLines: [],\n        typewriterEffect: true,\n        typeSpeed: 50 // milliseconds per character\n      };\n      break;\n      \n    case 'terminal':\n      scene.content = {\n        type: 'terminal',\n        commands: section.terminalCommands || [],\n        prompt: '$ ',\n        theme: 'dark',\n        typeSpeed: 80\n      };\n      break;\n      \n    case 'diagram':\n      scene.content = {\n        type: 'diagram',\n        diagramType: 'flowchart',\n        elements: section.keyPoints || [],\n        animation: 'progressive-reveal'\n      };\n      break;\n      \n    case 'screen':\n      scene.content = {\n        type: 'screen-recording',\n        overlayText: section.keyPoints || [],\n        highlights: true\n      };\n      break;\n      \n    default:\n      scene.content = {\n        type: 'text-overlay',\n        title: section.sectionTitle,\n        bullets: section.keyPoints || []\n      };\n  }\n\n  return scene;\n});\n\nfunction detectLanguage(code) {\n  if (code.includes('function') || code.includes('const') || code.includes('let')) return 'javascript';\n  if (code.includes('def ') || code.includes('import ')) return 'python';\n  if (code.includes('<?php')) return 'php';\n  if (code.includes('public class')) return 'java';\n  return 'plaintext';\n}\n\nreturn {\n  json: {\n    videoTitle: outline.videoTitle,\n    totalDuration: outline.totalDurationSeconds + 18, // Including intro/outro\n    scenes: videoScenes,\n    resolution: '1920x1080',\n    fps: 30,\n    codec: 'h264'\n  }\n};"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "d3cb67a4-a464-402e-9355-8eaa1875f232",
      "name": "Render Video with Remotion",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "\ud83c\udfac Renders the complete video using Remotion API with all scenes and audio",
      "position": [
        1280,
        176
      ],
      "parameters": {
        "url": "https://api.remotion.dev/v1/render",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "composition",
              "value": "TutorialVideo"
            },
            {
              "name": "inputProps",
              "value": "={{ JSON.stringify($json) }}"
            },
            {
              "name": "codec",
              "value": "h264"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "96df4d92-d72b-4609-b968-440f64a1caa3",
      "name": "Check Render Status",
      "type": "n8n-nodes-base.if",
      "notes": "\u2705 Verifies video rendering completed successfully before proceeding",
      "position": [
        1504,
        176
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "caseSensitive": false
          },
          "conditions": [
            {
              "id": "render-status-check",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "completed"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "43c44e0f-a26e-433d-85bf-766d38c5f877",
      "name": "Backup to Google Drive",
      "type": "n8n-nodes-base.googleDrive",
      "notes": "\ud83d\udcbe Saves a backup copy to Google Drive for archival",
      "position": [
        1728,
        272
      ],
      "parameters": {
        "name": "={{ $('Structure Tutorial Outline').item.json.videoTitle }}.mp4",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "root",
          "cachedResultName": "/ (Root folder)"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 3
    },
    {
      "id": "ca04e80b-ef43-4e0f-87dd-12c7b886e219",
      "name": "Compile Response",
      "type": "n8n-nodes-base.code",
      "notes": "\ud83d\udcca Compiles final response with video URLs and metadata",
      "position": [
        1952,
        176
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Compile final response with video details\nconst outline = $('Structure Tutorial Outline').item.json;\nconst youtubeData = $('Upload to YouTube').item.json;\nconst driveData = $('Backup to Google Drive').item.json;\n\nconst response = {\n  success: true,\n  message: 'Tutorial video created successfully',\n  video: {\n    title: outline.videoTitle,\n    duration: `${outline.totalDurationMinutes} minutes`,\n    sections: outline.totalSections,\n    sourceUrl: outline.sourceUrl,\n    createdAt: outline.createdAt\n  },\n  youtube: {\n    videoId: youtubeData.id || 'pending',\n    url: youtubeData.id ? `https://youtube.com/watch?v=${youtubeData.id}` : 'pending',\n    status: youtubeData.status || 'uploaded'\n  },\n  backup: {\n    driveId: driveData.id || '',\n    driveUrl: driveData.webViewLink || ''\n  },\n  metadata: {\n    prerequisites: outline.prerequisites || [],\n    learningOutcomes: outline.learningOutcomes || []\n  }\n};\n\nreturn {\n  json: response\n};"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "71137596-bb0a-400f-8e6c-ff47f9fcf00c",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "notes": "\u2705 Sends success response back to API caller with video details",
      "position": [
        2176,
        176
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json, null, 2) }}"
      },
      "notesInFlow": true,
      "typeVersion": 1
    },
    {
      "id": "b7c3b9f1-be6b-4556-9be7-a8854320741d",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1584,
        -336
      ],
      "parameters": {
        "width": 620,
        "height": 824,
        "content": "## AI Documentation to Training Video Creator\n\nAutomatically transforms any technical documentation or blog post into a professional tutorial video with code highlighting, terminal animations, and AI-generated narration.\n\n### How it works\n\nThe workflow accepts a documentation URL via webhook, fetches and parses the HTML content to extract text, code blocks, and structure. Claude AI analyzes the content and creates a detailed tutorial outline with sections, narration scripts, and visual types. The workflow generates audio narration using Google Text-to-Speech, creates visual scenes with code highlights and terminal animations, and renders the complete video using Remotion. Finally, it uploads the video to YouTube and backs it up to Google Drive.\n\n### Setup steps\n\n1. Configure Claude AI credentials (Anthropic API key) in the \"Analyze with Claude AI\" node\n2. Set up Google Cloud Text-to-Speech API credentials with service account authentication\n3. Configure Remotion API credentials for video rendering (requires Remotion account)\n4. Add YouTube OAuth2 credentials for video uploads with upload permissions\n5. Add Google Drive OAuth2 credentials for backup storage\n6. Activate the workflow to enable the webhook endpoint\n7. Send POST requests to the webhook URL with JSON body: {\"documentationUrl\": \"https://docs.example.com/guide\"}\n8. Monitor executions and check YouTube for published videos\n\n### Customization\n\nModify narration voice settings in \"Generate Narration Script\" node. Adjust video resolution, FPS, and codec in \"Create Visual Scenes\" node. Customize code editor themes and terminal styles. Change YouTube category and privacy settings. Add custom intro/outro animations or branding overlays."
      },
      "typeVersion": 1
    },
    {
      "id": "0192412c-8687-4011-916a-facadefe8835",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -816,
        60
      ],
      "parameters": {
        "color": 3,
        "width": 640,
        "height": 276,
        "content": "## Content Extraction\n\nFetches documentation and extracts text, code blocks, and structure"
      },
      "typeVersion": 1
    },
    {
      "id": "f71c72f0-d66d-4444-b267-733074e14ace",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -104,
        12
      ],
      "parameters": {
        "color": 3,
        "width": 464,
        "height": 324,
        "content": "## AI Analysis & Planning\n\nAnalyzes content and creates structured tutorial outline"
      },
      "typeVersion": 1
    },
    {
      "id": "b3b6bd0b-0f4d-40ec-90b3-50953a0209f3",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        -4
      ],
      "parameters": {
        "color": 3,
        "width": 672,
        "height": 340,
        "content": "## Audio & Visual Generation\n\nGenerates narration audio and creates visual scenes with code/terminal animations"
      },
      "typeVersion": 1
    },
    {
      "id": "5bfc9a35-e284-4d79-8ec8-aa7e02e52318",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1224,
        -4
      ],
      "parameters": {
        "color": 3,
        "width": 1104,
        "height": 436,
        "content": "## Video Rendering & Distribution\n\nRenders final video and uploads to YouTube and Google Drive"
      },
      "typeVersion": 1
    },
    {
      "id": "97795e7b-6fa8-461f-a61f-4232b2be1ea1",
      "name": "Mistral Cloud Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatMistralCloud",
      "position": [
        -24,
        400
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "mistralCloudApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "88da4deb-0bb5-4756-970e-d0891dc88302",
      "name": "Claude AI to analyze documentation",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -96,
        176
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "e9a3b491-2a0c-4fbd-9112-949b140675e9",
      "name": "Upload to Youtube",
      "type": "n8n-nodes-base.youTube",
      "position": [
        1728,
        80
      ],
      "parameters": {
        "title": "your_title",
        "options": {},
        "resource": "video",
        "operation": "upload",
        "categoryId": "=nji876tfcxswe34567yhgnmklp09",
        "regionCode": "IN"
      },
      "credentials": {
        "youTubeOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "18088e03-edfa-4aff-a8f4-a80d0b3ef877",
      "name": "Generate Audio with Google TTS1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        704,
        176
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "1dcf0768-8cce-4e4a-9c5e-c9fd2cf8aad9",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        776,
        400
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "420a851d-9ef4-4da0-9a78-b1181227bb35",
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Fetch Documentation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compile Response": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to Youtube": {
      "main": [
        [
          {
            "node": "Compile Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Render Status": {
      "main": [
        [
          {
            "node": "Backup to Google Drive",
            "type": "main",
            "index": 0
          },
          {
            "node": "Upload to Youtube",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Documentation": {
      "main": [
        [
          {
            "node": "Parse Documentation Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Visual Scenes": {
      "main": [
        [
          {
            "node": "Render Video with Remotion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Backup to Google Drive": {
      "main": [
        [
          {
            "node": "Compile Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Audio with Google TTS1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Mistral Cloud Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Claude AI to analyze documentation",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Generate Narration Script": {
      "main": [
        [
          {
            "node": "Generate Audio with Google TTS1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Render Video with Remotion": {
      "main": [
        [
          {
            "node": "Check Render Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structure Tutorial Outline": {
      "main": [
        [
          {
            "node": "Generate Narration Script",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Documentation Content": {
      "main": [
        [
          {
            "node": "Claude AI to analyze documentation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Audio with Google TTS1": {
      "main": [
        [
          {
            "node": "Create Visual Scenes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude AI to analyze documentation": {
      "main": [
        [
          {
            "node": "Structure Tutorial Outline",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}