---
name: suno-music-video-production
description: Полный пайплайн генерации музыки через Suno API (+ callback handler) и монтажа видео через VideoFactory на Mac
version: "1.0"
author: Hermes Agent (via Valentin)
tags: [suno, music, video, ffmpeg, callback, veo3, kieai]
---

# Suno Music + Video Production Pipeline

## Общая архитектура

```
Mac (LuckyDirector) → VPS Proxy (:18999) → kie.ai API → Suno генерация
                                                          ↓
Mac ← SCP скачивание ← VPS Callback Handler (:18998) ←── callback (nginx)
     ↓
   ffmpeg монтаж → готовое видео
```

## 1. Suno API через VEO3 Proxy

Прокси на VPS: `http://157.173.122.51:18999` — проксирует запросы к `https://api.kie.ai` с API-ключом.

**ВАЖНО: API только callback-based!** Нет endpoint-ов для polling/status. Без callback URL кредиты сгорают.

### Правильные параметры запроса

```json
{
  "model": "V3_5",
  "customMode": true,
  "prompt": "описание стиля и настроения музыки",
  "instrumental": false,
  "style": "жанры через запятую",
  "customPrompt": "[Verse] текст песни... [Chorus] ...",
  "callBackUrl": "https://n8n.valentinnews.sailor.bar/suno-callback"
}
```

### Модели (от дешёвой к дорогой)
- `V3_5` — самая дешёвая, для тестов
- `V4` / `V4_5` / `V5` / `V5_5` — дороже, качественнее

### Критические правила
1. **callBackUrl обязателен** — без него трек генерится, но результат потерян
2. **Не упоминать имена артистов** (Rammstein, Metallica и т.д.) — Suno блокирует с кодом 400
3. **`customMode: true`** обязателен если используешь `customPrompt`
4. Поле `model` не может быть null — без него вернёт `"model cannot be null"`

## 2. Callback Handler (VPS)

### Файл: `/root/suno_callback_v2.py`, порт 18998

Реализован как **systemd service** для надёжности:

```bash
/etc/systemd/system/suno-callback.service
[Unit]
Description=Suno Music Callback Handler
After=network.target

[Service]
ExecStart=/usr/bin/python3 /root/suno_callback_v2.py
WorkingDirectory=/root/suno_music
Restart=always
User=root
```

Управление:
```bash
systemctl start|stop|restart suno-callback
systemctl status suno-callback
journalctl -u suno-callback -n 20
```

### nginx прокси

Callback URL `https://n8n.valentinnews.sailor.bar/suno-callback` проксируется на `localhost:18998`.

**Известная проблема:** BrokenPipeError при ответе — nginx закрывает соединение раньше чем Python успевает ответить. Фикс: слать `send_response(200)` до скачивания, или игнорировать BrokenPipe в except.

### Структура callback

Callback приходит с такими полями (парсить вложенность `body.data.data[]`):

```json
{
  "body": {
    "data": {
      "data": [{
        "title": "Название трека",
        "audio_url": "",
        "source_stream_audio_url": "https://audiopipe.suno.ai/?item_id=...",
        "stream_audio_url": "https://musicfile.kie.ai/...",
        "prompt": "промпт",
        "tags": "стили"
      }]
    }
  }
}
```

**Важно:** `audio_url` всегда пустой. Реальный URL в `source_stream_audio_url`.

### V2 обработчик

```python
def do_POST(self):
    data = json.loads(body)
    payload = data.get("body", data).get("data", data)
    items = payload.get("data", [])
    if isinstance(items, dict): items = [items]
    for item in items:
        audio_url = (item.get("audio_url") or item.get("audioUrl") or
                    item.get("source_stream_audio_url") or
                    item.get("stream_audio_url") or "")
        if not audio_url: continue
        req = urllib.request.Request(audio_url,
            headers={"User-Agent": "Mozilla/5.0"})
        audio_data = urllib.request.urlopen(req, timeout=300).read()
        with open(f"{title}.mp3", 'wb') as f: f.write(audio_data)
```

## 3. LuckyDirector (бывш. VideoFactory) на Mac

Проект переименован в **Lucky Director**.

### Структура
```
~/Desktop/LuckyDirector/
├── Input/         # .mp4 клипы
├── Work/          # временные seg_*.mp4
├── Music/         # скачанные треки
├── Output/        # готовые видео
├── edit_family.py # основной скрипт (единственный рабочий)
└── video_factory.py
```

### Основной скрипт: edit_family.py

Проверенный рабочий подход:

1. **EDL** — 8 клипов, каждый со start/duration/title/subtitle
2. **render_clip** — ffmpeg:
   - scale + pad до 720x1280 (вертикаль)
   - fade in/out 0.3s
   - drawtext для субтитров (bottom, box=black@0.4)
3. **concat + crossfade** — склейка через concat demuxer
4. **mix_audio** — музыка поверх:
   - `[music]volume=0.35[m];[ambient]volume=0.10[a];amix=inputs=2`
5. Продолжительность: 30 секунд

### Шаблон нарратива
- 0-7с: Море (мотало по волнам)
- 7-15с: Путь/шторм (через штормы к берегу)
- 15-23с: Порт/марина (тёплый причал)
- 23-30с: Закат/финал (семья и друзья)
### Перенос трека с VPS на Mac

```bash
ssh root@157.173.122.51 "cp /root/suno_music/Оригинал.mp3 /root/suno_music/track.mp3"
scp root@157.173.122.51:/root/suno_music/track.mp3 ~/Desktop/LuckyDirector/Music/
```

**Проблема:** имена с кириллицей и пробелами не копируются через scp. Выход — переименовать на сервере перед копированием.

### ffmpeg особенности Mac
- ffmpeg в `~/.local/bin/` (не системный)
- `boxborder_w` параметр недоступен в drawtext (использовать `borderw`)
- write_file tool НЕ работает на Mac — файлы создавай через execute_code() или terminal()

## 4. Типичный workflow

```
1. Написать текст песни
2. POST /api/v1/generate на VPS proxy (модель V3_5 для тестов)
3. Подождать callback (~30-60 сек)
4. Проверить /root/suno_music/ на VPS
5. scp трека на Mac
6. Поменять music_file в edit_family.py
7. Запустить edit_family.py
8. Готовое видео в Output/
```

## 5. Pitfalls

- ❌ callBackUrl пустой или недоступен — кредиты сгорают, трек потерян
- ❌ Упоминание артистов в prompt — 400 error
- ❌ Русские имена файлов — scp падает
- ❌ nginx BrokenPipe — callback handler не отвечает 200, Suno ретраит
- ❌ write_file на Mac не сохраняет файлы
- ❌ Whisper не установлен на Mac/VPS
- ❌ User rejected complex rule-based editing scripts (classic rules, beat sync, smart-select). Keep it SIMPLE: sequential clip concatenation with fades + music overlay. edit_family.py is the only approved approach.
- ❌ VideoFactory → LuckyDirector папка переименована, все скрипты обновлены
