{
  "name": "YouTube Shorts Otomasyonu - Haftal\u0131k E\u011flenceli Video",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": 1,
              "triggerAtHour": 10
            }
          ]
        }
      },
      "id": "schedule-trigger",
      "name": "Haftal\u0131k Zamanlay\u0131c\u0131",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openAiApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"gpt-4o\",\n  \"response_format\": { \"type\": \"json_object\" },\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"Sen bir YouTube Shorts i\u00e7erik \u00fcreticisisin. T\u00fcrk\u00e7e, e\u011flenceli, viral olabilecek k\u0131sa video scriptleri yaz\u0131yorsun. Her seferinde rastgele bir tema se\u00e7: ilgin\u00e7 bilgiler (fun facts), komedi/abs\u00fcrt humor, motivasyon, bilim, tarih, psikoloji, hayvan d\u00fcnyas\u0131, uzay, teknoloji. Script maksimum 50 saniye okunacak uzunlukta olmal\u0131. Enerjik, samimi ve dikkat \u00e7ekici bir dil kullan. Cevab\u0131n\u0131 JSON format\u0131nda ver.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"Yeni bir YouTube Shorts videosu i\u00e7in i\u00e7erik \u00fcret. JSON format\u0131nda \u015funlar\u0131 ver:\\n- title: T\u00fcrk\u00e7e video ba\u015fl\u0131\u011f\u0131 (dikkat \u00e7ekici, max 60 karakter)\\n- script: T\u00fcrk\u00e7e seslendirme scripti (max 50 saniye okunacak, k\u0131sa c\u00fcmleler)\\n- search_keywords: \u0130ngilizce 3 arama kelimesi (Pexels stock video aramak i\u00e7in, virg\u00fclle ayr\u0131lm\u0131\u015f)\\n- description: T\u00fcrk\u00e7e YouTube a\u00e7\u0131klamas\u0131 (2-3 c\u00fcmle + hashtag'ler)\\n- tags: T\u00fcrk\u00e7e YouTube etiketleri (virg\u00fclle ayr\u0131lm\u0131\u015f, 5-8 adet)\\n- category: Videonun kategorisi (fun_fact, comedy, motivation, science, history, psychology, animals, space, tech)\"\n    }\n  ]\n}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "openai-generate-content",
      "name": "AI Konu ve Script \u00dcret",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        300,
        0
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "mode": "raw",
        "jsonOutput": "={{ JSON.parse($json.choices[0].message.content) }}",
        "options": {}
      },
      "id": "parse-ai-response",
      "name": "AI Cevab\u0131n\u0131 Parse Et",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        520,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/audio/speech",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openAiApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"tts-1\",\n  \"input\": \"{{ $json.script }}\",\n  \"voice\": \"nova\",\n  \"response_format\": \"mp3\",\n  \"speed\": 1.05\n}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "audioData"
            }
          },
          "timeout": 60000
        }
      },
      "id": "openai-tts",
      "name": "Seslendirme (TTS)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        740,
        0
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "command": "=mkdir -p /tmp/youtube-shorts && echo '{{ $('AI Konu ve Script \u00dcret').item.json.choices[0].message.content }}' > /tmp/youtube-shorts/content.json && echo 'directory_ready'"
      },
      "id": "prepare-directory",
      "name": "Dizin Haz\u0131rla",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        740,
        250
      ]
    },
    {
      "parameters": {
        "operation": "toFile",
        "fileName": "voiceover.mp3",
        "options": {}
      },
      "id": "save-audio",
      "name": "Ses Dosyas\u0131 Kaydet",
      "type": "n8n-nodes-base.convertToFile",
      "typeVersion": 1.1,
      "position": [
        960,
        0
      ]
    },
    {
      "parameters": {
        "command": "=cat /tmp/youtube-shorts/content.json | python3 -c \"\nimport json, sys\ndata = json.load(sys.stdin)\nkw = data.get('search_keywords', 'nature,landscape,abstract')\nprint(kw.split(',')[0].strip())\n\""
      },
      "id": "extract-keyword",
      "name": "Arama Kelimesi \u00c7\u0131kar",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        960,
        250
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://api.pexels.com/videos/search?query={{ encodeURIComponent($('extract-keyword').item.json.stdout.trim()) }}&orientation=portrait&size=medium&per_page=3",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "timeout": 30000
        }
      },
      "id": "pexels-search",
      "name": "Pexels Video Ara",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1180,
        250
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "command": "=# Pexels videolar\u0131n\u0131 indir\nmkdir -p /tmp/youtube-shorts/clips\n\n# JSON'dan video URL'lerini \u00e7\u0131kar ve indir\nVIDEOS='{{ JSON.stringify($json.videos) }}'\n\necho \"$VIDEOS\" | python3 -c \"\nimport json, sys, subprocess\n\nvideos = json.load(sys.stdin)\ndownloaded = 0\n\nfor i, video in enumerate(videos[:3]):\n    # En uygun HD portrait video dosyas\u0131n\u0131 bul\n    files = video.get('video_files', [])\n    best = None\n    for f in files:\n        w = f.get('width', 0)\n        h = f.get('height', 0)\n        if h > w and h <= 1920:  # Portrait ve max 1080p\n            if best is None or h > best.get('height', 0):\n                best = f\n    if best is None and files:\n        best = files[0]\n    if best:\n        url = best['link']\n        subprocess.run(['curl', '-sL', '-o', f'/tmp/youtube-shorts/clips/clip_{i}.mp4', url], timeout=120)\n        downloaded += 1\n        print(f'Downloaded clip_{i}.mp4')\n\nprint(f'Total downloaded: {downloaded}')\n\" 2>&1",
        "options": {
          "timeout": 180000
        }
      },
      "id": "download-videos",
      "name": "Videolar\u0131 \u0130ndir",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        1400,
        250
      ]
    },
    {
      "parameters": {
        "command": "=# Ses dosyas\u0131n\u0131 kaydet\ncp /tmp/youtube-shorts/voiceover.mp3 /tmp/youtube-shorts/audio.mp3 2>/dev/null || true\n\n# Ses s\u00fcresini al\nAUDIO_DURATION=$(ffprobe -v error -show_entries format=duration -of csv=p=0 /tmp/youtube-shorts/audio.mp3 2>/dev/null | cut -d. -f1)\nif [ -z \"$AUDIO_DURATION\" ] || [ \"$AUDIO_DURATION\" -eq 0 ]; then\n  AUDIO_DURATION=30\nfi\n\n# Max 59 saniye (Shorts limiti)\nif [ \"$AUDIO_DURATION\" -gt 59 ]; then\n  AUDIO_DURATION=59\nfi\n\necho \"Audio duration: ${AUDIO_DURATION}s\"\n\n# Mevcut klipleri listele\nCLIPS=$(ls /tmp/youtube-shorts/clips/clip_*.mp4 2>/dev/null | sort)\nCLIP_COUNT=$(echo \"$CLIPS\" | wc -l)\n\nif [ \"$CLIP_COUNT\" -eq 0 ]; then\n  echo 'ERROR: No clips found'\n  exit 1\nfi\n\necho \"Found $CLIP_COUNT clips\"\n\n# Her klibi 9:16 format\u0131na \u00e7evir ve s\u00fcreyi e\u015fit b\u00f6l\nPER_CLIP_DURATION=$((AUDIO_DURATION / CLIP_COUNT))\nIDX=0\nfor CLIP in $CLIPS; do\n  ffmpeg -y -i \"$CLIP\" \\\n    -vf \"scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,setsar=1\" \\\n    -t $PER_CLIP_DURATION \\\n    -an \\\n    -c:v libx264 -preset ultrafast -crf 23 \\\n    \"/tmp/youtube-shorts/clips/processed_${IDX}.mp4\" 2>/dev/null\n  IDX=$((IDX + 1))\ndone\n\necho \"Processed $IDX clips\"\n\n# Klipleri birle\u015ftirmek i\u00e7in concat dosyas\u0131 olu\u015ftur\n> /tmp/youtube-shorts/clips/concat.txt\nfor i in $(seq 0 $((IDX - 1))); do\n  echo \"file 'processed_${i}.mp4'\" >> /tmp/youtube-shorts/clips/concat.txt\ndone\n\n# Klipleri birle\u015ftir\nffmpeg -y -f concat -safe 0 -i /tmp/youtube-shorts/clips/concat.txt \\\n  -c:v libx264 -preset ultrafast -crf 23 \\\n  /tmp/youtube-shorts/video_only.mp4 2>/dev/null\n\n# Ba\u015fl\u0131k metnini oku\nTITLE=$(python3 -c \"\nimport json\nwith open('/tmp/youtube-shorts/content.json') as f:\n    data = json.load(f)\nprint(data.get('title', 'E\u011flenceli Video'))\n\" 2>/dev/null || echo 'E\u011flenceli Video')\n\n# Video + ses + ba\u015fl\u0131k overlay birle\u015ftir\nffmpeg -y \\\n  -i /tmp/youtube-shorts/video_only.mp4 \\\n  -i /tmp/youtube-shorts/audio.mp3 \\\n  -filter_complex \"\n    [0:v]drawtext=text='${TITLE}':fontsize=42:fontcolor=white:borderw=3:bordercolor=black:x=(w-text_w)/2:y=h*0.08:enable='between(t,0,4)'[v]\n  \" \\\n  -map \"[v]\" -map 1:a \\\n  -c:v libx264 -preset medium -crf 20 \\\n  -c:a aac -b:a 192k \\\n  -t $AUDIO_DURATION \\\n  -shortest \\\n  -movflags +faststart \\\n  /tmp/youtube-shorts/final_short.mp4 2>/dev/null\n\n# Sonu\u00e7 kontrol\u00fc\nif [ -f /tmp/youtube-shorts/final_short.mp4 ]; then\n  FILESIZE=$(du -h /tmp/youtube-shorts/final_short.mp4 | cut -f1)\n  DURATION=$(ffprobe -v error -show_entries format=duration -of csv=p=0 /tmp/youtube-shorts/final_short.mp4 2>/dev/null)\n  echo \"SUCCESS: Final video created - Size: $FILESIZE, Duration: ${DURATION}s\"\n  echo \"/tmp/youtube-shorts/final_short.mp4\"\nelse\n  echo 'ERROR: Failed to create final video'\n  exit 1\nfi",
        "options": {
          "timeout": 300000
        }
      },
      "id": "ffmpeg-combine",
      "name": "FFmpeg Video Birle\u015ftir",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        1620,
        250
      ]
    },
    {
      "parameters": {
        "command": "cat /tmp/youtube-shorts/final_short.mp4 | base64"
      },
      "id": "read-final-video",
      "name": "Final Videoyu Oku",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        1840,
        250
      ]
    },
    {
      "parameters": {
        "operation": "toBinary",
        "sourceProperty": "stdout",
        "options": {
          "fileName": "youtube_short.mp4",
          "mimeType": "video/mp4",
          "encoding": "base64"
        }
      },
      "id": "convert-to-binary",
      "name": "Binary'ye \u00c7evir",
      "type": "n8n-nodes-base.convertToFile",
      "typeVersion": 1.1,
      "position": [
        2060,
        250
      ]
    },
    {
      "parameters": {
        "resource": "video",
        "operation": "upload",
        "title": "={{ $('parse-ai-response').item.json.title }} #Shorts",
        "categoryId": "22",
        "options": {
          "description": "={{ $('parse-ai-response').item.json.description }}\n\n#Shorts #YouTube #E\u011flence #T\u00fcrk\u00e7e",
          "tags": "={{ $('parse-ai-response').item.json.tags }}",
          "privacyStatus": "public",
          "defaultLanguage": "tr"
        }
      },
      "id": "youtube-upload",
      "name": "YouTube'a Y\u00fckle",
      "type": "n8n-nodes-base.youTube",
      "typeVersion": 1,
      "position": [
        2280,
        250
      ],
      "credentials": {
        "youTubeOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "command": "rm -rf /tmp/youtube-shorts && echo 'Cleanup complete'"
      },
      "id": "cleanup",
      "name": "Temizlik",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        2500,
        250
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "status",
              "name": "status",
              "value": "=Video ba\u015far\u0131yla y\u00fcklendi! Ba\u015fl\u0131k: {{ $('parse-ai-response').item.json.title }}",
              "type": "string"
            },
            {
              "id": "uploaded_at",
              "name": "uploaded_at",
              "value": "={{ $now.toISO() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "final-status",
      "name": "Durum Bilgisi",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2720,
        250
      ]
    }
  ],
  "connections": {
    "Haftal\u0131k Zamanlay\u0131c\u0131": {
      "main": [
        [
          {
            "node": "AI Konu ve Script \u00dcret",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Konu ve Script \u00dcret": {
      "main": [
        [
          {
            "node": "AI Cevab\u0131n\u0131 Parse Et",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Cevab\u0131n\u0131 Parse Et": {
      "main": [
        [
          {
            "node": "Seslendirme (TTS)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Dizin Haz\u0131rla",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Seslendirme (TTS)": {
      "main": [
        [
          {
            "node": "Ses Dosyas\u0131 Kaydet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ses Dosyas\u0131 Kaydet": {
      "main": [
        [
          {
            "node": "FFmpeg Video Birle\u015ftir",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dizin Haz\u0131rla": {
      "main": [
        [
          {
            "node": "Arama Kelimesi \u00c7\u0131kar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Arama Kelimesi \u00c7\u0131kar": {
      "main": [
        [
          {
            "node": "Pexels Video Ara",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pexels Video Ara": {
      "main": [
        [
          {
            "node": "Videolar\u0131 \u0130ndir",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Videolar\u0131 \u0130ndir": {
      "main": [
        [
          {
            "node": "FFmpeg Video Birle\u015ftir",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FFmpeg Video Birle\u015ftir": {
      "main": [
        [
          {
            "node": "Final Videoyu Oku",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Final Videoyu Oku": {
      "main": [
        [
          {
            "node": "Binary'ye \u00c7evir",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Binary'ye \u00c7evir": {
      "main": [
        [
          {
            "node": "YouTube'a Y\u00fckle",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "YouTube'a Y\u00fckle": {
      "main": [
        [
          {
            "node": "Temizlik",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Temizlik": {
      "main": [
        [
          {
            "node": "Durum Bilgisi",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": ""
  },
  "staticData": null,
  "tags": [
    {
      "name": "youtube",
      "createdAt": "2026-03-02T00:00:00.000Z",
      "updatedAt": "2026-03-02T00:00:00.000Z"
    },
    {
      "name": "automation",
      "createdAt": "2026-03-02T00:00:00.000Z",
      "updatedAt": "2026-03-02T00:00:00.000Z"
    },
    {
      "name": "shorts",
      "createdAt": "2026-03-02T00:00:00.000Z",
      "updatedAt": "2026-03-02T00:00:00.000Z"
    }
  ],
  "triggerCount": 1
}