From c3bf5119ebb7c17e8c32f149598508674b0acd39 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Tue, 30 Aug 2022 16:08:01 +0600 Subject: [PATCH] feat(broken): Broken Warning! Initial Local Audio Player --- .vscode/launch.json | 3 +- lib/components/Library/UserLibrary.dart | 5 +- lib/components/Library/UserLocalTracks.dart | 71 +++++++++++++++++++ lib/provider/Playback.dart | 2 +- lib/utils/type_conversion_utils.dart | 32 +++++++++ linux/flutter/generated_plugin_registrant.cc | 4 ++ linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 11 ++- pubspec.yaml | 3 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 12 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 lib/components/Library/UserLocalTracks.dart diff --git a/.vscode/launch.json b/.vscode/launch.json index e6eedc57..b3769b2b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,8 +6,7 @@ "type": "dart", "request": "launch", "program": "lib/main.dart" - } - + }, ], "compounds": [] } \ No newline at end of file diff --git a/lib/components/Library/UserLibrary.dart b/lib/components/Library/UserLibrary.dart index 098298f9..760d34fc 100644 --- a/lib/components/Library/UserLibrary.dart +++ b/lib/components/Library/UserLibrary.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotube/components/Library/UserAlbums.dart'; import 'package:spotube/components/Library/UserArtists.dart'; import 'package:spotube/components/Library/UserDownloads.dart'; +import 'package:spotube/components/Library/UserLocalTracks.dart'; import 'package:spotube/components/Library/UserPlaylists.dart'; import 'package:spotube/components/Shared/AnonymousFallback.dart'; @@ -12,7 +13,7 @@ class UserLibrary extends ConsumerWidget { Widget build(BuildContext context, ref) { return Expanded( child: DefaultTabController( - length: 4, + length: 5, child: SafeArea( child: Scaffold( appBar: const TabBar( @@ -22,6 +23,7 @@ class UserLibrary extends ConsumerWidget { Tab(text: "Artists"), Tab(text: "Album"), Tab(text: "Downloads"), + Tab(text: "Local"), ], ), body: TabBarView(children: [ @@ -29,6 +31,7 @@ class UserLibrary extends ConsumerWidget { AnonymousFallback(child: UserArtists()), const AnonymousFallback(child: UserAlbums()), const UserDownloads(), + const UserLocalTracks(), ]), ), ), diff --git a/lib/components/Library/UserLocalTracks.dart b/lib/components/Library/UserLocalTracks.dart new file mode 100644 index 00000000..2aa56cc9 --- /dev/null +++ b/lib/components/Library/UserLocalTracks.dart @@ -0,0 +1,71 @@ +import 'dart:io'; + +import 'package:flutter/widgets.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_media_metadata/flutter_media_metadata.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mime/mime.dart'; +import 'package:spotify/spotify.dart'; +import 'package:spotube/provider/UserPreferences.dart'; +import 'package:spotube/utils/type_conversion_utils.dart'; + +const supportedAudioTypes = [ + "audio/webm", + "audio/ogg", + "audio/mpeg", + "audio/opus", + "audio/wav", + "audio/aac", +]; + +List usePullLocalTracks(WidgetRef ref) { + final downloadDir = Directory( + ref.watch(userPreferencesProvider.select((s) => s.downloadLocation)), + ); + final localTracks = useState>([]); + + useEffect(() { + (() async { + if (!await downloadDir.exists()) { + await downloadDir.create(recursive: true); + return; + } + final entities = downloadDir.listSync(recursive: true); + final filesWithMetadata = (await Future.wait( + entities.map((e) => File(e.path)).where((file) { + final mimetype = lookupMimeType(file.path); + return mimetype != null && supportedAudioTypes.contains(mimetype); + }).map( + (f) async => { + "metadata": await MetadataRetriever.fromFile(f), + "file": f, + }, + ), + )); + + final tracks = filesWithMetadata + .map( + (fileWithMetadata) => TypeConversionUtils.localTrack_X_Track( + fileWithMetadata["metadata"] as Metadata, + fileWithMetadata["file"] as File), + ) + .toList(); + + localTracks.value = tracks; + })(); + + return; + }, [downloadDir]); + + return localTracks.value; +} + +class UserLocalTracks extends HookConsumerWidget { + const UserLocalTracks({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, ref) { + final tracks = usePullLocalTracks(ref); + return Column(); + } +} diff --git a/lib/provider/Playback.dart b/lib/provider/Playback.dart index c679d9d5..9a2573ff 100644 --- a/lib/provider/Playback.dart +++ b/lib/provider/Playback.dart @@ -126,7 +126,7 @@ class Playback extends PersistedChangeNotifier { playlist?.tracks.indexWhere((t) => t.id == track?.id); // when the track progress is above 80%, track isn't the last - // and is not already fetch and nothing is fetching currently + // and is not already fetched and nothing is fetching currently if (pos.inSeconds > currentDuration.inSeconds * .8 && playlist != null && currentTrackIndex != playlist!.tracks.length - 1 && diff --git a/lib/utils/type_conversion_utils.dart b/lib/utils/type_conversion_utils.dart index ce8181bf..7d617da9 100644 --- a/lib/utils/type_conversion_utils.dart +++ b/lib/utils/type_conversion_utils.dart @@ -1,6 +1,9 @@ // ignore_for_file: non_constant_identifier_names +import 'dart:io'; + import 'package:flutter/widgets.dart' hide Image; +import 'package:flutter_media_metadata/flutter_media_metadata.dart'; import 'package:spotube/components/Shared/LinkText.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/utils/primitive_utils.dart'; @@ -81,4 +84,33 @@ abstract class TypeConversionUtils { track.uri = trackSmp.uri; return track; } + + static Track localTrack_X_Track(Metadata metadata, File file) { + final track = Track(); + track.album = Album() + ..name = metadata.albumName + ..genres = [if (metadata.genre != null) metadata.genre!] + ..artists = [ + Artist() + ..name = metadata.albumArtistName + ..id = metadata.albumArtistName + ..type = "artist", + ] + ..id = "${metadata.albumName}${metadata.albumLength}"; + track.artists = metadata.trackArtistNames + ?.map((name) => Artist() + ..name = name + ..id = name) + .toList(); + + track.discNumber = metadata.discNumber; + track.durationMs = metadata.trackDuration; + track.id = "${metadata.trackName}${metadata.trackDuration}"; + track.name = metadata.trackName; + track.trackNumber = metadata.trackNumber; + track.type = "track"; + track.uri = file.path; + + return track; + } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 01b8e0f7..cce5b687 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { @@ -17,6 +18,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin"); bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar); + g_autoptr(FlPluginRegistrar) flutter_media_metadata_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterMediaMetadataPlugin"); + flutter_media_metadata_plugin_register_with_registrar(flutter_media_metadata_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 9aebc645..4e314d80 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux bitsdojo_window_linux + flutter_media_metadata url_launcher_linux ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 3e375fb3..594b241f 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,6 +9,7 @@ import audio_service import audio_session import audioplayers_darwin import bitsdojo_window_macos +import flutter_media_metadata import package_info_plus_macos import path_provider_macos import shared_preferences_macos @@ -20,6 +21,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin")) + FlutterMediaMetadataPlugin.register(with: registry.registrar(forPlugin: "FlutterMediaMetadataPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) diff --git a/pubspec.lock b/pubspec.lock index bc06f3ac..ce4760d1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -573,6 +573,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + flutter_media_metadata: + dependency: "direct main" + description: + path: "../flutter_media_metadata" + relative: true + source: path + version: "1.0.0" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -787,7 +794,7 @@ packages: source: hosted version: "1.7.0" mime: - dependency: transitive + dependency: "direct main" description: name: mime url: "https://pub.dartlang.org" @@ -1389,5 +1396,5 @@ packages: source: hosted version: "1.11.0" sdks: - dart: ">=2.17.1 <3.0.0" + dart: ">=2.17.5 <3.0.0" flutter: ">=3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index abdfc023..3d6b2bc1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -70,6 +70,9 @@ dependencies: queue: ^3.1.0+1 auto_size_text: ^3.0.0 badges: ^2.0.3 + mime: ^1.0.2 + flutter_media_metadata: + path: ../flutter_media_metadata dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 3e689c38..c8153a66 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -16,6 +17,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); BitsdojoWindowPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("BitsdojoWindowPlugin")); + FlutterMediaMetadataPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterMediaMetadataPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index c8e970a8..d8bb9c42 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_windows bitsdojo_window_windows + flutter_media_metadata permission_handler_windows url_launcher_windows )