import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../app/tema.dart'; import '../cekirdek/ffmpeg_servisi.dart'; import 'direktor_state.dart'; import 'timeline.dart'; final _medyaAramaProvider = StateProvider((ref) => ''); /// CapCut tarzı Medya Havuzu — içe aktar, ara, timeline'a sürükle/ekle. class MedyaHavuzu extends ConsumerWidget { const MedyaHavuzu({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final havuz = ref.watch(direktorProvider.select((d) => d.medyaHavuzu)); final arama = ref.watch(_medyaAramaProvider).toLowerCase(); final m = ref.read(direktorProvider.notifier); final liste = <(int, MedyaOge)>[ for (var i = 0; i < havuz.length; i++) if (arama.isEmpty || havuz[i].ad.toLowerCase().contains(arama)) (i, havuz[i]), ]; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row(children: [ Expanded( child: SizedBox( height: 32, child: TextField( decoration: const InputDecoration( isDense: true, prefixIcon: Icon(Icons.search, size: 16), hintText: 'Medya ara...', ), style: const TextStyle(fontSize: 11.5), onChanged: (v) => ref.read(_medyaAramaProvider.notifier).state = v, ), ), ), const SizedBox(width: 6), FilledButton.icon( style: FilledButton.styleFrom( visualDensity: VisualDensity.compact, backgroundColor: FikoRenkler.neonCyan.withValues(alpha: 0.85)), icon: const Icon(Icons.file_download, size: 15), label: const Text('İçe Aktar', style: TextStyle(fontSize: 11)), onPressed: () async { final s = await FilePicker.platform.pickFiles( allowMultiple: true, type: FileType.custom, allowedExtensions: const [ 'png', 'jpg', 'jpeg', 'webp', 'bmp', 'mp4', 'mov', 'mkv', 'webm', 'avi', 'mp3', 'wav', 'flac', 'ogg', 'm4a', 'aac', ]); if (s != null) { await m.medyaIceAktar( [for (final f in s.files) if (f.path != null) f.path!]); } }, ), ]), const SizedBox(height: 8), Expanded( child: havuz.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.perm_media_outlined, size: 38, color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.3)), const SizedBox(height: 8), Text('Medya içe aktar — resim, video, ses\nTimeline\'a sürükle veya ➕ ile ekle', textAlign: TextAlign.center, style: TextStyle( fontSize: 10.5, color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.45))), ], )) : GridView.builder( gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 128, mainAxisExtent: 104, crossAxisSpacing: 8, mainAxisSpacing: 8, ), itemCount: liste.length, itemBuilder: (_, k) { final (i, oge) = liste[k]; return _MedyaKart(indeks: i, oge: oge); }, ), ), ], ); } } class _MedyaKart extends ConsumerWidget { const _MedyaKart({required this.indeks, required this.oge}); final int indeks; final MedyaOge oge; @override Widget build(BuildContext context, WidgetRef ref) { final m = ref.read(direktorProvider.notifier); final (renk, ikon) = switch (oge.tip) { MedyaTipi.resim => (FikoRenkler.neonPembe, Icons.image), MedyaTipi.video => (FikoRenkler.neonMor, Icons.movie), MedyaTipi.ses => (FikoRenkler.neonCyan, Icons.audiotrack), }; final kart = Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(8), border: Border.all(color: renk.withValues(alpha: 0.35)), ), clipBehavior: Clip.antiAlias, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Expanded( child: Stack(fit: StackFit.expand, children: [ _onizleme(ikon, renk), // tip + süre rozeti Positioned( left: 3, top: 3, child: Container( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), decoration: BoxDecoration( color: Colors.black54, borderRadius: BorderRadius.circular(4)), child: Row(mainAxisSize: MainAxisSize.min, children: [ Icon(ikon, size: 9, color: renk), if (oge.sure > 0) ...[ const SizedBox(width: 3), Text('${oge.sure.toStringAsFixed(0)}s', style: const TextStyle( fontSize: 8, color: Colors.white)), ], ]), ), ), // aksiyonlar Positioned( right: 2, top: 2, child: Column(children: [ _MiniAksiyon(Icons.add, 'Timeline\'a ekle', () { final ph = ref.read(playheadProvider); m.medyaTimelineEkle(indeks, playhead: ph); }), _MiniAksiyon(Icons.close, 'Havuzdan çıkar', () => m.medyaSil(indeks)), ]), ), ]), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 3), child: Text(oge.ad, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 9)), ), ], ), ); // Timeline'a sürükle-bırak return Draggable( data: MedyaTasima(indeks), feedback: Material( color: Colors.transparent, child: Container( width: 90, height: 64, decoration: BoxDecoration( color: renk.withValues(alpha: 0.85), borderRadius: BorderRadius.circular(8), ), child: Icon(ikon, color: Colors.white, size: 26), ), ), childWhenDragging: Opacity(opacity: 0.35, child: kart), child: GestureDetector( onDoubleTap: () { final ph = ref.read(playheadProvider); m.medyaTimelineEkle(indeks, playhead: ph); }, child: kart, ), ); } Widget _onizleme(IconData ikon, Color renk) { if (oge.tip == MedyaTipi.resim && File(oge.yol).existsSync()) { return Image.file(File(oge.yol), fit: BoxFit.cover, cacheWidth: 200); } if (oge.tip == MedyaTipi.video) { return FutureBuilder( future: FfmpegServisi.videoKapak(oge.yol), builder: (c, snap) => snap.data != null && File(snap.data!).existsSync() ? Image.file(File(snap.data!), fit: BoxFit.cover, cacheWidth: 200) : ColoredBox( color: Colors.black26, child: Icon(ikon, size: 26, color: renk)), ); } return ColoredBox( color: Colors.black26, child: Icon(ikon, size: 26, color: renk)); } } class _MiniAksiyon extends StatelessWidget { const _MiniAksiyon(this.ikon, this.ipucu, this.onTap); final IconData ikon; final String ipucu; final VoidCallback onTap; @override Widget build(BuildContext context) => Tooltip( message: ipucu, child: GestureDetector( onTap: onTap, child: Container( margin: const EdgeInsets.only(bottom: 2), padding: const EdgeInsets.all(2), decoration: const BoxDecoration( color: Colors.black54, shape: BoxShape.circle), child: Icon(ikon, size: 11, color: Colors.white), ), ), ); }