import 'dart:io'; import 'package:dio/dio.dart'; import '../../cekirdek/log.dart'; /// FikoYT RVC Voice Clone API istemcisi. /// Sunucu: rvc-uretim/sunucu.py — port 8004 (Rvc-Sunucu-Baslat.bat ile başlatılır). /// /// Endpoints: /// - GET /health → kurulu mu? /// - GET /models → mevcut .pth modeller /// - POST /clone/baslat → klonlama başlat → {is_id} /// - GET /clone/job/{is_id} → durum (0/1/2) /// - POST /upload → dosya yükle → {yol} class RvcApiIstemci { final String baseUrl; late final Dio _dio; RvcApiIstemci({this.baseUrl = 'http://127.0.0.1:8004'}) { _dio = Dio(BaseOptions( baseUrl: baseUrl, connectTimeout: const Duration(seconds: 5), receiveTimeout: const Duration(minutes: 10), headers: {'Content-Type': 'application/json'}, )); } /// /health çağrısı. RVC kurulu olmasa bile sunucu ayakta ise true. Future sagliklimi() async { try { final r = await _dio.get('/health', options: Options(receiveTimeout: const Duration(seconds: 3))); return r.statusCode == 200; } catch (_) { return false; } } /// Detaylı sağlık raporu (rvc_kurulu, rvc_path, vb.) Future?> saglikDetay() async { try { final r = await _dio.get('/health', options: Options(receiveTimeout: const Duration(seconds: 3))); if (r.data is Map) { return Map.from(r.data as Map); } } catch (e) { Log.uyari('RvcApi', 'Sağlık alınamadı: $e'); } return null; } static Future portAcikMi( {String host = '127.0.0.1', int port = 8004}) async { try { final s = await Socket.connect(host, port, timeout: const Duration(milliseconds: 500)); s.destroy(); return true; } catch (_) { return false; } } /// /models — RVC weights klasöründeki .pth dosyaları. /// Returns: List<{isim, yol, boyut_mb}> Future>> modelListesi() async { try { final r = await _dio.get('/models'); final data = r.data; if (data is Map && data['modeller'] is List) { return (data['modeller'] as List) .map((e) => Map.from(e as Map)) .toList(); } } catch (e) { Log.uyari('RvcApi', 'Model listesi hatası: $e'); } return []; } /// Sadece isimleri kolay tüketim için. Future> modelIsimleri() async { final liste = await modelListesi(); return liste .map((m) => (m['isim'] ?? m['yol'] ?? '').toString()) .where((s) => s.isNotEmpty) .toList(); } /// Klonlama görevi başlatır → is_id. /// Hata durumunda null döner ([Log] uyarısı bırakır). Future cloneBaslat({ required String kaynakSes, required String modelYolu, String? indexYolu, int transpoze = 0, }) async { try { final r = await _dio.post('/clone/baslat', data: { 'kaynak_ses': kaynakSes, 'model_yolu': modelYolu, if (indexYolu != null && indexYolu.isNotEmpty) 'index_yolu': indexYolu, 'transpoze': transpoze, }); final data = r.data; if (data is Map && data['basarili'] == true && data['is_id'] != null) { return data['is_id'].toString(); } Log.uyari('RvcApi', 'Klonlama başlatılamadı: ${data is Map ? data['hata'] : data}'); } catch (e) { Log.uyari('RvcApi', 'Clone hatası: $e'); } return null; } /// İş durumu sorgular. /// Returns: {is_id, durum (0/1/2), ilerleme, cikti_yolu, hata, ...} Future?> jobDurumu(String isId) async { try { final r = await _dio.get('/clone/job/$isId'); if (r.data is Map) { return Map.from(r.data as Map); } } catch (e) { Log.uyari('RvcApi', 'Durum alınamadı: $e'); } return null; } /// Yerel dosyayı sunucuya yükler → kalıcı yol döner. Future dosyaYukle(String yerelYol) async { try { final dosya = File(yerelYol); if (!await dosya.exists()) return null; final form = FormData.fromMap({ 'dosya': await MultipartFile.fromFile(yerelYol, filename: dosya.uri.pathSegments.last), }); final r = await _dio.post('/upload', data: form); final data = r.data; if (data is Map && data['basarili'] == true && data['yol'] != null) { return data['yol'].toString(); } } catch (e) { Log.uyari('RvcApi', 'Yükleme hatası: $e'); } return null; } /// Çıktıyı indirip belirtilen hedefe yazar. Future ciktiyiIndir(String isId, String hedefYol) async { try { final r = await _dio.get( '/cikti/$isId', options: Options(responseType: ResponseType.bytes), ); final f = File(hedefYol); await f.parent.create(recursive: true); await f.writeAsBytes(r.data as List); return f; } catch (e) { Log.uyari('RvcApi', 'İndirme hatası: $e'); } return null; } }