import 'dart:async'; import 'dart:io'; import 'package:spotube/services/logger/logger.dart'; import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:metadata_god/metadata_god.dart'; import 'package:mime/mime.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/extensions/track.dart'; import 'package:spotube/models/local_track.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; // ignore: depend_on_referenced_packages import 'package:flutter_rust_bridge/flutter_rust_bridge.dart' show FrbException; const supportedAudioTypes = [ "audio/webm", "audio/ogg", "audio/mpeg", "audio/mp4", "audio/opus", "audio/wav", "audio/aac", ]; const imgMimeToExt = { "image/png": ".png", "image/jpeg": ".jpg", "image/webp": ".webp", "image/gif": ".gif", }; final localTracksProvider = FutureProvider>>((ref) async { try { if (kIsWeb) return {}; final Map> libraryToTracks = {}; final downloadLocation = ref.watch( userPreferencesProvider.select((s) => s.downloadLocation), ); final downloadDir = Directory(downloadLocation); if (!await downloadDir.exists()) { await downloadDir.create(recursive: true); } final localLibraryLocations = ref.watch( userPreferencesProvider.select((s) => s.localLibraryLocation), ); for (final location in [downloadLocation, ...localLibraryLocations]) { if (location.isEmpty) continue; final entities = []; if (await Directory(location).exists()) { try { final dirEntities = await Directory(location).list(recursive: true).toList(); entities.addAll( dirEntities .where( (e) => e is File && supportedAudioTypes.contains(lookupMimeType(e.path)), ) .cast(), ); } catch (e, stack) { AppLogger.reportError(e, stack); } } final List> filesWithMetadata = []; for (final file in entities) { try { final metadata = await MetadataGod.readMetadata(file: file.path); await Future.delayed(const Duration(milliseconds: 50)); final imageFile = File(join( (await getTemporaryDirectory()).path, "spotube", basenameWithoutExtension(file.path) + imgMimeToExt[metadata.picture?.mimeType ?? "image/jpeg"]!, )); if (!await imageFile.exists() && metadata.picture != null) { await imageFile.create(recursive: true); await imageFile.writeAsBytes( metadata.picture?.data ?? [], mode: FileMode.writeOnly, ); } filesWithMetadata.add( {"metadata": metadata, "file": file, "art": imageFile.path}, ); } catch (e, stack) { if (e case FrbException() || TimeoutException()) { filesWithMetadata.add({"file": file}); } AppLogger.reportError(e, stack); continue; } } final tracksFromMetadata = filesWithMetadata .map( (fileWithMetadata) => LocalTrack.fromTrack( track: Track().fromFile( fileWithMetadata["file"], metadata: fileWithMetadata["metadata"], art: fileWithMetadata["art"], ), path: fileWithMetadata["file"].path, ), ) .toList(); libraryToTracks[location] = tracksFromMetadata; } return libraryToTracks; } catch (e, stack) { AppLogger.reportError(e, stack); return {}; } });