9c504d2c3d
Features: - Frontend Flutter avec thème néon cyberpunk - Backend FastAPI avec streaming YouTube - Base de données PostgreSQL + Redis - Authentification JWT complète - Recherche multi-source (DB + YouTube) - Playlists CRUD avec drag & drop - Queue management - Settings avec audio quality - Interface adaptative (Desktop + Mobile) Tech Stack: - Frontend: Flutter 3.2+, Riverpod - Backend: Python 3.11+, FastAPI - Database: PostgreSQL 15+ - Cache: Redis 7+ - Streaming: yt-dlp + FFmpeg 🚀 Generated with Claude Code Co-Authored-By: Claude <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);
|
|
});
|