import 'dart:io'; import 'package:intl/intl.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import '../cekirdek/hata.dart'; import '../cekirdek/sabitler.dart'; /// Çıktı klasör yapısı + dosya adı şablonu yöneticisi. /// /// Kök: `/FikoFilmStudyo/` /// - `outputs//{frames,segments,audio_segments,final,thumbnails}/` /// - `tmp/` class CiktiYoneticisi { CiktiYoneticisi._(); static final CiktiYoneticisi ornek = CiktiYoneticisi._(); Directory? _kok; Future kokDizin() async { if (_kok != null) return _kok!; try { final docs = await getApplicationDocumentsDirectory(); final d = Directory(p.join(docs.path, 'FikoFilmStudyo')); if (!await d.exists()) await d.create(recursive: true); _kok = d; return d; } catch (e) { throw FikoHata.dosya('Çıktı kök dizini oluşturulamadı: $e'); } } Future outputsKok() async { final kok = await kokDizin(); final d = Directory(p.join(kok.path, FikoSabitler.klasorOutput)); if (!await d.exists()) await d.create(recursive: true); return d; } Future tmpKok() async { final kok = await kokDizin(); final d = Directory(p.join(kok.path, FikoSabitler.klasorTmp)); if (!await d.exists()) await d.create(recursive: true); return d; } /// `//` — alt klasörler hazırlanır. Future filmKlasoru(String filmId) async { if (filmId.trim().isEmpty) { throw FikoHata.dogrulama('Film id boş olamaz.'); } final outputs = await outputsKok(); final film = Directory(p.join(outputs.path, _gunvenliAd(filmId))); if (!await film.exists()) { await film.create(recursive: true); // alt klasörler for (final ad in const [ 'frames', 'segments', 'audio_segments', 'final', 'thumbnails', ]) { await Directory(p.join(film.path, ad)).create(); } } else { // Eksik alt klasörleri tamamla for (final ad in const [ 'frames', 'segments', 'audio_segments', 'final', 'thumbnails', ]) { final alt = Directory(p.join(film.path, ad)); if (!await alt.exists()) await alt.create(); } } return film; } Future framesKlasoru(String filmId) async { final f = await filmKlasoru(filmId); return Directory(p.join(f.path, 'frames')); } Future segmentsKlasoru(String filmId) async { final f = await filmKlasoru(filmId); return Directory(p.join(f.path, 'segments')); } Future sesSegmentKlasoru(String filmId) async { final f = await filmKlasoru(filmId); return Directory(p.join(f.path, 'audio_segments')); } Future finalKlasoru(String filmId) async { final f = await filmKlasoru(filmId); return Directory(p.join(f.path, 'final')); } Future thumbnailsKlasoru(String filmId) async { final f = await filmKlasoru(filmId); return Directory(p.join(f.path, 'thumbnails')); } /// Tek bir filmin altındaki tüm dosyaları toplar. Future> filmDosyalari(String filmId) async { final f = await filmKlasoru(filmId); final liste = []; await for (final v in f.list(recursive: true, followLinks: false)) { if (v is File) liste.add(v); } return liste; } /// `{date}_{seed}_{prompt(50)}` benzeri şablonu doldurur. /// /// Desteklenen anahtarlar: `{date}`, `{time}`, `{datetime}`, `{seed}`, /// `{prompt}`, `{prompt(N)}`, `{film}`, `{sahne}`, `{ext}`. String adUret({ String sablon = '{date}_{seed}_{prompt(50)}', String? prompt, int? seed, String? filmId, String? sahneId, String uzanti = 'mp4', }) { final simdi = DateTime.now(); final date = DateFormat('yyyyMMdd').format(simdi); final time = DateFormat('HHmmss').format(simdi); final datetime = '${date}_$time'; final temizPrompt = (prompt ?? '') .replaceAll(RegExp(r'[^a-zA-Z0-9 ]'), '') .replaceAll(RegExp(r'\s+'), '_'); final regex = RegExp(r'\{prompt\((\d+)\)\}'); var sonuc = sablon .replaceAll('{date}', date) .replaceAll('{time}', time) .replaceAll('{datetime}', datetime) .replaceAll('{seed}', '${seed ?? 0}') .replaceAll('{film}', _gunvenliAd(filmId ?? '')) .replaceAll('{sahne}', _gunvenliAd(sahneId ?? '')) .replaceAll('{ext}', uzanti); sonuc = sonuc.replaceAllMapped(regex, (m) { final n = int.tryParse(m.group(1) ?? '50') ?? 50; return temizPrompt.length > n ? temizPrompt.substring(0, n) : temizPrompt; }); sonuc = sonuc.replaceAll('{prompt}', temizPrompt); if (!sonuc.endsWith('.$uzanti')) sonuc = '$sonuc.$uzanti'; return _gunvenliAd(sonuc); } /// Win/macOS dosya adı için güvenli hale getir. String _gunvenliAd(String ad) { return ad .replaceAll(RegExp(r'[<>:"/\\|?*\x00-\x1F]'), '_') .replaceAll(RegExp(r'\.+$'), '_') .trim(); } }