├── workflows-screenshot.png ├── LICENSE ├── README.md └── video_to_shorts_Automation.json /workflows-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mismai-li/n8n-youtube-to-shorts-workflow/HEAD/workflows-screenshot.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 MI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # n8n Workflow: YouTube Video to Multiple Shorts Automation 2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | 5 | This repository contains a free n8n workflow template designed to automate the process of converting long-form YouTube videos into multiple YouTube Shorts. It integrates with [Swiftia.io](https://www.swiftia.io/) for video analysis/rendering and uses a Large Language Model (LLM) for metadata generation, finally uploading and scheduling the shorts to your YouTube channel. 6 | 7 | **Save time and streamline your content repurposing!** 8 | 9 | ## Features 10 | 11 | * **Form Trigger:** Easily start the process by providing a YouTube Video ID, scheduling parameters, and optional styling info via an n8n form. 12 | * **External API Integration:** Uses HTTP Request nodes to interact with [Swiftia.io](https://www.swiftia.io/) for: 13 | * Analyzing long videos to identify potential short clips. 14 | * Rendering individual short clips, optionally applying custom caption styling/branding. 15 | * **Flexible LLM Integration:** Leverages n8n's LangChain nodes to generate optimized titles, descriptions, tags, and YouTube category IDs using **your preferred LLM provider** (OpenAI, Google Gemini, Groq, etc.). 16 | * **Automatic Scheduling:** Calculates and sets publication dates for each Short based on your specified start time and interval. 17 | * **Direct YouTube Upload:** Downloads rendered shorts and uploads them directly to your YouTube channel using the official YouTube Data API v3 (resumable uploads). 18 | * **Adaptable Template:** Built as a foundation – adapt the API calls to your specific video service. 19 | 20 | ## Disclaimer 21 | 22 | * This is a **template workflow** it works with Swiftia's API. You **will need to adapt** the HTTP Request nodes (`generateShorts`, `get_shorts`, `renderShort`, `getRender`) in case you prefer using other platforms (such as Opus clip, klap app, getmunch, or spikes studio) you will need to match their specific API endpoints, request formats, and authentication methods. 23 | 24 | ## Screenshot 25 | Alt Text 26 | 27 | ## Prerequisites 28 | 29 | Before you start, ensure you have the following: 30 | 31 | 1. **n8n Instance:** A running instance (self-hosted or Cloud). 32 | * **\[Self-Hosted Users]** Video processing can be memory-intensive. Consider increasing allocated RAM or setting the environment variable `N8N_DEFAULT_BINARY_DATA_MODE=filesystem` (ensure sufficient disk space). 33 | 2. **Video Analysis/Rendering Service:** An account and API Key/Credentials for **a service capable of identifying clips in long videos and rendering them via API** (in this template we are using swiftia.io) . 34 | 3. **Google Account & YouTube Channel:** The target channel for uploads. 35 | 4. **Google Cloud Platform (GCP) Project:** 36 | * YouTube Data API v3 Enabled. 37 | * OAuth 2.0 Credentials (Client ID & Secret). 38 | 5. **LLM Provider Account & API Key:** An API key for your chosen provider (e.g., OpenAI, Google AI/Gemini, Groq, Anthropic). 39 | 6. **n8n Credentials:** Ready to configure credentials within n8n for the services above. 40 | 7. **n8n LangChain Nodes:** (If required by your LLM provider) Ensure the `@n8n/n8n-nodes-langchain` package (or similar) is available in your n8n instance. 41 | 8. **(Optional) Caption Styling Info:** Knowledge of the format (e.g., JSON) required by *your chosen video service* for caption styling. 42 | 43 | ## Setup Instructions 44 | 45 | 1. **Download/Clone:** Get the `video_to_shorts_Automation.json` file from this repository. 46 | 2. **Import:** Import the workflow file into your n8n instance. 47 | 3. **Create n8n Credentials:** 48 | * **Video Service Auth:** Configure the necessary authentication credential in n8n for your chosen video service (e.g., Header Auth, API Key, OAuth2). 49 | * **YouTube:** Create a "YouTube OAuth2 API" credential using your GCP OAuth details and authenticate it. 50 | * **LLM Provider:** Create the appropriate n8n credential for your chosen LLM provider (e.g., "OpenAI API", "Google Gemini API"). 51 | 4. **Configure Workflow Nodes:** 52 | * **IMPORTANT - Adapt HTTP Requests:** Modify the following HTTP Request nodes to match **your chosen video service's API documentation** in case you are using something other than swiftia: 53 | * `generateShorts` (Initiate analysis) 54 | * `get_shorts` (Check analysis status) 55 | * `renderShort` (Initiate rendering) 56 | * `getRender` (Check rendering status) 57 | * **Select Credentials:** In the YouTube nodes (`setupMetaData`, `Sendshorttoyoutube`) and the LLM Chat Model node, select the corresponding credentials you created in n8n. 58 | * **LLM Node:** The template uses "Google Gemini Chat Model". If using a different provider, **delete** this node and **add** the appropriate one (e.g., "OpenAI Chat Model"). Connect it correctly within the LangChain steps and select your LLM credential. 59 | * **Review ALL Nodes:** Double-check all nodes for any remaining placeholder values (like URLs, keys in headers if not using credentials properly) and replace them. 60 | 5. **Activate:** Save and activate the workflow. 61 | 62 | ## Running the Workflow 63 | 64 | 1. Once active, locate the "Webhook URL" provided by the "n8n Form Trigger" node in the workflow editor. 65 | 2. Open this URL in your browser. 66 | 3. Fill out the form: 67 | * YouTube Video ID (e.g., `dQw4w9WgXcQ`) 68 | * First publication date/time (ISO 8601 format, e.g., `2025-05-10T08:00:00Z`) 69 | * Interval between shorts (in hours) 70 | * (Optional) Caption styling information (as required by your video service) 71 | 4. Submit the form. n8n will start the process. 72 | 73 | ## Important Notes 74 | 75 | * **Costs:** Be mindful of potential costs associated with your chosen video processing service, the YouTube Data API (beyond free quotas), and your LLM provider. 76 | * **Testing:** **Strongly recommended:** Initially set the `privacyStatus` in the `setupMetaData` node to `private` for testing purposes before using `publishAt` for scheduled public/unlisted shorts. 77 | * **Error Handling:** This template has basic checks but can be enhanced with more robust error handling using n8n's built-in features. 78 | 79 | ## Customization 80 | 81 | Feel free to modify and enhance this workflow: 82 | 83 | * Adjust the prompt in the `generatingMetaData` node for different LLM outputs. 84 | * Change the maximum number of shorts processed in the `maxShortsnumber` node. 85 | * Add notification steps (e.g., Slack, Discord) upon completion or failure. 86 | * Improve error handling logic. 87 | 88 | ## License 89 | 90 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 91 | 92 | ## Contributing (Optional) 93 | 94 | Contributions, issues, and feature requests are welcome! Feel free to check [issues page](link-to-your-issues-page). 95 | 96 | ## Support (Optional) 97 | 98 | If you have questions, please open an issue on the [GitHub repository issues page](link-to-your-issues-page). 99 | -------------------------------------------------------------------------------- /video_to_shorts_Automation.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "video to shorts Automation", 3 | "nodes": [ 4 | { 5 | "parameters": { 6 | "amount": 30 7 | }, 8 | "id": "5989cd85-2ecc-4011-9533-78a96fc990c8", 9 | "name": "Wait", 10 | "type": "n8n-nodes-base.wait", 11 | "typeVersion": 1.1, 12 | "position": [ 13 | 820, 14 | -20 15 | ], 16 | "webhookId": "7546076f-92cc-4b1e-b4ed-53818ca36559" 17 | }, 18 | { 19 | "parameters": { 20 | "conditions": { 21 | "options": { 22 | "caseSensitive": true, 23 | "leftValue": "", 24 | "typeValidation": "strict" 25 | }, 26 | "conditions": [ 27 | { 28 | "id": "61659638-66b5-4b11-a526-6b09977f3297", 29 | "leftValue": "={{ $('get_shorts').item.json.status }}", 30 | "rightValue": "COMPLETED", 31 | "operator": { 32 | "type": "string", 33 | "operation": "equals", 34 | "name": "filter.operator.equals" 35 | } 36 | } 37 | ], 38 | "combinator": "and" 39 | }, 40 | "options": {} 41 | }, 42 | "id": "1393d430-56a7-4441-9850-c2405bfbc5a3", 43 | "name": "If", 44 | "type": "n8n-nodes-base.if", 45 | "typeVersion": 2, 46 | "position": [ 47 | 1120, 48 | -20 49 | ] 50 | }, 51 | { 52 | "parameters": { 53 | "url": "=https://app.swiftia.io/api/jobs/{{ $('generateShorts').item.json.id }}", 54 | "sendHeaders": true, 55 | "headerParameters": { 56 | "parameters": [ 57 | { 58 | "name": "Authorization", 59 | "value": "Bearer " 60 | } 61 | ] 62 | }, 63 | "options": {} 64 | }, 65 | "id": "3cf276c7-44cd-4b3f-baa0-722d93de3b84", 66 | "name": "get_shorts", 67 | "type": "n8n-nodes-base.httpRequest", 68 | "typeVersion": 4.2, 69 | "position": [ 70 | 980, 71 | -20 72 | ] 73 | }, 74 | { 75 | "parameters": { 76 | "fieldToSplitOut": "data.shorts", 77 | "include": "selectedOtherFields", 78 | "fieldsToInclude": "id,title", 79 | "options": {} 80 | }, 81 | "id": "550a0c59-9a94-4ed6-a25c-cec034456e9f", 82 | "name": "Split Out", 83 | "type": "n8n-nodes-base.splitOut", 84 | "typeVersion": 1, 85 | "position": [ 86 | 1200, 87 | -160 88 | ] 89 | }, 90 | { 91 | "parameters": {}, 92 | "id": "1b23af22-9fb4-46ec-946e-47f175632412", 93 | "name": "Wait1", 94 | "type": "n8n-nodes-base.wait", 95 | "typeVersion": 1.1, 96 | "position": [ 97 | 2420, 98 | -240 99 | ], 100 | "webhookId": "ad25b936-01d5-4baf-a1b4-51a42c49feda" 101 | }, 102 | { 103 | "parameters": { 104 | "url": "=https://app.swiftia.io/api/render/{{ $('renderShort').item.json.renderId }}", 105 | "sendHeaders": true, 106 | "headerParameters": { 107 | "parameters": [ 108 | { 109 | "name": "Authorization", 110 | "value": "Bearer " 111 | } 112 | ] 113 | }, 114 | "options": {} 115 | }, 116 | "id": "12d97ef2-71ad-487c-8489-3484000e367e", 117 | "name": "getRender", 118 | "type": "n8n-nodes-base.httpRequest", 119 | "typeVersion": 4.2, 120 | "position": [ 121 | 2060, 122 | -20 123 | ] 124 | }, 125 | { 126 | "parameters": { 127 | "url": "={{ $('getRender').first().json.url }}", 128 | "options": {} 129 | }, 130 | "id": "0fb82f27-c209-4e3b-9784-046cc8c8b47b", 131 | "name": "downloadshorts", 132 | "type": "n8n-nodes-base.httpRequest", 133 | "typeVersion": 4.2, 134 | "position": [ 135 | 2640, 136 | 40 137 | ] 138 | }, 139 | { 140 | "parameters": { 141 | "method": "PUT", 142 | "url": "={{ $node[\"setupMetaData\"].json.headers.location }} ", 143 | "authentication": "predefinedCredentialType", 144 | "nodeCredentialType": "youTubeOAuth2Api", 145 | "sendHeaders": true, 146 | "headerParameters": { 147 | "parameters": [ 148 | { 149 | "name": "Content-Type", 150 | "value": "video/mp4" 151 | } 152 | ] 153 | }, 154 | "sendBody": true, 155 | "contentType": "binaryData", 156 | "inputDataFieldName": "data", 157 | "options": {} 158 | }, 159 | "id": "0328fd3e-8f30-433b-9386-76cd6c430095", 160 | "name": "Sendshorttoyoutube", 161 | "type": "n8n-nodes-base.httpRequest", 162 | "typeVersion": 4.1, 163 | "position": [ 164 | 2820, 165 | 40 166 | ], 167 | "credentials": { 168 | "youTubeOAuth2Api": { 169 | "id": "", 170 | "name": "" 171 | } 172 | } 173 | }, 174 | { 175 | "parameters": { 176 | "method": "POST", 177 | "url": "https://www.googleapis.com/upload/youtube/v3/videos?part=snippet,status&uploadType=resumable", 178 | "authentication": "predefinedCredentialType", 179 | "nodeCredentialType": "youTubeOAuth2Api", 180 | "sendHeaders": true, 181 | "headerParameters": { 182 | "parameters": [ 183 | { 184 | "name": "Content-Type", 185 | "value": "application/json" 186 | }, 187 | { 188 | "name": "X-Upload-Content-Type", 189 | "value": "video/webm" 190 | } 191 | ] 192 | }, 193 | "sendBody": true, 194 | "contentType": "raw", 195 | "rawContentType": "RAW/JSON", 196 | "body": "={\n \"snippet\": {\n \"title\": \"{{ $('generatingMetaData').item.json.output.short_title }}\",\n \"description\": \"{{ $('generatingMetaData').item.json.output.short_description }}\",\n \"tags\": \"{{ $('generatingMetaData').item.json.output.short_tags.slice(0,6).join(', ') }}\",\n \"categoryId\": \"{{ $('generatingMetaData').item.json.output.youtube_category_id }}\",\n \"defaultLanguage\": \"en_US\",\n \"defaultAudioLanguage\": \"en_US\"\n },\n \"status\": {\n \"privacyStatus\": \"private\",\n \"publishAt\": \"{{ $('current_item_ref').item.json.publicationDate }}\", \n \"license\": \"youtube\",\n \"embeddable\": true,\n \"publicStatsViewable\": true,\n \"madeForKids\": false,\n \"selfDeclaredMadeForKids\": false\n }\n}", 197 | "options": { 198 | "response": { 199 | "response": { 200 | "fullResponse": true 201 | } 202 | } 203 | } 204 | }, 205 | "id": "62ac15f9-fb16-4360-934a-23a45b50f4d8", 206 | "name": "setupMetaData", 207 | "type": "n8n-nodes-base.httpRequest", 208 | "typeVersion": 4.1, 209 | "position": [ 210 | 3120, 211 | -140 212 | ], 213 | "credentials": { 214 | "youTubeOAuth2Api": { 215 | "id": "", 216 | "name": "" 217 | } 218 | } 219 | }, 220 | { 221 | "parameters": { 222 | "path": "d47e6412-23af-453a-a5eb-9179b54d3ae1", 223 | "formTitle": "youtube to shorts", 224 | "formDescription": "This workflow allows you to turn your youtube videos into shorts, apply your branding and caption style and schedule their publication dates.", 225 | "formFields": { 226 | "values": [ 227 | { 228 | "fieldLabel": "youtube video ID", 229 | "requiredField": true 230 | }, 231 | { 232 | "fieldLabel": "First short publication date using the following format: 2025-05-10T08:00:00Z", 233 | "requiredField": true 234 | }, 235 | { 236 | "fieldLabel": "Interval between each short publication in hours", 237 | "fieldType": "number", 238 | "requiredField": true 239 | }, 240 | { 241 | "fieldLabel": "Captions styling ( you can use one of the presets or create your own on Swiftia's playground past it here ", 242 | "fieldType": "textarea", 243 | "requiredField": true 244 | } 245 | ] 246 | }, 247 | "responseMode": "responseNode", 248 | "options": {} 249 | }, 250 | "id": "b98aebd1-4a2e-42f6-b327-a2ee5262b407", 251 | "name": "n8n Form Trigger", 252 | "type": "n8n-nodes-base.formTrigger", 253 | "typeVersion": 2, 254 | "position": [ 255 | 560, 256 | -460 257 | ], 258 | "webhookId": "d47e6412-23af-453a-a5eb-9179b54d3ae1" 259 | }, 260 | { 261 | "parameters": { 262 | "method": "POST", 263 | "url": "https://app.swiftia.io/api/jobs", 264 | "sendHeaders": true, 265 | "headerParameters": { 266 | "parameters": [ 267 | { 268 | "name": "Authorization", 269 | "value": "Bearer " 270 | } 271 | ] 272 | }, 273 | "sendBody": true, 274 | "specifyBody": "json", 275 | "jsonBody": "={\n \"functionName\": \"VideoShorts\",\n \"youtubeVideoId\": \"{{ $('preparingField').item.json.videoId }}\",\n \"videoSource\":\"{{ $json.videoSource }}\"\n} ", 276 | "options": {} 277 | }, 278 | "id": "4e9d1e41-2e70-4c50-8c40-3837d00da9f9", 279 | "name": "generateShorts", 280 | "type": "n8n-nodes-base.httpRequest", 281 | "typeVersion": 4.2, 282 | "position": [ 283 | 820, 284 | -240 285 | ] 286 | }, 287 | { 288 | "parameters": { 289 | "options": {} 290 | }, 291 | "id": "250a308f-463e-41b4-96f4-56c49324e861", 292 | "name": "Loop Over Items", 293 | "type": "n8n-nodes-base.splitInBatches", 294 | "typeVersion": 3, 295 | "position": [ 296 | 1880, 297 | -320 298 | ] 299 | }, 300 | { 301 | "parameters": { 302 | "method": "POST", 303 | "url": "https://app.swiftia.io/api/render/", 304 | "sendHeaders": true, 305 | "headerParameters": { 306 | "parameters": [ 307 | { 308 | "name": "Authorization", 309 | "value": "Bearer " 310 | } 311 | ] 312 | }, 313 | "sendBody": true, 314 | "specifyBody": "json", 315 | "jsonBody": "={\n \"id\": \"{{ $('current_item_ref').item.json.id }}\",\n \"target\": {{ $json['data.shorts'].id }},\n \"{{ $json.stylingType }}\": {{ $json.styling.toJsonString() }}\n} ", 316 | "options": {} 317 | }, 318 | "id": "2a429e8c-2af2-4d81-99d2-7d3bf68770da", 319 | "name": "renderShort", 320 | "type": "n8n-nodes-base.httpRequest", 321 | "typeVersion": 4.2, 322 | "position": [ 323 | 2260, 324 | -240 325 | ], 326 | "executeOnce": true 327 | }, 328 | { 329 | "parameters": { 330 | "modelName": "models/gemini-2.0-flash-lite", 331 | "options": {} 332 | }, 333 | "id": "a19e317e-2641-4a06-9b84-4f9e15114f9c", 334 | "name": "Google Gemini Chat Model", 335 | "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini", 336 | "typeVersion": 1, 337 | "position": [ 338 | 2640, 339 | -120 340 | ], 341 | "credentials": { 342 | "googlePalmApi": { 343 | "id": "", 344 | "name": "" 345 | } 346 | } 347 | }, 348 | { 349 | "parameters": {}, 350 | "id": "c3502939-0ee8-4684-959c-97fe1d02bd68", 351 | "name": "Auto-fixing Output Parser", 352 | "type": "@n8n/n8n-nodes-langchain.outputParserAutofixing", 353 | "typeVersion": 1, 354 | "position": [ 355 | 2740, 356 | -120 357 | ] 358 | }, 359 | { 360 | "parameters": { 361 | "jsonSchema": "{\n \"title\": \"YouTube Short Metadata\",\n \"description\": \"Schema for generating optimized metadata for a YouTube Short based on a segment from a longer video.\",\n \"type\": \"object\",\n \"properties\": {\n \"short_title\": {\n \"description\": \"Concise, hook-driven title for the YouTube Short (ideally under 70 characters).\",\n \"type\": \"string\",\n \"minLength\": 1,\n \"maxLength\": 70\n },\n \"short_description\": {\n \"description\": \"Keyword-rich description for the Short, including context and the #shorts hashtag.\",\n \"type\": \"string\",\n \"minLength\": 1,\n \"maxLength\": 500, \n \"pattern\": \"#shorts\"\n },\n \"short_tags\": {\n \"description\": \"List of relevant keywords/tags for the Short, including 'shorts', 'youtubeshorts'.\",\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"minItems\": 1,\n \"maxItems\": 25,\n \"uniqueItems\": true\n },\n \"youtube_category_id\": {\n \"description\": \"The single, most suitable YouTube category ID for the Short.\",\n \"type\": \"integer\",\n \"enum\": [\n 1, \n 2, \n 10,\n 15, \n 17, \n 18, \n 19, \n 20, \n 21,\n 22, \n 23, \n 24,\n 25,\n 26,\n 27,\n 28,\n 29,\n 30,\n 31,\n 32, \n 33, \n 34, \n 35,\n 36,\n 37, \n 38, \n 39,\n 40, \n 41,\n 42,\n 43, \n 44 \n ]\n }\n },\n \"required\": [\n \"short_title\",\n \"short_description\",\n \"short_tags\",\n \"youtube_category_id\"\n ]\n}" 362 | }, 363 | "id": "def998c5-567e-4146-b929-bcbc4f764d85", 364 | "name": "Structured Output Parser", 365 | "type": "@n8n/n8n-nodes-langchain.outputParserStructured", 366 | "typeVersion": 1.1, 367 | "position": [ 368 | 2980, 369 | -120 370 | ] 371 | }, 372 | { 373 | "parameters": {}, 374 | "id": "f9cb8691-90f2-4a88-91f3-f62825bf2512", 375 | "name": "current_item_ref", 376 | "type": "n8n-nodes-base.noOp", 377 | "typeVersion": 1, 378 | "position": [ 379 | 2100, 380 | -240 381 | ] 382 | }, 383 | { 384 | "parameters": { 385 | "assignments": { 386 | "assignments": [ 387 | { 388 | "id": "36260632-851a-413f-86f5-78e7b8385b42", 389 | "name": "videoSource", 390 | "value": "youtube", 391 | "type": "string" 392 | }, 393 | { 394 | "id": "e4152316-138f-4f63-8cec-32e5f2f71909", 395 | "name": "videoId", 396 | "value": "={{ $json['youtube video ID'] }}", 397 | "type": "string" 398 | }, 399 | { 400 | "id": "1be2767a-2c2f-45de-9adc-dcb852060da9", 401 | "name": "firstPublicationAt", 402 | "value": "={{ $json['First short publication date using the following format: 2025-05-10T08:00:00Z'] }}", 403 | "type": "string" 404 | }, 405 | { 406 | "id": "cb07bdb4-ef01-47e7-81ef-ebbe2734b269", 407 | "name": "intervalHours", 408 | "value": "={{ $json['Interval between each short publication in hours'] }}", 409 | "type": "number" 410 | }, 411 | { 412 | "id": "aaf4c810-1b7d-45a0-a5ab-51a60ec4f530", 413 | "name": "cStyling", 414 | "value": "={{ $json['Captions styling ( you can create your own on Swiftia\\'s playground past it heare '] }}", 415 | "type": "string" 416 | } 417 | ] 418 | }, 419 | "options": {} 420 | }, 421 | "id": "b14465e8-4e8f-49d4-9f70-b670be621264", 422 | "name": "preparingField", 423 | "type": "n8n-nodes-base.set", 424 | "typeVersion": 3.3, 425 | "position": [ 426 | 820, 427 | -460 428 | ] 429 | }, 430 | { 431 | "parameters": { 432 | "mode": "runOnceForEachItem", 433 | "jsCode": "// Get the stringified JSON from the input item's cStyling field\nconst cStylingString = item.json.cStyling;\n\n// Initialize variables to hold the results\nlet foundKeyName = null;\nlet foundKeyValue = null;\nlet parseError = null; // Optional: Track parsing errors\n\n// Check if cStyling exists and is a non-empty string\nif (typeof cStylingString === 'string' && cStylingString.trim() !== '') {\n try {\n // Parse the string\n const parsedStyling = JSON.parse(cStylingString);\n\n // Check if the parsed object exists\n if (parsedStyling) {\n // Check for 'options' first\n if (parsedStyling.options !== undefined) {\n foundKeyName = \"options\"; // Store the name of the key found\n foundKeyValue = parsedStyling.options; // Store the value associated with that key\n }\n // Else if 'options' doesn't exist, check for 'preset'\n else if (parsedStyling.preset !== undefined) {\n foundKeyName = \"preset\"; // Store the name of the key found\n foundKeyValue = parsedStyling.preset; // Store the value associated with that key\n }\n // If neither key exists, the variables remain null\n }\n } catch (error) {\n console.error(\"Failed to parse cStyling JSON:\", error);\n parseError = `Failed to parse cStyling JSON: ${error.message}`;\n // Keep variables null on error\n }\n} else {\n console.warn(\"cStyling missing, not a string, or empty.\");\n parseError = \"cStyling missing, not a string, or empty\";\n // Keep variables null\n}\n\n// --- Construct the NEW output item ---\n\n// Check if a key name was actually found\nif (foundKeyName !== null) {\n // Return a new item containing the key name and its value\n return { json: { stylingType: foundKeyName, styling: foundKeyValue } };\n} else {\n // If nothing was found or parsing failed, return an empty item\n // Optionally include the error: return { json: { error: parseError } };\n return { json: {} };\n\n // Alternatively, to completely filter out items where neither key is found:\n // return null;\n // or\n // return [];\n}" 434 | }, 435 | "id": "2b883ff6-264e-458e-99af-f0854bd46f05", 436 | "name": "cleaningStyling", 437 | "type": "n8n-nodes-base.code", 438 | "typeVersion": 2, 439 | "position": [ 440 | 1280, 441 | -460 442 | ] 443 | }, 444 | { 445 | "parameters": { 446 | "mode": "combine", 447 | "combinationMode": "multiplex", 448 | "options": {} 449 | }, 450 | "id": "67e0ad2e-3341-42d2-9f59-3420e96c692a", 451 | "name": "Merge", 452 | "type": "n8n-nodes-base.merge", 453 | "typeVersion": 2.1, 454 | "position": [ 455 | 1680, 456 | -440 457 | ] 458 | }, 459 | { 460 | "parameters": { 461 | "jsCode": "const startDateString = $node[\"preparingField\"].json.firstPublicationAt; // Adjust field name/path as needed\nconst startDate = new Date(startDateString);\nconst intervalHours = $node[\"preparingField\"].json.intervalHours; // Interval between publishing dates (in hours)\n\nitems.forEach((item, index) => {\n const publishingDate = new Date(startDate.getTime() + index * intervalHours * 60 * 60 * 1000);\n \n // Format the date to ISO 8601 (compatible with YouTube API)\n item.json.publicationDate = publishingDate.toISOString();\n});\n\nreturn items;\n" 462 | }, 463 | "id": "02cfc68d-b442-4972-852b-0fc720487d95", 464 | "name": "shcedulingCode", 465 | "type": "n8n-nodes-base.code", 466 | "typeVersion": 2, 467 | "position": [ 468 | 1280, 469 | -300 470 | ] 471 | }, 472 | { 473 | "parameters": { 474 | "respondWith": "json", 475 | "responseBody": "={\n \"formSubmittedText\": \"The workflow has completed, here are the scheduled shorts: {{ $input.all().map(item => item.json.shortID) }}\"\n}", 476 | "options": {} 477 | }, 478 | "id": "df9a4538-d242-4b4c-b4b9-3703c7ad7a51", 479 | "name": "Respond to Webhook1", 480 | "type": "n8n-nodes-base.respondToWebhook", 481 | "typeVersion": 1.1, 482 | "position": [ 483 | 2360, 484 | -560 485 | ] 486 | }, 487 | { 488 | "parameters": { 489 | "maxItems": 10 490 | }, 491 | "id": "fd7aa0be-2965-4172-bab1-54e2d29a8d28", 492 | "name": "maxShortsnumber", 493 | "type": "n8n-nodes-base.limit", 494 | "typeVersion": 1, 495 | "position": [ 496 | 1520, 497 | -300 498 | ] 499 | }, 500 | { 501 | "parameters": {}, 502 | "id": "66db8533-d873-463a-bb4d-7cc830277032", 503 | "name": "verifyingLoopOutput", 504 | "type": "n8n-nodes-base.noOp", 505 | "typeVersion": 1, 506 | "position": [ 507 | 2120, 508 | -560 509 | ] 510 | }, 511 | { 512 | "parameters": { 513 | "content": "# Shorts generation", 514 | "height": 874.3714485405606, 515 | "width": 669.8249890758466, 516 | "color": 4 517 | }, 518 | "id": "bb280529-87ea-4ce8-a559-c8c8698c965d", 519 | "name": "Sticky Note", 520 | "type": "n8n-nodes-base.stickyNote", 521 | "typeVersion": 1, 522 | "position": [ 523 | 760, 524 | -640 525 | ] 526 | }, 527 | { 528 | "parameters": { 529 | "content": "# Number of shorts to schedule + styling assignment", 530 | "height": 873.3701769220911, 531 | "width": 356.5339510395039, 532 | "color": 5 533 | }, 534 | "id": "55ba6b4a-0a85-44bc-9da8-7deaddd2f36c", 535 | "name": "Sticky Note1", 536 | "type": "n8n-nodes-base.stickyNote", 537 | "typeVersion": 1, 538 | "position": [ 539 | 1460, 540 | -640 541 | ] 542 | }, 543 | { 544 | "parameters": { 545 | "promptType": "define", 546 | "text": "=original video title: {{ $('current_item_ref').item.json.title }}\nshort transcript: {{ $('current_item_ref').item.json['data.shorts'].text }}\nReason: {{ $('current_item_ref').item.json['data.shorts'].reason }}", 547 | "hasOutputParser": true, 548 | "messages": { 549 | "messageValues": [ 550 | { 551 | "message": "You are an expert YouTube Shorts Content Strategist AI. Your primary goal is to maximize the virality and engagement potential of YouTube Shorts created from segments of longer videos. You will be given the following information for each segment: long_video_title: The title of the original, longer video. Use this for context regarding the overall topic and potential audience. segment_transcript: The full transcript of the specific video segment chosen for the Short. This is the core content you need to analyze. reason_for_choosing: An explanation of why this particular segment was selected (e.g., \"funny moment,\" \"key insight,\" \"controversial point,\" \"quick tutorial,\" \"emotional peak\"). This highlights the intended hook or value proposition of the Short. Based on these inputs, you MUST generate the following outputs, specifically optimized for the YouTube Shorts algorithm and audience behavior: short_title: Concise: Aim for 50 characters or less if possible, definitely under 70. Hook-driven: Immediately grab attention, create curiosity, or clearly state the value/topic. Use keywords from the transcript and align with the reason_for_choosing. Intriguing: Use power words, questions, or highlight the most compelling aspect of the segment. Avoid generic titles. short_description: Keyword-rich: Include relevant keywords from the transcript and the long_video_title context. Contextual: Briefly explain what the Short is about or expand slightly on the title's hook. Essential Hashtag: MUST include #shorts (ideally at the beginning or end). (Optional but Recommended): Include 1-2 additional relevant hashtags (e.g., #techtips, #funnyfails, #motivation). (Optional): Consider a subtle Call-to-Action (e.g., \"Watch the full video for more! [Link]\"). Keep it brief. short_tags: Relevant: Extract key terms, concepts, names, and topics directly from the segment_transcript and reason_for_choosing. Contextual: Include broader topic tags related to the long_video_title. Platform Specific: Include shorts, youtubeshorts, and potentially shortsvideo. Mix: Use a mix of specific (long-tail) and broader (short-tail) keywords. Aim for 5-15 strong tags. youtube_category_id: Accurate Classification: Based on the segment_transcript and long_video_title context, determine the single MOST suitable YouTube category for this Short. Valid ID: You MUST output only the numerical ID from the official list below. Do not output the category name. Official YouTube Category IDs: 1: Film & Animation 2: Autos & Vehicles 10: Music 15: Pets & Animals 17: Sports 18: Short Movies 19: Travel & Events 20: Gaming 21: Videoblogging 22: People & Blogs 23: Comedy 24: Entertainment 25: News & Politics 26: Howto & Style 27: Education 28: Science & Technology 29: Nonprofits & Activism 30: Movies 31: Anime/Animation 32: Action/Adventure 33: Classics 34: Comedy (Movies Category) 35: Documentary 36: Drama 37: Family 38: Foreign 39: Horror 40: Sci-Fi/Fantasy 41: Thriller 42: Shorts (Specific Category - Use cautiously, often better to categorize by topic) 43: Shows 44: Trailers" 552 | } 553 | ] 554 | } 555 | }, 556 | "id": "24d16867-6e1b-4d40-af62-1cdee2ffc06b", 557 | "name": "generatingMetaData", 558 | "type": "@n8n/n8n-nodes-langchain.chainLlm", 559 | "typeVersion": 1.4, 560 | "position": [ 561 | 2720, 562 | -260 563 | ] 564 | }, 565 | { 566 | "parameters": { 567 | "content": "# responding to the form", 568 | "height": 222.9952395999548, 569 | "width": 1495.6125228631818, 570 | "color": 6 571 | }, 572 | "id": "df5359fd-d72b-4564-87ea-0816cb9daf34", 573 | "name": "Sticky Note2", 574 | "type": "n8n-nodes-base.stickyNote", 575 | "typeVersion": 1, 576 | "position": [ 577 | 1846.3828307362278, 578 | -640 579 | ] 580 | }, 581 | { 582 | "parameters": { 583 | "content": "## Applying the captions and branding to short", 584 | "height": 504.18282833585727, 585 | "width": 544.4186093010096, 586 | "color": 3 587 | }, 588 | "id": "ad545b6c-e50c-406f-b610-7c2e2aaade82", 589 | "name": "Sticky Note3", 590 | "type": "n8n-nodes-base.stickyNote", 591 | "typeVersion": 1, 592 | "position": [ 593 | 2012, 594 | -325.83592968447346 595 | ] 596 | }, 597 | { 598 | "parameters": { 599 | "content": "## Meta data generation:\n### short title, short description, short tags, short category", 600 | "height": 554.2648153283677, 601 | "width": 718.5667680472393, 602 | "color": 2 603 | }, 604 | "id": "6788624e-ae97-426a-b89b-4f657bf8aa4c", 605 | "name": "Sticky Note4", 606 | "type": "n8n-nodes-base.stickyNote", 607 | "typeVersion": 1, 608 | "position": [ 609 | 2580, 610 | -380 611 | ] 612 | }, 613 | { 614 | "parameters": { 615 | "assignments": { 616 | "assignments": [ 617 | { 618 | "id": "7d55be19-6c6a-450e-93c8-19ec8726e59d", 619 | "name": "shortID", 620 | "value": "={{ $json.id }}", 621 | "type": "string" 622 | }, 623 | { 624 | "id": "9d069756-0b7b-47f6-9d0e-7db5ed72753a", 625 | "name": "shortTile", 626 | "value": "={{ $json.snippet.title }}", 627 | "type": "string" 628 | }, 629 | { 630 | "id": "815fe847-f167-488f-bf68-49b13b6abb01", 631 | "name": "publishAt", 632 | "value": "={{ $json.status.publishAt }}", 633 | "type": "string" 634 | }, 635 | { 636 | "id": "1a265a1e-891d-4f3c-942f-df2f70700fef", 637 | "name": "currentStatus", 638 | "value": "={{ $json.status.privacyStatus }}", 639 | "type": "string" 640 | }, 641 | { 642 | "id": "f07c27a9-d268-4ca9-b769-0e11eca8a12f", 643 | "name": "youtubeChannel", 644 | "value": "={{ $json.snippet.channelId }}", 645 | "type": "string" 646 | } 647 | ] 648 | }, 649 | "options": {} 650 | }, 651 | "id": "5bdbef8e-e442-4c06-ac38-cf89ff8a538a", 652 | "name": "mappingShortResults", 653 | "type": "n8n-nodes-base.set", 654 | "typeVersion": 3.3, 655 | "position": [ 656 | 3000, 657 | 40 658 | ] 659 | }, 660 | { 661 | "parameters": { 662 | "content": "# Looping over shorts", 663 | "height": 634.4666296932535, 664 | "width": 1510.2511614508649 665 | }, 666 | "id": "81746e4c-2092-47da-9f52-95aa4ce8e533", 667 | "name": "Sticky Note5", 668 | "type": "n8n-nodes-base.stickyNote", 669 | "typeVersion": 1, 670 | "position": [ 671 | 1835.437520841403, 672 | -400 673 | ] 674 | }, 675 | { 676 | "parameters": { 677 | "conditions": { 678 | "options": { 679 | "caseSensitive": true, 680 | "leftValue": "", 681 | "typeValidation": "strict" 682 | }, 683 | "conditions": [ 684 | { 685 | "id": "f7b8572e-69f8-417f-ba5f-9635ff9d764e", 686 | "leftValue": "={{ $json.type }}", 687 | "rightValue": "done", 688 | "operator": { 689 | "type": "string", 690 | "operation": "equals", 691 | "name": "filter.operator.equals" 692 | } 693 | } 694 | ], 695 | "combinator": "and" 696 | }, 697 | "options": {} 698 | }, 699 | "id": "7900f052-0e67-40d2-9d9a-bc7f04bd1457", 700 | "name": "iscompleted ?", 701 | "type": "n8n-nodes-base.if", 702 | "typeVersion": 2, 703 | "position": [ 704 | 2360, 705 | -40 706 | ] 707 | }, 708 | { 709 | "parameters": { 710 | "conditions": { 711 | "options": { 712 | "caseSensitive": true, 713 | "leftValue": "", 714 | "typeValidation": "strict" 715 | }, 716 | "conditions": [ 717 | { 718 | "id": "cfa65f82-c434-41f8-8c93-ae0057921c76", 719 | "leftValue": "={{ $json.type }}", 720 | "rightValue": "error", 721 | "operator": { 722 | "type": "string", 723 | "operation": "equals", 724 | "name": "filter.operator.equals" 725 | } 726 | } 727 | ], 728 | "combinator": "and" 729 | }, 730 | "options": {} 731 | }, 732 | "id": "63dea199-6dcb-4ece-9d15-9a8113c4190c", 733 | "name": "isError ?", 734 | "type": "n8n-nodes-base.if", 735 | "typeVersion": 2, 736 | "position": [ 737 | 2200, 738 | -20 739 | ] 740 | } 741 | ], 742 | "pinData": {}, 743 | "connections": { 744 | "Wait": { 745 | "main": [ 746 | [ 747 | { 748 | "node": "get_shorts", 749 | "type": "main", 750 | "index": 0 751 | } 752 | ] 753 | ] 754 | }, 755 | "get_shorts": { 756 | "main": [ 757 | [ 758 | { 759 | "node": "If", 760 | "type": "main", 761 | "index": 0 762 | } 763 | ] 764 | ] 765 | }, 766 | "If": { 767 | "main": [ 768 | [ 769 | { 770 | "node": "Split Out", 771 | "type": "main", 772 | "index": 0 773 | } 774 | ], 775 | [ 776 | { 777 | "node": "Wait", 778 | "type": "main", 779 | "index": 0 780 | } 781 | ] 782 | ] 783 | }, 784 | "Split Out": { 785 | "main": [ 786 | [ 787 | { 788 | "node": "shcedulingCode", 789 | "type": "main", 790 | "index": 0 791 | } 792 | ] 793 | ] 794 | }, 795 | "Wait1": { 796 | "main": [ 797 | [ 798 | { 799 | "node": "getRender", 800 | "type": "main", 801 | "index": 0 802 | } 803 | ] 804 | ] 805 | }, 806 | "getRender": { 807 | "main": [ 808 | [ 809 | { 810 | "node": "isError ?", 811 | "type": "main", 812 | "index": 0 813 | } 814 | ] 815 | ] 816 | }, 817 | "downloadshorts": { 818 | "main": [ 819 | [ 820 | { 821 | "node": "Sendshorttoyoutube", 822 | "type": "main", 823 | "index": 0 824 | } 825 | ] 826 | ] 827 | }, 828 | "setupMetaData": { 829 | "main": [ 830 | [ 831 | { 832 | "node": "downloadshorts", 833 | "type": "main", 834 | "index": 0 835 | } 836 | ] 837 | ] 838 | }, 839 | "n8n Form Trigger": { 840 | "main": [ 841 | [ 842 | { 843 | "node": "preparingField", 844 | "type": "main", 845 | "index": 0 846 | } 847 | ] 848 | ] 849 | }, 850 | "generateShorts": { 851 | "main": [ 852 | [ 853 | { 854 | "node": "Wait", 855 | "type": "main", 856 | "index": 0 857 | } 858 | ] 859 | ] 860 | }, 861 | "Loop Over Items": { 862 | "main": [ 863 | [ 864 | { 865 | "node": "verifyingLoopOutput", 866 | "type": "main", 867 | "index": 0 868 | } 869 | ], 870 | [ 871 | { 872 | "node": "current_item_ref", 873 | "type": "main", 874 | "index": 0 875 | } 876 | ] 877 | ] 878 | }, 879 | "renderShort": { 880 | "main": [ 881 | [ 882 | { 883 | "node": "Wait1", 884 | "type": "main", 885 | "index": 0 886 | } 887 | ] 888 | ] 889 | }, 890 | "Google Gemini Chat Model": { 891 | "ai_languageModel": [ 892 | [ 893 | { 894 | "node": "generatingMetaData", 895 | "type": "ai_languageModel", 896 | "index": 0 897 | }, 898 | { 899 | "node": "Auto-fixing Output Parser", 900 | "type": "ai_languageModel", 901 | "index": 0 902 | } 903 | ] 904 | ] 905 | }, 906 | "Auto-fixing Output Parser": { 907 | "ai_outputParser": [ 908 | [ 909 | { 910 | "node": "generatingMetaData", 911 | "type": "ai_outputParser", 912 | "index": 0 913 | } 914 | ] 915 | ] 916 | }, 917 | "Structured Output Parser": { 918 | "ai_outputParser": [ 919 | [ 920 | { 921 | "node": "Auto-fixing Output Parser", 922 | "type": "ai_outputParser", 923 | "index": 0 924 | } 925 | ] 926 | ] 927 | }, 928 | "current_item_ref": { 929 | "main": [ 930 | [ 931 | { 932 | "node": "renderShort", 933 | "type": "main", 934 | "index": 0 935 | } 936 | ] 937 | ] 938 | }, 939 | "preparingField": { 940 | "main": [ 941 | [ 942 | { 943 | "node": "generateShorts", 944 | "type": "main", 945 | "index": 0 946 | }, 947 | { 948 | "node": "cleaningStyling", 949 | "type": "main", 950 | "index": 0 951 | } 952 | ] 953 | ] 954 | }, 955 | "cleaningStyling": { 956 | "main": [ 957 | [ 958 | { 959 | "node": "Merge", 960 | "type": "main", 961 | "index": 0 962 | } 963 | ] 964 | ] 965 | }, 966 | "shcedulingCode": { 967 | "main": [ 968 | [ 969 | { 970 | "node": "maxShortsnumber", 971 | "type": "main", 972 | "index": 0 973 | } 974 | ] 975 | ] 976 | }, 977 | "Merge": { 978 | "main": [ 979 | [ 980 | { 981 | "node": "Loop Over Items", 982 | "type": "main", 983 | "index": 0 984 | } 985 | ] 986 | ] 987 | }, 988 | "Sendshorttoyoutube": { 989 | "main": [ 990 | [ 991 | { 992 | "node": "mappingShortResults", 993 | "type": "main", 994 | "index": 0 995 | } 996 | ] 997 | ] 998 | }, 999 | "maxShortsnumber": { 1000 | "main": [ 1001 | [ 1002 | { 1003 | "node": "Merge", 1004 | "type": "main", 1005 | "index": 1 1006 | } 1007 | ] 1008 | ] 1009 | }, 1010 | "verifyingLoopOutput": { 1011 | "main": [ 1012 | [ 1013 | { 1014 | "node": "Respond to Webhook1", 1015 | "type": "main", 1016 | "index": 0 1017 | } 1018 | ] 1019 | ] 1020 | }, 1021 | "generatingMetaData": { 1022 | "main": [ 1023 | [ 1024 | { 1025 | "node": "setupMetaData", 1026 | "type": "main", 1027 | "index": 0 1028 | } 1029 | ] 1030 | ] 1031 | }, 1032 | "mappingShortResults": { 1033 | "main": [ 1034 | [ 1035 | { 1036 | "node": "Loop Over Items", 1037 | "type": "main", 1038 | "index": 0 1039 | } 1040 | ] 1041 | ] 1042 | }, 1043 | "iscompleted ?": { 1044 | "main": [ 1045 | [ 1046 | { 1047 | "node": "generatingMetaData", 1048 | "type": "main", 1049 | "index": 0 1050 | } 1051 | ], 1052 | [ 1053 | { 1054 | "node": "Wait1", 1055 | "type": "main", 1056 | "index": 0 1057 | } 1058 | ] 1059 | ] 1060 | }, 1061 | "isError ?": { 1062 | "main": [ 1063 | [ 1064 | { 1065 | "node": "renderShort", 1066 | "type": "main", 1067 | "index": 0 1068 | } 1069 | ], 1070 | [ 1071 | { 1072 | "node": "iscompleted ?", 1073 | "type": "main", 1074 | "index": 0 1075 | } 1076 | ] 1077 | ] 1078 | } 1079 | }, 1080 | "active": false, 1081 | "settings": { 1082 | "executionOrder": "v1" 1083 | }, 1084 | "versionId": "6552de4f-c6f5-43a1-99a3-963551752c78", 1085 | "meta": { 1086 | "templateCredsSetupCompleted": true, 1087 | "instanceId": "" 1088 | }, 1089 | "id": "hGpeZnulV01ifHHy", 1090 | "tags": [] 1091 | } 1092 | --------------------------------------------------------------------------------