Skip to content

Instantly share code, notes, and snippets.

@yuceltoluyag
Created April 19, 2026 19:35
Show Gist options
  • Select an option

  • Save yuceltoluyag/2b08bff182d3b4df944b902ebbfde340 to your computer and use it in GitHub Desktop.

Select an option

Save yuceltoluyag/2b08bff182d3b4df944b902ebbfde340 to your computer and use it in GitHub Desktop.
long video to shorts zero cost Shorts Clip Extractor
services:
baserow:
image: baserow/baserow:1.32.5
container_name: baserow
environment:
- BASEROW_PUBLIC_URL=http://host.docker.internal:86
volumes:
- D:\docker_sources\baserow-data:/baserow/data
ports:
- "86:80"
restart: unless-stopped
n8n:
image: n8nio/n8n
container_name: n8n
volumes:
- D:\docker_sources\n8n-data:/home/node/.n8n
ports:
- "5678:5678"
restart: unless-stopped
minio:
image: quay.io/minio/minio:RELEASE.2025-04-22T22-12-26Z
container_name: miniio
ports:
- "9000:9000"
- "9001:9001"
volumes:
- D:\docker_sources\minio-data:/data
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: password123
command: server /data --console-address ":9001"
kokoro-tts:
image: ghcr.io/remsky/kokoro-fastapi-gpu:v0.2.2
container_name: kokoro-tts
ports:
- "8880:8880"
runtime: nvidia
environment:
NVIDIA_VISIBLE_DEVICES: all
nca-toolkit:
image: stephengpope/no-code-architects-toolkit:latest
container_name: nca-toolkit
ports:
- "8080:8080"
environment:
API_KEY: "thekey"
S3_ENDPOINT_URL: "http://minio:9000"
S3_ACCESS_KEY: "keykey"
S3_SECRET_KEY: "keykey"
S3_BUCKET_NAME: "nca-toolkit"
S3_REGION: "None"
GUNICORN_TIMEOUT: 0
GUNICORN_WORKERS: 2
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
yt-dlp:
image: jauderho/yt-dlp:latest
container_name: yt-dlp
entrypoint: ["tail", "-f", "/dev/null"]
{
"name": "Shorts Clip Extractor (MinIO Source)",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
16,
96
],
"id": "ff8e0d15-9b3b-465d-9a0f-e2b49187400b",
"name": "When clicking 'Execute workflow'"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "src-video",
"name": "source_video_url",
"value": "http://host.docker.internal:9000/raw-videos/microsoft.mp4",
"type": "string"
},
{
"id": "base_url",
"name": "base_url",
"value": "http://host.docker.internal:8080",
"type": "string"
},
{
"id": "api_key",
"name": "api_key",
"value": "thekey",
"type": "string"
},
{
"id": "lm_studio_url",
"name": "lm_studio_url",
"value": "http://host.docker.internal:1234/api/v1/chat",
"type": "string"
},
{
"id": "lm_model",
"name": "lm_model",
"value": "google/gemma-4-e4b:2",
"type": "string"
},
{
"id": "clip_count",
"name": "clip_count",
"value": 3,
"type": "number"
},
{
"id": "clip_duration",
"name": "clip_duration",
"value": 60,
"type": "number"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
320,
96
],
"id": "ffd82f79-0f97-45d7-a939-805b9070c131",
"name": "Set Variables"
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Set Variables').first().json.base_url }}/v1/media/transcribe",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "x-api-key",
"value": "={{ $('Set Variables').first().json.api_key }}"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={ \"media_url\": \"{{ $('Set Variables').first().json.source_video_url }}\", \"language\": \"tr\", \"include_segments\": true, \"word_timestamps\": false, \"response_type\": \"direct\" }",
"options": {
"timeout": 1800000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
800,
96
],
"id": "d2ca2bfe-fd0b-415f-8d59-6c3fc0c8f638",
"name": "Transcribe (Turkish)"
},
{
"parameters": {
"jsCode": "const segments = $('Transcribe (Turkish)').first().json.response.segments;\nconst videoUrl = $('Set Variables').first().json.source_video_url;\n\n// Use all segments directly, no chunking - compact format for large transcripts\nconst transcriptText = segments.map((s, i) => `[${i}]${s.start.toFixed(0)}s: ${s.text}`).join(' ');\n\nconst prompt = `Viral video uzmanısın. Transkripsiyonu analiz et, en viral potansiyelli kısımları seç.\n\nKRITER: Duygusal etki, hikaye, paylaşılabilirlik.\n\nHer seçim için JSON objesi:\n{\"chunk_index\":id,\"start\":saniye,\"end\":saniye(start+60 max),\"viral_score\":1-10,\"reason\":\"kısa\",\"hook\":\"3sn\"}\n\nTRANSCRIPSYON:\n${transcriptText}\n\nSADECE JSON array döndür, başka hiçbir şey yazma.`;\n\n// Create 60s chunks for post-analysis reference\nconst chunkSize = 60;\nconst chunks = [];\nlet currentChunk = { start: null, end: null, texts: [] };\nsegments.forEach(seg => {\n if (currentChunk.start === null) {\n currentChunk.start = seg.start;\n currentChunk.end = seg.end;\n currentChunk.texts.push(seg.text);\n } else if (seg.end - currentChunk.start <= chunkSize) {\n currentChunk.end = seg.end;\n currentChunk.texts.push(seg.text);\n } else {\n chunks.push({ start: currentChunk.start, end: currentChunk.end, text: currentChunk.texts.join(' ').trim() });\n currentChunk = { start: seg.start, end: seg.end, texts: [seg.text] };\n }\n});\nif (currentChunk.texts.length > 0) chunks.push({ start: currentChunk.start, end: currentChunk.end, text: currentChunk.texts.join(' ').trim() });\n\nreturn [{ json: { prompt, chunks, video_url: videoUrl } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1040,
96
],
"id": "3264e21c-506f-47bf-8f60-dbed5579fb98",
"name": "Prepare AI Analysis Prompt"
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Set Variables').first().json.lm_studio_url }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ model: $('Set Variables').first().json.lm_model, system_prompt: 'You are a YouTube viral content expert. Output ONLY valid JSON.', input: $json.prompt, stream: false}) }}",
"options": {
"timeout": 300000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1280,
96
],
"id": "55bdd894-f250-4ac7-8ced-b079aa6ff12f",
"name": "🤖 LM Studio - Viral Clip Analysis"
},
{
"parameters": {
"jsCode": "const lmResponse = $('🤖 LM Studio - Viral Clip Analysis').first().json;\n\n// Extract AI response text from Gemma format\nlet aiResponse = '';\nif (lmResponse.output && Array.isArray(lmResponse.output)) {\n const msgObj = lmResponse.output.find(o => o.type === 'message');\n if (msgObj) aiResponse = msgObj.content;\n}\nif (!aiResponse) {\n aiResponse = lmResponse.message?.content || lmResponse.choices?.[0]?.message?.content || lmResponse.response || lmResponse.content || '';\n}\n\n// Strip markdown code fences if present\naiResponse = aiResponse.replace(/^```(?:json)?\\n?([\\s\\S]*?)\\n?```$/gm, '$1').trim();\n\nconst videoUrl = $('Prepare AI Analysis Prompt').first().json.video_url;\nconst segments = $('Transcribe (Turkish)').first().json.response.segments;\nconst clipCount = $('Set Variables').first().json.clip_count;\nconst clipDuration = $('Set Variables').first().json.clip_duration;\n\nlet scoredClips;\ntry {\n const jsonMatch = aiResponse.match(/\\[[\\s\\S]*\\]/);\n if (jsonMatch) {\n scoredClips = JSON.parse(jsonMatch[0]);\n } else {\n const parsed = JSON.parse(aiResponse);\n scoredClips = parsed.clips || parsed.results || [parsed];\n }\n} catch (e) {\n throw new Error('AI response parsing failed: ' + e.message + '\\n\\nRaw response: ' + aiResponse);\n}\n\nconst topClips = scoredClips\n .sort((a, b) => (b.viral_score || 0) - (a.viral_score || 0))\n .slice(0, clipCount)\n .map(clip => {\n const startTime = clip.start;\n const actualEnd = Math.min(clip.end || startTime + clipDuration, startTime + clipDuration);\n const closestSeg = segments.find(s => Math.abs(s.start - startTime) < 2);\n const textPreview = closestSeg?.text?.substring(0, 100) || 'N/A';\n return {\n start_time: startTime,\n end_time: actualEnd,\n viral_score: clip.viral_score,\n reason: clip.reason,\n hook: clip.hook,\n text_preview: textPreview\n };\n });\n\nreturn [{ json: { clips: topClips, video_url: videoUrl, total_analyzed: segments.length, ai_recommendations: topClips.length } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1520,
96
],
"id": "9c069345-c7c8-46b3-9438-e3f38c9cbb88",
"name": "Parse AI Results & Select Clips"
},
{
"parameters": {
"fieldToSplitOut": "clips",
"options": {}
},
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [
1760,
96
],
"id": "5f74d6a4-0469-402b-86f5-60d5dc4d4b9d",
"name": "Split Clips"
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Set Variables').first().json.base_url }}/v1/ffmpeg/compose",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "x-api-key",
"value": "={{ $('Set Variables').first().json.api_key }}"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ inputs: [{ file_url: $('Set Variables').first().json.source_video_url }], outputs: [{ options: [{ option: '-ss', argument: String($json.start_time) }, { option: '-t', argument: String($json.end_time - $json.start_time) }, { option: '-c:v', argument: 'libx264' }, { option: '-preset', argument: 'fast' }, { option: '-crf', argument: '23' }, { option: '-c:a', argument: 'aac' }] }], id: 'extract-clip-' + $json.start_time.toFixed(0) }) }}",
"options": {
"timeout": 600000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2000,
96
],
"id": "c025a7ae-b234-4ee0-907f-2622e65b535a",
"name": "Extract AI-Selected Clip"
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Set Variables').first().json.base_url }}/v1/ffmpeg/compose",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "x-api-key",
"value": "={{ $('Set Variables').first().json.api_key }}"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"inputs\": [\n {\n \"file_url\": \"{{ $json.response[0].file_url }}\"\n }\n ],\n \"filters\": [\n {\n \"filter\": \"crop=w=ih*9/16:h=ih\"\n }\n ],\n \"outputs\": [\n {\n \"options\": [\n {\n \"option\": \"-c:v\",\n \"argument\": \"libx264\"\n },\n {\n \"option\": \"-preset\",\n \"argument\": \"fast\"\n },\n {\n \"option\": \"-crf\",\n \"argument\": \"23\"\n },\n {\n \"option\": \"-c:a\",\n \"argument\": \"aac\"\n }\n ]\n }\n ],\n \"id\": \"resize-clip-{{ $now.format('x') }}\"\n}",
"options": {
"timeout": 600000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2240,
96
],
"id": "3075182b-f738-45c6-a671-fcb92513e46b",
"name": "Resize to 9:16 (Shorts)"
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Set Variables').first().json.base_url }}/v1/video/caption",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "x-api-key",
"value": "={{ $('Set Variables').first().json.api_key }}"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"video_url\": \"{{ $('Resize to 9:16 (Shorts)').first().json.response[0].file_url }}\",\n \"language\": \"tr\",\n \"settings\": {\n \"line_color\": \"#FFFFFF\",\n \"word_color\": \"#FFD700\",\n \"all_caps\": false,\n \"max_words_per_line\": 3,\n \"font_size\": 50,\n \"bold\": true,\n \"italic\": false,\n \"underline\": false,\n \"strikeout\": false,\n \"outline_width\": 3,\n \"shadow_offset\": 4,\n \"style\": \"highlight\",\n \"font_family\": \"The Bold Font\",\n \"position\": \"bottom_center\"\n },\n \"id\": \"ai-caption-{{ $now.format('x') }}\"\n}\n",
"options": {
"timeout": 600000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2480,
96
],
"id": "6e153e6b-f539-4e3c-828c-54aeb9c65187",
"name": "Add Turkish Captions"
},
{
"parameters": {
"jsCode": "const allClips = $('Split Clips').all();\nconst finalClips = allClips.map((clip, idx) => ({\n clip_number: idx + 1,\n start_time: clip.json.start_time.toFixed(1) + 's',\n end_time: clip.json.end_time.toFixed(1) + 's',\n duration: (clip.json.end_time - clip.json.start_time).toFixed(1) + 's',\n viral_score: clip.json.viral_score,\n ai_reason: clip.json.reason,\n ai_hook: clip.json.hook,\n final_url: clip.json.response\n}));\n\nreturn [{\n json: {\n total_clips: finalClips.length,\n clips: finalClips,\n summary: `🤖 AI ${finalClips.length} viral clip seçti ve işledi`,\n ai_analysis_complete: true\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2720,
96
],
"id": "5f327ec5-4ac0-4b2b-a0a9-ff28f8ff502b",
"name": "Compile Final Results"
},
{
"parameters": {
"content": "## 1️⃣ Configuration",
"height": 300,
"width": 480,
"color": 3
},
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
-16
],
"typeVersion": 1,
"id": "25e042c1-d7ac-418e-8b32-c7a79d48966d",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## 2️⃣ Transcribe & AI Analysis",
"height": 300,
"width": 480,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"position": [
672,
-16
],
"typeVersion": 1,
"id": "d6583361-cd80-482d-adf0-2a1a4e3a5799",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "## 3️⃣ AI Analysis (LM Studio)",
"height": 300,
"width": 720,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"position": [
1152,
-16
],
"typeVersion": 1,
"id": "08eda9fd-4d97-4f80-b15c-83907d19a8b1",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "## 4️⃣ Cut & Resize",
"height": 300,
"width": 480,
"color": 2
},
"type": "n8n-nodes-base.stickyNote",
"position": [
1872,
-16
],
"typeVersion": 1,
"id": "6e57ae8e-4950-42e0-ab48-746982446d8f",
"name": "Sticky Note3"
},
{
"parameters": {
"content": "## 5️⃣ Caption & Export",
"height": 300,
"width": 720,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [
2352,
-16
],
"typeVersion": 1,
"id": "9c74c44f-d3a9-4bf0-afe6-f1955fb341c0",
"name": "Sticky Note4"
}
],
"pinData": {},
"connections": {
"When clicking 'Execute workflow'": {
"main": [
[
{
"node": "Set Variables",
"type": "main",
"index": 0
}
]
]
},
"Set Variables": {
"main": [
[
{
"node": "Transcribe (Turkish)",
"type": "main",
"index": 0
}
]
]
},
"Transcribe (Turkish)": {
"main": [
[
{
"node": "Prepare AI Analysis Prompt",
"type": "main",
"index": 0
}
]
]
},
"Prepare AI Analysis Prompt": {
"main": [
[
{
"node": "🤖 LM Studio - Viral Clip Analysis",
"type": "main",
"index": 0
}
]
]
},
"🤖 LM Studio - Viral Clip Analysis": {
"main": [
[
{
"node": "Parse AI Results & Select Clips",
"type": "main",
"index": 0
}
]
]
},
"Parse AI Results & Select Clips": {
"main": [
[
{
"node": "Split Clips",
"type": "main",
"index": 0
}
]
]
},
"Split Clips": {
"main": [
[
{
"node": "Extract AI-Selected Clip",
"type": "main",
"index": 0
}
]
]
},
"Extract AI-Selected Clip": {
"main": [
[
{
"node": "Resize to 9:16 (Shorts)",
"type": "main",
"index": 0
}
]
]
},
"Resize to 9:16 (Shorts)": {
"main": [
[
{
"node": "Add Turkish Captions",
"type": "main",
"index": 0
}
]
]
},
"Add Turkish Captions": {
"main": [
[
{
"node": "Compile Final Results",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate"
},
"versionId": "71c5ff7a-5398-4ccc-a34e-53de75a3deaa",
"meta": {
"instanceId": "72d749b030d64f8213e78d4219c14df0cf533645d20a7f66d7ed1a0d670713e2"
},
"id": "xBgTD4eDTXe2FUau",
"tags": []
}

🛠️ Toollarımız - Kurulum Rehberi

Bu rehber, tüm araçlarımızı kolayca kurabilmeniz için hazırlanmıştır.

Ön Gereksinim

Başlamadan önce Docker Desktop’u bilgisayarınıza indirip kurun. Tüm araçları Docker üzerinden çalıştıracağız.


✅ Adım 1: Docker Desktop Kurulumu

  1. Bu adresten Docker Desktop’u indirin.
  2. Karşınıza çıkan kurulum adımlarını takip ederek kurulumu tamamlayın.
  3. Docker Desktop’un çalıştığından emin olun.

Kurulum konusunda yardıma ihtiyacınız olursa şu videoyu izleyebilirsiniz:
Docker Desktop Kurulum Videosu


📦 Adım 2: Araçları Docker ile Çalıştırma

Aşağıdaki tüm araçlar Docker container olarak çalışır. Bilgisayarınıza normal program kurmaktan hiçbir farkı yoktur.

🔄 n8n (Automation Tool)

docker run -d --name n8n \
  -p 5678:5678 \
  -e WEBHOOK_URL=http://host.docker.internal:5678 \
  -e N8N_DEFAULT_BINARY_DATA_MODE=filesystem \
  -v C:\Docker\n8n-data:/home/node/.n8n \
  docker.n8n.io/n8nio/n8n

Not: VPS veya farklı bir adresten erişecekseniz WEBHOOK_URL kısmını kendi adresinize göre değiştirin.


💾 MiniIO (Depolama)

Önemli: En son sürümü değil, aşağıdaki stabil sürümü kullanmanızı öneririz.

docker run -p 9000:9000 -p 9001:9001 --name miniio \
  -v C:\Docker\minio-data:/data \
  -e MINIO_ROOT_USER=admin \
  -e MINIO_ROOT_PASSWORD=password123 \
  quay.io/minio/minio:RELEASE.2025-04-22T22-12-26Z server /data --console-address ":9001"

Konsola Erişim:

Sonraki Adımlar:

  1. Digital Yörük kanalındaki MiniIO kurulum videosunu izleyin.
  2. MiniIO arayüzüne giriş yapın.
  3. nca-toolkit adında bir bucket oluşturun.
  4. Oluşturduğunuz Access Key ve Secret Key değerlerini bir yere kaydedin (NCA Toolkit kurarken gerekecek).

🗣️ Kokoro TTS (Text-to-Speech)

GPU’lu Sistemler için (önerilen):

docker run -d --gpus all -p 8880:8880 --name kokoro-tts \
  ghcr.io/remsky/kokoro-fastapi-gpu:v0.2.2

Sadece CPU için:

docker run -p 8880:8880 --name kokoro-tts-cpu \
  ghcr.io/remsky/kokoro-fastapi-cpu:v0.2.2

📋 Baserow (Veritabanı Arayüzü)

docker run -d --name baserow \
  -e BASEROW_PUBLIC_URL=http://host.docker.internal:85 \
  -v C:\Docker\baserow-data:/baserow/data \
  -p 85:80 -p 443:443 \
  --restart unless-stopped \
  --shm-size=256mb \
  baserow/baserow:1.34.2

🚀 Adım 3: NCA Toolkit Kurulumu

MiniIO hazır ve keylerinizi aldıysanız NCA Toolkit’i kurabilirsiniz:

docker run -d -p 8080:8080 --name nca-toolkit \
  -e API_KEY=thekey \
  -e S3_ENDPOINT_URL=http://host.docker.internal:9000 \
  -e S3_ACCESS_KEY=your_access_key \
  -e S3_SECRET_KEY=your_secret_key \
  -e S3_BUCKET_NAME=nca-toolkit \
  -e S3_REGION=None \
  stephengpope/no-code-architects-toolkit:latest

your_access_key ve your_secret_key değerlerini MiniIO’dan aldığınız bilgilerle değiştirin.


✅ Hazırsınız!

Aşağıdaki adreslerden araçlarınıza erişebilirsiniz:

Service URL
n8n http://host.docker.internal:5678
MiniIO http://host.docker.internal:9001
Kokoro TTS http://host.docker.internal:8880/web
Baserow http://host.docker.internal:85
NCA Toolkit http://host.docker.internal:8080

Not: NCA Toolkit’in görsel bir arayüzü yoktur. GitHub sayfası üzerinden API endpoint’lerini ve açıklamalarını inceleyebilirsiniz.


Ekstra:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment