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.

Revisions

  1. yuceltoluyag created this gist Apr 19, 2026.
    68 changes: 68 additions & 0 deletions docker-compose.yml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,68 @@
    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"]
    477 changes: 477 additions & 0 deletions n8n.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,477 @@
    {
    "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": []
    }
    137 changes: 137 additions & 0 deletions youtubavizardai.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,137 @@
    🛠️ **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](https://www.docker.com/products/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](https://www.docker.com/products/docker-desktop/) 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](https://yuceltoluyag.github.io/arch-linux-docker-kurulumu/)
    ---

    ### 📦 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)**

    ```bash
    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.
    ```bash
    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:**
    - **Adres:** [http://localhost:9001](http://localhost:9001/)
    - **Username:** `admin`
    - **Password:** `password123`

    **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):**

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

    **Sadece CPU için:**

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

    ---

    #### 📋 **Baserow (Veritabanı Arayüzü)**

    ```bash
    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:

    ```bash
    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](http://host.docker.internal:5678/) |
    | **MiniIO** | [http://host.docker.internal:9001](http://localhost:9001/) |
    | **Kokoro TTS**| [http://host.docker.internal:8880/web](http://host.docker.internal:8880/web) |
    | **Baserow** | [http://host.docker.internal:85](http://host.docker.internal:85/) |
    | **NCA Toolkit**| [http://host.docker.internal:8080](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:**
    - [Arch Linux'ta Ollama ve WebUI Kurulumu](https://yuceltoluyag.github.io/arch-linux-ollama-webui-kurulumu-docker/)