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>
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
/// 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);
|
||||
});
|
||||
Reference in New Issue
Block a user