# Fiko Film Stüdyosu — Tasarım Dokümanı

**Tarih:** 2026-05-04
**Hedef:** Profesyonel bir Flutter Windows desktop uygulaması — sadece WanGP + ACE-Step kullanarak film, müzik klip, influencer içeriği, görsel üretimi yapan tek-stop bir film stüdyosu.

## Vizyon

Kullanıcı:
- Bir karakter yaratır (yüz seti + persona prompt + LoRA + ses kimliği)
- Sahne sahne prompt yazar veya bir şarkı yükler
- Hızlı bir sihirbazdan şablon seçer
- Çıktıyı sürekli zincirleme + otomatik yüz tutarlılığı + lipsync ile üretir
- Tüm sahneler ffmpeg ile birleşip son film olarak iner

Tek motor: **WanGP** (Wan, InfiniteTalk, Hunyuan, LTX2, Flux, qwen_image). Müzik üretimi: **ACE-Step**.

## Mimari

```
┌─────────────────────────────────────────────────────────────┐
│  Flutter Windows App (fiko_film_studyo)                     │
│                                                             │
│  UI ──► Riverpod State ──► JobOrchestrator                  │
│                              │                              │
│                              ▼                              │
│                         WanGP Bridge Client (Dart HTTP)     │
│                         ACE-Step Client (Dart HTTP)         │
└──────────────┬──────────────────────────┬───────────────────┘
               │                          │
               ▼                          ▼
   ┌──────────────────────┐    ┌────────────────────────┐
   │ WanGP Bridge (FastAPI)│    │ ACE-Step API (port 8001)│
   │  port 7861             │    │  POST /release_task     │
   │  POST /jobs            │    │  POST /query_result     │
   │  GET  /jobs/:id/events │    │  GET  /v1/audio         │
   │  POST /jobs/:id/cancel │    └────────────────────────┘
   │  GET  /file?path=...   │
   └──────────────┬─────────┘
                  │ in-process
                  ▼
        ┌────────────────────┐
        │ WanGP shared/api.py│
        │  port 7860 Gradio  │
        └────────────────────┘
```

**Kritik karar:** WanGP REST sunmadığı için, WanGP'nin venv'inde küçük bir **FastAPI köprü** (`wangp_bridge.py`) yazıyoruz. Bu köprü:
- WanGP'nin `shared.api.init()` ile session açar (model bir kere yüklenir)
- `/jobs` POST → `session.submit_task()` → SessionJob saklar, job_id döner
- `/jobs/:id/events` SSE → WanGP'nin event akışını JSON satırı olarak yayar
- `/file?path=` → çıktı dosyaları indirir
- `/cancel` → job iptal

## Domain Modeli

### Karakter (aggregate root)
```dart
class Karakter {
  String id;
  String isim, rumuz;
  String? aciklama;
  Cinsiyet cinsiyet;            // kadin/erkek/belirsiz
  String dil;                   // "tr-TR", "en-US"

  // Yüz seti (referans foto)
  List<KarakterReferans> referanslar;  // {tip: yuz/portre/tamGovde, dosyaYolu, bbox?, _id}
  String? birincilYuzDosyaYolu;        // FaceFusion source

  // Prompt mühendisliği
  PromptParcalari prompt;       // sac, goz, yas, ten, vucut, stil, varsayilanKiyafet
  String negativePrompt;
  List<String> stilEtiketleri;  // "cinematic", "studio lighting"

  // Tutarlılık (Karakter Kilidi)
  KarakterKilidi kilit;
  // - sabitSeed: int?
  // - faceSwapAktif: bool, faceSwapBlend: 0..100
  // - faceRestore: gfpgan/codeformer/yok, faceRestoreGuc: 0..100
  // - bestFrameAday: 4|8|16
  // - lora: KarakterLora? { dosyaYolu, agirlik, tetik }

  // Preset'ler
  List<KiyafetPreset> kiyafetler;
  List<SahnePreset> sahneler;
  List<SesKimligi> sesler;       // ACE-Step ref audio + RVC profile

  UretimAyari varsayilanAyar;
  DateTime olusturma, guncelleme;
}
```

### Film & Sahne
```dart
class Film {
  String id, isim;
  String? karakterId;            // ana karakter (opsiyonel)
  String? sarkiDosyaYolu;        // yüklenmiş veya ACE-Step üretilmiş
  String? lirikler;
  AnalizSonuclari? sesAnalizi;   // enerji bantları, sahne önerileri
  List<Sahne> sahneler;
  Sablon? aktifSablon;
  CikisAyarlari cikis;           // resolution, fps, codec, en boy oranı
  KarakterKilidi globalKilit;    // tüm sahneler için varsayılan
  String? finalCiktiYolu;
  DateTime olusturma, guncelleme;
}

class Sahne {
  String id;
  int sira;
  SahneTipi tipi;                // video / gorsel / lipsyncSegment / araGorsel
  String prompt;
  String negativePrompt;

  // Süre kuralı: WanGP modeline göre 5/6/8/10/20 sn'den biri (validator)
  int sureSn;

  // Girdi
  String? girisGorselYolu;       // ilk sahne veya görsel yükleme
  String? sesParcaYolu;           // bu sahnenin ses dilimi
  double? sesBaslangic, sesBitis;

  // Üretim ayarları (override)
  UretimAyari? ayarOverride;
  KarakterKilidi? kilitOverride;

  // Çıktı
  String? sonKareYolu;            // Laplacian best-frame
  String? videoYolu;
  List<String> varyantYolu;
  EnerjiSeviyesi? enerji;         // silence/low/medium/high/climax (audio analiz)
  SahneDurum durum;               // bos/kuyruk/uretiliyor/tamam/hata/atlandi

  // Görsel rozetler
  bool faceSwapAktif, faceRestoreAktif, lipsyncAktif, vokalAyirmaAktif;
}
```

### Üretim Ayarı (per-sahne override edilebilir)
```dart
class UretimAyari {
  String modelType;              // "i2v", "infinitetalk", "qwen_image_20B", "ltx2_22B_distilled"...
  String resolution;             // "832x480", "1280x704", "720x1280" (dikey)
  int videoLength;               // frame
  int numInferenceSteps;
  double guidanceScale;
  double flowShift;
  String sampleSolver;           // "unipc", "dpm++", ...
  int seed;                      // -1 rastgele
  String? imageStart;
  String? audioGuide;
  int forceFps;                  // 16/24
  List<LoraSecimi> activatedLoras;
  // ... 80+ alan, ozelAlanlar Map<String, dynamic> escape
  Map<String, dynamic> ozelAlanlar;
}
```

### Sablon (preset)
```dart
class Sablon {
  String id, isim, kategori;     // "klip"/"reklam"/"reels"/"sinematik"...
  String? ikon, hex;
  Map<String, dynamic> sahneVarsayilanlari;
  KarakterKilidi onerilenKilit;
  UretimAyari onerilenAyar;
  bool sistemMi;                 // kod-içi vs kullanıcı
}
```

## Modlar (hepsi aynı domain üstüne oturur)

| Mod | Sayfa | Sihirbaz | Kullandığı Modeller |
|-----|-------|----------|---------------------|
| 🎬 Film Stüdyosu | film_studyosu_sayfasi | Film Sihirbazı + Sahne Devam Sihirbazı | Wan i2v, InfiniteTalk, qwen_image |
| 🎵 Müzik Klip | muzik_klip_sayfasi | Müzik Klip Sihirbazı | InfiniteTalk Single/Multi, ACE-Step |
| 👤 Karakter Defteri | karakter_defteri_sayfasi | Karakter Yaratma Sihirbazı | qwen_image (yüz seti tamamlama) |
| 📸 Influencer | influencer_sayfasi | Influencer Sihirbazı | Wan i2v + face-swap chain |
| 🎤 Sanatçı | sanatci_sayfasi | Sanatçı Sihirbazı | ACE-Step + InfiniteTalk |
| 🖼️ Görsel Üretim | gorsel_uretim_sayfasi | Görsel Sihirbazı | qwen_image_20B, flux*, hunyuan_image |
| 🎬 Serbest Video | serbest_video_sayfasi | Hızlı Sahne Sihirbazı | tüm i2v/t2v modelleri |

Tüm modlar bir **Film** nesnesi yaratır (1+ sahne). Influencer/Görsel modu = tek sahneli film.

## Sihirbaz Çekirdeği

Tüm sihirbazlar `SihirbazIskelet` widget'ından türer:
- Sol panel: adımlar (1/2/3...) + tamamlanma ✓
- Orta: aktif adım (form/seçici/önizleme)
- Sağ panel: canlı özet ("şu an seçili" tablosu)
- Alt bar: ← Geri / İleri → / Kuyruğa At / Şimdi Üret / Şablon Olarak Kaydet
- Her adımda: 💡 ipucu + 🎲 rastgele öneri + 📋 geçmişten + ⚠️ validasyon

## Karakter Kilidi (yüz/ağız tutarlılığı katmanı)

Bir tek toggle açıldığında otomatik:
1. **Best-frame Laplacian** — son 8 kareden en keskini sonraki sahneye giriş
2. **FaceFusion swap** — kaynak yüz görseliyle son kare üzerinde swap (blend 0..100)
3. **Face restore** — GFPGAN / CodeFormer ile detay (güç 0..100)
4. **Sabit seed** — biyometrik tutarlılık
5. **Karakter LoRA** — agirlik 0..1.5 otomatik enjekte
6. **Çoklu yüz açısı** — front/sol/sağ profil setinden en uygun

Lipsync katmanı:
- **InfiniteTalk audio_prompt_type=AVN** zorunlu (VAD ile sessizlikte ağız kapalı)
- **Wav2Lip post-pass** opsiyonel (lipsync olmayan sahne için)
- **Vokal ayırma** — audio-separator ile vokali InfiniteTalk'a, full mix'i final'e
- **Ağız kurtarma** dialog'u — kullanıcı kötü segmenti seçip yeniden lipsync

Hepsi açık varsayılan, kullanıcı kapatabilir, per-sahne override.

## Üretim Boru Hattı

```
1. Kullanıcı sihirbazda "Şimdi Üret"
2. Film + sahneler → JobOrchestrator
3. Her sahne için Job kuyrukla (Hive 'joblar' box, resume on startup)
4. JobOrchestrator paralel veya seri (kullanıcı seçimi):
   a. Sahne ses dilimi varsa → ffmpeg ile dilimle (tmp/audio_segments/)
   b. Karakter Kilidi açıksa → giriş görselinde face-restore + swap (ön-işleme)
   c. WanGP Bridge'e POST /jobs → SessionJob başlar
   d. SSE /jobs/:id/events → progress → UI Riverpod stream
   e. Çıktı mp4 indir → outputs/<film_id>/sahne_<i>.mp4
   f. Best-frame extract → sahne.sonKareYolu
   g. Karakter Kilidi açıksa → son kare üzerinde face-swap + restore (post-işleme)
   h. Sahne durum: tamam
5. Tüm sahneler tamamlandığında:
   a. ffmpeg concat → film_concat.mp4
   b. Şarkı varsa → add_audio_to_video (orijinal ses üst üste bindir)
   c. final → outputs/<film_id>/film_final.mp4
6. UI'a bildir, lightbox'ta önizle
```

## Klasör Yapısı

```
fiko_film_studyo/
├── docs/
│   ├── TASARIM.md (bu)
│   ├── KOPRU.md (FastAPI köprü kullanım)
│   └── KULLANIM.md (kullanıcı rehberi)
├── lib/
│   ├── main.dart
│   ├── app.dart
│   ├── yonlendirici.dart
│   ├── cekirdek/                    # utils, sabitler, hata
│   │   ├── sabitler.dart
│   │   ├── hata.dart
│   │   ├── dosya_yardimcisi.dart
│   │   ├── ffmpeg_yardimci.dart
│   │   ├── kare_cikarici.dart       # Laplacian best-frame
│   │   └── ses_analizi.dart          # enerji bantları
│   ├── modeller/                    # freezed + json_serializable
│   │   ├── karakter.dart
│   │   ├── film.dart
│   │   ├── sahne.dart
│   │   ├── uretim_ayari.dart
│   │   ├── karakter_kilidi.dart
│   │   ├── sablon.dart
│   │   ├── job.dart
│   │   ├── lora.dart
│   │   ├── ses_kimligi.dart
│   │   ├── enums.dart
│   │   └── ...
│   ├── servisler/
│   │   ├── wangp/
│   │   │   ├── wangp_kopru_istemci.dart
│   │   │   └── wangp_modeller.dart  # model katalogu (defaults/*.json'dan)
│   │   ├── ace_step/
│   │   │   └── ace_step_istemci.dart
│   │   ├── job_orkestratoru.dart
│   │   ├── karakter_deposu.dart     # Hive
│   │   ├── film_deposu.dart         # Hive
│   │   ├── sablon_deposu.dart
│   │   ├── prompt_gecmisi.dart
│   │   ├── face_fusion_runner.dart
│   │   ├── face_restore_runner.dart
│   │   └── cikti_yoneticisi.dart
│   ├── durum/                       # Riverpod providers
│   │   ├── tema_durumu.dart
│   │   ├── karakter_durumu.dart
│   │   ├── film_durumu.dart
│   │   ├── job_durumu.dart
│   │   └── ayar_durumu.dart
│   └── ui/
│       ├── tema/
│       │   ├── renkler.dart
│       │   ├── tema_tanimi.dart     # light + dark
│       │   └── animasyonlar.dart
│       ├── ortak/
│       │   ├── sihirbaz_iskelet.dart
│       │   ├── adim_gostergesi.dart
│       │   ├── prompt_alani.dart
│       │   ├── dosya_birak.dart
│       │   ├── ses_oynatici.dart
│       │   ├── video_onizleme.dart
│       │   ├── chip_grup.dart
│       │   ├── slider_etiketli.dart
│       │   └── boş_durum.dart
│       ├── ana_pencere.dart
│       ├── sayfalar/
│       │   ├── ana_sayfa.dart        # dashboard
│       │   ├── film_studyosu.dart
│       │   ├── muzik_klip.dart
│       │   ├── karakter_defteri.dart
│       │   ├── influencer.dart
│       │   ├── sanatci.dart
│       │   ├── gorsel_uretim.dart
│       │   ├── serbest_video.dart
│       │   ├── kuyruk.dart           # job listesi
│       │   ├── gecmis.dart           # üretilmiş filmler
│       │   ├── sablonlar.dart
│       │   └── ayarlar.dart
│       └── sihirbaz/
│           ├── film_sihirbazi.dart
│           ├── muzik_klip_sihirbazi.dart
│           ├── karakter_yaratma_sihirbazi.dart
│           ├── influencer_sihirbazi.dart
│           ├── sanatci_sihirbazi.dart
│           ├── gorsel_sihirbazi.dart
│           └── sahne_devam_sihirbazi.dart
└── kopru/                           # Python FastAPI köprü
    ├── wangp_bridge.py
    ├── requirements.txt
    └── README.md
```

## Paket Bağımlılıkları (pubspec)

State/Mimari: `flutter_riverpod`, `freezed`, `freezed_annotation`, `json_serializable`, `json_annotation`, `go_router`, `hive`, `hive_flutter`, `path_provider`

UI: `fluent_ui`, `flutter_acrylic`, `window_manager`, `flutter_animate`, `cupertino_icons`, `material_symbols_icons`

Medya/IO: `dio`, `http`, `file_picker`, `desktop_drop`, `path`, `video_player`, `video_player_win`, `audioplayers`, `image`, `crop_your_image`

Yardımcı: `uuid`, `crypto`, `archive`, `csv`, `intl` (locale tr_TR), `logger`

Dev: `build_runner`, `flutter_lints`, `freezed`, `json_serializable`, `hive_generator`

## Yol Haritası (bu oturum)

**Faz 1 — Çekirdek (paralel):**
1. FastAPI köprü servisi (Python)
2. pubspec + tema + main shell + go_router + Hive setup
3. Domain modelleri (freezed)

**Faz 2 — Servisler (paralel):**
4. WanGP + ACE-Step Dart istemcileri
5. JobOrchestrator + Hive kuyruk
6. Karakter/Film deposu

**Faz 3 — UI (paralel):**
7. Karakter Defteri sayfası + Karakter Yaratma Sihirbazı
8. Film Stüdyosu sayfası + Film Sihirbazı + Sahne Timeline
9. Müzik Klip + Görsel Üretim sayfaları + sihirbazları
10. Influencer + Sanatçı + Serbest Video sayfaları

**Faz 4 — Doğrulama:**
11. flutter analyze + smoke test
12. README + KULLANIM.md

## Önemli Mühendislik Notları

- **Süre validasyonu:** WanGP modeli + çözünürlüğe göre `snap_to_allowed_duration()` her sahnede zorunlu (5/6/8/10/20)
- **20 sn'lik segment:** her sahne max 20 sn, daha uzun şarkı/film için sahne zinciri (segmentli)
- **Auto-zincir:** sahne biter → best-frame → sonraki sahnenin girdisi (`girisGorselYolu`)
- **Sliding window:** InfiniteTalk modunda 81 frame, overlap 9-17, vokal ayrı verirsen %30 daha keskin lipsync
- **Bridge port:** Köprü 7861, WanGP 7860 ile çakışmıyor
- **Token:** Köprü sadece localhost dinler, auth gerekmez (loopback only)
- **VRAM güvenliği:** UretimProfili → "RTX 4070 12GB / RTX 4090 24GB" preset'leri ile compile/quantization otomatik
- **Resume:** Açılışta Hive 'joblar' box'tan `calisiyor` job'ları `kuyruk`'a geri at
- **İptal:** Köprünün `/cancel` endpoint'i `job.cancel()` çağırır, Dart Completer ile UI'da iptal görünür
- **Türkçe domain:** tüm değişken/metod/dosya isimleri Türkçe (camelCase, kebab-case)
- **Dark + Light tema:** CSS-benzeri tokens, Riverpod ile reaktif geçiş
