mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
Merge branch 'KRTirtho:master' into feature_duration_matching
This commit is contained in:
commit
0fce2c6347
@ -39,6 +39,7 @@ const imgMimeToExt = {
|
||||
};
|
||||
|
||||
final localTracksProvider = FutureProvider<List<Track>>((ref) async {
|
||||
try {
|
||||
final downloadDir = Directory(
|
||||
ref.watch(userPreferencesProvider.select((s) => s.downloadLocation)),
|
||||
);
|
||||
@ -47,22 +48,31 @@ final localTracksProvider = FutureProvider<List<Track>>((ref) async {
|
||||
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 {
|
||||
try {
|
||||
final bytes = f.readAsBytes();
|
||||
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(
|
||||
(await getTemporaryDirectory()).path,
|
||||
"spotube",
|
||||
basenameWithoutExtension(f.path) +
|
||||
imgMimeToExt[
|
||||
mp3Instance.metaTags["APIC"]?["mime"] ?? "image/jpeg"]!,
|
||||
imgMimeToExt[mp3Instance.metaTags["APIC"]?["mime"] ??
|
||||
"image/jpeg"]!,
|
||||
))
|
||||
: null;
|
||||
if (imageFile != null &&
|
||||
@ -91,6 +101,14 @@ final localTracksProvider = FutureProvider<List<Track>>((ref) async {
|
||||
"art": imageFile?.path,
|
||||
"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();
|
||||
|
||||
return tracks;
|
||||
} catch (e, stack) {
|
||||
getLogger(FutureProvider).e("[LocalTracksProvider]", e, stack);
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
class UserLocalTracks extends HookConsumerWidget {
|
||||
|
@ -63,6 +63,66 @@ class Settings extends HookConsumerWidget {
|
||||
constraints: const BoxConstraints(maxWidth: 1366),
|
||||
child: ListView(
|
||||
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(
|
||||
leading: const Icon(Icons.dark_mode_outlined),
|
||||
title: const Text("Theme"),
|
||||
@ -122,6 +182,55 @@ class Settings extends HookConsumerWidget {
|
||||
),
|
||||
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(
|
||||
leading: const Icon(Icons.shopping_bag_rounded),
|
||||
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(
|
||||
leading: const Icon(Icons.screen_search_desktop_rounded),
|
||||
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(
|
||||
leading: const Icon(Icons.low_priority_rounded),
|
||||
title: const AutoSizeText(
|
||||
@ -294,56 +333,37 @@ class Settings extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
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(() {});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
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,
|
||||
const Text(
|
||||
" Downloads",
|
||||
style:
|
||||
TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.file_download_outlined),
|
||||
title: const Text("Download Location"),
|
||||
subtitle: Text(preferences.downloadLocation),
|
||||
trailing: ElevatedButton(
|
||||
child: const Text("Logout"),
|
||||
style: ButtonStyle(
|
||||
backgroundColor:
|
||||
MaterialStateProperty.all(Colors.red),
|
||||
foregroundColor:
|
||||
MaterialStateProperty.all(Colors.white),
|
||||
child: const Icon(Icons.folder_rounded),
|
||||
onPressed: pickDownloadLocation,
|
||||
),
|
||||
onPressed: () async {
|
||||
auth.logout();
|
||||
GoRouter.of(context).pop();
|
||||
onTap: pickDownloadLocation,
|
||||
),
|
||||
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(
|
||||
leading: const Icon(
|
||||
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()
|
||||
],
|
||||
),
|
||||
|
@ -64,6 +64,20 @@ class Id3Tags {
|
||||
"genre": genre,
|
||||
"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 {
|
||||
|
@ -16,6 +16,7 @@ import 'package:spotube/models/SpotubeTrack.dart';
|
||||
import 'package:spotube/provider/Playback.dart';
|
||||
import 'package:spotube/provider/UserPreferences.dart';
|
||||
import 'package:spotube/provider/YouTube.dart';
|
||||
import 'package:spotube/utils/platform.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
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",
|
||||
);
|
||||
|
||||
// Tagging isn't supported in Android currently
|
||||
if (kIsAndroid) return;
|
||||
|
||||
final imageUri = TypeConversionUtils.image_X_UrlString(
|
||||
track.album?.images ?? [],
|
||||
);
|
||||
final response = await get(
|
||||
Uri.parse(
|
||||
TypeConversionUtils.image_X_UrlString(
|
||||
track.album?.images ?? [],
|
||||
),
|
||||
imageUri,
|
||||
),
|
||||
);
|
||||
final picture = AttachedPicture.base64(
|
||||
|
@ -463,13 +463,6 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1417,5 +1410,5 @@ packages:
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
sdks:
|
||||
dart: ">=2.17.5 <3.0.0"
|
||||
dart: ">=2.17.1 <3.0.0"
|
||||
flutter: ">=3.0.0"
|
||||
|
Loading…
Reference in New Issue
Block a user