Download lyrics with track support

nightly build links in README
This commit is contained in:
Kingkor Roy Tirtho 2022-04-04 22:54:00 +06:00
parent f7510d7eae
commit d62f3ce6a1
5 changed files with 68 additions and 8 deletions

View File

@ -102,8 +102,8 @@ $ flatpak install flathub com.github.KRTirtho.Spotube
## Mac OS ## 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 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
## Nightly Builds
**I'll/try to upload the package binaries to linux debian/arch/ubuntu/snap/flatpack/redhat/chocolatey/homebrew stores or software centers or repositories** Get the latest nightly builds of Spotube [here](https://nightly.link/KRTirtho/spotube/workflows/flutter-build/build)
## Optional Configurations ## Optional Configurations
<details> <details>

View File

@ -163,6 +163,19 @@ class Settings extends HookConsumerWidget {
], ],
), ),
const SizedBox(height: 10), 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) if (auth.isAnonymous)
Wrap( Wrap(
spacing: 20, spacing: 20,

View File

@ -2,8 +2,12 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/helpers/artist-to-string.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:youtube_explode_dart/youtube_explode_dart.dart';
import 'package:path_provider/path_provider.dart' as path_provider; import 'package:path_provider/path_provider.dart' as path_provider;
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
@ -11,12 +15,14 @@ import 'package:permission_handler/permission_handler.dart';
enum TrackStatus { downloading, idle, done } enum TrackStatus { downloading, idle, done }
class DownloadTrackButton extends HookWidget { class DownloadTrackButton extends HookConsumerWidget {
final Track? track; final Track? track;
const DownloadTrackButton({Key? key, this.track}) : super(key: key); const DownloadTrackButton({Key? key, this.track}) : super(key: key);
@override @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>(TrackStatus.idle); final status = useState<TrackStatus>(TrackStatus.idle);
YoutubeExplode yt = useMemoized(() => YoutubeExplode()); YoutubeExplode yt = useMemoized(() => YoutubeExplode());
@ -44,8 +50,10 @@ class DownloadTrackButton extends HookWidget {
: (await path_provider.getDownloadsDirectory())!.path, : (await path_provider.getDownloadsDirectory())!.path,
"Spotube"); "Spotube");
String fileName = String fileName =
"${track?.name} - ${artistsToString<Artist>(track?.artists ?? [])}.mp3"; "${track?.name} - ${artistsToString<Artist>(track?.artists ?? [])}";
File outputFile = File(path.join(downloadFolder, fileName)); File outputFile = File(path.join(downloadFolder, "$fileName.mp3"));
File outputLyricsFile =
File(path.join(downloadFolder, "$fileName-lyrics.txt"));
if (await outputFile.exists()) { if (await outputFile.exists()) {
final shouldReplace = await showDialog<bool>( final shouldReplace = await showDialog<bool>(
@ -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<Artist>(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(); IOSink outputFileStream = outputFile.openWrite();
await audioStream.pipe(outputFileStream); await audioStream.pipe(outputFileStream);
await outputFileStream.flush(); await outputFileStream.flush();
@ -120,7 +147,13 @@ class DownloadTrackButton extends HookWidget {
} }
return statusCb.cancel(); return statusCb.cancel();
}); });
}, [track, status, yt]); }, [
track,
status,
yt,
preferences.saveTrackLyrics,
playback.currentTrack,
]);
useEffect(() { useEffect(() {
return () => yt.close(); return () => yt.close();

View File

@ -1,4 +1,5 @@
abstract class LocalStorageKeys { abstract class LocalStorageKeys {
static String saveTrackLyrics = 'save_track_lyrics';
static String recommendationMarket = 'recommendation_market'; static String recommendationMarket = 'recommendation_market';
static String clientId = 'client_id'; static String clientId = 'client_id';
static String clientSecret = 'client_secret'; static String clientSecret = 'client_secret';

View File

@ -11,6 +11,7 @@ import 'package:spotube/models/generated_secrets.dart';
class UserPreferences extends ChangeNotifier { class UserPreferences extends ChangeNotifier {
String recommendationMarket; String recommendationMarket;
bool saveTrackLyrics;
String geniusAccessToken; String geniusAccessToken;
HotKey? nextTrackHotKey; HotKey? nextTrackHotKey;
HotKey? prevTrackHotKey; HotKey? prevTrackHotKey;
@ -18,6 +19,7 @@ class UserPreferences extends ChangeNotifier {
UserPreferences({ UserPreferences({
required this.geniusAccessToken, required this.geniusAccessToken,
required this.recommendationMarket, required this.recommendationMarket,
this.saveTrackLyrics = false,
this.nextTrackHotKey, this.nextTrackHotKey,
this.prevTrackHotKey, this.prevTrackHotKey,
this.playPauseHotKey, this.playPauseHotKey,
@ -46,6 +48,9 @@ class UserPreferences extends ChangeNotifier {
String? accessToken = String? accessToken =
localStorage.getString(LocalStorageKeys.geniusAccessToken); localStorage.getString(LocalStorageKeys.geniusAccessToken);
saveTrackLyrics =
localStorage.getBool(LocalStorageKeys.saveTrackLyrics) ?? false;
recommendationMarket = recommendationMarket =
localStorage.getString(LocalStorageKeys.recommendationMarket) ?? 'US'; localStorage.getString(LocalStorageKeys.recommendationMarket) ?? 'US';
geniusAccessToken = accessToken != null && accessToken.isNotEmpty 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) { void setRecommendationMarket(String country) {
recommendationMarket = country; recommendationMarket = country;
SharedPreferences.getInstance().then((value) { SharedPreferences.getInstance().then((value) {