Add n8n workflow JSON files using FOSS alternatives

This commit introduces three n8n workflow JSON files:
- RedditVideoMakerBotMain.json
- RedditVideoMakerBotProcessPost.json
- RedditVideoMakerBotGenerateTTS.json

These workflows are designed to automate the creation of videos from Reddit posts using open-source tools like Piper TTS for text-to-speech and ffmpeg for audio/video manipulation and captioning.

The workflows require you to configure paths to these tools, voice models, local video background folders, and font files within the n8n environment.
pull/2347/head
google-labs-jules[bot] 3 months ago
parent 64bf647de9
commit fd1a666f41

@ -0,0 +1,187 @@
{
"name": "RedditVideoMakerBot Generate TTS",
"tags": ["TTS", "Audio", "Subworkflow", "FOSS", "Piper"],
"nodes": [
{
"parameters": {
"content": "# Reddit Video Maker Bot - Generate TTS Subworkflow (Piper TTS)\n\nThis subworkflow generates text-to-speech audio for a given text segment using Piper TTS (via command line) and converts it to MP3 using ffmpeg.\n\n**Inputs:**\n- `text_segment` (string): The text to convert to speech.\n- `piper_voice_model_path` (string): Path to the Piper voice model file (e.g., en_US-lessac-medium.onnx).\n\n**Outputs:**\n- `tts_audio_binary` (binary): The generated MP3 audio data.\n- `audio_file_path` (string): The path to the saved MP3 audio file (e.g., `audio_cache/tts_output.mp3`).\n\n**Prerequisites on n8n execution environment:**\n- Piper TTS installed and accessible via `piper` command.\n- ffmpeg installed and accessible via `ffmpeg` command.\n- `audio_cache/` directory must be writable by n8n."
},
"name": "Sticky Note: Explanation",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-50,
-450
],
"id": "StickyNote_TTS_Explanation"
},
{
"parameters": {
"workflowInputs": {
"values": [
{
"name": "text_segment",
"type": "string",
"example": "Hello, this is a test sentence."
},
{
"name": "piper_voice_model_path",
"type": "string",
"example": "/models/piper/en_US-lessac-medium.onnx"
}
]
}
},
"name": "When Executed by Another Workflow",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.1,
"position": [
0,
-200
],
"id": "ExecuteWorkflowTrigger_TTS"
},
{
"parameters": {
"command": "mkdir -p audio_cache && piper --model {{ $json.piper_voice_model_path }} --output_file audio_cache/tts_output.wav",
"stdin": "{{ $json.text_segment }}",
"options": {
"shell": true,
"timeout": 120000
}
},
"name": "Generate WAV with Piper TTS",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 2.1,
"position": [
250,
-200
],
"id": "ExecuteCommand_PiperTTS",
"notesInFlow": true,
"notes": "Ensure 'audio_cache' exists. Timeout set to 2 mins."
},
{
"parameters": {
"command": "ffmpeg -y -i audio_cache/tts_output.wav -codec:a libmp3lame -qscale:a 2 audio_cache/tts_output.mp3",
"options": {
"shell": true,
"timeout": 60000
}
},
"name": "Convert WAV to MP3",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 2.1,
"position": [
500,
-200
],
"id": "ExecuteCommand_FFmpeg_WAV_to_MP3",
"notesInFlow": true,
"notes": "Overwrite if exists. Timeout 1 min."
},
{
"parameters": {
"filePath": "audio_cache/tts_output.mp3",
"outputPropertyName": "tts_audio_binary",
"options": {}
},
"name": "Read MP3 Audio File",
"type": "n8n-nodes-base.readBinaryFile",
"typeVersion": 1.2,
"position": [
750,
-200
],
"id": "ReadBinaryFile_MP3"
},
{
"parameters": {
"values": {
"string": [
{
"name": "audio_file_path",
"value": "={{ $json.tts_audio_binary.fileName || 'audio_cache/tts_output.mp3' }}"
}
]
},
"options": {
"keepOnlySet": false
}
},
"name": "Set Output Data",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1000,
-200
],
"id": "Set_Output_Data"
}
],
"connections": {
"ExecuteWorkflowTrigger_TTS": [
{
"main": [
[
{
"node": "Generate WAV with Piper TTS",
"type": "main",
"index": 0
}
]
]
}
],
"Generate WAV with Piper TTS": [
{
"main": [
[
{
"node": "Convert WAV to MP3",
"type": "main",
"index": 0
}
]
]
}
],
"Convert WAV to MP3": [
{
"main": [
[
{
"node": "Read MP3 Audio File",
"type": "main",
"index": 0
}
]
]
}
],
"Read MP3 Audio File": [
{
"main": [
[
{
"node": "Set Output Data",
"type": "main",
"index": 0
}
]
]
}
]
},
"active": false,
"settings": {
"executionOrder": "v1",
"timezone": "Europe/London",
"callerPolicy": "workflowsFromSameOwner"
},
"versionId": "1b9a12a4-8f6c-4c2e-9d1a-5b7c0f8e2d3a",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "YOUR_INSTANCE_ID"
}
}

@ -0,0 +1,236 @@
{
"name": "RedditVideoMakerBot Main",
"tags": ["Reddit", "Video Automation", "FOSS", "Main Workflow"],
"nodes": [
{
"parameters": {
"content": "# Reddit Video Maker Bot - Main Workflow (FOSS)\n\nThis workflow automates short-form video creation from Reddit posts using FOSS tools.\n\n**Steps:**\n1. **Schedule Trigger**: Runs periodically.\n2. **Set Configuration**: Defines global parameters (subreddit, Piper paths, ffmpeg paths, local folders, etc.). **USER MUST CONFIGURE THESE PATHS.**\n3. **Reddit - Get Top Posts**: Fetches posts.\n4. **Code - Filter Posts**: Filters posts.\n5. **Split In Batches**: Processes one post at a time.\n6. **Execute Subworkflow: Process Post**: Triggers video generation for each post.\n7. **Log Processed Post**: Logs completion.\n\n**Prerequisites for n8n environment:**\n- `ffmpeg` installed.\n- Piper TTS installed, with voice models at specified paths.\n- Specified local folders (`video_background_local_folder_path`, `audio_cache/`, `results/`) must exist and be writable/readable."
},
"name": "Sticky Note: Main Explanation",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"id": "StickyNote_Main_Explanation",
"position": [
0,
-400
]
},
{
"parameters": {
"triggerTimes": {
"item": [
{
"hour": 3,
"minute": 0
}
]
}
},
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"id": "ScheduleTrigger_Main",
"position": [
0,
-150
]
},
{
"parameters": {
"values": {
"string": [
{
"name": "subreddit",
"value": "askreddit"
},
{
"name": "timeframe",
"value": "day"
},
{
"name": "piper_voice_model_path_author",
"value": "/opt/n8n/piper_voices/en_US-lessac-medium.onnx"
},
{
"name": "piper_voice_model_path_commenter",
"value": "/opt/n8n/piper_voices/en_US-ryan-medium.onnx"
},
{
"name": "video_background_local_folder_path",
"value": "/opt/n8n/video_backgrounds/"
},
{
"name": "output_resolution",
"value": "1080x1920"
},
{
"name": "caption_font_path",
"value": "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
},
{
"name": "caption_font_size",
"value": "48"
},
{
"name": "caption_color",
"value": "white"
},
{
"name": "ffmpeg_path",
"value": "ffmpeg"
},
{
"name": "piper_path",
"value": "piper"
}
],
"number": [
{
"name": "post_limit",
"value": 3
},
{
"name": "min_score",
"value": 50
},
{
"name": "min_title_length",
"value": 10
},
{
"name": "min_content_length",
"value": 30
}
]
},
"options": {}
},
"name": "Set Configuration",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"id": "Set_Configuration_Main",
"position": [
250,
-150
],
"notes": "USER ACTION: Update all paths and settings here to match your environment!"
},
{
"parameters": {
"url": "https://www.reddit.com/r/{{$node['Set Configuration'].json['subreddit']}}/top.json?t={{$node['Set Configuration'].json['timeframe']}}&limit={{$node['Set Configuration'].json['post_limit']}}",
"options": {
"response": {
"responseFormat": "json"
}
},
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "n8n-RedditVideoMakerBot/1.0"
}
]
}
},
"name": "Reddit - Get Top Posts",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"id": "HttpRequest_GetRedditPosts",
"position": [
500,
-150
]
},
{
"parameters": {
"jsCode": "const minScore = $node['Set Configuration'].json.min_score;\nconst minTitleLength = $node['Set Configuration'].json.min_title_length;\nconst minContentLength = $node['Set Configuration'].json.min_content_length;\n\nlet filteredItems = [];\nif (items[0].json.data && items[0].json.data.children) {\n filteredItems = items[0].json.data.children.filter(item => {\n const post = item.data;\n const isSelfPost = post.is_self;\n const score = post.score;\n const titleLength = post.title.length;\n const contentLength = isSelfPost ? post.selftext.length : 0;\n return score >= minScore && titleLength >= minTitleLength && (isSelfPost ? contentLength >= minContentLength : true);\n });\n}\nreturn filteredItems.map(item => ({ json: item.data }));"
},
"name": "Code - Filter Posts",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"id": "Code_FilterPosts",
"position": [
750,
-150
]
},
{
"parameters": {
"batchSize": 1,
"options": {}
},
"name": "Split In Batches",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"id": "SplitInBatches_Main",
"position": [
1000,
-150
]
},
{
"parameters": {
"workflowId": {
"__rl": true,
"mode": "name",
"value": "RedditVideoMakerBot Process Post"
},
"workflowInputs": {
"value": {
"reddit_post_data": "={{ $json }}",
"config": "={{ $node['Set Configuration'].json }}"
},
"schema": [
{ "id": "reddit_post_data", "type": "object", "displayName": "Reddit Post Data" },
{ "id": "config", "type": "object", "displayName": "Global Configuration" }
]
},
"options": {
"waitForSubWorkflow": true
}
},
"name": "Execute Subworkflow: Process Post",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"id": "ExecuteWorkflow_ProcessPost",
"position": [
1250,
-150
]
},
{
"parameters": {
"message": "Successfully processed Reddit post: {{ $json.reddit_post_data.title }}",
"logLevel": "info",
"options": {}
},
"name": "Log Processed Post",
"type": "n8n-nodes-base.logMessage",
"typeVersion": 1,
"id": "LogMessage_ProcessedPost",
"position": [
1500,
-150
]
}
],
"connections": {
"ScheduleTrigger_Main": [{"main":[[{"node":"Set Configuration","type":"main","index":0}]]}],
"Set Configuration": [{"main":[[{"node":"Reddit - Get Top Posts","type":"main","index":0}]]}],
"Reddit - Get Top Posts": [{"main":[[{"node":"Code - Filter Posts","type":"main","index":0}]]}],
"Code - Filter Posts": [{"main":[[{"node":"Split In Batches","type":"main","index":0}]]}],
"Split In Batches": [{"main":[[{"node":"Execute Subworkflow: Process Post","type":"main","index":0}]]}],
"Execute Subworkflow: Process Post": [{"main":[[{"node":"Log Processed Post","type":"main","index":0}]]}]
},
"active": false,
"settings": {
"executionOrder": "v1",
"timezone": "Europe/London",
"callerPolicy": "workflowsFromSameOwner"
},
"versionId": "cc8f1b9a-3e4d-4f2a-8c9a-1b2c3d4e5f6a",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "YOUR_INSTANCE_ID"
}
}

@ -0,0 +1,174 @@
{
"name": "RedditVideoMakerBot Process Post",
"tags": ["Reddit", "Video Automation", "Subworkflow", "FOSS", "ffmpeg"],
"nodes": [
{
"parameters": {
"content": "# Reddit Video Maker Bot - Process Post Subworkflow (FOSS)\n\nProcesses a single Reddit post to create a video using FOSS tools.\n\n**Inputs:**\n- `reddit_post_data` (object): Data for the Reddit post.\n- `config` (object): Global configuration from main workflow.\n\n**Steps:**\n1. Fetch Reddit comments.\n2. Prepare script (title, selftext, comments).\n3. Generate TTS for each script part via 'Generate TTS' subworkflow.\n4. Concatenate all TTS audio files using ffmpeg.\n5. Select a random background video from local folder.\n6. Generate SRT captions from script.\n7. Assemble final video (background + audio + captions) using ffmpeg.\n8. Generate basic YouTube metadata.\n9. (Future Step: Upload to YouTube - HTTPRequest node for this is complex and omitted for brevity but would be similar to original user example if needed)\n\n**Prerequisites on n8n execution environment:**\n- As per Main and TTS workflows (ffmpeg, Piper, local folders)."
},
"name": "Sticky Note: Process Post Explanation",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"id": "StickyNote_ProcessPost_Explanation",
"position": [0, -600]
},
{
"parameters": {
"workflowInputs": {
"values": [
{ "name": "reddit_post_data", "type": "object" },
{ "name": "config", "type": "object" }
]
}
},
"name": "When Executed by Another Workflow",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.1,
"id": "ExecuteWorkflowTrigger_ProcessPost",
"position": [0, -400]
},
{
"parameters": {
"url": "https://www.reddit.com{{ $json.reddit_post_data.permalink }}.json",
"options": { "response": { "responseFormat": "json" } },
"headerParameters": { "parameters": [{ "name": "User-Agent", "value": "n8n-RedditVideoMakerBot/1.0" }] }
},
"name": "Reddit - Get Comments",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"id": "HttpRequest_GetComments",
"position": [250, -400]
},
{
"parameters": {
"jsCode": "const post = $node['When Executed by Another Workflow'].json.reddit_post_data;\nconst commentsResponse = $input.item.json; // Response from Get Comments\nlet script = [];\n\nscript.push({ text: post.title, speaker: 'author', duration_estimate: Math.max(3, post.title.length / 15) });\nif (post.is_self && post.selftext) {\n script.push({ text: post.selftext, speaker: 'author', duration_estimate: Math.max(5, post.selftext.length / 15) });\n}\n\nif (commentsResponse && commentsResponse.length > 1 && commentsResponse[1].data && commentsResponse[1].data.children) {\n const comments = commentsResponse[1].data.children;\n comments.slice(0, 5).forEach(commentItem => { // Top 5 comments\n if (commentItem.data && commentItem.data.body && commentItem.data.body.length > 10) { // Basic filter for comment quality\n script.push({ text: commentItem.data.body, speaker: 'commenter', duration_estimate: Math.max(3, commentItem.data.body.length / 15) });\n }\n });\n}\nreturn [{ json: { script: script, post_id: post.id } }];"
},
"name": "Code - Prepare Script & Estimate Durations",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"id": "Code_PrepareScript",
"position": [500, -400]
},
{
"parameters": { "batchSize": 1, "fieldToSplitOut": "script", "options": {} },
"name": "Split Script for TTS",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"id": "SplitInBatches_TTS",
"position": [750, -400]
},
{
"parameters": {
"workflowId": { "__rl": true, "mode": "name", "value": "RedditVideoMakerBot Generate TTS" },
"workflowInputs": {
"value": {
"text_segment": "={{ $json.text }}",
"piper_voice_model_path": "={{ $json.speaker === 'author' ? $node['When Executed by Another Workflow'].json.config.piper_voice_model_path_author : $node['When Executed by Another Workflow'].json.config.piper_voice_model_path_commenter }}"
}
},
"options": { "waitForSubWorkflow": true }
},
"name": "Execute Subworkflow: Generate TTS",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"id": "ExecuteWorkflow_GenerateTTS",
"position": [1000, -400]
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst fs = require('fs');\nconst postId = items[0].json.post_id; // Ensure post_id is available from one of the items\nlet concatListContent = '';\nlet tempFilePaths = [];\n\nif (!fs.existsSync('audio_cache')) fs.mkdirSync('audio_cache', { recursive: true });\n\nfor (let i = 0; i < items.length; i++) {\n if (items[i].binary && items[i].binary.tts_audio_binary && items[i].binary.tts_audio_binary.data) {\n const audioData = Buffer.from(items[i].binary.tts_audio_binary.data, 'base64');\n const tempFileName = `audio_cache/segment_${postId}_${i}.mp3`;\n try {\n fs.writeFileSync(tempFileName, audioData);\n concatListContent += `file '${tempFileName.replace(/'/g, \"'\\\\\\\\'"')}'\\n`; // Escape single quotes in filenames for ffmpeg list\n tempFilePaths.push(tempFileName);\n } catch (e) {\n console.error(`Error writing temp audio file ${tempFileName}:`, e);\n throw e; // Propagate error\n }\n } else {\n console.warn(`Missing binary audio data for item ${i}`);\n }\n}\nconst listFilePath = `audio_cache/ffmpeg_concat_list_${postId}.txt`;\nfs.writeFileSync(listFilePath, concatListContent);\n\nreturn { json: { concat_list_path: listFilePath, concatenated_audio_path: `audio_cache/concatenated_audio_${postId}.mp3`, postId: postId, temp_audio_files: tempFilePaths } };"
},
"name": "Code - Write Audios & Prepare Concat List",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"id": "Code_WriteAudiosAndPrepareConcatList",
"position": [1250, -400],
"notes": "Writes TTS binaries to temp files and creates ffmpeg concat list. Requires fs access."
},
{
"parameters": {
"command": "mkdir -p audio_cache && {{ $node['When Executed by Another Workflow'].json.config.ffmpeg_path }} -y -f concat -safe 0 -i {{ $json.concat_list_path }} -c copy {{ $json.concatenated_audio_path }}",
"options": {"shell": true, "timeout": 120000 }
},
"name": "Execute Command: Concatenate Audio",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 2.1,
"id": "ExecuteCommand_ConcatAudio",
"position": [1500, -400]
},
{
"parameters": {
"jsCode": "const fs = require('fs');\nconst folderPath = $node['When Executed by Another Workflow'].json.config.video_background_local_folder_path;\nlet background_video_path = '';\ntry {\n if (!fs.existsSync(folderPath)) { throw new Error(`Background video folder not found: ${folderPath}`); }\n const files = fs.readdirSync(folderPath);\n const mp4Files = files.filter(file => file.toLowerCase().endsWith('.mp4'));\n if (mp4Files.length > 0) {\n background_video_path = folderPath + (folderPath.endsWith('/') ? '' : '/') + mp4Files[Math.floor(Math.random() * mp4Files.length)];\n } else { throw new Error(`No .mp4 files found in ${folderPath}`); }\n} catch (e) {\n console.error('Error accessing background video folder:', e);\n throw e; // Propagate error\n}\nreturn { json: { ...$input.item.json, background_video_path: background_video_path } };"
},
"name": "Code - Get Random Background Video",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"id": "Code_GetBackgroundVideo",
"position": [0, -150],
"notes": "Needs fs access. Ensure folder and MP4 files exist."
},
{
"parameters": {
"jsCode": "const script = $node['Code - Prepare Script & Estimate Durations'].json.script;\nconst postId = $input.item.json.postId; \nlet srtContent = '';\nlet currentTime = 0; // seconds\n\nfunction formatTime(seconds) {\n const h = Math.floor(seconds / 3600).toString().padStart(2, '0');\n const m = Math.floor((seconds % 3600) / 60).toString().padStart(2, '0');\n const s = Math.floor(seconds % 60).toString().padStart(2, '0');\n const ms = Math.floor((seconds - Math.floor(seconds)) * 1000).toString().padStart(3, '0');\n return `${h}:${m}:${s},${ms}`;\n}\n\nfor (let i = 0; i < script.length; i++) {\n const segment = script[i];\n const startTime = currentTime;\n const duration = segment.duration_estimate > 0 ? segment.duration_estimate : 3; // Ensure duration is positive\n const endTime = startTime + duration;\n \n srtContent += `${i + 1}\\n`;\n srtContent += `${formatTime(startTime)} --> ${formatTime(endTime)}\\n`;\n srtContent += `${segment.text.replace(/\\n/g, ' ')}\\n\\n`; // Replace newlines in text to avoid SRT issues\n \n currentTime = endTime;\n}\n\nconst fs = require('fs');\nif (!fs.existsSync('audio_cache')) fs.mkdirSync('audio_cache', { recursive: true });\nconst srtFilePath = `audio_cache/captions_${postId}.srt`;\nfs.writeFileSync(srtFilePath, srtContent);\n\nreturn { json: { ...$input.item.json, srt_file_path: srtFilePath, total_script_duration: currentTime } };"
},
"name": "Code - Generate SRT Captions",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"id": "Code_GenerateSRTCaptions",
"position": [250, -150],
"notes": "Needs fs access. Caption timing is based on estimated duration."
},
{
"parameters": {
"command": "mkdir -p results && {{ $node['When Executed by Another Workflow'].json.config.ffmpeg_path }} -y -i {{ $json.background_video_path }} -i {{ $json.concatenated_audio_path }} -vf \"subtitles='{{ $json.srt_file_path }}':force_style='FontFile={{ $node['When Executed by Another Workflow'].json.config.caption_font_path }},FontSize={{ $node['When Executed by Another Workflow'].json.config.caption_font_size }},PrimaryColour=&HFF{{ $node['When Executed by Another Workflow'].json.config.caption_color.replace('#', '') }}',scale={{ $node['When Executed by Another Workflow'].json.config.output_resolution }}\" -c:v libx264 -c:a aac -strict experimental -shortest -t {{ $json.total_script_duration + 1 }} results/final_video_{{ $json.postId }}.mp4",
"options": { "shell": true, "timeout": 600000 }
},
"name": "Execute Command: Assemble Video",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 2.1,
"id": "ExecuteCommand_AssembleVideo",
"position": [500, -150],
"notes": "Timeout 10 mins. Ensure font path (escaped) and color format are correct for ffmpeg. PrimaryColour is AABBGGRR."
},
{
"parameters": {
"values": {
"string": [
{ "name": "youtube_title", "value": "Reddit Story: {{ $node['When Executed by Another Workflow'].json.reddit_post_data.title }}" },
{ "name": "youtube_description", "value": "A video generated from the Reddit post titled: {{ $node['When Executed by Another Workflow'].json.reddit_post_data.title }}\\n\\n{{ $node['When Executed by Another Workflow'].json.reddit_post_data.selftext ? $node['When Executed by Another Workflow'].json.reddit_post_data.selftext.substring(0,500) : '' }}\\n\\n#Reddit #AskReddit #Shorts" },
{ "name": "final_video_path", "value": "results/final_video_{{ $json.postId }}.mp4" }
]
},
"options": {"keepOnlySet": false}
},
"name": "Set - YouTube Metadata & Final Path",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"id": "Set_YouTubeMetadata",
"position": [750, -150]
}
],
"connections": {
"ExecuteWorkflowTrigger_ProcessPost": [{"main":[[{"node":"Reddit - Get Comments","type":"main","index":0}]]}],
"Reddit - Get Comments": [{"main":[[{"node":"Code - Prepare Script & Estimate Durations","type":"main","index":0}]]}],
"Code - Prepare Script & Estimate Durations": [{"main":[[{"node":"Split Script for TTS","type":"main","index":0}]]}],
"Split Script for TTS": [{"main":[[{"node":"Execute Subworkflow: Generate TTS","type":"main","index":0}]]}],
"Execute Subworkflow: Generate TTS": [{"main":[[{"node":"Code - Write Audios & Prepare Concat List","type":"main","index":0}]]}],
"Code - Write Audios & Prepare Concat List": [{"main":[[{"node":"Execute Command: Concatenate Audio","type":"main","index":0}]]}],
"Execute Command: Concatenate Audio": [{"main":[[{"node":"Code - Get Random Background Video","type":"main","index":0}]]}],
"Code - Get Random Background Video": [{"main":[[{"node":"Code - Generate SRT Captions","type":"main","index":0}]]}],
"Code - Generate SRT Captions": [{"main":[[{"node":"Execute Command: Assemble Video","type":"main","index":0}]]}],
"Execute Command: Assemble Video": [{"main":[[{"node":"Set - YouTube Metadata & Final Path","type":"main","index":0}]]}]
},
"active": false,
"settings": {
"executionOrder": "v1",
"timezone": "Europe/London",
"callerPolicy": "workflowsFromSameOwner"
},
"versionId": "9d5e2c8f-1a2b-3c4d-5e6f-7a8b9c0d1e2f",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "YOUR_INSTANCE_ID"
}
}
Loading…
Cancel
Save