import 'dart:async'; import 'dart:convert'; import 'package:hive/hive.dart'; import 'package:logger/logger.dart'; import 'package:uuid/uuid.dart'; import '../cekirdek/hata.dart'; import '../cekirdek/sabitler.dart'; import '../modeller/sablon.dart'; import '../modeller/uretim_ayari.dart'; import '../modeller/karakter_kilidi.dart'; /// Şablon Hive deposu + sistem şablon seed mekanizması. class SablonDeposu { SablonDeposu._(); static final SablonDeposu ornek = SablonDeposu._(); final Logger _log = Logger(printer: SimplePrinter(colors: true)); bool _seedYapildi = false; Box _box() { if (!Hive.isBoxOpen(FikoSabitler.boxSablonlar)) { throw FikoHata.dosya( 'Şablonlar kutusu açık değil — main.dart Hive init eksik.', ); } return Hive.box(FikoSabitler.boxSablonlar); } Future> hepsiniGetir({String? kategori}) async { await _sistemSablonlariSeed(); final box = _box(); final liste = []; for (final v in box.values) { try { final ham = v is String ? jsonDecode(v) : v; if (ham is Map) { liste.add(Sablon.fromJson(Map.from(ham))); } } catch (e) { _log.w('Şablon okunamadı: $e'); } } final filtreli = kategori == null ? liste : liste.where((s) => s.kategori == kategori).toList(); filtreli.sort((a, b) => b.guncelleme.compareTo(a.guncelleme)); return filtreli; } Future getir(String id) async { final box = _box(); final v = box.get(id); if (v == null) return null; try { final ham = v is String ? jsonDecode(v) : v; return Sablon.fromJson(Map.from(ham as Map)); } catch (e) { throw FikoHata.dosya('Şablon parse hatası: $e'); } } Future kaydet(Sablon s) async { final box = _box(); final guncel = s.copyWith(guncelleme: DateTime.now()); await box.put(guncel.id, jsonEncode(guncel.toJson())); } Future sil(String id) async { await _box().delete(id); } Stream> izle({String? kategori}) { final box = _box(); final ctrl = StreamController>(sync: false); Future yayinla() async { try { ctrl.add(await hepsiniGetir(kategori: kategori)); } catch (e) { ctrl.addError(e); } } yayinla(); final abone = box.watch().listen((_) => yayinla()); ctrl.onCancel = () async { await abone.cancel(); }; return ctrl.stream; } // ─── Sistem şablonları seed ────────────────────────────────────────────── Future _sistemSablonlariSeed() async { if (_seedYapildi) return; final box = _box(); final mevcut = {}; for (final v in box.values) { try { final ham = v is String ? jsonDecode(v) : v; if (ham is Map && ham['sistemMi'] == true) { mevcut.add(ham['id']?.toString() ?? ''); } } catch (_) {} } final simdi = DateTime.now(); for (final s in _varsayilanSablonlar(simdi)) { if (!mevcut.contains(s.id)) { await box.put(s.id, jsonEncode(s.toJson())); } } _seedYapildi = true; } /// Varsayılan sistem şablonları (8 adet). List _varsayilanSablonlar(DateTime simdi) { Sablon yap({ required String id, required String isim, required String kategori, required String aciklama, String? ikon, String? hexRenk, List? sahneSureleri, int? tahminToplam, UretimAyari? ayar, KarakterKilidi? kilit, }) { return Sablon( id: id, isim: isim, kategori: kategori, aciklama: aciklama, ikon: ikon, hexRenk: hexRenk, sahneSureleri: sahneSureleri, tahminEdilenToplam: tahminToplam, sistemMi: true, olusturma: simdi, guncelleme: simdi, onerilenAyar: ayar ?? const UretimAyari(), onerilenKilit: kilit ?? const KarakterKilidi(), ); } return [ yap( id: 'sys_hizli_klip_30', isim: 'Hızlı Klip 30sn', kategori: 'klip', aciklama: '6 sahne x 5sn — hızlı sosyal medya klibi.', ikon: 'flash', hexRenk: '#22D3EE', sahneSureleri: const [5, 5, 5, 5, 5, 5], tahminToplam: 30, ), yap( id: 'sys_ilahi_klip_3dk', isim: 'İlahi Klip 3dk', kategori: 'ilahi', aciklama: '20+ sahne, sözlerle senkronize anlatım.', ikon: 'star', hexRenk: '#A78BFA', sahneSureleri: List.generate(22, (_) => 8), tahminToplam: 176, ), yap( id: 'sys_reels_15', isim: 'Reels 15sn', kategori: 'reels', aciklama: 'Dikey 480x832 — 3 hızlı sahne.', ikon: 'phone', hexRenk: '#F472B6', sahneSureleri: const [5, 5, 5], tahminToplam: 15, ayar: const UretimAyari( cozunurluk: '480x832', videoLength: 81, forceFps: 16, ), ), yap( id: 'sys_sinematik_2dk', isim: 'Sinematik Kısa Film 2dk', kategori: 'sinematik', aciklama: '12 sahne x 10sn, sinematik look.', ikon: 'film', hexRenk: '#FB7185', sahneSureleri: List.generate(12, (_) => 10), tahminToplam: 120, ayar: const UretimAyari( cozunurluk: '1280x720', numInferenceSteps: 30, guidanceScale: 6, flowShift: 5, ), ), yap( id: 'sys_konusan_bas_60', isim: 'Konuşan Baş 1dk', kategori: 'konusan_bas', aciklama: 'Lipsync ses dilimi (InfiniteTalk) — 60sn.', ikon: 'mic', hexRenk: '#34D399', sahneSureleri: const [10, 10, 10, 10, 10, 10], tahminToplam: 60, ayar: const UretimAyari(modelTipi: 'lipsync'), ), yap( id: 'sys_reklam_30', isim: 'Reklam 30sn', kategori: 'reklam', aciklama: 'Marka odaklı 5 sahne x 6sn.', ikon: 'tag', hexRenk: '#F59E0B', sahneSureleri: const [6, 6, 6, 6, 6], tahminToplam: 30, ), yap( id: 'sys_anime_sahne', isim: 'Anime Sahne', kategori: 'klip', aciklama: 'Stilize anime LoRA + dinamik kamera.', ikon: 'sparkles', hexRenk: '#60A5FA', sahneSureleri: const [8, 8, 8], tahminToplam: 24, ), yap( id: 'sys_cinematic_wide', isim: 'Cinematic Wide', kategori: 'sinematik', aciklama: '21:9 anamorfik geniş ekran sahne.', ikon: 'aspect-ratio', hexRenk: '#94A3B8', sahneSureleri: const [10, 10, 10], tahminToplam: 30, ayar: const UretimAyari(cozunurluk: '1280x704'), ), ]; } /// Yeni kullanıcı şablonu için id üret. String yeniId() => 'sablon_${const Uuid().v4()}'; }