Files
AudiOhm/frontend/lib/presentation/providers/album_provider.dart
T
root a89c7894cf Initial commit: AudiOhm - Alternative Spotify avec streaming YouTube
Backend:
- FastAPI avec PostgreSQL et Redis
- Authentification JWT complète
- API REST pour musique, playlists, recherche
- Streaming audio via yt-dlp
- SQLAlchemy 2.0 async

Frontend:
- Flutter avec thème néon cyberpunk
- State management Riverpod
- Layout adaptatif desktop/mobile
- Lecteur audio avec mini-player

Infrastructure:
- Docker Compose (PostgreSQL + Redis)
- Scripts d'installation automatisés
- Scripts de build pour exécutables

Fichiers ajoutés:
- BUILD_CLIENT_*.bat/sh: Scripts de compilation
- BUILD_CLIENT_README.md: Documentation compilation
- CHECK_FLUTTER.sh: Vérificateur d'environnement
- requirements.txt mis à jour pour Python 3.13
- Modèles SQLAlchemy corrigés (metadata -> extra_metadata)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 20:08:36 +00:00

167 lines
4.6 KiB
Dart

/// Album Provider - Album details state management
library;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:math';
import '../../../infrastructure/datasources/remote/music_api_service.dart';
import '../../../domain/entities/album.dart';
import '../../../domain/entities/track.dart';
import 'music_provider.dart';
/// Album state
class AlbumState {
final Album? album;
final List<Track> tracks;
final bool isLoading;
final String? error;
const AlbumState({
this.album,
this.tracks = const [],
this.isLoading = false,
this.error,
});
AlbumState copyWith({
Album? album,
List<Track>? tracks,
bool? isLoading,
String? error,
}) {
return AlbumState(
album: album ?? this.album,
tracks: tracks ?? this.tracks,
isLoading: isLoading ?? this.isLoading,
error: error,
);
}
/// Get total duration of all tracks in seconds
int get totalDuration {
return tracks.fold(0, (sum, track) {
return sum + (track.duration ?? 0);
});
}
/// Get formatted total duration (hours:minutes:seconds)
String get formattedTotalDuration {
final totalSeconds = totalDuration;
final hours = totalSeconds ~/ 3600;
final minutes = (totalSeconds % 3600) ~/ 60;
final seconds = totalSeconds % 60;
if (hours > 0) {
return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
} else {
return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
}
}
}
/// Album notifier
class AlbumNotifier extends StateNotifier<AlbumState> {
AlbumNotifier(this._musicApiService) : super(const AlbumState());
final MusicApiService _musicApiService;
/// Load complete album information with tracks
Future<void> loadAlbum(String albumId) async {
state = state.copyWith(isLoading: true, error: null);
try {
// Load album details and tracks in parallel
final results = await Future.wait([
_musicApiService.getAlbum(albumId),
_musicApiService.getAlbumTracks(albumId),
]);
final album = Album.fromJson(results[0] as Map<String, dynamic>);
final tracks = (results[1] as List)
.map((json) => Track.fromJson(json as Map<String, dynamic>))
.toList();
// Sort tracks by track number
tracks.sort((a, b) {
final aNum = a.trackNumber ?? 0;
final bNum = b.trackNumber ?? 0;
return aNum.compareTo(bNum);
});
state = AlbumState(
album: album,
tracks: tracks,
isLoading: false,
);
} catch (e) {
state = state.copyWith(
isLoading: false,
error: e.toString(),
);
}
}
/// Play all tracks from album
Future<void> playAll(PlayerNotifier playerNotifier) async {
if (state.tracks.isEmpty) return;
playerNotifier.setQueue(state.tracks, startIndex: 0);
await playerNotifier.loadTrack(state.tracks.first);
await playerNotifier.play();
}
/// Shuffle and play all tracks from album
Future<void> shuffle(PlayerNotifier playerNotifier) async {
if (state.tracks.isEmpty) return;
// Create shuffled list
final shuffledTracks = List<Track>.from(state.tracks);
final random = Random();
for (int i = shuffledTracks.length - 1; i > 0; i--) {
final j = random.nextInt(i + 1);
final temp = shuffledTracks[i];
shuffledTracks[i] = shuffledTracks[j];
shuffledTracks[j] = temp;
}
playerNotifier.setQueue(shuffledTracks, startIndex: 0);
await playerNotifier.loadTrack(shuffledTracks.first);
await playerNotifier.play();
}
/// Play specific track from album
Future<void> playTrack(
PlayerNotifier playerNotifier,
Track track,
) async {
final index = state.tracks.indexWhere((t) => t.id == track.id);
if (index == -1) return;
playerNotifier.setQueue(state.tracks, startIndex: index);
await playerNotifier.loadTrack(track);
await playerNotifier.play();
}
/// Clear state
void clear() {
state = const AlbumState();
}
}
/// Album provider
final albumProvider =
StateNotifierProvider<AlbumNotifier, AlbumState>((ref) {
final musicApiService = ref.watch(musicApiServiceProvider);
return AlbumNotifier(musicApiService);
});
/// Album data provider for a specific album ID
final albumDataProvider = Provider.family<AlbumState, String>((ref, albumId) {
final notifier = ref.watch(albumProvider.notifier);
// Load data when first accessed
if (notifier.state.album?.id != albumId) {
Future.microtask(() => notifier.loadAlbum(albumId));
}
return notifier.state;
});