mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
lyrics not working bugfix
Download track feature complete support in Android Android sudden solid color screen inno-script updated for permission_handler gensums updated for support for android apk updated description of Spotube in every file changes added to CHANGELOG
This commit is contained in:
parent
0cf5bfea50
commit
45f9d08595
3
.github/workflows/flutter-build.yml
vendored
3
.github/workflows/flutter-build.yml
vendored
@ -29,6 +29,7 @@ jobs:
|
||||
- run: make appimage
|
||||
# Building Android Application
|
||||
- run: flutter build apk
|
||||
- run: mv build/app/outputs/apk/release/app-release.apk build/Spotube-android-arm64-v8a.apk
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Spotube-Linux-Bundle
|
||||
@ -36,7 +37,7 @@ jobs:
|
||||
build/Spotube-linux-x86_64.deb
|
||||
build/Spotube-linux-x86_64.tar.xz
|
||||
build/Spotube-*-x86_64.AppImage
|
||||
build/app/outputs/apk/release/app-release.apk
|
||||
build/Spotube-android-arm64-v8a.apk
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
|
27
CHANGELOG.md
27
CHANGELOG.md
@ -1,3 +1,30 @@
|
||||
# v2.0.0
|
||||
|
||||
### New
|
||||
- Android Support https://github.com/KRTirtho/spotube/issues/24
|
||||
- Responsive UI (Mobile, Tablet)
|
||||
- Anonymous/Guest Account
|
||||
- Mini floating player
|
||||
- Full page PlayerView for smaller devices
|
||||
- Horizontal CategoryCard Scroll & pagination for quicker access to Playlists
|
||||
- Bottom bar for smaller devices
|
||||
- Collapsed Sidebar for medium sized devices
|
||||
- Persists Volume level
|
||||
- Android NavigationPanel controls (OS media controls of Android)
|
||||
|
||||
### Improved
|
||||
- Search - now scrolls & paginates for Playlists & Albums
|
||||
- Authentication - allows guest accounts making authentication optional
|
||||
- Lyrics - can be fetched without requiring GeniusAccessToken. This makes geniusAccessToken optional
|
||||
- UI snappiness & faster load times
|
||||
- Simpler logic, faster calculations & better caching (flutter_hooks)
|
||||
- shared state management - uses riverpod & hooks combination
|
||||
|
||||
### Bug fixes
|
||||
- Can't play any song in macos https://github.com/KRTirtho/spotube/issues/23
|
||||
- Downloaded tracks can't be played as they're WebAudio (.weba) instead of MP3
|
||||
- delay while changing Playlist/Single tracks
|
||||
|
||||
# v1.2.0
|
||||
|
||||
### New
|
||||
|
31
README.md
31
README.md
@ -27,7 +27,9 @@ Spotube is a [Flutter](https://flutter.dev) based lightweight spotify client. It
|
||||
Following are the features that currently spotube offers:
|
||||
|
||||
- Open Source
|
||||
- No telementry, diagnostics or user data collection
|
||||
- Anonymous/Guest Login
|
||||
- Cross platform
|
||||
- No telemetry, diagnostics or user data collection
|
||||
- Lightweight & resource friendly
|
||||
- Native performance (Thanks to Flutter+Skia)
|
||||
- Playback control is on user's machine instead of server based
|
||||
@ -36,7 +38,7 @@ Following are the features that currently spotube offers:
|
||||
- Lyrics
|
||||
- Downloadable track
|
||||
|
||||
<a href="https://www.producthunt.com/posts/spotube?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-spotube" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=327965&theme=dark" alt="Spotube - A lightweight+free Spotify desktop-client made with flutter | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
<a href="https://www.producthunt.com/posts/spotube?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-spotube" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=327965&theme=dark" alt="Spotube - A lightweight+free Spotify crossplatform-client made with flutter | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
|
||||
# Installation
|
||||
|
||||
@ -44,6 +46,10 @@ I'm always releasing newer versions of binary of the software each 2-3 month wit
|
||||
|
||||
All the binaries are located in the [releases](https://github.com/krtirtho/spotube/releases), just download
|
||||
|
||||
## Android
|
||||
|
||||
Download the [Android app](https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-android-arm64-v8a.apk) & install it on your phone/tablet
|
||||
|
||||
## Windows
|
||||
|
||||
Download the [setup file](https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-windows-x86_64-setup.exe) & follow along the installer
|
||||
@ -98,9 +104,10 @@ Download the [Mac OS Disk Image (.dmg) file](https://github.com/KRTirtho/spotube
|
||||
|
||||
|
||||
**I'll/try to upload the package binaries to linux debian/arch/ubuntu/snap/flatpack/redhat/chocolatey/homebrew stores or software centers or repositories**
|
||||
# Configuration
|
||||
|
||||
There are some configurations that needs to be done to start using this software
|
||||
## Optional Configurations
|
||||
<details>
|
||||
<summary>Login with <b>Spotify</b></summary>
|
||||
|
||||
You need a spotify account & a developer app for
|
||||
|
||||
@ -120,8 +127,10 @@ You need a spotify account & a developer app for
|
||||
|
||||
- Click on **SHOW CLIENT SECRET** to reveal the **clientSecret**. Then copy the **clientID**, **clientSecret** & paste in the **Spotube's** respective fields
|
||||

|
||||
</details>
|
||||
|
||||
**Setup Genius Lyrics (Optional)**
|
||||
<details>
|
||||
<summary>Setup <b>Genius Lyrics</b></summary>
|
||||
|
||||
- Signup/Login into [genius](https://genius.com/signup) for **lyrics**
|
||||
- Go To [Genius Developer Portal](https://genius.com/api-clients/new) for creating an API client
|
||||
@ -132,6 +141,7 @@ You need a spotify account & a developer app for
|
||||

|
||||
|
||||
> **Note!**: No personal data or any kind of sensitive information won't be collected from spotify. Don't believe? See the code for yourself
|
||||
</details>
|
||||
|
||||
# TODO:
|
||||
|
||||
@ -140,6 +150,7 @@ You need a spotify account & a developer app for
|
||||
- [x] Track download
|
||||
- [ ] Support for playing/streaming podcasts/shows
|
||||
- [x] Artist, User & Album pages
|
||||
- [x] Android Support
|
||||
|
||||
# Building from source
|
||||
|
||||
@ -179,6 +190,16 @@ Bu why? You can learn about it [here](https://dev.to/krtirtho/choosing-open-sour
|
||||
- [bitsdojo_window](https://github.com/bitsdojo/bitsdojo_window) - A Flutter package that makes it easy to customize and work with your Flutter desktop app window on Windows, macOS and Linux
|
||||
- [hotkey_manager](https://github.com/leanflutter/hotkey_manager) - A flutter plugin that allow Flutter desktop apps to defines system/inapp wide hotkey
|
||||
- [Inno Setup](https://jrsoftware.org/isinfo.php) - Inno Setup is a free installer for Windows programs by Jordan Russell and Martijn Laan
|
||||
- [collection](https://github.com/dart-lang/collection) - The collection package for Dart contains a number of separate libraries with utility functions and classes that makes working with collections easier
|
||||
- [flutter_riverpod](https://riverpod.dev/) - A Reactive Caching and Data-binding Framework
|
||||
- [flutter_hooks](https://github.com/rrousselGit/flutter_hooks) - React hooks for Flutter. Hooks are a new kind of object that manages a Widget life-cycles. They are used to increase code sharing between widgets and as a complete replacement for StatefulWidget
|
||||
- [hooks_riverpod](https://riverpod.dev/) - Riverpod with hooks
|
||||
- [go_router](https://github.com/flutter/packages/tree/main/packages/go_router) - A declarative router for Flutter based on Navigation 2 supporting deep linking, data-driven routes and more
|
||||
- [palette_generator](https://github.com/flutter/packages/tree/main/packages/palette_generator) - Flutter package for generating palette colors from a source image.
|
||||
- [audio_session](https://github.com/ryanheise/audio_session) - Sets the iOS audio session category and Android audio attributes for your app, and manages your app's audio focus, mixing and ducking behaviour.
|
||||
- [logger](https://github.com/leisim/logger) - Small, easy to use and extensible logger which prints beautiful logs
|
||||
- [flutter_launcher_icons](https://github.com/fluttercommunity/flutter_launcher_icons) - A package which simplifies the task of updating your Flutter app's launcher icon.
|
||||
- [permission_handler](https://github.com/baseflow/flutter-permission-handler) - Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.
|
||||
|
||||
|
||||
# Social handlers
|
||||
|
@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
compileSdkVersion 31
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
|
@ -3,6 +3,8 @@
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<queries>
|
||||
<!-- If your app opens https URLs -->
|
||||
@ -12,7 +14,7 @@
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application android:label="Spotube" android:name="${applicationName}" android:icon="@mipmap/ic_launcher" android:usesCleartextTraffic="true">
|
||||
<application android:label="Spotube" android:name="${applicationName}" android:icon="@mipmap/ic_launcher" android:usesCleartextTraffic="true" android:requestLegacyExternalStorage="true">
|
||||
<activity android:name="com.ryanheise.audioservice.AudioServiceActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
|
@ -1,5 +1,5 @@
|
||||
pkgbase = spotube-bin
|
||||
pkgdesc = A lightweight free Spotify desktop-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||
pkgdesc = A lightweight free Spotify crossplatform-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||
pkgver = 1.2.0
|
||||
pkgrel = 2
|
||||
url = https://github.com/KRTirtho/spotube/
|
||||
|
@ -3,7 +3,7 @@ pkgname=spotube-bin
|
||||
pkgver=1.2.0
|
||||
pkgrel=2
|
||||
epoch=
|
||||
pkgdesc="A lightweight free Spotify desktop-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed"
|
||||
pkgdesc="A lightweight free Spotify crossplatform-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed"
|
||||
arch=(x86_64)
|
||||
url="https://github.com/KRTirtho/spotube/"
|
||||
license=('BSD-4-Clause')
|
||||
|
@ -22,7 +22,7 @@
|
||||
<docsUrl>https://github.com/KRTirtho/spotube#readme</docsUrl>
|
||||
<bugTrackerUrl>https://github.com/KRTirtho/spotube/issues/new</bugTrackerUrl>
|
||||
<tags>spotube music audio spotify youtube flutter</tags>
|
||||
<summary>A lightweight free Spotify 🎧 desktop-client 🖥 which handles playback manually, streams music using Youtube & no Spotify premium account is needed 😱</summary>
|
||||
<summary>A lightweight free Spotify 🎧 crossplatform-client 🖥📱 which handles playback manually, streams music using Youtube & no Spotify premium account is needed 😱</summary>
|
||||
<description>
|
||||
Spotube is a Flutter based lightweight spotify client. It utilizes the power
|
||||
of Spotify & Youtube's public API & creates a hazardless, performant & resource
|
||||
|
@ -7,5 +7,5 @@ Essential: no
|
||||
Installed-Size: 24400
|
||||
Depends: libkeybinder-3.0-0 (>= 0.3.2)
|
||||
Maintainer: Kingkor Roy Tirtho <krtirtho@gmail.com>
|
||||
Description: A lightweight free Spotify desktop-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||
Description: A lightweight free Spotify crossplatform-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||
Homepage: https://github.com/KRTirtho/spotube
|
||||
|
@ -15,12 +15,11 @@ class Lyrics extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, ref) {
|
||||
Playback playback = ref.watch(playbackProvider);
|
||||
UserPreferences userPreferences = ref.watch(userPreferencesProvider);
|
||||
var lyrics = useState({});
|
||||
final lyrics = useState({});
|
||||
|
||||
bool hasToken = userPreferences.geniusAccessToken.isNotEmpty;
|
||||
var lyricsFuture = useMemoized(() {
|
||||
final lyricsFuture = useMemoized(() {
|
||||
if (playback.currentTrack == null ||
|
||||
!hasToken ||
|
||||
userPreferences.geniusAccessToken.isEmpty ||
|
||||
(playback.currentTrack?.id != null &&
|
||||
playback.currentTrack?.id == lyrics.value["id"])) {
|
||||
return null;
|
||||
@ -31,9 +30,9 @@ class Lyrics extends HookConsumerWidget {
|
||||
apiKey: userPreferences.geniusAccessToken,
|
||||
optimizeQuery: true,
|
||||
);
|
||||
}, [playback.currentTrack]);
|
||||
}, [playback.currentTrack, userPreferences.geniusAccessToken]);
|
||||
|
||||
var lyricsSnapshot = useFuture(lyricsFuture);
|
||||
final lyricsSnapshot = useFuture(lyricsFuture);
|
||||
|
||||
useEffect(() {
|
||||
if (lyricsSnapshot.hasData &&
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
@ -40,9 +41,16 @@ class Player extends HookConsumerWidget {
|
||||
/// [disposeAllPlayers] method which is throwing
|
||||
/// [UnimplementedException] in the [PlatformInterface]
|
||||
/// implementation
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
playback.audioSession
|
||||
?.setActive(true)
|
||||
.then((_) => player.setAsset("assets/warmer.mp3"));
|
||||
.then((_) => player.setAsset("assets/warmer.mp3"))
|
||||
.catchError((e) {
|
||||
logger.e("useEffect", e, StackTrace.current);
|
||||
});
|
||||
} else {
|
||||
player.setAsset("assets/warmer.mp3");
|
||||
}
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
@ -64,7 +72,7 @@ class Player extends HookConsumerWidget {
|
||||
|
||||
final entryRef = useRef<OverlayEntry?>(null);
|
||||
|
||||
disposeOverlay() {
|
||||
void disposeOverlay() {
|
||||
try {
|
||||
entryRef.value?.remove();
|
||||
entryRef.value = null;
|
||||
@ -76,25 +84,37 @@ class Player extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
useEffect(() {
|
||||
// clearing the overlay-entry as passing the already available
|
||||
// entry will result in splashing while resizing the window
|
||||
if (entryRef.value != null) disposeOverlay();
|
||||
if (breakpoint.isLessThanOrEqualTo(Breakpoints.md)) {
|
||||
entryRef.value = OverlayEntry(
|
||||
opaque: false,
|
||||
builder: (context) => PlayerOverlay(albumArt: albumArt),
|
||||
);
|
||||
// I can't believe useEffect doesn't run Post Frame aka
|
||||
// after rendering/painting the UI
|
||||
// `My disappointment is immeasurable and my day is ruined` XD
|
||||
WidgetsBinding.instance?.addPostFrameCallback((time) {
|
||||
// clearing the overlay-entry as passing the already available
|
||||
// entry will result in splashing while resizing the window
|
||||
if (breakpoint.isLessThanOrEqualTo(Breakpoints.md) &&
|
||||
entryRef.value == null &&
|
||||
playback.currentTrack != null) {
|
||||
entryRef.value = OverlayEntry(
|
||||
opaque: false,
|
||||
builder: (context) => PlayerOverlay(albumArt: albumArt),
|
||||
);
|
||||
try {
|
||||
Overlay.of(context)?.insert(entryRef.value!);
|
||||
} catch (e) {
|
||||
if (e is AssertionError &&
|
||||
e.message ==
|
||||
'The specified entry is already present in the Overlay.') {
|
||||
disposeOverlay();
|
||||
Overlay.of(context)?.insert(entryRef.value!);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
disposeOverlay();
|
||||
}
|
||||
});
|
||||
return () {
|
||||
disposeOverlay();
|
||||
};
|
||||
}, [breakpoint]);
|
||||
}, [breakpoint, playback.currentTrack]);
|
||||
|
||||
// returning an empty non spacious Container as the overlay will take
|
||||
// place in the global overlay stack aka [_entries]
|
||||
|
@ -66,14 +66,17 @@ class Settings extends HookConsumerWidget {
|
||||
? () async {
|
||||
SharedPreferences localStorage =
|
||||
await SharedPreferences.getInstance();
|
||||
if (geniusAccessToken.value != null &&
|
||||
geniusAccessToken.value!.isNotEmpty) {
|
||||
preferences.setGeniusAccessToken(
|
||||
geniusAccessToken.value ?? "");
|
||||
geniusAccessToken.value!,
|
||||
);
|
||||
localStorage.setString(
|
||||
LocalStorageKeys.geniusAccessToken,
|
||||
geniusAccessToken.value ?? "");
|
||||
geniusAccessToken.value!);
|
||||
}
|
||||
|
||||
geniusAccessToken.value = null;
|
||||
|
||||
textEditingController.text = "";
|
||||
}
|
||||
: null,
|
||||
@ -141,12 +144,12 @@ class Settings extends HookConsumerWidget {
|
||||
const SizedBox(height: 10),
|
||||
if (auth.isAnonymous)
|
||||
Wrap(
|
||||
spacing: 20,
|
||||
runSpacing: 20,
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
runAlignment: WrapAlignment.center,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
const Text("Login with your Spotify account"),
|
||||
const SizedBox(width: 20),
|
||||
ElevatedButton(
|
||||
child: Text("Connect with Spotify".toUpperCase()),
|
||||
onPressed: () {
|
||||
|
@ -1,10 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class AnonymousFallback extends StatelessWidget {
|
||||
const AnonymousFallback({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(child: Text("You're not logged in"));
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text("You're not logged in"),
|
||||
const SizedBox(height: 10),
|
||||
ElevatedButton(
|
||||
child: const Text("Login with Spotify"),
|
||||
onPressed: () => GoRouter.of(context).push("/settings"),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import 'package:spotube/helpers/artist-to-string.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;
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
enum TrackStatus { downloading, idle, done }
|
||||
|
||||
@ -21,16 +22,31 @@ class DownloadTrackButton extends HookWidget {
|
||||
|
||||
var _downloadTrack = useCallback(() async {
|
||||
if (track == null) return;
|
||||
if ((Platform.isAndroid || Platform.isIOS) &&
|
||||
!await Permission.storage.isGranted &&
|
||||
!await Permission.storage.isPermanentlyDenied) {
|
||||
final status = await Permission.storage.request();
|
||||
if (!status.isGranted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text("Couldn't download track. Not enough permissions"),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
StreamManifest manifest =
|
||||
await yt.videos.streamsClient.getManifest(track?.href);
|
||||
|
||||
var audioStream = yt.videos.streamsClient.get(
|
||||
final audioStream = yt.videos.streamsClient
|
||||
.get(
|
||||
manifest.audioOnly
|
||||
.where((audio) => audio.codec.mimeType == "audio/mp4")
|
||||
.withHighestBitrate(),
|
||||
);
|
||||
)
|
||||
.asBroadcastStream();
|
||||
|
||||
var statusCb = audioStream.listen(
|
||||
final statusCb = audioStream.listen(
|
||||
(event) {
|
||||
if (status.value != TrackStatus.downloading) {
|
||||
status.value = TrackStatus.downloading;
|
||||
@ -50,7 +66,10 @@ class DownloadTrackButton extends HookWidget {
|
||||
);
|
||||
|
||||
String downloadFolder = path.join(
|
||||
(await path_provider.getDownloadsDirectory())!.path, "Spotube");
|
||||
Platform.isAndroid
|
||||
? "/storage/emulated/0/Download"
|
||||
: (await path_provider.getDownloadsDirectory())!.path,
|
||||
"Spotube");
|
||||
String fileName =
|
||||
"${track?.name} - ${artistsToString<Artist>(track?.artists ?? [])}.mp3";
|
||||
File outputFile = File(path.join(downloadFolder, fileName));
|
||||
|
@ -88,7 +88,7 @@ Future<List?> searchSong(
|
||||
Future<String?> getLyrics(
|
||||
String title,
|
||||
String artist, {
|
||||
String apiKey = "",
|
||||
required String apiKey,
|
||||
bool optimizeQuery = false,
|
||||
bool authHeader = false,
|
||||
}) async {
|
||||
|
@ -107,8 +107,15 @@ class Playback extends ChangeNotifier {
|
||||
_processingStateStreamListener =
|
||||
player.processingStateStream.listen((event) async {
|
||||
try {
|
||||
if (event == ProcessingState.completed && _currentTrack?.id != null) {
|
||||
if (event != ProcessingState.completed) return;
|
||||
if (_currentTrack?.id != null) {
|
||||
movePlaylistPositionBy(1);
|
||||
} else {
|
||||
await audioSession?.setActive(false);
|
||||
_isPlaying = false;
|
||||
_duration = null;
|
||||
_callAllDurationListeners(null);
|
||||
notifyListeners();
|
||||
}
|
||||
} catch (e, stack) {
|
||||
_logger.e("PrecessingStateStreamListener", e, stack);
|
||||
@ -167,6 +174,7 @@ class Playback extends ChangeNotifier {
|
||||
_callAllDurationListeners(null);
|
||||
_currentPlaylist = null;
|
||||
_currentTrack = null;
|
||||
_audioSession?.setActive(false);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@ -196,7 +204,7 @@ class Playback extends ChangeNotifier {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
movePlaylistPositionBy(int pos) {
|
||||
void movePlaylistPositionBy(int pos) {
|
||||
if (_currentTrack != null && _currentPlaylist != null) {
|
||||
int index = _currentPlaylist!.trackIds.indexOf(_currentTrack!.id!) + pos;
|
||||
|
||||
|
@ -4,8 +4,10 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:spotube/helpers/get-random-element.dart';
|
||||
import 'package:spotube/models/LocalStorageKeys.dart';
|
||||
import 'package:spotube/models/Logger.dart';
|
||||
import 'package:spotube/models/generated_secrets.dart';
|
||||
|
||||
class UserPreferences extends ChangeNotifier {
|
||||
String geniusAccessToken;
|
||||
@ -33,15 +35,17 @@ class UserPreferences extends ChangeNotifier {
|
||||
}
|
||||
return HotKey.fromJson(json);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
onInit() async {
|
||||
Future<void> onInit() async {
|
||||
try {
|
||||
SharedPreferences localStorage = await SharedPreferences.getInstance();
|
||||
String? accessToken =
|
||||
localStorage.getString(LocalStorageKeys.geniusAccessToken);
|
||||
|
||||
if (accessToken != null) geniusAccessToken = accessToken;
|
||||
geniusAccessToken = accessToken != null && accessToken.isNotEmpty
|
||||
? accessToken
|
||||
: getRandomElement(lyricsSecrets);
|
||||
|
||||
nextTrackHotKey ??= (await _getHotKeyFromLocalStorage(
|
||||
localStorage,
|
||||
@ -75,12 +79,12 @@ class UserPreferences extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
setGeniusAccessToken(String token) {
|
||||
void setGeniusAccessToken(String token) {
|
||||
geniusAccessToken = token;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
setNextTrackHotKey(HotKey? value) {
|
||||
void setNextTrackHotKey(HotKey? value) {
|
||||
nextTrackHotKey = value;
|
||||
SharedPreferences.getInstance().then((preferences) {
|
||||
preferences.setString(
|
||||
@ -91,7 +95,7 @@ class UserPreferences extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
setPrevTrackHotKey(HotKey? value) {
|
||||
void setPrevTrackHotKey(HotKey? value) {
|
||||
prevTrackHotKey = value;
|
||||
SharedPreferences.getInstance().then((preferences) {
|
||||
preferences.setString(
|
||||
@ -102,7 +106,7 @@ class UserPreferences extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
setPlayPauseHotKey(HotKey? value) {
|
||||
void setPlayPauseHotKey(HotKey? value) {
|
||||
playPauseHotKey = value;
|
||||
SharedPreferences.getInstance().then((preferences) {
|
||||
preferences.setString(
|
||||
@ -114,5 +118,5 @@ class UserPreferences extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
var userPreferencesProvider =
|
||||
final userPreferencesProvider =
|
||||
ChangeNotifierProvider((_) => UserPreferences(geniusAccessToken: ""));
|
||||
|
@ -3,7 +3,7 @@
|
||||
<id>com.github.KRTirtho.Spotube</id>
|
||||
<name>Spotube</name>
|
||||
<summary>
|
||||
A lightweight free Spotify desktop-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||
A lightweight free Spotify crossplatform-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||
</summary>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>BSD-4-Clause</project_license>
|
||||
|
35
pubspec.lock
35
pubspec.lock
@ -506,6 +506,41 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "9.2.0"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "9.0.2+1"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "9.0.3"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.7.0"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: spotube
|
||||
description: A lightweight free Spotify desktop-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||
description: A lightweight free Spotify crossplatform-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
@ -56,6 +56,7 @@ dependencies:
|
||||
audio_session: ^0.1.6+1
|
||||
just_audio_background: ^0.0.1-beta.5
|
||||
logger: ^1.1.0
|
||||
permission_handler: ^9.2.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/env bash
|
||||
md5sum build/**/*.{AppImage,deb,tar.xz,dmg,exe,nupkg} >build/RELEASE.md5sum
|
||||
sha256sum build/**/*.{AppImage,deb,tar.xz,dmg,exe,nupkg} >build/RELEASE.sha256sum
|
||||
md5sum build/**/*.{AppImage,deb,tar.xz,dmg,exe,nupkg,apk} >build/RELEASE.md5sum
|
||||
sha256sum build/**/*.{AppImage,deb,tar.xz,dmg,exe,nupkg,apk} >build/RELEASE.sha256sum
|
||||
sed -i 's|build/Spotube-.*-Bundle/||' build/RELEASE.sha256sum build/RELEASE.md5sum
|
@ -10,7 +10,7 @@
|
||||
[Setup]
|
||||
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
|
||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||
AppId={{80B901C8-D6FE-494E-8AF7-A2BD440E8644}
|
||||
AppId={80B901C8-D6FE-494E-8AF7-A2BD440E8644}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
;AppVerName={#MyAppName} {#MyAppVersion}
|
||||
@ -43,6 +43,7 @@ Source: "..\build\windows\runner\Release\flutter_windows.dll"; DestDir: "{app}";
|
||||
Source: "..\build\windows\runner\Release\hotkey_manager_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\build\windows\runner\Release\libwinmedia.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\build\windows\runner\Release\libwinmedia_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\build\windows\runner\Release\permission_handler_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\build\windows\runner\Release\spotube.exp"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\build\windows\runner\Release\spotube.lib"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\build\windows\runner\Release\url_launcher_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
|
||||
#include <hotkey_manager/hotkey_manager_plugin.h>
|
||||
#include <libwinmedia/libwinmedia_plugin.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
@ -18,6 +19,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
registry->GetRegistrarForPlugin("HotkeyManagerPlugin"));
|
||||
LibwinmediaPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("LibwinmediaPlugin"));
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
bitsdojo_window_windows
|
||||
hotkey_manager
|
||||
libwinmedia
|
||||
permission_handler_windows
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user