mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
Merge branch 'master' into build
This commit is contained in:
commit
8dfa13dd06
@ -1,7 +1,7 @@
|
|||||||
# Maintainer: Kingkor Roy Tirtho <krtirho@gmail.com>
|
# Maintainer: Kingkor Roy Tirtho <krtirho@gmail.com>
|
||||||
pkgname=spotube-bin
|
pkgname=spotube-bin
|
||||||
pkgver=%{{SPOTUBE_VERSION}}%
|
pkgver=%{{SPOTUBE_VERSION}}%
|
||||||
pkgrel=%{{PKGREL}}$
|
pkgrel=%{{PKGREL}}%
|
||||||
epoch=
|
epoch=
|
||||||
pkgdesc="A lightweight free Spotify crossplatform-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)
|
arch=(x86_64)
|
||||||
|
@ -20,7 +20,7 @@ class PlayerOverlay extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final breakpoint = useBreakpoints();
|
final breakpoint = useBreakpoints();
|
||||||
final isCurrentRoute = useIsCurrentRoute("/");
|
final isCurrentRoute = useIsCurrentRoute("/");
|
||||||
final paletteColor = usePaletteColor(context, albumArt);
|
final paletteColor = usePaletteColor(context, albumArt, ref);
|
||||||
final playback = ref.watch(playbackProvider);
|
final playback = ref.watch(playbackProvider);
|
||||||
|
|
||||||
if (isCurrentRoute == false) {
|
if (isCurrentRoute == false) {
|
||||||
@ -37,7 +37,8 @@ class PlayerOverlay extends HookConsumerWidget {
|
|||||||
right: (breakpoint.isMd ? 10 : 5),
|
right: (breakpoint.isMd ? 10 : 5),
|
||||||
left: (breakpoint.isSm ? 5 : 80),
|
left: (breakpoint.isSm ? 5 : 80),
|
||||||
bottom: (breakpoint.isSm ? 63 : 10),
|
bottom: (breakpoint.isSm ? 63 : 10),
|
||||||
child: Container(
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
height: 50,
|
height: 50,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
@ -44,7 +44,7 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
[currentTrack?.album?.images],
|
[currentTrack?.album?.images],
|
||||||
);
|
);
|
||||||
|
|
||||||
final PaletteColor paletteColor = usePaletteColor(context, albumArt);
|
final PaletteColor paletteColor = usePaletteColor(context, albumArt, ref);
|
||||||
|
|
||||||
final backgroundColor = Theme.of(context).backgroundColor;
|
final backgroundColor = Theme.of(context).backgroundColor;
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ class Login extends HookConsumerWidget {
|
|||||||
const Text(
|
const Text(
|
||||||
"Don't worry, any of your credentials won't be collected or shared with anyone"),
|
"Don't worry, any of your credentials won't be collected or shared with anyone"),
|
||||||
const Hyperlink("How to get these client-id & client-secret?",
|
const Hyperlink("How to get these client-id & client-secret?",
|
||||||
"https://github.com/KRTirtho/spotube#configuration"),
|
"https://github.com/KRTirtho/spotube#optional-configurations"),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
|
@ -280,9 +280,6 @@ class Settings extends HookConsumerWidget {
|
|||||||
MaterialStateProperty.all(Colors.white),
|
MaterialStateProperty.all(Colors.white),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
SharedPreferences localStorage =
|
|
||||||
await SharedPreferences.getInstance();
|
|
||||||
await localStorage.clear();
|
|
||||||
auth.logout();
|
auth.logout();
|
||||||
GoRouter.of(context).pop();
|
GoRouter.of(context).pop();
|
||||||
},
|
},
|
||||||
|
@ -20,7 +20,7 @@ class TitleBarActionButtons extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: const Icon(Icons.minimize_rounded)),
|
child: const Icon(Icons.minimize_rounded)),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
appWindow.maximizeOrRestore();
|
appWindow.maximizeOrRestore();
|
||||||
},
|
},
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
|
@ -1,36 +1,18 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
class SpotubePageRoute extends PageRouteBuilder {
|
class SpotubePageRoute extends PageRouteBuilder {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
SpotubePageRoute({required this.child})
|
SpotubePageRoute({required this.child})
|
||||||
: super(
|
: super(
|
||||||
pageBuilder: (context, animation, secondaryAnimation) => child,
|
pageBuilder: (context, animation, secondaryAnimation) => child,
|
||||||
settings: RouteSettings(name: child.key.toString()));
|
settings: RouteSettings(
|
||||||
|
name: child.key.toString(),
|
||||||
@override
|
),
|
||||||
Widget buildTransitions(BuildContext context, Animation<double> animation,
|
|
||||||
Animation<double> secondaryAnimation, Widget child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: animation,
|
|
||||||
child: child,
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpotubePage extends CustomTransitionPage {
|
class SpotubePage extends MaterialPage {
|
||||||
SpotubePage({
|
const SpotubePage({
|
||||||
required Widget child,
|
required Widget child,
|
||||||
}) : super(
|
}) : super(child: child);
|
||||||
child: child,
|
|
||||||
transitionsBuilder: (BuildContext context,
|
|
||||||
Animation<double> animation,
|
|
||||||
Animation<double> secondaryAnimation,
|
|
||||||
Widget child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: animation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,6 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
Text(duration),
|
Text(duration),
|
||||||
],
|
],
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
if (auth.isLoggedIn)
|
|
||||||
FutureBuilder<bool>(
|
FutureBuilder<bool>(
|
||||||
future: spotify.tracks.me.containsOne(track.value.id!),
|
future: spotify.tracks.me.containsOne(track.value.id!),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -237,6 +236,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
icon: const Icon(Icons.more_horiz_rounded),
|
icon: const Icon(Icons.more_horiz_rounded),
|
||||||
itemBuilder: (context) {
|
itemBuilder: (context) {
|
||||||
return [
|
return [
|
||||||
|
if (auth.isLoggedIn)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: const [
|
children: const [
|
||||||
@ -247,7 +247,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
value: "add-playlist",
|
value: "add-playlist",
|
||||||
),
|
),
|
||||||
if (userPlaylist)
|
if (userPlaylist && auth.isLoggedIn)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: const [
|
children: const [
|
||||||
@ -258,6 +258,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
value: "remove-playlist",
|
value: "remove-playlist",
|
||||||
),
|
),
|
||||||
|
if (auth.isLoggedIn)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:hookified_infinite_scroll_pagination/hookified_infinite_scroll_pagination.dart';
|
||||||
import 'package:spotube/hooks/usePagingController.dart';
|
|
||||||
|
|
||||||
PagingController<P, ItemType> usePaginatedFutureProvider<T, P, ItemType>(
|
PagingController<P, ItemType> usePaginatedFutureProvider<T, P, ItemType>(
|
||||||
FutureProvider<T> Function(P pageKey) createSnapshot, {
|
FutureProvider<T> Function(P pageKey) createSnapshot, {
|
||||||
@ -18,18 +17,13 @@ PagingController<P, ItemType> usePaginatedFutureProvider<T, P, ItemType>(
|
|||||||
}) {
|
}) {
|
||||||
final currentPageKey = useState(firstPageKey);
|
final currentPageKey = useState(firstPageKey);
|
||||||
final snapshot = ref.watch(createSnapshot(currentPageKey.value));
|
final snapshot = ref.watch(createSnapshot(currentPageKey.value));
|
||||||
final pagingController =
|
final pagingController = usePagingController<P, ItemType>(
|
||||||
usePagingController<P, ItemType>(firstPageKey: firstPageKey);
|
firstPageKey: firstPageKey,
|
||||||
useEffect(() {
|
onPageRequest: (pageKey, pagingController) {
|
||||||
listener(pageKey) {
|
|
||||||
if (currentPageKey.value != pageKey) {
|
if (currentPageKey.value != pageKey) {
|
||||||
currentPageKey.value = pageKey;
|
currentPageKey.value = pageKey;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
pagingController.addPageRequestListener(listener);
|
|
||||||
return () => pagingController.removePageRequestListener(listener);
|
|
||||||
}, [snapshot, currentPageKey]);
|
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
snapshot.whenOrNull(
|
snapshot.whenOrNull(
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
|
||||||
|
|
||||||
PagingController<PageKeyType, ItemType>
|
|
||||||
usePagingController<PageKeyType, ItemType>({
|
|
||||||
required final PageKeyType firstPageKey,
|
|
||||||
final int? invisibleItemsThreshold,
|
|
||||||
List<Object?>? keys,
|
|
||||||
}) {
|
|
||||||
return use(
|
|
||||||
_PagingControllerHook<PageKeyType, ItemType>(
|
|
||||||
firstPageKey: firstPageKey,
|
|
||||||
invisibleItemsThreshold: invisibleItemsThreshold,
|
|
||||||
keys: keys,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PagingControllerHook<PageKeyType, ItemType>
|
|
||||||
extends Hook<PagingController<PageKeyType, ItemType>> {
|
|
||||||
const _PagingControllerHook({
|
|
||||||
required this.firstPageKey,
|
|
||||||
this.invisibleItemsThreshold,
|
|
||||||
List<Object?>? keys,
|
|
||||||
}) : super(keys: keys);
|
|
||||||
|
|
||||||
final PageKeyType firstPageKey;
|
|
||||||
final int? invisibleItemsThreshold;
|
|
||||||
|
|
||||||
@override
|
|
||||||
HookState<PagingController<PageKeyType, ItemType>,
|
|
||||||
Hook<PagingController<PageKeyType, ItemType>>>
|
|
||||||
createState() => _PagingControllerHookState<PageKeyType, ItemType>();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PagingControllerHookState<PageKeyType, ItemType> extends HookState<
|
|
||||||
PagingController<PageKeyType, ItemType>,
|
|
||||||
_PagingControllerHook<PageKeyType, ItemType>> {
|
|
||||||
late final controller = PagingController<PageKeyType, ItemType>(
|
|
||||||
firstPageKey: hook.firstPageKey,
|
|
||||||
invisibleItemsThreshold: hook.invisibleItemsThreshold);
|
|
||||||
|
|
||||||
@override
|
|
||||||
PagingController<PageKeyType, ItemType> build(BuildContext context) =>
|
|
||||||
controller;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() => controller.dispose();
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get debugLabel => 'usePagingController';
|
|
||||||
}
|
|
@ -1,11 +1,18 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
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:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:palette_generator/palette_generator.dart';
|
import 'package:palette_generator/palette_generator.dart';
|
||||||
|
|
||||||
PaletteColor usePaletteColor(BuildContext context, String imageUrl) {
|
final _paletteColorState = StateProvider<PaletteColor>(
|
||||||
final paletteColor =
|
(ref) {
|
||||||
useState<PaletteColor>(PaletteColor(Colors.grey[300]!, 0));
|
return PaletteColor(Colors.grey[300]!, 0);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
PaletteColor usePaletteColor(
|
||||||
|
BuildContext context, String imageUrl, WidgetRef ref) {
|
||||||
|
final paletteColor = ref.watch(_paletteColorState);
|
||||||
final mounted = useIsMounted();
|
final mounted = useIsMounted();
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
@ -23,11 +30,11 @@ PaletteColor usePaletteColor(BuildContext context, String imageUrl) {
|
|||||||
? palette.lightMutedColor ?? palette.lightVibrantColor
|
? palette.lightMutedColor ?? palette.lightVibrantColor
|
||||||
: palette.darkMutedColor ?? palette.darkVibrantColor;
|
: palette.darkMutedColor ?? palette.darkVibrantColor;
|
||||||
if (color != null) {
|
if (color != null) {
|
||||||
paletteColor.value = color;
|
ref.read(_paletteColorState.notifier).state = color;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
}, [imageUrl]);
|
}, [imageUrl]);
|
||||||
|
|
||||||
return paletteColor.value;
|
return paletteColor;
|
||||||
}
|
}
|
||||||
|
@ -37,10 +37,9 @@ void main() async {
|
|||||||
doWhenWindowReady(() {
|
doWhenWindowReady(() {
|
||||||
appWindow.minSize =
|
appWindow.minSize =
|
||||||
Size(Platform.isAndroid || Platform.isIOS ? 280 : 359, 700);
|
Size(Platform.isAndroid || Platform.isIOS ? 280 : 359, 700);
|
||||||
appWindow.size = const Size(900, 700);
|
|
||||||
appWindow.alignment = Alignment.center;
|
appWindow.alignment = Alignment.center;
|
||||||
appWindow.maximize();
|
|
||||||
appWindow.title = "Spotube";
|
appWindow.title = "Spotube";
|
||||||
|
appWindow.maximize();
|
||||||
appWindow.show();
|
appWindow.show();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -50,11 +49,10 @@ void main() async {
|
|||||||
playbackProvider.overrideWithProvider(ChangeNotifierProvider(
|
playbackProvider.overrideWithProvider(ChangeNotifierProvider(
|
||||||
(ref) {
|
(ref) {
|
||||||
final youtube = ref.watch(youtubeProvider);
|
final youtube = ref.watch(youtubeProvider);
|
||||||
final preferences = ref.watch(userPreferencesProvider);
|
|
||||||
return Playback(
|
return Playback(
|
||||||
player: audioPlayerHandler,
|
player: audioPlayerHandler,
|
||||||
youtube: youtube,
|
youtube: youtube,
|
||||||
preferences: preferences,
|
ref: ref,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
@ -51,7 +51,7 @@ class Auth extends PersistedChangeNotifier {
|
|||||||
updatePersistence();
|
updatePersistence();
|
||||||
}
|
}
|
||||||
|
|
||||||
logout() {
|
void logout() {
|
||||||
_clientId = null;
|
_clientId = null;
|
||||||
_clientSecret = null;
|
_clientSecret = null;
|
||||||
_accessToken = null;
|
_accessToken = null;
|
||||||
|
@ -30,11 +30,11 @@ class Playback extends ChangeNotifier {
|
|||||||
|
|
||||||
AudioPlayerHandler player;
|
AudioPlayerHandler player;
|
||||||
YoutubeExplode youtube;
|
YoutubeExplode youtube;
|
||||||
UserPreferences preferences;
|
Ref ref;
|
||||||
Playback({
|
Playback({
|
||||||
required this.player,
|
required this.player,
|
||||||
required this.youtube,
|
required this.youtube,
|
||||||
required this.preferences,
|
required this.ref,
|
||||||
CurrentPlaylist? currentPlaylist,
|
CurrentPlaylist? currentPlaylist,
|
||||||
Track? currentTrack,
|
Track? currentTrack,
|
||||||
}) : _currentPlaylist = currentPlaylist,
|
}) : _currentPlaylist = currentPlaylist,
|
||||||
@ -206,6 +206,7 @@ class Playback extends ChangeNotifier {
|
|||||||
// await player.play();
|
// await player.play();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final preferences = ref.read(userPreferencesProvider);
|
||||||
final spotubeTrack = await toSpotubeTrack(
|
final spotubeTrack = await toSpotubeTrack(
|
||||||
youtube: youtube,
|
youtube: youtube,
|
||||||
track: track,
|
track: track,
|
||||||
@ -250,10 +251,9 @@ class Playback extends ChangeNotifier {
|
|||||||
final playbackProvider = ChangeNotifierProvider<Playback>((ref) {
|
final playbackProvider = ChangeNotifierProvider<Playback>((ref) {
|
||||||
final player = AudioPlayerHandler();
|
final player = AudioPlayerHandler();
|
||||||
final youtube = ref.watch(youtubeProvider);
|
final youtube = ref.watch(youtubeProvider);
|
||||||
final preferences = ref.watch(userPreferencesProvider);
|
|
||||||
return Playback(
|
return Playback(
|
||||||
player: player,
|
player: player,
|
||||||
youtube: youtube,
|
youtube: youtube,
|
||||||
preferences: preferences,
|
ref: ref,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
13
pubspec.lock
13
pubspec.lock
@ -268,6 +268,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
|
hookified_infinite_scroll_pagination:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: hookified_infinite_scroll_pagination
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.0"
|
||||||
hooks_riverpod:
|
hooks_riverpod:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -311,12 +318,12 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
infinite_scroll_pagination:
|
infinite_scroll_pagination:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: infinite_scroll_pagination
|
name: infinite_scroll_pagination
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.2.0"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -906,5 +913,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.11.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.17.0 <3.0.0"
|
dart: ">=2.17.1 <3.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.0.0"
|
||||||
|
@ -41,7 +41,6 @@ dependencies:
|
|||||||
git: https://github.com/KRTirtho/spotify-dart.git
|
git: https://github.com/KRTirtho/spotify-dart.git
|
||||||
url_launcher: ^6.0.17
|
url_launcher: ^6.0.17
|
||||||
youtube_explode_dart: ^1.10.8
|
youtube_explode_dart: ^1.10.8
|
||||||
infinite_scroll_pagination: ^3.1.0
|
|
||||||
bitsdojo_window: ^0.1.2
|
bitsdojo_window: ^0.1.2
|
||||||
hotkey_manager: ^0.1.6
|
hotkey_manager: ^0.1.6
|
||||||
just_audio: ^0.9.18
|
just_audio: ^0.9.18
|
||||||
@ -61,6 +60,7 @@ dependencies:
|
|||||||
package_info_plus: ^1.4.2
|
package_info_plus: ^1.4.2
|
||||||
version: ^2.0.0
|
version: ^2.0.0
|
||||||
audio_service: ^0.18.4
|
audio_service: ^0.18.4
|
||||||
|
hookified_infinite_scroll_pagination: ^0.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
Reference in New Issue
Block a user