import 'package:dio/dio.dart'; import 'package:path/path.dart'; class DownloadService { late Dio _dio; final String baseUrl; final String _downloadPath; DownloadService({String? baseUrl, String? downloadPath}) : baseUrl = baseUrl ?? 'https://your-server.com', _downloadPath = downloadPath ?? '/storage/emulated/0/Android/data/com.example.swingmusic/files/Downloads'; DownloadService() { _dio = Dio(BaseOptions( baseUrl: baseUrl, connectTimeout: const Duration(seconds: 30), receiveTimeout: const Duration(seconds: 30), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, )); _dio.interceptors.add(LogInterceptor( requestBody: true, responseBody: true, logPrint: (obj) { print('Download API: $obj'); }, )); _dio.interceptors.add(InterceptorsWrapper( onError: (error, handler) { String errorMessage = 'Download failed'; if (error.type == DioExceptionType.connectionTimeout || error.type == DioExceptionType.receiveTimeout) { errorMessage = 'Network timeout - please check your connection'; } else if (error.response?.statusCode == 404) { errorMessage = 'Download not found'; } else if (error.response?.statusCode == 500) { errorMessage = 'Server error - please try again later'; } else if (error.response?.statusCode == 503) { errorMessage = 'Service unavailable - downloads are disabled'; } print('Download Error: $errorMessage'); handler.next(error); }, )); } void setAuthToken(String token) { _dio.options.headers['Authorization'] = 'Bearer $token'; } Future> getDownloads() async { try { final response = await _dio.get('/downloads'); if (response.statusCode == 200) { return response.data as Map? ?? {}; } else { return {}; } } catch (e) { print('Error fetching downloads: $e'); return {}; } } Future?> getDownload(String downloadId) async { try { final response = await _dio.get('/download/$downloadId'); if (response.statusCode == 200) { return response.data as Map?; } else { return null; } } catch (e) { print('Error fetching download: $e'); return null; } } Future downloadTrack(String trackHash, { String quality = '320kbps', bool wifiOnly = false, }) async { try { final response = await _dio.post('/download/track', data: { 'trackHash': trackHash, 'quality': quality, 'wifiOnly': wifiOnly, }); if (response.statusCode == 200) { final data = response.data; return data['downloadId'] as String? ?? ''; } else { throw Exception('Failed to start download'); } } catch (e) { throw Exception('Failed to start download: $e'); } } Future downloadAlbum(String albumHash, { String quality = '320kbps', bool wifiOnly = false, }) async { try { final response = await _dio.post('/download/album', data: { 'albumHash': albumHash, 'quality': quality, 'wifiOnly': wifiOnly, }); if (response.statusCode == 200) { final data = response.data; return data['downloadId'] as String? ?? ''; } else { throw Exception('Failed to start download'); } } catch (e) { throw Exception('Failed to start download: $e'); } } Future downloadArtist(String artistHash, { String quality = '320kbps', bool wifiOnly = false, }) async { try { final response = await _dio.post('/download/artist', data: { 'artistHash': artistHash, 'quality': quality, 'wifiOnly': wifiOnly, }); if (response.statusCode == 200) { final data = response.data; return data['downloadId'] as String? ?? ''; } else { throw Exception('Failed to start download'); } } catch (e) { throw Exception('Failed to start download: $e'); } } Future downloadPlaylist(String playlistId, { String quality = '320kbps', bool wifiOnly = false, }) async { try { final response = await _dio.post('/download/playlist', data: { 'playlistId': playlistId, 'quality': quality, 'wifiOnly': wifiOnly, }); if (response.statusCode == 200) { final data = response.data; return data['downloadId'] as String? ?? ''; } else { throw Exception('Failed to start download'); } } catch (e) { throw Exception('Failed to start download: $e'); } } Future pauseDownload(String downloadId) async { try { final response = await _dio.post('/download/$downloadId/pause'); return response.statusCode == 200; } catch (e) { print('Error pausing download: $e'); return false; } } Future resumeDownload(String downloadId) async { try { final response = await _dio.post('/download/$downloadId/resume'); return response.statusCode == 200; } catch (e) { print('Error resuming download: $e'); return false; } } Future cancelDownload(String downloadId) async { try { final response = await _dio.post('/download/$downloadId/cancel'); return response.statusCode == 200; } catch (e) { print('Error canceling download: $e'); return false; } } Future deleteDownload(String downloadId) async { try { final response = await _dio.delete('/download/$downloadId'); return response.statusCode == 200; } catch (e) { print('Error deleting download: $e'); return false; } } Future> getDownloadStats() async { try { final response = await _dio.get('/download/stats'); if (response.statusCode == 200) { return response.data as Map? ?? {}; } else { return {}; } } catch (e) { print('Error fetching download stats: $e'); return {}; } } Future getDownloadPath() async { // TODO: Implement actual path resolution return _downloadPath; } Future updateDownloadSettings({ String? downloadPath, String? defaultQuality, bool? wifiOnly, int? maxConcurrentDownloads, }) async { try { final response = await _dio.post('/download/settings', data: { if (downloadPath != null) 'downloadPath': downloadPath, if (defaultQuality != null) 'defaultQuality': defaultQuality, if (wifiOnly != null) 'wifiOnly': wifiOnly, if (maxConcurrentDownloads != null) 'maxConcurrentDownloads': maxConcurrentDownloads, }); return response.statusCode == 200; } catch (e) { print('Error updating download settings: $e'); return false; } } Future> getDownloadSettings() async { try { final response = await _dio.get('/download/settings'); if (response.statusCode == 200) { return response.data as Map? ?? {}; } else { return {}; } } catch (e) { print('Error fetching download settings: $e'); return {}; } } Stream> watchDownloadProgress(String downloadId) { // TODO: Implement WebSocket or SSE for real-time progress updates // For now, return periodic polling return Stream.periodic(const Duration(seconds: 1), (count) async { final download = await getDownload(downloadId); if (download != null) { return { 'downloadId': downloadId, 'progress': download['progress'] ?? 0.0, 'status': download['status'] ?? 'unknown', 'speed': download['speed'] ?? 0.0, 'eta': download['eta'] ?? 0, }; } return {}; }); } }