a89c7894cf
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>
165 lines
4.4 KiB
Dart
165 lines
4.4 KiB
Dart
/// Music API Service
|
|
library;
|
|
|
|
import 'package:dio/dio.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../../core/constants/api_constants.dart';
|
|
import '../../../domain/entities/track.dart';
|
|
import 'api_service.dart';
|
|
|
|
/// Music API Service
|
|
class MusicApiService {
|
|
MusicApiService(this._dio);
|
|
|
|
final Dio _dio;
|
|
|
|
/// Search for music
|
|
Future<Map<String, dynamic>> search(
|
|
String query, {
|
|
String type = 'all',
|
|
int limit = 20,
|
|
int offset = 0,
|
|
}) async {
|
|
try {
|
|
final response = await _dio.get(
|
|
ApiConstants.searchMusic,
|
|
queryParameters: {
|
|
'q': query,
|
|
'type': type,
|
|
'limit': limit,
|
|
'offset': offset,
|
|
},
|
|
);
|
|
|
|
return response.data as Map<String, dynamic>;
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
|
|
/// Get track details
|
|
Future<Map<String, dynamic>> getTrack(String trackId) async {
|
|
try {
|
|
final response = await _dio.get('${ApiConstants.tracks}/$trackId');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
|
|
/// Get stream URL for a track
|
|
Future<Map<String, dynamic>> getStreamUrl(String trackId) async {
|
|
try {
|
|
final response = await _dio.get('${ApiConstants.tracks}/$trackId${ApiConstants.stream}');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
|
|
/// Get recommendations based on a track
|
|
Future<List<Map<String, dynamic>>> getRecommendations(
|
|
String trackId, {
|
|
int limit = 10,
|
|
}) async {
|
|
try {
|
|
final response = await _dio.get(
|
|
'${ApiConstants.recommendations}/$trackId/recommendations',
|
|
queryParameters: {'limit': limit},
|
|
);
|
|
return (response.data as List).cast<Map<String, dynamic>>();
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
|
|
/// Get trending tracks
|
|
Future<List<Map<String, dynamic>>> getTrending({int limit = 20}) async {
|
|
try {
|
|
final response = await _dio.get(
|
|
ApiConstants.trending,
|
|
queryParameters: {'limit': limit},
|
|
);
|
|
return (response.data as List).cast<Map<String, dynamic>>();
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
|
|
/// Get artist details
|
|
Future<Map<String, dynamic>> getArtist(String artistId) async {
|
|
try {
|
|
final response = await _dio.get('${ApiConstants.artists}/$artistId');
|
|
return response.data as Map<String, dynamic>;
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
|
|
/// Get artist's top tracks
|
|
Future<List<Map<String, dynamic>>> getArtistTopTracks(
|
|
String artistId, {
|
|
int limit = 10,
|
|
}) async {
|
|
try {
|
|
final response = await _dio.get(
|
|
'${ApiConstants.artists}/$artistId/tracks',
|
|
queryParameters: {'limit': limit},
|
|
);
|
|
return (response.data as List).cast<Map<String, dynamic>>();
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
|
|
/// Get artist's albums
|
|
Future<List<Map<String, dynamic>>> getArtistAlbums(
|
|
String artistId, {
|
|
int limit = 20,
|
|
}) async {
|
|
try {
|
|
final response = await _dio.get(
|
|
'${ApiConstants.artists}/$artistId/albums',
|
|
queryParameters: {'limit': limit},
|
|
);
|
|
return (response.data as List).cast<Map<String, dynamic>>();
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
|
|
/// Get album details
|
|
Future<Map<String, dynamic>> getAlbum(String albumId) async {
|
|
try {
|
|
final response = await _dio.get('${ApiConstants.albums}/$albumId');
|
|
return response.data as Map<String, dynamic>;
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
|
|
/// Get album tracks
|
|
Future<List<Map<String, dynamic>>> getAlbumTracks(String albumId) async {
|
|
try {
|
|
final response = await _dio.get('${ApiConstants.albums}/$albumId/tracks');
|
|
return (response.data as List).cast<Map<String, dynamic>>();
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
|
|
Exception _handleError(DioException error) {
|
|
if (error.response != null) {
|
|
final message = error.response!.data['detail'] ?? 'An error occurred';
|
|
return Exception('${error.response!.statusCode}: $message');
|
|
}
|
|
return Exception('Network error: ${error.message}');
|
|
}
|
|
}
|
|
|
|
final musicApiServiceProvider = Provider<MusicApiService>((ref) {
|
|
final dio = ref.watch(apiServiceProvider);
|
|
return MusicApiService(dio);
|
|
});
|