import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as p; import '../../cekirdek/log.dart'; import 'wangp_client.dart'; /// WanGP standalone defaults klasöründen mevcut modelleri ve LoRA'ları okur. /// Gradio API model listesi vermediğinden dosya sistemi taranır. /// /// Cache'lidir; ilk çağrıda taranır, sonrası RAM'den döner. class WanGpModelKesfi { static const _wgpKok = r'D:\__sistem__\Masaustu\FikoYT.Yeni.Cag\video-uretim\ltx-desktop-wangp\LTX-Desktop-WanGP\Wan2GP'; static List? _cacheModeller; static List? _cacheLoralar; static DateTime? _cacheZamani; static const _cacheSuresi = Duration(minutes: 5); /// `defaults/*.json` dosyalarını okur ve model listesi döner. /// Klasör yoksa boş liste yerine `WanGpClient.bilinenModeller` döner. static Future> modelleriListele({ bool cacheKullan = true, }) async { if (cacheKullan && _cacheModeller != null && _cacheZamani != null) { if (DateTime.now().difference(_cacheZamani!) < _cacheSuresi) { return _cacheModeller!; } } final list = []; final dir = Directory(p.join(_wgpKok, 'defaults')); if (!await dir.exists()) { Log.uyari('WanGpModelKesfi', 'defaults klasörü bulunamadı: ${dir.path}'); _cacheModeller = WanGpClient.bilinenModeller.map((id) => WanGpModelBilgi(id)).toList(); _cacheZamani = DateTime.now(); return _cacheModeller!; } try { final dosyalar = dir .listSync(followLinks: false) .whereType() .where((f) => f.path.toLowerCase().endsWith('.json')) .toList() ..sort((a, b) => a.path.compareTo(b.path)); for (final f in dosyalar) { try { final id = p.basenameWithoutExtension(f.path); // ReadMe.txt vb. atla if (id.startsWith('ReadMe') || id.startsWith('readme')) continue; final raw = await f.readAsString(); final j = jsonDecode(raw); if (j is! Map) { list.add(WanGpModelBilgi(id)); continue; } final mdl = j['model'] is Map ? j['model'] as Map : const {}; list.add(WanGpModelBilgi( id, etiket: mdl['name']?.toString(), mimari: mdl['architecture']?.toString(), aciklama: mdl['description']?.toString(), varsayilanCozunurluk: j['resolution']?.toString(), varsayilanSteps: (j['num_inference_steps'] as num?)?.toInt(), varsayilanFrameSayisi: (j['video_length'] as num?)?.toInt(), )); } catch (e) { Log.uyari('WanGpModelKesfi', '${f.path} okunamadı: $e'); } } } catch (e) { Log.uyari('WanGpModelKesfi', 'tarama hatası: $e'); } if (list.isEmpty) { list.addAll( WanGpClient.bilinenModeller.map((id) => WanGpModelBilgi(id))); } _cacheModeller = list; _cacheZamani = DateTime.now(); return list; } /// `loras/` klasöründeki dosya isimlerini döner (.safetensors / .pt). /// Çoklu LoRA seçimi için kullanılır. static Future> loralariListele({ bool cacheKullan = true, }) async { if (cacheKullan && _cacheLoralar != null && _cacheZamani != null) { if (DateTime.now().difference(_cacheZamani!) < _cacheSuresi) { return _cacheLoralar!; } } final list = []; for (final klasor in ['loras', 'loras_i2v']) { final dir = Directory(p.join(_wgpKok, klasor)); if (!await dir.exists()) continue; try { for (final f in dir.listSync(followLinks: false).whereType()) { final ad = p.basename(f.path).toLowerCase(); if (ad.endsWith('.safetensors') || ad.endsWith('.pt') || ad.endsWith('.ckpt')) { list.add(p.basename(f.path)); } } } catch (e) { Log.uyari('WanGpModelKesfi', 'LoRA tarama: $e'); } } list.sort(); _cacheLoralar = list; _cacheZamani = DateTime.now(); return list; } static void cacheTemizle() { _cacheModeller = null; _cacheLoralar = null; _cacheZamani = null; } } class WanGpModelBilgi { final String id; final String? etiket; final String? mimari; final String? aciklama; final String? varsayilanCozunurluk; final int? varsayilanSteps; final int? varsayilanFrameSayisi; const WanGpModelBilgi( this.id, { this.etiket, this.mimari, this.aciklama, this.varsayilanCozunurluk, this.varsayilanSteps, this.varsayilanFrameSayisi, }); String gosterimAdi() => etiket?.isNotEmpty == true ? '$etiket ($id)' : id; }