Merge branch 'KRTirtho:master' into feature_duration_matching

This commit is contained in:
Demizo 2022-09-03 11:46:41 -05:00 committed by GitHub
commit 0fce2c6347
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 258 additions and 194 deletions

View File

@ -39,6 +39,7 @@ const imgMimeToExt = {
}; };
final localTracksProvider = FutureProvider<List<Track>>((ref) async { final localTracksProvider = FutureProvider<List<Track>>((ref) async {
try {
final downloadDir = Directory( final downloadDir = Directory(
ref.watch(userPreferencesProvider.select((s) => s.downloadLocation)), ref.watch(userPreferencesProvider.select((s) => s.downloadLocation)),
); );
@ -47,22 +48,31 @@ final localTracksProvider = FutureProvider<List<Track>>((ref) async {
return []; return [];
} }
final entities = downloadDir.listSync(recursive: true); final entities = downloadDir.listSync(recursive: true);
final filesWithMetadata = (await Future.wait( final filesWithMetadata = (await Future.wait(
entities.map((e) => File(e.path)).where((file) { entities.map((e) => File(e.path)).where((file) {
final mimetype = lookupMimeType(file.path); final mimetype = lookupMimeType(file.path);
return mimetype != null && supportedAudioTypes.contains(mimetype); return mimetype != null && supportedAudioTypes.contains(mimetype);
}).map( }).map(
(f) async { (f) async {
try {
final bytes = f.readAsBytes(); final bytes = f.readAsBytes();
final mp3Instance = MP3Instance(await bytes); final mp3Instance = MP3Instance(await bytes);
final imageFile = mp3Instance.parseTagsSync() bool isParsed = false;
try {
isParsed = mp3Instance.parseTagsSync();
} catch (e, stack) {
getLogger(MP3Instance).e("[parseTagsSync]", e, stack);
}
final imageFile = isParsed
? File(join( ? File(join(
(await getTemporaryDirectory()).path, (await getTemporaryDirectory()).path,
"spotube", "spotube",
basenameWithoutExtension(f.path) + basenameWithoutExtension(f.path) +
imgMimeToExt[ imgMimeToExt[mp3Instance.metaTags["APIC"]?["mime"] ??
mp3Instance.metaTags["APIC"]?["mime"] ?? "image/jpeg"]!, "image/jpeg"]!,
)) ))
: null; : null;
if (imageFile != null && if (imageFile != null &&
@ -91,6 +101,14 @@ final localTracksProvider = FutureProvider<List<Track>>((ref) async {
"art": imageFile?.path, "art": imageFile?.path,
"duration": duration, "duration": duration,
}; };
} catch (e, stack) {
getLogger(FutureProvider).e("[Fetching metadata]", e, stack);
return {
"metadata": <Tag>[],
"file": f,
"duration": Duration.zero,
};
}
}, },
), ),
)); ));
@ -107,6 +125,10 @@ final localTracksProvider = FutureProvider<List<Track>>((ref) async {
.toList(); .toList();
return tracks; return tracks;
} catch (e, stack) {
getLogger(FutureProvider).e("[LocalTracksProvider]", e, stack);
return [];
}
}); });
class UserLocalTracks extends HookConsumerWidget { class UserLocalTracks extends HookConsumerWidget {

View File

@ -63,6 +63,66 @@ class Settings extends HookConsumerWidget {
constraints: const BoxConstraints(maxWidth: 1366), constraints: const BoxConstraints(maxWidth: 1366),
child: ListView( child: ListView(
children: [ children: [
const Text(
" Account",
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
if (auth.isAnonymous)
AdaptiveListTile(
leading: Icon(
Icons.login_rounded,
color: Theme.of(context).primaryColor,
),
title: AutoSizeText(
"Login with your Spotify account",
style: TextStyle(
color: Theme.of(context).primaryColor,
),
),
trailing: (context, update) => ElevatedButton(
child: Text("Connect with Spotify".toUpperCase()),
onPressed: () {
GoRouter.of(context).push("/login");
},
style: ButtonStyle(
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25.0),
),
),
),
),
),
if (auth.isLoggedIn)
Builder(builder: (context) {
Auth auth = ref.watch(authProvider);
return ListTile(
leading: const Icon(Icons.logout_rounded),
title: const AutoSizeText(
"Log out of this account",
maxLines: 1,
),
trailing: ElevatedButton(
child: const Text("Logout"),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.red),
foregroundColor:
MaterialStateProperty.all(Colors.white),
),
onPressed: () async {
auth.logout();
GoRouter.of(context).pop();
},
),
);
}),
const Text(
" Appearance",
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
AdaptiveListTile( AdaptiveListTile(
leading: const Icon(Icons.dark_mode_outlined), leading: const Icon(Icons.dark_mode_outlined),
title: const Text("Theme"), title: const Text("Theme"),
@ -122,6 +182,55 @@ class Settings extends HookConsumerWidget {
), ),
onTap: pickColorScheme(ColorSchemeType.background), onTap: pickColorScheme(ColorSchemeType.background),
), ),
const Text(
" Playback",
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
AdaptiveListTile(
leading: const Icon(Icons.multitrack_audio_rounded),
title: const Text("Audio Quality"),
trailing: (context, update) =>
DropdownButton<AudioQuality>(
value: preferences.audioQuality,
items: const [
DropdownMenuItem(
child: Text(
"High",
),
value: AudioQuality.high,
),
DropdownMenuItem(
child: Text("Low"),
value: AudioQuality.low,
),
],
onChanged: (value) {
if (value != null) {
preferences.setAudioQuality(value);
update?.call(() {});
}
},
),
),
ListTile(
leading: const Icon(Icons.fast_forward_rounded),
title: const Text(
"Skip non-music segments (SponsorBlock)",
),
trailing: Switch.adaptive(
activeColor: Theme.of(context).primaryColor,
value: preferences.skipSponsorSegments,
onChanged: (state) {
preferences.setSkipSponsorSegments(state);
},
),
),
const Text(
" Search",
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
AdaptiveListTile( AdaptiveListTile(
leading: const Icon(Icons.shopping_bag_rounded), leading: const Icon(Icons.shopping_bag_rounded),
title: Text( title: Text(
@ -155,16 +264,6 @@ class Settings extends HookConsumerWidget {
), ),
), ),
), ),
ListTile(
leading: const Icon(Icons.file_download_outlined),
title: const Text("Download Location"),
subtitle: Text(preferences.downloadLocation),
trailing: ElevatedButton(
child: const Icon(Icons.folder_rounded),
onPressed: pickDownloadLocation,
),
onTap: pickDownloadLocation,
),
AdaptiveListTile( AdaptiveListTile(
leading: const Icon(Icons.screen_search_desktop_rounded), leading: const Icon(Icons.screen_search_desktop_rounded),
title: const AutoSizeText( title: const AutoSizeText(
@ -195,66 +294,6 @@ class Settings extends HookConsumerWidget {
), ),
), ),
), ),
ListTile(
leading: const Icon(Icons.fast_forward_rounded),
title: const Text(
"Skip non-music segments (SponsorBlock)",
),
trailing: Switch.adaptive(
activeColor: Theme.of(context).primaryColor,
value: preferences.skipSponsorSegments,
onChanged: (state) {
preferences.setSkipSponsorSegments(state);
},
),
),
ListTile(
leading: const Icon(Icons.lyrics_rounded),
title: const Text("Download lyrics along with the Track"),
trailing: Switch.adaptive(
activeColor: Theme.of(context).primaryColor,
value: preferences.saveTrackLyrics,
onChanged: (state) {
preferences.setSaveTrackLyrics(state);
},
),
),
if (auth.isAnonymous)
AdaptiveListTile(
leading: Icon(
Icons.login_rounded,
color: Theme.of(context).primaryColor,
),
title: AutoSizeText(
"Login with your Spotify account",
style: TextStyle(
color: Theme.of(context).primaryColor,
),
),
trailing: (context, update) => ElevatedButton(
child: Text("Connect with Spotify".toUpperCase()),
onPressed: () {
GoRouter.of(context).push("/login");
},
style: ButtonStyle(
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25.0),
),
),
),
),
),
ListTile(
leading: const Icon(Icons.update_rounded),
title: const Text("Check for Update"),
trailing: Switch.adaptive(
activeColor: Theme.of(context).primaryColor,
value: preferences.checkUpdate,
onChanged: (checked) =>
preferences.setCheckUpdate(checked),
),
),
AdaptiveListTile( AdaptiveListTile(
leading: const Icon(Icons.low_priority_rounded), leading: const Icon(Icons.low_priority_rounded),
title: const AutoSizeText( title: const AutoSizeText(
@ -294,56 +333,37 @@ class Settings extends HookConsumerWidget {
}, },
), ),
), ),
AdaptiveListTile( const Text(
leading: const Icon(Icons.multitrack_audio_rounded), " Downloads",
title: const Text("Audio Quality"), style:
trailing: (context, update) => TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
DropdownButton<AudioQuality>(
value: preferences.audioQuality,
items: const [
DropdownMenuItem(
child: Text(
"High",
),
value: AudioQuality.high,
),
DropdownMenuItem(
child: Text("Low"),
value: AudioQuality.low,
),
],
onChanged: (value) {
if (value != null) {
preferences.setAudioQuality(value);
update?.call(() {});
}
},
),
),
if (auth.isLoggedIn)
Builder(builder: (context) {
Auth auth = ref.watch(authProvider);
return ListTile(
leading: const Icon(Icons.logout_rounded),
title: const AutoSizeText(
"Log out of this account",
maxLines: 1,
), ),
ListTile(
leading: const Icon(Icons.file_download_outlined),
title: const Text("Download Location"),
subtitle: Text(preferences.downloadLocation),
trailing: ElevatedButton( trailing: ElevatedButton(
child: const Text("Logout"), child: const Icon(Icons.folder_rounded),
style: ButtonStyle( onPressed: pickDownloadLocation,
backgroundColor:
MaterialStateProperty.all(Colors.red),
foregroundColor:
MaterialStateProperty.all(Colors.white),
), ),
onPressed: () async { onTap: pickDownloadLocation,
auth.logout(); ),
GoRouter.of(context).pop(); ListTile(
leading: const Icon(Icons.lyrics_rounded),
title: const Text("Download lyrics along with the Track"),
trailing: Switch.adaptive(
activeColor: Theme.of(context).primaryColor,
value: preferences.saveTrackLyrics,
onChanged: (state) {
preferences.setSaveTrackLyrics(state);
}, },
), ),
); ),
}), const Text(
" About",
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
AdaptiveListTile( AdaptiveListTile(
leading: const Icon( leading: const Icon(
Icons.favorite_border_rounded, Icons.favorite_border_rounded,
@ -373,6 +393,16 @@ class Settings extends HookConsumerWidget {
}, },
), ),
), ),
ListTile(
leading: const Icon(Icons.update_rounded),
title: const Text("Check for Update"),
trailing: Switch.adaptive(
activeColor: Theme.of(context).primaryColor,
value: preferences.checkUpdate,
onChanged: (checked) =>
preferences.setCheckUpdate(checked),
),
),
const About() const About()
], ],
), ),

View File

@ -64,6 +64,20 @@ class Id3Tags {
"genre": genre, "genre": genre,
"picture": picture, "picture": picture,
}; };
String? get artist => tpe2;
String? get year => tdrc;
Map<String, String> toAndroidJson(String artwork) {
return {
"title": title ?? "Unknown",
"artist": artist ?? "Unknown",
"album": album ?? "Unknown",
"genre": genre ?? "Unknown",
"artwork": artwork,
"year": year ?? "Unknown",
};
}
} }
extension CommentJson on Comment { extension CommentJson on Comment {

View File

@ -16,6 +16,7 @@ import 'package:spotube/models/SpotubeTrack.dart';
import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/Playback.dart';
import 'package:spotube/provider/UserPreferences.dart'; import 'package:spotube/provider/UserPreferences.dart';
import 'package:spotube/provider/YouTube.dart'; import 'package:spotube/provider/YouTube.dart';
import 'package:spotube/utils/platform.dart';
import 'package:spotube/utils/type_conversion_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart' hide Comment; import 'package:youtube_explode_dart/youtube_explode_dart.dart' hide Comment;
@ -96,11 +97,15 @@ class Downloader with ChangeNotifier {
"[addToQueue] Download of ${file.path} is done successfully", "[addToQueue] Download of ${file.path} is done successfully",
); );
// Tagging isn't supported in Android currently
if (kIsAndroid) return;
final imageUri = TypeConversionUtils.image_X_UrlString(
track.album?.images ?? [],
);
final response = await get( final response = await get(
Uri.parse( Uri.parse(
TypeConversionUtils.image_X_UrlString( imageUri,
track.album?.images ?? [],
),
), ),
); );
final picture = AttachedPicture.base64( final picture = AttachedPicture.base64(

View File

@ -463,13 +463,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
eztags:
dependency: "direct main"
description:
name: eztags
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
fading_edge_scrollview: fading_edge_scrollview:
dependency: transitive dependency: transitive
description: description:
@ -1417,5 +1410,5 @@ packages:
source: hosted source: hosted
version: "1.11.0" version: "1.11.0"
sdks: sdks:
dart: ">=2.17.5 <3.0.0" dart: ">=2.17.1 <3.0.0"
flutter: ">=3.0.0" flutter: ">=3.0.0"