import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:mirror_protocol/mirror_protocol.dart'; import 'package:mirror_ui/mirror_ui.dart'; import '../providers/display_state_provider.dart'; import '../providers/player_provider.dart'; class MediaScreen extends ConsumerWidget { const MediaScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final playback = ref.watch(displayStateProvider.select((s) => s.playback)); final spotify = ref.watch(displayStateProvider.select((s) => s.spotify)); // Spotify playback if (spotify != null && spotify.trackName != null) { return _buildSpotify(context, spotify); } // Video playback (YouTube / Plex) if (playback != null && playback.source != MediaSource.workout) { return _buildVideoPlayback(context, ref, playback); } // Nothing playing return Container( color: MirrorTheme.background, width: double.infinity, height: double.infinity, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.music_note, size: 64, color: MirrorTheme.textMuted), const SizedBox(height: 24), Text( 'Nothing playing', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: MirrorTheme.textMuted, ), ), ], ), ), ); } Widget _buildSpotify(BuildContext context, SpotifyState spotify) { final progress = spotify.durationMs > 0 ? spotify.progressMs / spotify.durationMs : 0.0; return Container( color: MirrorTheme.background, width: double.infinity, height: double.infinity, child: Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 80), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Source indicator Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.music_note, size: 20, color: MirrorTheme.accent), const SizedBox(width: 8), Text( 'SPOTIFY', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: MirrorTheme.accent, letterSpacing: 2, ), ), ], ), const SizedBox(height: 40), // Album art if (spotify.albumImageUrl != null) ClipRRect( borderRadius: BorderRadius.circular(24), child: Image.network( spotify.albumImageUrl!, width: 400, height: 400, fit: BoxFit.cover, errorBuilder: (_, __, ___) => _albumArtPlaceholder(), ), ) else _albumArtPlaceholder(), const SizedBox(height: 60), // Track name Text( spotify.trackName ?? '', style: Theme.of(context).textTheme.headlineLarge, textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 12), // Artist name Text( spotify.artistName ?? '', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: MirrorTheme.textSecondary, ), textAlign: TextAlign.center, ), const SizedBox(height: 40), // Progress bar ClipRRect( borderRadius: BorderRadius.circular(2), child: LinearProgressIndicator( value: progress, minHeight: 4, backgroundColor: MirrorTheme.surfaceLight, color: MirrorTheme.accent, ), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( _formatDuration(Duration(milliseconds: spotify.progressMs)), style: Theme.of(context).textTheme.bodySmall?.copyWith( fontFamily: MirrorTheme.fontMono, ), ), Text( _formatDuration(Duration(milliseconds: spotify.durationMs)), style: Theme.of(context).textTheme.bodySmall?.copyWith( fontFamily: MirrorTheme.fontMono, ), ), ], ), const SizedBox(height: 40), Icon( spotify.isPlaying ? Icons.pause_circle_filled : Icons.play_circle_filled, size: 72, color: MirrorTheme.textPrimary, ), ], ), ), ), ); } Widget _buildVideoPlayback(BuildContext context, WidgetRef ref, PlaybackState playback) { final progress = playback.duration.inMilliseconds > 0 ? playback.position.inMilliseconds / playback.duration.inMilliseconds : 0.0; return Container( color: Colors.black, width: double.infinity, height: double.infinity, child: Stack( children: [ // Video output Consumer( builder: (context, ref, _) { final controller = ref.watch(videoControllerProvider); return Video(controller: controller); }, ), // Paused indicator if (!playback.isPlaying) const Center( child: Icon(Icons.pause_circle_filled, size: 120, color: Colors.white70), ), // Source indicator top-left Positioned( top: 30, left: 30, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.black54, borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( _sourceIcon(playback.source), size: 18, color: MirrorTheme.accent, ), const SizedBox(width: 8), Text( playback.source.toJson().toUpperCase(), style: Theme.of(context).textTheme.bodySmall?.copyWith( color: MirrorTheme.accent, letterSpacing: 1, ), ), ], ), ), ), // Bottom overlay Positioned( bottom: 0, left: 0, right: 0, child: Container( padding: const EdgeInsets.all(40), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.transparent, Colors.black87], ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( playback.title, style: Theme.of(context).textTheme.headlineMedium, ), const SizedBox(height: 12), ClipRRect( borderRadius: BorderRadius.circular(2), child: LinearProgressIndicator( value: progress, minHeight: 4, backgroundColor: Colors.white24, color: MirrorTheme.accent, ), ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( _formatDuration(playback.position), style: Theme.of(context).textTheme.bodySmall?.copyWith( fontFamily: MirrorTheme.fontMono, ), ), Text( _formatDuration(playback.duration), style: Theme.of(context).textTheme.bodySmall?.copyWith( fontFamily: MirrorTheme.fontMono, ), ), ], ), ], ), ), ), ], ), ); } Widget _albumArtPlaceholder() { return Container( width: 400, height: 400, decoration: BoxDecoration( color: MirrorTheme.surface, borderRadius: BorderRadius.circular(24), ), child: Icon( Icons.music_note, size: 120, color: MirrorTheme.textMuted, ), ); } IconData _sourceIcon(MediaSource source) => switch (source) { MediaSource.youtube => Icons.smart_display, MediaSource.plex => Icons.movie, MediaSource.local => Icons.folder, _ => Icons.music_note, }; String _formatDuration(Duration d) { final minutes = d.inMinutes; final seconds = (d.inSeconds % 60).toString().padLeft(2, '0'); return '$minutes:$seconds'; } }