{ "name": "Content Pipeline - BlendedFamilyKitchen", "nodes": [ { "parameters": { "rule": { "interval": [{"field": "minutes", "minutesInterval": 30}] } }, "id": "cp-trigger", "name": "Schedule Trigger", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.2, "position": [-200, 300] }, { "parameters": { "method": "POST", "url": "http://192.168.2.200:5000/webapi/entry.cgi", "sendQuery": true, "queryParameters": { "parameters": [ {"name": "api", "value": "SYNO.API.Auth"}, {"name": "version", "value": "6"}, {"name": "method", "value": "login"}, {"name": "account", "value": "={{$env.NAS_USER}}"}, {"name": "passwd", "value": "={{$env.NAS_PASS}}"}, {"name": "session", "value": "FileStation"}, {"name": "format", "value": "sid"} ] } }, "id": "cp-nas-login", "name": "HTTP - NAS Login", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [40, 300] }, { "parameters": { "url": "http://192.168.2.200:5000/webapi/entry.cgi", "sendQuery": true, "queryParameters": { "parameters": [ {"name": "api", "value": "SYNO.FileStation.List"}, {"name": "version", "value": "2"}, {"name": "method", "value": "list"}, {"name": "folder_path", "value": "/BlendedFamilyKitchen/raw"}, {"name": "_sid", "value": "={{$json.data.sid}}"} ] } }, "id": "cp-poll-nas", "name": "HTTP - Poll NAS for New Files", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [280, 300] }, { "parameters": { "conditions": { "options": {"caseSensitive": true, "leftValue": ""}, "conditions": [ { "id": "cond-files", "leftValue": "={{$json.data.files.length}}", "rightValue": "0", "operator": {"type": "number", "operation": "gt"} } ], "combinator": "and" } }, "id": "cp-if-files", "name": "IF - New Files Found?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [520, 300] }, { "parameters": { "options": {} }, "id": "cp-split-batch", "name": "Split In Batches", "type": "n8n-nodes-base.splitInBatches", "typeVersion": 3, "position": [740, 300] }, { "parameters": { "mode": "manual", "duplicateItem": false, "assignments": { "assignments": [ {"id": "a1", "name": "filename", "value": "={{$json.name}}", "type": "string"}, {"id": "a2", "name": "filepath", "value": "={{$json.path}}", "type": "string"}, {"id": "a3", "name": "filesize_mb", "value": "={{Math.round($json.additional.size / 1048576 * 100) / 100}}", "type": "number"}, {"id": "a4", "name": "created", "value": "={{$json.additional.time.crtime}}", "type": "string"}, {"id": "a5", "name": "nas_sid", "value": "={{$('HTTP - NAS Login').item.json.data.sid}}", "type": "string"} ] } }, "id": "cp-set-meta", "name": "Set - Extract File Metadata", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [960, 300] }, { "parameters": { "url": "http://192.168.2.200:5000/webapi/entry.cgi", "sendQuery": true, "queryParameters": { "parameters": [ {"name": "api", "value": "SYNO.FileStation.Download"}, {"name": "version", "value": "2"}, {"name": "method", "value": "download"}, {"name": "path", "value": "={{$json.filepath}}"}, {"name": "_sid", "value": "={{$json.nas_sid}}"} ] }, "options": {"timeout": 300000} }, "id": "cp-download", "name": "HTTP - Download Raw Video", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1200, 300] }, { "parameters": { "method": "POST", "url": "http://localhost:8080/api/ffmpeg/extract-audio", "sendBody": true, "bodyParameters": { "parameters": [ {"name": "input_file", "value": "={{$json.filepath}}"}, {"name": "output_format", "value": "wav"}, {"name": "sample_rate", "value": "16000"} ] }, "options": {"timeout": 120000} }, "id": "cp-extract-audio", "name": "HTTP - Extract Audio (FFmpeg)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1440, 300] }, { "parameters": { "method": "POST", "url": "http://localhost:9000/asr", "sendQuery": true, "queryParameters": { "parameters": [ {"name": "output", "value": "srt"}, {"name": "language", "value": "en"}, {"name": "word_timestamps", "value": "true"} ] }, "sendBody": true, "bodyParameters": { "parameters": [ {"name": "audio_file", "value": "={{$json.audio_path}}"} ] }, "options": {"timeout": 300000} }, "id": "cp-whisper", "name": "HTTP - Transcribe Audio (Whisper)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1680, 300] }, { "parameters": { "method": "POST", "url": "https://api.anthropic.com/v1/messages", "sendHeaders": true, "headerParameters": { "parameters": [ {"name": "x-api-key", "value": "={{$env.CLAUDE_API_KEY}}"}, {"name": "anthropic-version", "value": "2023-06-01"}, {"name": "content-type", "value": "application/json"} ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={\"model\":\"claude-sonnet-4-20250514\",\"max_tokens\":1024,\"system\":\"You are a TikTok content strategist for BlendedFamilyKitchen, a blended family cooking channel. Generate engaging hooks, captions, and hashtags for cooking videos.\",\"messages\":[{\"role\":\"user\",\"content\":\"Based on this video transcript, generate:\\n1. Three hook options (short, punchy, first 3 seconds)\\n2. A TikTok caption (under 150 chars, engaging, with CTA)\\n3. 10 relevant hashtags\\n4. Best posting time suggestion\\n\\nTranscript:\\n\" + $json.transcript}]}", "options": {"timeout": 60000} }, "id": "cp-ai-hooks", "name": "HTTP - Generate Hooks & Caption (AI)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1920, 300] }, { "parameters": { "method": "POST", "url": "http://localhost:8080/api/ffmpeg/normalize-video", "sendBody": true, "bodyParameters": { "parameters": [ {"name": "input_file", "value": "={{$json.video_path}}"}, {"name": "target_resolution", "value": "1080x1920"}, {"name": "aspect_ratio", "value": "9:16"}, {"name": "color_correction", "value": "brightness=0.06:contrast=1.1:saturation=1.2"}, {"name": "audio_normalize", "value": "true"}, {"name": "codec", "value": "h264"}, {"name": "bitrate", "value": "6M"} ] }, "options": {"timeout": 300000} }, "id": "cp-normalize", "name": "HTTP - Normalize Video (FFmpeg)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [2160, 300] }, { "parameters": { "method": "POST", "url": "http://localhost:8080/api/ffmpeg/burn-captions", "sendBody": true, "bodyParameters": { "parameters": [ {"name": "input_file", "value": "={{$json.normalized_video_path}}"}, {"name": "srt_file", "value": "={{$json.srt_path}}"}, {"name": "style", "value": "FontSize=24,PrimaryColour=&HFFFFFF,OutlineColour=&H000000,BorderStyle=3,Outline=2,Alignment=2"}, {"name": "position", "value": "bottom-third"} ] }, "options": {"timeout": 300000} }, "id": "cp-captions", "name": "HTTP - Burn Captions (FFmpeg)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [2400, 300] }, { "parameters": { "method": "POST", "url": "http://localhost:8080/api/ffmpeg/mix-audio", "sendBody": true, "bodyParameters": { "parameters": [ {"name": "input_file", "value": "={{$json.captioned_video_path}}"}, {"name": "music_file", "value": "={{$json.selected_music_track}}"}, {"name": "music_volume", "value": "-20dB"}, {"name": "fade_in", "value": "2"}, {"name": "fade_out", "value": "3"} ] }, "options": {"timeout": 300000} }, "id": "cp-mix-music", "name": "HTTP - Mix Background Music (FFmpeg)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [2640, 300] }, { "parameters": { "method": "POST", "url": "http://localhost:8080/api/ffmpeg/extract-thumbnail", "sendBody": true, "bodyParameters": { "parameters": [ {"name": "input_file", "value": "={{$json.final_video_path}}"}, {"name": "method", "value": "scene-detection"}, {"name": "output_format", "value": "jpg"}, {"name": "quality", "value": "95"} ] }, "options": {"timeout": 60000} }, "id": "cp-thumbnail", "name": "HTTP - Extract Thumbnail (FFmpeg)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [2880, 300] }, { "parameters": { "method": "POST", "url": "http://192.168.2.200:5000/webapi/entry.cgi", "sendQuery": true, "queryParameters": { "parameters": [ {"name": "api", "value": "SYNO.FileStation.Upload"}, {"name": "version", "value": "2"}, {"name": "method", "value": "upload"}, {"name": "path", "value": "/BlendedFamilyKitchen/processed"}, {"name": "_sid", "value": "={{$json.nas_sid}}"} ] }, "options": {"timeout": 300000} }, "id": "cp-upload-nas", "name": "HTTP - Upload Enhanced Video to NAS", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [3120, 300] }, { "parameters": { "url": "https://apify.com/api/v2/acts/novi~tiktok-music-trend-api/runs/last/dataset/items", "sendQuery": true, "queryParameters": { "parameters": [ {"name": "token", "value": "={{$env.APIFY_API_TOKEN}}"}, {"name": "limit", "value": "5"} ] }, "options": {"timeout": 30000} }, "id": "cp-trending", "name": "HTTP - Scrape Trending Sounds", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [3360, 300] }, { "parameters": { "mode": "manual", "duplicateItem": false, "assignments": { "assignments": [ {"id": "s1", "name": "trending_sounds", "value": "={{$json}}", "type": "string"}, {"id": "s2", "name": "curated_picks", "value": "=[{\"name\":\"Sunny Kitchen Vibes\",\"mood\":\"upbeat-cooking\",\"file\":\"/BlendedFamilyKitchen/music/upbeat/sunny_kitchen.mp3\"},{\"name\":\"Family Dinner Warmth\",\"mood\":\"cozy-family\",\"file\":\"/BlendedFamilyKitchen/music/cozy/family_dinner.mp3\"},{\"name\":\"Kids in the Kitchen\",\"mood\":\"fun-kids\",\"file\":\"/BlendedFamilyKitchen/music/fun/kids_cooking.mp3\"}]", "type": "string"} ] } }, "id": "cp-format-sounds", "name": "Set - Format Sound Suggestions", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [3600, 300] }, { "parameters": { "method": "POST", "url": "https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage", "sendBody": true, "specifyBody": "json", "jsonBody": "={\"chat_id\":\"{{$env.CLOE_CHAT_ID}}\",\"parse_mode\":\"HTML\",\"text\":\"\\ud83c\\udfac New Video Ready: {{$('Set - Extract File Metadata').item.json.filename}}\\n\\n\\ud83d\\udcdd AI-Generated Caption:\\n{{$('HTTP - Generate Hooks & Caption (AI)').item.json.caption}}\\n\\n\\ud83c\\udfa3 Hook Options (reply 1, 2, or 3):\\n1. {{$('HTTP - Generate Hooks & Caption (AI)').item.json.hooks[0]}}\\n2. {{$('HTTP - Generate Hooks & Caption (AI)').item.json.hooks[1]}}\\n3. {{$('HTTP - Generate Hooks & Caption (AI)').item.json.hooks[2]}}\\n\\n\\ud83c\\udfb5 Trending Now:\\n{{$json.trending_sounds_formatted}}\\n\\n\\ud83c\\udf73 Kitchen Picks:\\n{{$json.curated_picks_formatted}}\\n\\n\\u23f0 Suggested Post Time: {{$('HTTP - Generate Hooks & Caption (AI)').item.json.best_time}}\",\"reply_markup\":{\"inline_keyboard\":[[{\"text\":\"\\u2705 Approve\",\"callback_data\":\"approve\"},{\"text\":\"\\u270f\\ufe0f Edit\",\"callback_data\":\"edit\"}],[{\"text\":\"\\u23f0 Schedule\",\"callback_data\":\"schedule\"},{\"text\":\"\\u274c Reject\",\"callback_data\":\"reject\"}]]}}", "options": {"timeout": 30000} }, "id": "cp-notify-cloe", "name": "HTTP - Send Cloe Preview (Telegram)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [3840, 300] }, { "parameters": { "httpMethod": "POST", "path": "cloe-response", "responseMode": "responseNode", "options": {} }, "id": "cp-webhook-cloe", "name": "Webhook - Cloe Response", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "position": [4080, 300], "webhookId": "cloe-response-001" }, { "parameters": { "rules": { "rules": [ {"outputIndex": 0, "value": "approve"}, {"outputIndex": 1, "value": "edit"}, {"outputIndex": 2, "value": "schedule"}, {"outputIndex": 3, "value": "reject"} ] }, "dataType": "string", "value1": "={{$json.body.action}}" }, "id": "cp-switch-decision", "name": "Switch - Cloe Decision", "type": "n8n-nodes-base.switch", "typeVersion": 3.2, "position": [4320, 300] }, { "parameters": { "method": "POST", "url": "https://open.tiktokapis.com/v2/post/publish/content/init/", "sendHeaders": true, "headerParameters": { "parameters": [ {"name": "Authorization", "value": "Bearer {{$env.TIKTOK_ACCESS_TOKEN}}"}, {"name": "Content-Type", "value": "application/json"} ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={\"post_info\":{\"title\":\"{{$('HTTP - Generate Hooks & Caption (AI)').item.json.caption}}\",\"privacy_level\":\"SELF_ONLY\",\"disable_duet\":false,\"disable_stitch\":false,\"disable_comment\":false},\"source_info\":{\"source\":\"PULL_FROM_URL\",\"video_url\":\"{{$('HTTP - Upload Enhanced Video to NAS').item.json.video_url}}\"}}", "options": {"timeout": 60000} }, "id": "cp-tiktok-post", "name": "HTTP - Post to TikTok", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [4620, 100] }, { "parameters": { "method": "POST", "url": "https://open.tiktokapis.com/v2/post/publish/content/init/", "sendHeaders": true, "headerParameters": { "parameters": [ {"name": "Authorization", "value": "Bearer {{$env.TIKTOK_ACCESS_TOKEN}}"}, {"name": "Content-Type", "value": "application/json"} ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={\"post_info\":{\"title\":\"{{$('HTTP - Generate Hooks & Caption (AI)').item.json.caption}}\",\"privacy_level\":\"SELF_ONLY\",\"schedule_time\":\"{{$('HTTP - Generate Hooks & Caption (AI)').item.json.best_time}}\"},\"source_info\":{\"source\":\"PULL_FROM_URL\",\"video_url\":\"{{$('HTTP - Upload Enhanced Video to NAS').item.json.video_url}}\"}}", "options": {"timeout": 60000} }, "id": "cp-tiktok-schedule", "name": "HTTP - Schedule TikTok Post", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [4620, 300] }, { "parameters": { "method": "POST", "url": "http://192.168.2.200:5000/webapi/entry.cgi", "sendQuery": true, "queryParameters": { "parameters": [ {"name": "api", "value": "SYNO.FileStation.CopyMove"}, {"name": "version", "value": "3"}, {"name": "method", "value": "start"}, {"name": "path", "value": "={{$('Set - Extract File Metadata').item.json.filepath}}"}, {"name": "dest_folder_path", "value": "/BlendedFamilyKitchen/archive"}, {"name": "remove_src", "value": "true"}, {"name": "_sid", "value": "={{$('Set - Extract File Metadata').item.json.nas_sid}}"} ] } }, "id": "cp-archive", "name": "HTTP - Archive Original", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [4860, 200] }, { "parameters": { "method": "POST", "url": "https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage", "sendBody": true, "specifyBody": "json", "jsonBody": "={\"chat_id\":\"{{$env.CLOE_CHAT_ID}}\",\"text\":\"\\u274c Video rejected: {{$('Set - Extract File Metadata').item.json.filename}}\\nArchived without posting.\"}", "options": {} }, "id": "cp-reject-notify", "name": "HTTP - Notify Rejection (Telegram)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [4620, 500] }, { "parameters": { "method": "POST", "url": "https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage", "sendBody": true, "specifyBody": "json", "jsonBody": "={\"chat_id\":\"{{$env.CLOE_CHAT_ID}}\",\"text\":\"\\u270f\\ufe0f Please reply with your edited caption for: {{$('Set - Extract File Metadata').item.json.filename}}\\nI'll update and re-send for approval.\"}", "options": {} }, "id": "cp-edit-prompt", "name": "HTTP - Prompt Edit (Telegram)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [4620, 700] }, { "parameters": {}, "id": "cp-error-trigger", "name": "Error Trigger", "type": "n8n-nodes-base.errorTrigger", "typeVersion": 1, "position": [2200, 700] }, { "parameters": { "method": "POST", "url": "https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage", "sendBody": true, "specifyBody": "json", "jsonBody": "={\"chat_id\":\"{{$env.JORDAN_CHAT_ID}}\",\"parse_mode\":\"HTML\",\"text\":\"\\ud83d\\udea8 Content Pipeline Error\\n\\nNode: {{$json.execution.error.node.name}}\\nError: {{$json.execution.error.message}}\\nTime: {{new Date().toLocaleString('en-US', {timeZone: 'America/Denver'})}}\\nWorkflow: Content Pipeline - BlendedFamilyKitchen\"}", "options": {} }, "id": "cp-error-notify", "name": "HTTP - Notify Error (Telegram)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [2440, 700] }, { "parameters": { "content": "\ud83d\udcc2 NAS DROP ZONE \u2014 File Detection\n\nFOLDER STRUCTURE ON NAS (192.168.2.200):\n/BlendedFamilyKitchen/\n \u251c\u2500\u2500 raw/ \u2190 Cloe drops videos here\n \u251c\u2500\u2500 processing/ \u2190 Temp during AI processing\n \u251c\u2500\u2500 processed/ \u2190 Enhanced videos ready for review\n \u251c\u2500\u2500 archive/ \u2190 Originals after posting\n \u2514\u2500\u2500 music/ \u2190 Curated background tracks\n\nSYNOLOGY FILESTATION API:\n\u2022 Login: POST /webapi/entry.cgi\n api=SYNO.API.Auth&method=login&account=&passwd=&session=FileStation&format=sid\n\u2022 List: GET /webapi/entry.cgi\n api=SYNO.FileStation.List&method=list&folder_path=/BlendedFamilyKitchen/raw&_sid=\n\u2022 Download: GET /webapi/entry.cgi\n api=SYNO.FileStation.Download&method=download&path=&_sid=\n\u2022 Upload: POST /webapi/entry.cgi\n api=SYNO.FileStation.Upload&method=upload&path=&_sid=\n\nFLOW:\n1. Schedule Trigger fires every 30 min\n2. Login to NAS \u2192 get session ID\n3. Poll /raw folder for new files\n4. IF files found \u2192 Split In Batches to process each\n5. Extract metadata (name, path, size, created time)\n\nINFRA TODO:\n\u25a1 Create NAS API user with limited FileStation permissions\n\u25a1 Create folder structure on NAS\n\u25a1 Set n8n env vars: NAS_USER, NAS_PASS\n\u25a1 Test with a sample .mp4 drop", "height": 620, "width": 560, "color": 4 }, "id": "cp-sticky-nas", "name": "Sticky - NAS Drop Zone", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [-220, -80] }, { "parameters": { "content": "\ud83e\udd16 AI PROCESSING PIPELINE\n\nAll processing runs on Docker services (CT 113 or VM).\nEach step is a separate API call for modularity and debugging.\n\nSERVICES NEEDED:\n\u2022 FFmpeg API \u2014 HTTP wrapper around FFmpeg\n - Docker: jrottenberg/ffmpeg or custom FastAPI wrapper\n - Endpoint: http://:8080/api/ffmpeg/\n - Actions: extract-audio, normalize-video, burn-captions, mix-audio, extract-thumbnail\n\n\u2022 Whisper \u2014 OpenAI Whisper for transcription\n - Docker: onerahmet/openai-whisper-asr-webservice\n - Endpoint: POST http://:9000/asr?output=srt\n - Returns: .srt subtitle file + raw transcript\n\n\u2022 Claude API \u2014 Hook generation and caption writing\n - POST to api.anthropic.com/v1/messages\n - System: TikTok content strategist for BlendedFamilyKitchen\n - Returns: 3 hook options, caption, 10 hashtags, best posting time\n\nPROCESSING CHAIN:\n1. Download raw video from NAS\n2. Extract audio \u2192 WAV (16kHz for Whisper)\n3. Transcribe \u2192 .srt captions + raw text\n4. AI generates hooks, caption, hashtags from transcript\n5. Normalize video \u2192 9:16, 1080x1920, warm color correction\n6. Burn captions \u2192 White bold, black outline, bottom-third\n7. Mix background music \u2192 curated track at -20dB, fade in/out\n8. Extract thumbnail \u2192 best frame via scene detection\n9. Upload enhanced video to NAS /processed folder\n\nFFMPEG SETTINGS:\n\u2022 Color: brightness=0.06, contrast=1.1, saturation=1.2\n\u2022 Codec: H.264, 6Mbps bitrate\n\u2022 Captions: FontSize=24, white with black outline\n\u2022 Music: -20dB volume, 2s fade-in, 3s fade-out\n\nINFRA TODO:\n\u25a1 Deploy FFmpeg API container on CT 113\n\u25a1 Deploy Whisper container (CPU fine for <5min videos)\n\u25a1 Set n8n env var: CLAUDE_API_KEY\n\u25a1 Create Claude prompt template\n\u25a1 Test full chain with sample 60-second video", "height": 780, "width": 560, "color": 6 }, "id": "cp-sticky-ai", "name": "Sticky - AI Processing Pipeline", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [1180, -120] }, { "parameters": { "content": "\ud83c\udfb5 TRENDING SOUNDS & MUSIC STRATEGY\n\nTWO SOURCES OF AUDIO SUGGESTIONS:\n\n1. TRENDING SOUNDS (scraped per-run):\n \u2022 Apify TikTok Music Trend API or similar scraper\n \u2022 GET top 5 currently trending sounds\n \u2022 Filter for family-friendly content\n \u2022 Presented as suggestions \u2014 Cloe picks if appropriate\n \u2022 She applies chosen sound in TikTok app before posting\n\n2. CURATED KITCHEN LIBRARY (pre-loaded on NAS):\n \u2022 10-20 royalty-free tracks in /BlendedFamilyKitchen/music/\n \u2022 Categories: upbeat-cooking, cozy-family, fun-kids, chill-prep\n \u2022 One auto-mixed at low volume as background\n \u2022 Cloe can swap or remove via her Telegram response\n\nMUSIC LIBRARY:\n/BlendedFamilyKitchen/music/\n \u251c\u2500\u2500 upbeat/ \u2190 energetic cooking montages\n \u251c\u2500\u2500 cozy/ \u2190 family dinner, slow-cook content\n \u251c\u2500\u2500 fun/ \u2190 kids helping, bloopers, reactions\n \u2514\u2500\u2500 chill/ \u2190 meal prep, quiet kitchen moments\n\nKEY PRINCIPLE:\nFor cooking content, sizzle and narration ARE the primary audio.\nBackground music is subtle enhancement, not the focus.\nTrending sounds work better for reaction/family-moment content.\nCloe maintains full creative control over final audio choice.\n\nTODO:\n\u25a1 Source 10-20 royalty-free tracks (Pixabay, Epidemic Sound free tier)\n\u25a1 Upload to NAS music folders\n\u25a1 Set up Apify actor or alt trending sounds scraper\n\u25a1 Set n8n env var: APIFY_API_TOKEN\n\u25a1 Build category-matching logic (video mood \u2192 music mood)", "height": 660, "width": 560, "color": 3 }, "id": "cp-sticky-music", "name": "Sticky - Trending Sounds & Music", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [3340, -100] }, { "parameters": { "content": "\ud83d\udcf1 CLOE APPROVAL FLOW \u2014 Creative Control\n\nCloe receives a Telegram message for EVERY processed video:\n\nMESSAGE FORMAT:\n\ud83c\udfac New Video Ready: [filename]\n\n\ud83d\udcdd AI-Generated Caption:\n[caption text with hashtags]\n\n\ud83c\udfa3 Hook Options (reply 1, 2, or 3):\n1. \"[hook option 1]\"\n2. \"[hook option 2]\"\n3. \"[hook option 3]\"\n\n\ud83c\udfb5 Trending Now:\n \ud83d\udd25 [sound 1] \u2014 [artist]\n \ud83d\udd25 [sound 2] \u2014 [artist]\n \ud83d\udd25 [sound 3] \u2014 [artist]\n\n\ud83c\udf73 Kitchen Picks:\n \ud83c\udf73 [curated 1] \u2014 [mood]\n \ud83c\udf73 [curated 2] \u2014 [mood]\n \ud83c\udf73 [curated 3] \u2014 [mood]\n\n\u23f0 Suggested Post Time: [optimal time]\n\nINLINE KEYBOARD:\n[ \u2705 Approve ] [ \u270f\ufe0f Edit ]\n[ \u23f0 Schedule ] [ \u274c Reject ]\n\nRESPONSE ROUTING:\n\u2022 Approve \u2192 Post immediately to TikTok with AI caption\n\u2022 Edit \u2192 Bot asks for modified caption, then re-approve\n\u2022 Schedule \u2192 Post at AI-suggested optimal time\n\u2022 Reject \u2192 Archive without posting, notify Jordan\n\nWEBHOOK:\nCloe's button press triggers callback to:\nhttp://192.168.2.113:5678/webhook/cloe-response\nPayload: {action, video_id, hook_choice, sound_choice}\n\nSETUP TODO:\n\u25a1 Set n8n env vars: TELEGRAM_BOT_TOKEN, CLOE_CHAT_ID, JORDAN_CHAT_ID\n\u25a1 Configure Telegram bot webhook for callbacks\n\u25a1 Test approval flow with a mock video", "height": 740, "width": 560, "color": 2 }, "id": "cp-sticky-approval", "name": "Sticky - Cloe Approval Flow", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [3820, -120] }, { "parameters": { "content": "\u26a0\ufe0f ERROR HANDLING\n\nSTRATEGY:\n\u2022 Error Trigger catches ANY node failure in the workflow\n\u2022 Sends details to Jordan via Telegram (not Cloe \u2014 keep her flow clean)\n\u2022 Includes: failed node name, error message, timestamp\n\nNOTIFICATION FORMAT:\n\ud83d\udea8 Content Pipeline Error\nNode: [failed_node]\nError: [message]\nTime: [timestamp MST]\nWorkflow: Content Pipeline - BlendedFamilyKitchen\n\nRETRY LOGIC (future enhancement):\n\u2022 FFmpeg failures \u2192 retry once with default/safe settings\n\u2022 NAS connection failures \u2192 retry 3x with 30s backoff\n\u2022 Whisper timeout \u2192 retry with smaller chunk size\n\u2022 API failures (TikTok, Claude) \u2192 hold for manual retry\n\nSETUP TODO:\n\u25a1 Set n8n env var: JORDAN_CHAT_ID\n\u25a1 Test error trigger with intentional failure\n\u25a1 Add retry nodes in future iteration", "height": 420, "width": 480, "color": 1 }, "id": "cp-sticky-error", "name": "Sticky - Error Handling", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [2180, 620] } ], "connections": { "Schedule Trigger": { "main": [[{"node": "HTTP - NAS Login", "type": "main", "index": 0}]] }, "HTTP - NAS Login": { "main": [[{"node": "HTTP - Poll NAS for New Files", "type": "main", "index": 0}]] }, "HTTP - Poll NAS for New Files": { "main": [[{"node": "IF - New Files Found?", "type": "main", "index": 0}]] }, "IF - New Files Found?": { "main": [ [{"node": "Split In Batches", "type": "main", "index": 0}], [] ] }, "Split In Batches": { "main": [ [{"node": "Set - Extract File Metadata", "type": "main", "index": 0}], [] ] }, "Set - Extract File Metadata": { "main": [[{"node": "HTTP - Download Raw Video", "type": "main", "index": 0}]] }, "HTTP - Download Raw Video": { "main": [[{"node": "HTTP - Extract Audio (FFmpeg)", "type": "main", "index": 0}]] }, "HTTP - Extract Audio (FFmpeg)": { "main": [[{"node": "HTTP - Transcribe Audio (Whisper)", "type": "main", "index": 0}]] }, "HTTP - Transcribe Audio (Whisper)": { "main": [[{"node": "HTTP - Generate Hooks & Caption (AI)", "type": "main", "index": 0}]] }, "HTTP - Generate Hooks & Caption (AI)": { "main": [[{"node": "HTTP - Normalize Video (FFmpeg)", "type": "main", "index": 0}]] }, "HTTP - Normalize Video (FFmpeg)": { "main": [[{"node": "HTTP - Burn Captions (FFmpeg)", "type": "main", "index": 0}]] }, "HTTP - Burn Captions (FFmpeg)": { "main": [[{"node": "HTTP - Mix Background Music (FFmpeg)", "type": "main", "index": 0}]] }, "HTTP - Mix Background Music (FFmpeg)": { "main": [[{"node": "HTTP - Extract Thumbnail (FFmpeg)", "type": "main", "index": 0}]] }, "HTTP - Extract Thumbnail (FFmpeg)": { "main": [[{"node": "HTTP - Upload Enhanced Video to NAS", "type": "main", "index": 0}]] }, "HTTP - Upload Enhanced Video to NAS": { "main": [[{"node": "HTTP - Scrape Trending Sounds", "type": "main", "index": 0}]] }, "HTTP - Scrape Trending Sounds": { "main": [[{"node": "Set - Format Sound Suggestions", "type": "main", "index": 0}]] }, "Set - Format Sound Suggestions": { "main": [[{"node": "HTTP - Send Cloe Preview (Telegram)", "type": "main", "index": 0}]] }, "HTTP - Send Cloe Preview (Telegram)": { "main": [[{"node": "Webhook - Cloe Response", "type": "main", "index": 0}]] }, "Webhook - Cloe Response": { "main": [[{"node": "Switch - Cloe Decision", "type": "main", "index": 0}]] }, "Switch - Cloe Decision": { "main": [ [{"node": "HTTP - Post to TikTok", "type": "main", "index": 0}], [{"node": "HTTP - Prompt Edit (Telegram)", "type": "main", "index": 0}], [{"node": "HTTP - Schedule TikTok Post", "type": "main", "index": 0}], [{"node": "HTTP - Notify Rejection (Telegram)", "type": "main", "index": 0}] ] }, "HTTP - Post to TikTok": { "main": [[{"node": "HTTP - Archive Original", "type": "main", "index": 0}]] }, "HTTP - Schedule TikTok Post": { "main": [[{"node": "HTTP - Archive Original", "type": "main", "index": 0}]] }, "Error Trigger": { "main": [[{"node": "HTTP - Notify Error (Telegram)", "type": "main", "index": 0}]] } }, "settings": { "executionOrder": "v1" } }