Files
AudiOhm/frontend/lib/presentation/providers/artist_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

197 lines
5.5 KiB
Dart

/// Artist Provider - Artist details state management
library;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../infrastructure/datasources/remote/music_api_service.dart';
import '../../../domain/entities/artist.dart';
import '../../../domain/entities/track.dart';
import '../../../domain/entities/album.dart';
/// Artist state
class ArtistState {
final Artist? artist;
final List<Track> topTracks;
final List<Album> albums;
final List<Track> relatedTracks;
final bool isLoading;
final String? error;
const ArtistState({
this.artist,
this.topTracks = const [],
this.albums = const [],
this.relatedTracks = const [],
this.isLoading = false,
this.error,
});
ArtistState copyWith({
Artist? artist,
List<Track>? topTracks,
List<Album>? albums,
List<Track>? relatedTracks,
bool? isLoading,
String? error,
}) {
return ArtistState(
artist: artist ?? this.artist,
topTracks: topTracks ?? this.topTracks,
albums: albums ?? this.albums,
relatedTracks: relatedTracks ?? this.relatedTracks,
isLoading: isLoading ?? this.isLoading,
error: error,
);
}
}
/// Artist notifier
class ArtistNotifier extends StateNotifier<ArtistState> {
ArtistNotifier(this._musicApiService) : super(const ArtistState());
final MusicApiService _musicApiService;
/// Load complete artist information
Future<void> loadArtist(String artistId) async {
state = state.copyWith(isLoading: true, error: null);
try {
// Load artist details
final artistData = await _musicApiService.getArtist(artistId);
final artist = Artist.fromJson(artistData);
state = state.copyWith(
artist: artist,
isLoading: false,
);
} catch (e) {
state = state.copyWith(
isLoading: false,
error: e.toString(),
);
}
}
/// Load artist's top tracks
Future<void> loadTopTracks(String artistId) async {
try {
final tracksData = await _musicApiService.getArtistTopTracks(artistId);
final tracks = tracksData
.map((json) => Track.fromJson(json as Map<String, dynamic>))
.toList();
state = state.copyWith(topTracks: tracks);
} catch (e) {
state = state.copyWith(error: e.toString());
}
}
/// Load artist's albums
Future<void> loadAlbums(String artistId) async {
try {
final albumsData = await _musicApiService.getArtistAlbums(artistId);
final albums = albumsData
.map((json) => Album.fromJson(json as Map<String, dynamic>))
.toList();
state = state.copyWith(albums: albums);
} catch (e) {
state = state.copyWith(error: e.toString());
}
}
/// Load related tracks (based on artist's top track)
Future<void> loadRelatedTracks(String artistId) async {
try {
// Get first track from top tracks for recommendations
if (state.topTracks.isEmpty) {
await loadTopTracks(artistId);
}
if (state.topTracks.isNotEmpty) {
final firstTrack = state.topTracks.first;
final relatedData =
await _musicApiService.getRecommendations(firstTrack.id);
final related = relatedData
.map((json) => Track.fromJson(json as Map<String, dynamic>))
.toList();
state = state.copyWith(relatedTracks: related);
}
} catch (e) {
state = state.copyWith(error: e.toString());
}
}
/// Load all artist data at once
Future<void> loadAllArtistData(String artistId) async {
state = state.copyWith(isLoading: true, error: null);
try {
// Load all data in parallel
final results = await Future.wait([
_musicApiService.getArtist(artistId),
_musicApiService.getArtistTopTracks(artistId),
_musicApiService.getArtistAlbums(artistId),
]);
final artist = Artist.fromJson(results[0] as Map<String, dynamic>);
final topTracks = (results[1] as List)
.map((json) => Track.fromJson(json as Map<String, dynamic>))
.toList();
final albums = (results[2] as List)
.map((json) => Album.fromJson(json as Map<String, dynamic>))
.toList();
// Load related tracks based on first top track
List<Track> relatedTracks = [];
if (topTracks.isNotEmpty) {
try {
final relatedData =
await _musicApiService.getRecommendations(topTracks.first.id);
relatedTracks = relatedData
.map((json) => Track.fromJson(json as Map<String, dynamic>))
.toList();
} catch (_) {
// Don't fail if recommendations fail
}
}
state = ArtistState(
artist: artist,
topTracks: topTracks,
albums: albums,
relatedTracks: relatedTracks,
isLoading: false,
);
} catch (e) {
state = state.copyWith(
isLoading: false,
error: e.toString(),
);
}
}
/// Clear state
void clear() {
state = const ArtistState();
}
}
/// Artist provider
final artistProvider =
StateNotifierProvider<ArtistNotifier, ArtistState>((ref) {
final musicApiService = ref.watch(musicApiServiceProvider);
return ArtistNotifier(musicApiService);
});
/// Artist data provider for a specific artist ID
final artistDataProvider = Provider.family<ArtistState, String>((ref, artistId) {
final notifier = ref.watch(artistProvider.notifier);
// Load data when first accessed
if (notifier.state.artist?.id != artistId) {
Future.microtask(() => notifier.loadAllArtistData(artistId));
}
return notifier.state;
});