mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
Download lyrics with track support
nightly build links in README
This commit is contained in:
parent
f7510d7eae
commit
d62f3ce6a1
@ -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>
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
@ -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';
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user