import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:path/path.dart' as p; import '../app/tema.dart'; import '../cekirdek/ffmpeg_servisi.dart'; import '../veri/isar_servisi.dart'; import '../veri/modeller/uretim_kaydi.dart'; /// Şarkı kes/böl dialogu — dalga formu üzerinde sürüklemeli seçim. /// Kesit Al: seçili aralığı yeni mp3 varyantı yapar. /// İkiye Böl: playhead'den iki ayrı mp3 üretir. Future showSesKesDialog( BuildContext context, WidgetRef ref, UretimKaydi kayit) async { if (kayit.ciktiDosyalari.isEmpty) return; final dosya = kayit.ciktiDosyalari.first; if (!File(dosya).existsSync()) return; final toplam = await FfmpegServisi.sesSuresi(dosya) ?? 0; if (toplam <= 0) return; final dalga = await FfmpegServisi.dalgaFormu(dosya); if (!context.mounted) return; await showDialog( context: context, builder: (ctx) => _SesKesDialog( kayit: kayit, dosya: dosya, toplam: toplam, dalga: dalga), ); } class _SesKesDialog extends ConsumerStatefulWidget { const _SesKesDialog( {required this.kayit, required this.dosya, required this.toplam, required this.dalga}); final UretimKaydi kayit; final String dosya; final double toplam; final String? dalga; @override ConsumerState<_SesKesDialog> createState() => _SesKesDialogState(); } class _SesKesDialogState extends ConsumerState<_SesKesDialog> { bool bolModu = false; late double bas = 0; late double bit = widget.toplam; late double bolNokta = widget.toplam / 2; bool calisiyor = false; String _mmss(double s) => '${(s ~/ 60).toString().padLeft(2, '0')}:${(s % 60).toStringAsFixed(1).padLeft(4, '0')}'; Future _uygula() async { setState(() => calisiyor = true); final klasor = ref.read(ayarlarProvider).ciktiKlasoru; final ts = DateTime.now().millisecondsSinceEpoch; final kayitlar = <(String, String)>[]; // (yol, etiket) if (bolModu) { final a = p.join(klasor, 'kesit', 'bolum1_$ts.mp3'); final b = p.join(klasor, 'kesit', 'bolum2_$ts.mp3'); final r1 = await FfmpegServisi.sesKes( kaynak: widget.dosya, baslangic: 0, sure: bolNokta, hedef: a); final r2 = await FfmpegServisi.sesKes( kaynak: widget.dosya, baslangic: bolNokta, sure: widget.toplam - bolNokta, hedef: b); if (r1 != null) kayitlar.add((r1, 'bölüm 1 (0-${_mmss(bolNokta)})')); if (r2 != null) { kayitlar.add((r2, 'bölüm 2 (${_mmss(bolNokta)}-${_mmss(widget.toplam)})')); } } else { final a = p.join(klasor, 'kesit', 'kesit_$ts.mp3'); final r = await FfmpegServisi.sesKes( kaynak: widget.dosya, baslangic: bas, sure: bit - bas, hedef: a); if (r != null) { kayitlar.add((r, 'kesit ${_mmss(bas)}-${_mmss(bit)}')); } } // Yeni kayıtlar → aynı varyant grubuna for (final (yol, etiket) in kayitlar) { final yeni = UretimKaydi() ..tip = UretimTipi.muzik ..baslik = '${widget.kayit.baslik} ✂ $etiket' ..parametrelerJson = widget.kayit.parametrelerJson ..sihirbazJson = widget.kayit.sihirbazJson ..durum = UretimDurumu.tamamlandi ..ciktiDosyalari = [yol] ..varyantGrubu = widget.kayit.etkinGrup ..olusturmaZamani = DateTime.now() ..bitisZamani = DateTime.now(); await isar.writeTxn(() => isar.uretimKaydis.put(yeni)); } if (mounted) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text('${kayitlar.length} parça oluşturuldu — varyant olarak eklendi'))); } } @override Widget build(BuildContext context) { return AlertDialog( title: Row(children: [ const Icon(Icons.content_cut, size: 18, color: FikoRenkler.neonCyan), const SizedBox(width: 8), Expanded( child: Text('Şarkıyı ${bolModu ? "Böl" : "Kes"} — ${widget.kayit.baslik}', maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 15))), ]), content: SizedBox( width: 560, child: Column(mainAxisSize: MainAxisSize.min, children: [ SegmentedButton( style: const ButtonStyle(visualDensity: VisualDensity.compact), segments: const [ ButtonSegment(value: false, label: Text('✂ Kesit Al')), ButtonSegment(value: true, label: Text('⚡ İkiye Böl')), ], selected: {bolModu}, onSelectionChanged: (s) => setState(() => bolModu = s.first), ), const SizedBox(height: 12), Text( bolModu ? 'Böl: ${_mmss(bolNokta)} → iki ayrı parça' : 'Seçim: ${_mmss(bas)} → ${_mmss(bit)} (${(bit - bas).toStringAsFixed(1)}sn)', style: const TextStyle( fontSize: 12, color: FikoRenkler.neonAmber)), const SizedBox(height: 6), LayoutBuilder(builder: (context, c) { final w = c.maxWidth; double xOf(double t) => t / widget.toplam * w; return SizedBox( height: 72, child: Stack(children: [ Positioned.fill( child: ClipRRect( borderRadius: BorderRadius.circular(7), child: Container( color: Colors.black26, child: widget.dalga != null && File(widget.dalga!).existsSync() ? Image.file(File(widget.dalga!), fit: BoxFit.fill, gaplessPlayback: true) : const SizedBox(), ), ), ), if (!bolModu) ...[ Positioned( left: 0, top: 0, bottom: 0, width: xOf(bas), child: Container( color: Colors.black.withValues(alpha: 0.55))), Positioned( left: xOf(bit), top: 0, bottom: 0, right: 0, child: Container( color: Colors.black.withValues(alpha: 0.55))), Positioned( left: xOf(bas), top: 0, bottom: 0, width: (xOf(bit) - xOf(bas)).clamp(2, w), child: Container( decoration: BoxDecoration( border: Border.all( color: FikoRenkler.neonAmber, width: 1.5), borderRadius: BorderRadius.circular(4), ), ), ), _Kol(x: xOf(bas), onDrag: (dx) { setState(() => bas = (bas + dx / w * widget.toplam) .clamp(0, bit - 0.2)); }), _Kol(x: xOf(bit), onDrag: (dx) { setState(() => bit = (bit + dx / w * widget.toplam) .clamp(bas + 0.2, widget.toplam)); }), ] else _Kol( x: xOf(bolNokta), renk: FikoRenkler.neonPembe, onDrag: (dx) { setState(() => bolNokta = (bolNokta + dx / w * widget.toplam) .clamp(0.5, widget.toplam - 0.5)); }), ]), ); }), ]), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Vazgeç')), FilledButton.icon( icon: calisiyor ? const SizedBox( width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2)) : const Icon(Icons.content_cut, size: 16), label: Text(bolModu ? 'Böl ve Kaydet' : 'Kes ve Kaydet'), onPressed: calisiyor ? null : _uygula, ), ], ); } } class _Kol extends StatelessWidget { const _Kol({required this.x, required this.onDrag, this.renk}); final double x; final void Function(double dx) onDrag; final Color? renk; @override Widget build(BuildContext context) { final r = renk ?? FikoRenkler.neonAmber; return Positioned( left: x - 9, top: 0, bottom: 0, width: 18, child: GestureDetector( behavior: HitTestBehavior.translucent, onHorizontalDragUpdate: (d) => onDrag(d.delta.dx), child: MouseRegion( cursor: SystemMouseCursors.resizeLeftRight, child: Center( child: Container( width: 4, decoration: BoxDecoration( color: r, borderRadius: BorderRadius.circular(2), boxShadow: [ BoxShadow(color: r.withValues(alpha: 0.6), blurRadius: 4) ], ), ), ), ), ), ); } }