import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/providers/media.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; import 'package:just_audio/just_audio.dart'; import 'package:path/path.dart' as p; import 'package:file_picker/file_picker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:universal_html/html.dart' as html; class MediaService { static final audioPlayer = AudioPlayer(); static String? audioPlayerTag; static StudioDetailsData? currentPodcast; static StudioRequestArgs? podcastPlaylistArgs; static int maxSizeInBytes = 15 * 1024 * 1024; static Duration? get duration => audioPlayer.duration; static Future handleAudioPlayback({ required dynamic audioSource, required int id, bool isNetworkAudio = true, bool isVoiceMessage = true, bool isBlob = false, void Function(bool isNext)? onTrackChanged, }) async { try { String tag; tag = '${currentPodcast?.description == 'radar' ? 'radar' : isVoiceMessage ? 'message' : 'podcast'}-$id'; if (!isVoiceMessage && MediaProvider.downloadedItemIds.contains(id)) { audioSource = '${StorageService.appDocsDir}/podcasts/podcast-$id.mp3'; isNetworkAudio = false; } if (audioPlayerTag == tag) { if (audioPlayer.playing) { await audioPlayer.pause(); } else { await audioPlayer.play(); } return; } await audioPlayer.stop(); audioPlayerTag = tag; String source; if (isNetworkAudio) { if (isVoiceMessage) { source = '${RequestHelper.baseUrl + audioSource}?accessToken=${RequestService.token}'; } else { source = audioSource; } audioPlayer.setUrl( source, tag: isVoiceMessage ? null : { "artist": 'استودیو دیدوان', "title": currentPodcast?.title ?? '', }, ); } else { if (isBlob) { audioPlayer.setAudioSource(AudioSource.uri(Uri.parse(audioSource))); } else { audioPlayer.setFilePath( audioSource, tag: isVoiceMessage ? null : { "artist": 'استودیو دیدوان', "title": currentPodcast?.title ?? '', }, ); } } await audioPlayer.play(); } catch (e) { // rethrow; } } static Future resetAudioPlayer() async { audioPlayerTag = null; currentPodcast = null; podcastPlaylistArgs = null; await MediaService.audioPlayer.stop(); } static Future pickImage({required ImageSource source}) async { try { final imagePicker = ImagePicker(); final XFile? pickedFile = await imagePicker.pickImage(source: source); return pickedFile; } catch (e) { e.printError(info: 'Pick Image Fail'); return null; } } static Future pickPdfFile() async { try { final FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['pdf'], allowMultiple: false, ); return result; } catch (e) { e.printError(info: 'Pick PDF Fail'); return null; } } static Future pickMultiFile() async { try { return await FilePicker.platform .pickFiles( type: FileType.any, allowMultiple: true, ) .then((result) { if (result != null && result.files.length > 3) { return null; } return result; }); } catch (e) { e.printError(info: 'Pick Multi File Fail'); return null; } } static Future pickAudioFile() async { try { return await FilePicker.platform.pickFiles( type: FileType.audio, allowMultiple: false, ); } catch (e) { e.printError(info: 'Pick Audio Fail'); return null; } } static Future downloadFile(String url, {final String? name}) async { debugPrint("Attempting to download file from URL: $url"); final basename = name ?? p.basename(url).split('?accessToken=').first; PermissionStatus status; if (Platform.isAndroid) { final androidInfo = await DeviceInfoPlugin().androidInfo; if (androidInfo.version.sdkInt >= 33) { debugPrint("Android 13+ detected. Requesting Photos permission."); status = await Permission.photos.request(); } else { debugPrint("Older Android version detected. Requesting Storage permission."); status = await Permission.storage.request(); } } else if (Platform.isIOS) { debugPrint("iOS detected. Requesting Photos permission."); status = await Permission.photos.request(); } else { status = PermissionStatus.granted; } debugPrint("Permission status after request: $status"); if (status.isGranted) { Directory? dir; try { if (Platform.isIOS) { dir = await getApplicationDocumentsDirectory(); } else { dir = await getExternalStorageDirectory(); } if (dir == null) { debugPrint("Error: Could not determine download directory."); return null; } final path = dir.path; debugPrint("Download directory determined: $path"); final taskId = await FlutterDownloader.enqueue( url: url, savedDir: path, fileName: basename, showNotification: true, openFileFromNotification: true, saveInPublicStorage: true, ); debugPrint("Download successfully enqueued with taskId: $taskId"); return path; } catch (e) { debugPrint("Error during download process: $e"); return null; } } else if (status.isPermanentlyDenied) { debugPrint("Storage permission is permanently denied. Opening app settings."); await openAppSettings(); return null; } else { debugPrint("Storage permission was denied."); return null; } } static String downloadFileFromWeb(String url) { final filename = url.split('/').last.split('?').first; html.AnchorElement anchorElement = html.AnchorElement(href: url); anchorElement.download = filename; anchorElement.click(); return anchorElement.pathname!; } static onLoadingPickFile(BuildContext context) { ActionSheetUtils(context).openDialog( barrierDismissible: false, data: ActionSheetData( content: Center( child: Image.asset( Assets.loadingAnimation, width: 60, height: 60, ), ), hasConfirmButton: false, hasDismissButton: false, )); } }