diff --git a/README.md b/README.md index 3978acbe..b82693fc 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,8 @@ $ flatpak install flathub com.github.KRTirtho.Spotube ## Mac OS Download the [Mac OS Disk Image (.dmg) file](https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-macos-x86_64.dmg) from the release & follow along the setup wizard - -**I'll/try to upload the package binaries to linux debian/arch/ubuntu/snap/flatpack/redhat/chocolatey/homebrew stores or software centers or repositories** +## Nightly Builds +Get the latest nightly builds of Spotube [here](https://nightly.link/KRTirtho/spotube/workflows/flutter-build/build) ## Optional Configurations
diff --git a/lib/components/Settings.dart b/lib/components/Settings.dart index 95e4b830..8f01fe32 100644 --- a/lib/components/Settings.dart +++ b/lib/components/Settings.dart @@ -163,6 +163,19 @@ class Settings extends HookConsumerWidget { ], ), const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("Download lyrics along with the Track"), + Switch.adaptive( + value: preferences.saveTrackLyrics, + onChanged: (state) { + preferences.setSaveTrackLyrics(state); + }, + ), + ], + ), + const SizedBox(height: 10), if (auth.isAnonymous) Wrap( spacing: 20, diff --git a/lib/components/Shared/DownloadTrackButton.dart b/lib/components/Shared/DownloadTrackButton.dart index baaf45a5..5bee714f 100644 --- a/lib/components/Shared/DownloadTrackButton.dart +++ b/lib/components/Shared/DownloadTrackButton.dart @@ -2,8 +2,12 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/helpers/artist-to-string.dart'; +import 'package:spotube/helpers/getLyrics.dart'; +import 'package:spotube/provider/Playback.dart'; +import 'package:spotube/provider/UserPreferences.dart'; import 'package:youtube_explode_dart/youtube_explode_dart.dart'; import 'package:path_provider/path_provider.dart' as path_provider; import 'package:path/path.dart' as path; @@ -11,12 +15,14 @@ import 'package:permission_handler/permission_handler.dart'; enum TrackStatus { downloading, idle, done } -class DownloadTrackButton extends HookWidget { +class DownloadTrackButton extends HookConsumerWidget { final Track? track; const DownloadTrackButton({Key? key, this.track}) : super(key: key); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, ref) { + final UserPreferences preferences = ref.watch(userPreferencesProvider); + final Playback playback = ref.watch(playbackProvider); final status = useState(TrackStatus.idle); YoutubeExplode yt = useMemoized(() => YoutubeExplode()); @@ -44,8 +50,10 @@ class DownloadTrackButton extends HookWidget { : (await path_provider.getDownloadsDirectory())!.path, "Spotube"); String fileName = - "${track?.name} - ${artistsToString(track?.artists ?? [])}.mp3"; - File outputFile = File(path.join(downloadFolder, fileName)); + "${track?.name} - ${artistsToString(track?.artists ?? [])}"; + File outputFile = File(path.join(downloadFolder, "$fileName.mp3")); + File outputLyricsFile = + File(path.join(downloadFolder, "$fileName-lyrics.txt")); if (await outputFile.exists()) { final shouldReplace = await showDialog( @@ -102,7 +110,26 @@ class DownloadTrackButton extends HookWidget { }, ); - if (!outputFile.existsSync()) outputFile.createSync(recursive: true); + if (!await outputFile.exists()) await outputFile.create(recursive: true); + + if (preferences.saveTrackLyrics && playback.currentTrack != null) { + if (!await outputLyricsFile.exists()) { + await outputLyricsFile.create(recursive: true); + } + final lyrics = await getLyrics( + playback.currentTrack!.name!, + artistsToString(playback.currentTrack!.artists ?? []), + apiKey: preferences.geniusAccessToken, + optimizeQuery: true, + ); + if (lyrics != null) { + await outputLyricsFile.writeAsString( + "$lyrics\n\nPowered by genius.com", + mode: FileMode.writeOnly, + ); + } + } + IOSink outputFileStream = outputFile.openWrite(); await audioStream.pipe(outputFileStream); await outputFileStream.flush(); @@ -120,7 +147,13 @@ class DownloadTrackButton extends HookWidget { } return statusCb.cancel(); }); - }, [track, status, yt]); + }, [ + track, + status, + yt, + preferences.saveTrackLyrics, + playback.currentTrack, + ]); useEffect(() { return () => yt.close(); diff --git a/lib/models/LocalStorageKeys.dart b/lib/models/LocalStorageKeys.dart index 9d6104ef..2de527e6 100644 --- a/lib/models/LocalStorageKeys.dart +++ b/lib/models/LocalStorageKeys.dart @@ -1,4 +1,5 @@ abstract class LocalStorageKeys { + static String saveTrackLyrics = 'save_track_lyrics'; static String recommendationMarket = 'recommendation_market'; static String clientId = 'client_id'; static String clientSecret = 'client_secret'; diff --git a/lib/provider/UserPreferences.dart b/lib/provider/UserPreferences.dart index 614606da..892f19f5 100644 --- a/lib/provider/UserPreferences.dart +++ b/lib/provider/UserPreferences.dart @@ -11,6 +11,7 @@ import 'package:spotube/models/generated_secrets.dart'; class UserPreferences extends ChangeNotifier { String recommendationMarket; + bool saveTrackLyrics; String geniusAccessToken; HotKey? nextTrackHotKey; HotKey? prevTrackHotKey; @@ -18,6 +19,7 @@ class UserPreferences extends ChangeNotifier { UserPreferences({ required this.geniusAccessToken, required this.recommendationMarket, + this.saveTrackLyrics = false, this.nextTrackHotKey, this.prevTrackHotKey, this.playPauseHotKey, @@ -46,6 +48,9 @@ class UserPreferences extends ChangeNotifier { String? accessToken = localStorage.getString(LocalStorageKeys.geniusAccessToken); + saveTrackLyrics = + localStorage.getBool(LocalStorageKeys.saveTrackLyrics) ?? false; + recommendationMarket = localStorage.getString(LocalStorageKeys.recommendationMarket) ?? 'US'; geniusAccessToken = accessToken != null && accessToken.isNotEmpty @@ -84,6 +89,14 @@ class UserPreferences extends ChangeNotifier { } } + void setSaveTrackLyrics(bool shouldSave) { + saveTrackLyrics = shouldSave; + SharedPreferences.getInstance().then((value) { + value.setBool(LocalStorageKeys.saveTrackLyrics, shouldSave); + notifyListeners(); + }); + } + void setRecommendationMarket(String country) { recommendationMarket = country; SharedPreferences.getInstance().then((value) {