mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
refactor: replace all instances of proxy playlist
This commit is contained in:
parent
f79fedefd4
commit
a83dd64476
@ -11,7 +11,7 @@ import 'package:spotube/pages/home/home.dart';
|
|||||||
import 'package:spotube/pages/library/library.dart';
|
import 'package:spotube/pages/library/library.dart';
|
||||||
import 'package:spotube/pages/lyrics/lyrics.dart';
|
import 'package:spotube/pages/lyrics/lyrics.dart';
|
||||||
import 'package:spotube/pages/search/search.dart';
|
import 'package:spotube/pages/search/search.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
@ -96,8 +96,8 @@ class SeekIntent extends Intent {
|
|||||||
class SeekAction extends Action<SeekIntent> {
|
class SeekAction extends Action<SeekIntent> {
|
||||||
@override
|
@override
|
||||||
invoke(intent) async {
|
invoke(intent) async {
|
||||||
final playlist = intent.ref.read(proxyPlaylistProvider);
|
final playlist = intent.ref.read(audioPlayerProvider.notifier);
|
||||||
if (playlist.isFetching) {
|
if (playlist.isFetching()) {
|
||||||
DirectionalFocusAction().invoke(
|
DirectionalFocusAction().invoke(
|
||||||
DirectionalFocusIntent(
|
DirectionalFocusIntent(
|
||||||
intent.forward ? TraversalDirection.right : TraversalDirection.left,
|
intent.forward ? TraversalDirection.right : TraversalDirection.left,
|
||||||
@ -105,7 +105,7 @@ class SeekAction extends Action<SeekIntent> {
|
|||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final position = (await audioPlayer.position ?? Duration.zero).inSeconds;
|
final position = audioPlayer.position.inSeconds;
|
||||||
await audioPlayer.seek(
|
await audioPlayer.seek(
|
||||||
Duration(
|
Duration(
|
||||||
seconds: intent.forward ? position + 5 : position - 5,
|
seconds: intent.forward ? position + 5 : position - 5,
|
||||||
|
@ -24,7 +24,7 @@ import 'package:spotube/provider/authentication/authentication.dart';
|
|||||||
import 'package:spotube/provider/blacklist_provider.dart';
|
import 'package:spotube/provider/blacklist_provider.dart';
|
||||||
import 'package:spotube/provider/download_manager_provider.dart';
|
import 'package:spotube/provider/download_manager_provider.dart';
|
||||||
import 'package:spotube/provider/local_tracks/local_tracks_provider.dart';
|
import 'package:spotube/provider/local_tracks/local_tracks_provider.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
|
|
||||||
@ -96,8 +96,8 @@ class TrackOptions extends HookConsumerWidget {
|
|||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
Track track,
|
Track track,
|
||||||
) async {
|
) async {
|
||||||
final playback = ref.read(proxyPlaylistProvider.notifier);
|
final playback = ref.read(audioPlayerProvider.notifier);
|
||||||
final playlist = ref.read(proxyPlaylistProvider);
|
final playlist = ref.read(audioPlayerProvider);
|
||||||
final spotify = ref.read(spotifyProvider);
|
final spotify = ref.read(spotifyProvider);
|
||||||
final query = "${track.name} Radio";
|
final query = "${track.name} Radio";
|
||||||
final pages =
|
final pages =
|
||||||
@ -160,8 +160,8 @@ class TrackOptions extends HookConsumerWidget {
|
|||||||
final router = GoRouter.of(context);
|
final router = GoRouter.of(context);
|
||||||
final ThemeData(:colorScheme) = Theme.of(context);
|
final ThemeData(:colorScheme) = Theme.of(context);
|
||||||
|
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final playback = ref.watch(proxyPlaylistProvider.notifier);
|
final playback = ref.watch(audioPlayerProvider.notifier);
|
||||||
final auth = ref.watch(authenticationProvider);
|
final auth = ref.watch(authenticationProvider);
|
||||||
ref.watch(downloadManagerProvider);
|
ref.watch(downloadManagerProvider);
|
||||||
final downloadManager = ref.watch(downloadManagerProvider.notifier);
|
final downloadManager = ref.watch(downloadManagerProvider.notifier);
|
||||||
@ -364,7 +364,7 @@ class TrackOptions extends HookConsumerWidget {
|
|||||||
: context.l10n.save_as_favorite,
|
: context.l10n.save_as_favorite,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (auth != null && !isLocalTrack) ...[
|
if (auth.asData?.value != null && !isLocalTrack) ...[
|
||||||
PopSheetEntry(
|
PopSheetEntry(
|
||||||
value: TrackOptionValue.startRadio,
|
value: TrackOptionValue.startRadio,
|
||||||
leading: const Icon(SpotubeIcons.radio),
|
leading: const Icon(SpotubeIcons.radio),
|
||||||
@ -376,7 +376,7 @@ class TrackOptions extends HookConsumerWidget {
|
|||||||
title: Text(context.l10n.add_to_playlist),
|
title: Text(context.l10n.add_to_playlist),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
if (userPlaylist && auth != null && !isLocalTrack)
|
if (userPlaylist && auth.asData?.value != null && !isLocalTrack)
|
||||||
PopSheetEntry(
|
PopSheetEntry(
|
||||||
value: TrackOptionValue.removeFromPlaylist,
|
value: TrackOptionValue.removeFromPlaylist,
|
||||||
leading: const Icon(SpotubeIcons.removeFilled),
|
leading: const Icon(SpotubeIcons.removeFilled),
|
||||||
|
@ -17,8 +17,9 @@ import 'package:spotube/extensions/constrains.dart';
|
|||||||
import 'package:spotube/extensions/duration.dart';
|
import 'package:spotube/extensions/duration.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
import 'package:spotube/models/local_track.dart';
|
import 'package:spotube/models/local_track.dart';
|
||||||
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
|
import 'package:spotube/provider/audio_player/state.dart';
|
||||||
import 'package:spotube/provider/blacklist_provider.dart';
|
import 'package:spotube/provider/blacklist_provider.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart';
|
|
||||||
|
|
||||||
class TrackTile extends HookConsumerWidget {
|
class TrackTile extends HookConsumerWidget {
|
||||||
/// [index] will not be shown if null
|
/// [index] will not be shown if null
|
||||||
@ -30,7 +31,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
final VoidCallback? onLongPress;
|
final VoidCallback? onLongPress;
|
||||||
final bool userPlaylist;
|
final bool userPlaylist;
|
||||||
final String? playlistId;
|
final String? playlistId;
|
||||||
final ProxyPlaylist playlist;
|
final AudioPlayerState playlist;
|
||||||
|
|
||||||
final List<Widget>? leadingActions;
|
final List<Widget>? leadingActions;
|
||||||
|
|
||||||
@ -160,7 +161,11 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
child: Skeleton.ignore(
|
child: Skeleton.ignore(
|
||||||
child: AnimatedSwitcher(
|
child: AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
child: (isPlaying && playlist.isFetching) ||
|
child: (isPlaying &&
|
||||||
|
ref
|
||||||
|
.watch(audioPlayerProvider
|
||||||
|
.notifier)
|
||||||
|
.isFetching()) ||
|
||||||
isLoading.value
|
isLoading.value
|
||||||
? const SizedBox(
|
? const SizedBox(
|
||||||
width: 26,
|
width: 26,
|
||||||
|
@ -18,7 +18,7 @@ import 'package:spotube/components/tracks_view/track_view_provider.dart';
|
|||||||
import 'package:spotube/models/connect/connect.dart';
|
import 'package:spotube/models/connect/connect.dart';
|
||||||
import 'package:spotube/provider/connect/connect.dart';
|
import 'package:spotube/provider/connect/connect.dart';
|
||||||
import 'package:spotube/provider/history/history.dart';
|
import 'package:spotube/provider/history/history.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
||||||
|
|
||||||
@ -27,8 +27,8 @@ class TrackViewBodySection extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
final historyNotifier = ref.watch(playbackHistoryProvider.notifier);
|
final historyNotifier = ref.watch(playbackHistoryProvider.notifier);
|
||||||
final props = InheritedTrackView.of(context);
|
final props = InheritedTrackView.of(context);
|
||||||
final trackViewState = ref.watch(trackViewProvider(props.tracks));
|
final trackViewState = ref.watch(trackViewProvider(props.tracks));
|
||||||
|
@ -11,7 +11,7 @@ import 'package:spotube/extensions/context.dart';
|
|||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
import 'package:spotube/provider/download_manager_provider.dart';
|
import 'package:spotube/provider/download_manager_provider.dart';
|
||||||
import 'package:spotube/provider/history/history.dart';
|
import 'package:spotube/provider/history/history.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
|
|
||||||
class TrackViewBodyOptions extends HookConsumerWidget {
|
class TrackViewBodyOptions extends HookConsumerWidget {
|
||||||
@ -24,7 +24,7 @@ class TrackViewBodyOptions extends HookConsumerWidget {
|
|||||||
|
|
||||||
ref.watch(downloadManagerProvider);
|
ref.watch(downloadManagerProvider);
|
||||||
final downloader = ref.watch(downloadManagerProvider.notifier);
|
final downloader = ref.watch(downloadManagerProvider.notifier);
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
final historyNotifier = ref.watch(playbackHistoryProvider.notifier);
|
final historyNotifier = ref.watch(playbackHistoryProvider.notifier);
|
||||||
final audioSource =
|
final audioSource =
|
||||||
ref.watch(userPreferencesProvider.select((s) => s.audioSource));
|
ref.watch(userPreferencesProvider.select((s) => s.audioSource));
|
||||||
|
@ -11,7 +11,7 @@ import 'package:spotube/components/tracks_view/track_view_props.dart';
|
|||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
import 'package:spotube/provider/history/history.dart';
|
import 'package:spotube/provider/history/history.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
|
|
||||||
class TrackViewHeaderActions extends HookConsumerWidget {
|
class TrackViewHeaderActions extends HookConsumerWidget {
|
||||||
const TrackViewHeaderActions({super.key});
|
const TrackViewHeaderActions({super.key});
|
||||||
@ -20,8 +20,8 @@ class TrackViewHeaderActions extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final props = InheritedTrackView.of(context);
|
final props = InheritedTrackView.of(context);
|
||||||
|
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
final historyNotifier = ref.watch(playbackHistoryProvider.notifier);
|
final historyNotifier = ref.watch(playbackHistoryProvider.notifier);
|
||||||
|
|
||||||
final isActive = playlist.collections.contains(props.collectionId);
|
final isActive = playlist.collections.contains(props.collectionId);
|
||||||
@ -73,7 +73,7 @@ class TrackViewHeaderActions extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (props.onHeart != null && auth != null)
|
if (props.onHeart != null && auth.asData?.value != null)
|
||||||
HeartButton(
|
HeartButton(
|
||||||
isLiked: props.isLiked,
|
isLiked: props.isLiked,
|
||||||
icon: isUserPlaylist ? SpotubeIcons.trash : null,
|
icon: isUserPlaylist ? SpotubeIcons.trash : null,
|
||||||
|
@ -13,7 +13,7 @@ import 'package:spotube/extensions/context.dart';
|
|||||||
import 'package:spotube/models/connect/connect.dart';
|
import 'package:spotube/models/connect/connect.dart';
|
||||||
import 'package:spotube/provider/connect/connect.dart';
|
import 'package:spotube/provider/connect/connect.dart';
|
||||||
import 'package:spotube/provider/history/history.dart';
|
import 'package:spotube/provider/history/history.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
class TrackViewHeaderButtons extends HookConsumerWidget {
|
class TrackViewHeaderButtons extends HookConsumerWidget {
|
||||||
@ -28,8 +28,8 @@ class TrackViewHeaderButtons extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final props = InheritedTrackView.of(context);
|
final props = InheritedTrackView.of(context);
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
final historyNotifier = ref.watch(playbackHistoryProvider.notifier);
|
final historyNotifier = ref.watch(playbackHistoryProvider.notifier);
|
||||||
|
|
||||||
final isActive = playlist.collections.contains(props.collectionId);
|
final isActive = playlist.collections.contains(props.collectionId);
|
||||||
|
@ -3,15 +3,15 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
void useEndlessPlayback(WidgetRef ref) {
|
void useEndlessPlayback(WidgetRef ref) {
|
||||||
final auth = ref.watch(authenticationProvider);
|
final auth = ref.watch(authenticationProvider);
|
||||||
final playback = ref.watch(proxyPlaylistProvider.notifier);
|
final playback = ref.watch(audioPlayerProvider.notifier);
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider.select((s) => s.playlist));
|
||||||
final spotify = ref.watch(spotifyProvider);
|
final spotify = ref.watch(spotifyProvider);
|
||||||
final endlessPlayback =
|
final endlessPlayback =
|
||||||
ref.watch(userPreferencesProvider.select((s) => s.endlessPlayback));
|
ref.watch(userPreferencesProvider.select((s) => s.endlessPlayback));
|
||||||
@ -22,7 +22,7 @@ void useEndlessPlayback(WidgetRef ref) {
|
|||||||
|
|
||||||
void listener(int index) async {
|
void listener(int index) async {
|
||||||
try {
|
try {
|
||||||
final playlist = ref.read(proxyPlaylistProvider);
|
final playlist = ref.read(audioPlayerProvider);
|
||||||
if (index != playlist.tracks.length - 1) return;
|
if (index != playlist.tracks.length - 1) return;
|
||||||
|
|
||||||
final track = playlist.tracks.last;
|
final track = playlist.tracks.last;
|
||||||
@ -56,7 +56,7 @@ void useEndlessPlayback(WidgetRef ref) {
|
|||||||
await playback.addTracks(
|
await playback.addTracks(
|
||||||
tracks.toList()
|
tracks.toList()
|
||||||
..removeWhere((e) {
|
..removeWhere((e) {
|
||||||
final playlist = ref.read(proxyPlaylistProvider);
|
final playlist = ref.read(audioPlayerProvider);
|
||||||
final isDuplicate = playlist.tracks.any((t) => t.id == e.id);
|
final isDuplicate = playlist.tracks.any((t) => t.id == e.id);
|
||||||
return e.id == track.id || isDuplicate;
|
return e.id == track.id || isDuplicate;
|
||||||
}),
|
}),
|
||||||
@ -69,9 +69,9 @@ void useEndlessPlayback(WidgetRef ref) {
|
|||||||
// Sometimes user can change settings for which the currentIndexChanged
|
// Sometimes user can change settings for which the currentIndexChanged
|
||||||
// might not be called. So we need to check if the current track is the
|
// might not be called. So we need to check if the current track is the
|
||||||
// last track and if it is then we need to call the listener manually.
|
// last track and if it is then we need to call the listener manually.
|
||||||
if (playlist.active == playlist.tracks.length - 1 &&
|
if (playlist.index == playlist.medias.length - 1 &&
|
||||||
audioPlayer.isPlaying) {
|
audioPlayer.isPlaying) {
|
||||||
listener(playlist.active!);
|
listener(playlist.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
final subscription =
|
final subscription =
|
||||||
@ -82,7 +82,7 @@ void useEndlessPlayback(WidgetRef ref) {
|
|||||||
[
|
[
|
||||||
spotify,
|
spotify,
|
||||||
playback,
|
playback,
|
||||||
playlist.tracks,
|
playlist.medias,
|
||||||
endlessPlayback,
|
endlessPlayback,
|
||||||
auth,
|
auth,
|
||||||
],
|
],
|
||||||
|
@ -18,6 +18,7 @@ import 'package:spotube/hooks/configurators/use_close_behavior.dart';
|
|||||||
import 'package:spotube/hooks/configurators/use_deep_linking.dart';
|
import 'package:spotube/hooks/configurators/use_deep_linking.dart';
|
||||||
import 'package:spotube/hooks/configurators/use_disable_battery_optimizations.dart';
|
import 'package:spotube/hooks/configurators/use_disable_battery_optimizations.dart';
|
||||||
import 'package:spotube/hooks/configurators/use_get_storage_perms.dart';
|
import 'package:spotube/hooks/configurators/use_get_storage_perms.dart';
|
||||||
|
import 'package:spotube/provider/audio_player/audio_player_streams.dart';
|
||||||
import 'package:spotube/provider/server/bonsoir.dart';
|
import 'package:spotube/provider/server/bonsoir.dart';
|
||||||
import 'package:spotube/provider/server/server.dart';
|
import 'package:spotube/provider/server/server.dart';
|
||||||
import 'package:spotube/provider/tray_manager/tray_manager.dart';
|
import 'package:spotube/provider/tray_manager/tray_manager.dart';
|
||||||
@ -113,9 +114,10 @@ class Spotube extends HookConsumerWidget {
|
|||||||
ref.watch(paletteProvider.select((s) => s?.dominantColor?.color));
|
ref.watch(paletteProvider.select((s) => s?.dominantColor?.color));
|
||||||
final router = ref.watch(routerProvider);
|
final router = ref.watch(routerProvider);
|
||||||
|
|
||||||
ref.listen(serverProvider, (_, __) {});
|
ref.listen(audioPlayerStreamListenersProvider, (_, __) {});
|
||||||
ref.listen(bonsoirProvider, (_, __) {});
|
ref.listen(bonsoirProvider, (_, __) {});
|
||||||
ref.listen(connectClientsProvider, (_, __) {});
|
ref.listen(connectClientsProvider, (_, __) {});
|
||||||
|
ref.listen(serverProvider, (_, __) {});
|
||||||
ref.listen(trayManagerProvider, (_, __) {});
|
ref.listen(trayManagerProvider, (_, __) {});
|
||||||
|
|
||||||
useDisableBatteryOptimizations();
|
useDisableBatteryOptimizations();
|
||||||
|
@ -6,7 +6,7 @@ import 'dart:convert';
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:media_kit/media_kit.dart' hide Track;
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
import 'package:spotify/spotify.dart' hide Playlist;
|
import 'package:spotify/spotify.dart' hide Playlist;
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart';
|
import 'package:spotube/provider/audio_player/state.dart';
|
||||||
|
|
||||||
part 'connect.freezed.dart';
|
part 'connect.freezed.dart';
|
||||||
part 'connect.g.dart';
|
part 'connect.g.dart';
|
||||||
|
@ -325,12 +325,12 @@ class WebSocketErrorEvent extends WebSocketEvent<String> {
|
|||||||
WebSocketErrorEvent(String data) : super(WsEvent.error, data);
|
WebSocketErrorEvent(String data) : super(WsEvent.error, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebSocketQueueEvent extends WebSocketEvent<ProxyPlaylist> {
|
class WebSocketQueueEvent extends WebSocketEvent<AudioPlayerState> {
|
||||||
WebSocketQueueEvent(ProxyPlaylist data) : super(WsEvent.queue, data);
|
WebSocketQueueEvent(AudioPlayerState data) : super(WsEvent.queue, data);
|
||||||
|
|
||||||
factory WebSocketQueueEvent.fromJson(Map<String, dynamic> json) =>
|
factory WebSocketQueueEvent.fromJson(Map<String, dynamic> json) =>
|
||||||
WebSocketQueueEvent(
|
WebSocketQueueEvent(
|
||||||
ProxyPlaylist.fromJsonRaw(json),
|
AudioPlayerState.fromJson(json),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2599,11 +2599,6 @@ class $AudioPlayerStateTableTable extends AudioPlayerStateTable
|
|||||||
requiredDuringInsert: true,
|
requiredDuringInsert: true,
|
||||||
defaultConstraints:
|
defaultConstraints:
|
||||||
GeneratedColumn.constraintIsAlways('CHECK ("playing" IN (0, 1))'));
|
GeneratedColumn.constraintIsAlways('CHECK ("playing" IN (0, 1))'));
|
||||||
static const VerificationMeta _volumeMeta = const VerificationMeta('volume');
|
|
||||||
@override
|
|
||||||
late final GeneratedColumn<double> volume = GeneratedColumn<double>(
|
|
||||||
'volume', aliasedName, false,
|
|
||||||
type: DriftSqlType.double, requiredDuringInsert: true);
|
|
||||||
static const VerificationMeta _loopModeMeta =
|
static const VerificationMeta _loopModeMeta =
|
||||||
const VerificationMeta('loopMode');
|
const VerificationMeta('loopMode');
|
||||||
@override
|
@override
|
||||||
@ -2621,9 +2616,17 @@ class $AudioPlayerStateTableTable extends AudioPlayerStateTable
|
|||||||
requiredDuringInsert: true,
|
requiredDuringInsert: true,
|
||||||
defaultConstraints:
|
defaultConstraints:
|
||||||
GeneratedColumn.constraintIsAlways('CHECK ("shuffled" IN (0, 1))'));
|
GeneratedColumn.constraintIsAlways('CHECK ("shuffled" IN (0, 1))'));
|
||||||
|
static const VerificationMeta _collectionsMeta =
|
||||||
|
const VerificationMeta('collections');
|
||||||
|
@override
|
||||||
|
late final GeneratedColumnWithTypeConverter<List<String>, String>
|
||||||
|
collections = GeneratedColumn<String>('collections', aliasedName, false,
|
||||||
|
type: DriftSqlType.string, requiredDuringInsert: true)
|
||||||
|
.withConverter<List<String>>(
|
||||||
|
$AudioPlayerStateTableTable.$convertercollections);
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns =>
|
List<GeneratedColumn> get $columns =>
|
||||||
[id, playing, volume, loopMode, shuffled];
|
[id, playing, loopMode, shuffled, collections];
|
||||||
@override
|
@override
|
||||||
String get aliasedName => _alias ?? actualTableName;
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
@override
|
@override
|
||||||
@ -2644,12 +2647,6 @@ class $AudioPlayerStateTableTable extends AudioPlayerStateTable
|
|||||||
} else if (isInserting) {
|
} else if (isInserting) {
|
||||||
context.missing(_playingMeta);
|
context.missing(_playingMeta);
|
||||||
}
|
}
|
||||||
if (data.containsKey('volume')) {
|
|
||||||
context.handle(_volumeMeta,
|
|
||||||
volume.isAcceptableOrUnknown(data['volume']!, _volumeMeta));
|
|
||||||
} else if (isInserting) {
|
|
||||||
context.missing(_volumeMeta);
|
|
||||||
}
|
|
||||||
context.handle(_loopModeMeta, const VerificationResult.success());
|
context.handle(_loopModeMeta, const VerificationResult.success());
|
||||||
if (data.containsKey('shuffled')) {
|
if (data.containsKey('shuffled')) {
|
||||||
context.handle(_shuffledMeta,
|
context.handle(_shuffledMeta,
|
||||||
@ -2657,6 +2654,7 @@ class $AudioPlayerStateTableTable extends AudioPlayerStateTable
|
|||||||
} else if (isInserting) {
|
} else if (isInserting) {
|
||||||
context.missing(_shuffledMeta);
|
context.missing(_shuffledMeta);
|
||||||
}
|
}
|
||||||
|
context.handle(_collectionsMeta, const VerificationResult.success());
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2671,13 +2669,14 @@ class $AudioPlayerStateTableTable extends AudioPlayerStateTable
|
|||||||
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
|
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
|
||||||
playing: attachedDatabase.typeMapping
|
playing: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.bool, data['${effectivePrefix}playing'])!,
|
.read(DriftSqlType.bool, data['${effectivePrefix}playing'])!,
|
||||||
volume: attachedDatabase.typeMapping
|
|
||||||
.read(DriftSqlType.double, data['${effectivePrefix}volume'])!,
|
|
||||||
loopMode: $AudioPlayerStateTableTable.$converterloopMode.fromSql(
|
loopMode: $AudioPlayerStateTableTable.$converterloopMode.fromSql(
|
||||||
attachedDatabase.typeMapping
|
attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.string, data['${effectivePrefix}loop_mode'])!),
|
.read(DriftSqlType.string, data['${effectivePrefix}loop_mode'])!),
|
||||||
shuffled: attachedDatabase.typeMapping
|
shuffled: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.bool, data['${effectivePrefix}shuffled'])!,
|
.read(DriftSqlType.bool, data['${effectivePrefix}shuffled'])!,
|
||||||
|
collections: $AudioPlayerStateTableTable.$convertercollections.fromSql(
|
||||||
|
attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.string, data['${effectivePrefix}collections'])!),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2688,32 +2687,37 @@ class $AudioPlayerStateTableTable extends AudioPlayerStateTable
|
|||||||
|
|
||||||
static JsonTypeConverter2<PlaylistMode, String, String> $converterloopMode =
|
static JsonTypeConverter2<PlaylistMode, String, String> $converterloopMode =
|
||||||
const EnumNameConverter<PlaylistMode>(PlaylistMode.values);
|
const EnumNameConverter<PlaylistMode>(PlaylistMode.values);
|
||||||
|
static TypeConverter<List<String>, String> $convertercollections =
|
||||||
|
const StringListConverter();
|
||||||
}
|
}
|
||||||
|
|
||||||
class AudioPlayerStateTableData extends DataClass
|
class AudioPlayerStateTableData extends DataClass
|
||||||
implements Insertable<AudioPlayerStateTableData> {
|
implements Insertable<AudioPlayerStateTableData> {
|
||||||
final int id;
|
final int id;
|
||||||
final bool playing;
|
final bool playing;
|
||||||
final double volume;
|
|
||||||
final PlaylistMode loopMode;
|
final PlaylistMode loopMode;
|
||||||
final bool shuffled;
|
final bool shuffled;
|
||||||
|
final List<String> collections;
|
||||||
const AudioPlayerStateTableData(
|
const AudioPlayerStateTableData(
|
||||||
{required this.id,
|
{required this.id,
|
||||||
required this.playing,
|
required this.playing,
|
||||||
required this.volume,
|
|
||||||
required this.loopMode,
|
required this.loopMode,
|
||||||
required this.shuffled});
|
required this.shuffled,
|
||||||
|
required this.collections});
|
||||||
@override
|
@override
|
||||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, Expression>{};
|
final map = <String, Expression>{};
|
||||||
map['id'] = Variable<int>(id);
|
map['id'] = Variable<int>(id);
|
||||||
map['playing'] = Variable<bool>(playing);
|
map['playing'] = Variable<bool>(playing);
|
||||||
map['volume'] = Variable<double>(volume);
|
|
||||||
{
|
{
|
||||||
map['loop_mode'] = Variable<String>(
|
map['loop_mode'] = Variable<String>(
|
||||||
$AudioPlayerStateTableTable.$converterloopMode.toSql(loopMode));
|
$AudioPlayerStateTableTable.$converterloopMode.toSql(loopMode));
|
||||||
}
|
}
|
||||||
map['shuffled'] = Variable<bool>(shuffled);
|
map['shuffled'] = Variable<bool>(shuffled);
|
||||||
|
{
|
||||||
|
map['collections'] = Variable<String>(
|
||||||
|
$AudioPlayerStateTableTable.$convertercollections.toSql(collections));
|
||||||
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2721,9 +2725,9 @@ class AudioPlayerStateTableData extends DataClass
|
|||||||
return AudioPlayerStateTableCompanion(
|
return AudioPlayerStateTableCompanion(
|
||||||
id: Value(id),
|
id: Value(id),
|
||||||
playing: Value(playing),
|
playing: Value(playing),
|
||||||
volume: Value(volume),
|
|
||||||
loopMode: Value(loopMode),
|
loopMode: Value(loopMode),
|
||||||
shuffled: Value(shuffled),
|
shuffled: Value(shuffled),
|
||||||
|
collections: Value(collections),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2733,10 +2737,10 @@ class AudioPlayerStateTableData extends DataClass
|
|||||||
return AudioPlayerStateTableData(
|
return AudioPlayerStateTableData(
|
||||||
id: serializer.fromJson<int>(json['id']),
|
id: serializer.fromJson<int>(json['id']),
|
||||||
playing: serializer.fromJson<bool>(json['playing']),
|
playing: serializer.fromJson<bool>(json['playing']),
|
||||||
volume: serializer.fromJson<double>(json['volume']),
|
|
||||||
loopMode: $AudioPlayerStateTableTable.$converterloopMode
|
loopMode: $AudioPlayerStateTableTable.$converterloopMode
|
||||||
.fromJson(serializer.fromJson<String>(json['loopMode'])),
|
.fromJson(serializer.fromJson<String>(json['loopMode'])),
|
||||||
shuffled: serializer.fromJson<bool>(json['shuffled']),
|
shuffled: serializer.fromJson<bool>(json['shuffled']),
|
||||||
|
collections: serializer.fromJson<List<String>>(json['collections']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@override
|
@override
|
||||||
@ -2745,103 +2749,103 @@ class AudioPlayerStateTableData extends DataClass
|
|||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
'id': serializer.toJson<int>(id),
|
'id': serializer.toJson<int>(id),
|
||||||
'playing': serializer.toJson<bool>(playing),
|
'playing': serializer.toJson<bool>(playing),
|
||||||
'volume': serializer.toJson<double>(volume),
|
|
||||||
'loopMode': serializer.toJson<String>(
|
'loopMode': serializer.toJson<String>(
|
||||||
$AudioPlayerStateTableTable.$converterloopMode.toJson(loopMode)),
|
$AudioPlayerStateTableTable.$converterloopMode.toJson(loopMode)),
|
||||||
'shuffled': serializer.toJson<bool>(shuffled),
|
'shuffled': serializer.toJson<bool>(shuffled),
|
||||||
|
'collections': serializer.toJson<List<String>>(collections),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioPlayerStateTableData copyWith(
|
AudioPlayerStateTableData copyWith(
|
||||||
{int? id,
|
{int? id,
|
||||||
bool? playing,
|
bool? playing,
|
||||||
double? volume,
|
|
||||||
PlaylistMode? loopMode,
|
PlaylistMode? loopMode,
|
||||||
bool? shuffled}) =>
|
bool? shuffled,
|
||||||
|
List<String>? collections}) =>
|
||||||
AudioPlayerStateTableData(
|
AudioPlayerStateTableData(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
playing: playing ?? this.playing,
|
playing: playing ?? this.playing,
|
||||||
volume: volume ?? this.volume,
|
|
||||||
loopMode: loopMode ?? this.loopMode,
|
loopMode: loopMode ?? this.loopMode,
|
||||||
shuffled: shuffled ?? this.shuffled,
|
shuffled: shuffled ?? this.shuffled,
|
||||||
|
collections: collections ?? this.collections,
|
||||||
);
|
);
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return (StringBuffer('AudioPlayerStateTableData(')
|
return (StringBuffer('AudioPlayerStateTableData(')
|
||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
..write('playing: $playing, ')
|
..write('playing: $playing, ')
|
||||||
..write('volume: $volume, ')
|
|
||||||
..write('loopMode: $loopMode, ')
|
..write('loopMode: $loopMode, ')
|
||||||
..write('shuffled: $shuffled')
|
..write('shuffled: $shuffled, ')
|
||||||
|
..write('collections: $collections')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(id, playing, volume, loopMode, shuffled);
|
int get hashCode => Object.hash(id, playing, loopMode, shuffled, collections);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
(other is AudioPlayerStateTableData &&
|
(other is AudioPlayerStateTableData &&
|
||||||
other.id == this.id &&
|
other.id == this.id &&
|
||||||
other.playing == this.playing &&
|
other.playing == this.playing &&
|
||||||
other.volume == this.volume &&
|
|
||||||
other.loopMode == this.loopMode &&
|
other.loopMode == this.loopMode &&
|
||||||
other.shuffled == this.shuffled);
|
other.shuffled == this.shuffled &&
|
||||||
|
other.collections == this.collections);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AudioPlayerStateTableCompanion
|
class AudioPlayerStateTableCompanion
|
||||||
extends UpdateCompanion<AudioPlayerStateTableData> {
|
extends UpdateCompanion<AudioPlayerStateTableData> {
|
||||||
final Value<int> id;
|
final Value<int> id;
|
||||||
final Value<bool> playing;
|
final Value<bool> playing;
|
||||||
final Value<double> volume;
|
|
||||||
final Value<PlaylistMode> loopMode;
|
final Value<PlaylistMode> loopMode;
|
||||||
final Value<bool> shuffled;
|
final Value<bool> shuffled;
|
||||||
|
final Value<List<String>> collections;
|
||||||
const AudioPlayerStateTableCompanion({
|
const AudioPlayerStateTableCompanion({
|
||||||
this.id = const Value.absent(),
|
this.id = const Value.absent(),
|
||||||
this.playing = const Value.absent(),
|
this.playing = const Value.absent(),
|
||||||
this.volume = const Value.absent(),
|
|
||||||
this.loopMode = const Value.absent(),
|
this.loopMode = const Value.absent(),
|
||||||
this.shuffled = const Value.absent(),
|
this.shuffled = const Value.absent(),
|
||||||
|
this.collections = const Value.absent(),
|
||||||
});
|
});
|
||||||
AudioPlayerStateTableCompanion.insert({
|
AudioPlayerStateTableCompanion.insert({
|
||||||
this.id = const Value.absent(),
|
this.id = const Value.absent(),
|
||||||
required bool playing,
|
required bool playing,
|
||||||
required double volume,
|
|
||||||
required PlaylistMode loopMode,
|
required PlaylistMode loopMode,
|
||||||
required bool shuffled,
|
required bool shuffled,
|
||||||
|
required List<String> collections,
|
||||||
}) : playing = Value(playing),
|
}) : playing = Value(playing),
|
||||||
volume = Value(volume),
|
|
||||||
loopMode = Value(loopMode),
|
loopMode = Value(loopMode),
|
||||||
shuffled = Value(shuffled);
|
shuffled = Value(shuffled),
|
||||||
|
collections = Value(collections);
|
||||||
static Insertable<AudioPlayerStateTableData> custom({
|
static Insertable<AudioPlayerStateTableData> custom({
|
||||||
Expression<int>? id,
|
Expression<int>? id,
|
||||||
Expression<bool>? playing,
|
Expression<bool>? playing,
|
||||||
Expression<double>? volume,
|
|
||||||
Expression<String>? loopMode,
|
Expression<String>? loopMode,
|
||||||
Expression<bool>? shuffled,
|
Expression<bool>? shuffled,
|
||||||
|
Expression<String>? collections,
|
||||||
}) {
|
}) {
|
||||||
return RawValuesInsertable({
|
return RawValuesInsertable({
|
||||||
if (id != null) 'id': id,
|
if (id != null) 'id': id,
|
||||||
if (playing != null) 'playing': playing,
|
if (playing != null) 'playing': playing,
|
||||||
if (volume != null) 'volume': volume,
|
|
||||||
if (loopMode != null) 'loop_mode': loopMode,
|
if (loopMode != null) 'loop_mode': loopMode,
|
||||||
if (shuffled != null) 'shuffled': shuffled,
|
if (shuffled != null) 'shuffled': shuffled,
|
||||||
|
if (collections != null) 'collections': collections,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioPlayerStateTableCompanion copyWith(
|
AudioPlayerStateTableCompanion copyWith(
|
||||||
{Value<int>? id,
|
{Value<int>? id,
|
||||||
Value<bool>? playing,
|
Value<bool>? playing,
|
||||||
Value<double>? volume,
|
|
||||||
Value<PlaylistMode>? loopMode,
|
Value<PlaylistMode>? loopMode,
|
||||||
Value<bool>? shuffled}) {
|
Value<bool>? shuffled,
|
||||||
|
Value<List<String>>? collections}) {
|
||||||
return AudioPlayerStateTableCompanion(
|
return AudioPlayerStateTableCompanion(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
playing: playing ?? this.playing,
|
playing: playing ?? this.playing,
|
||||||
volume: volume ?? this.volume,
|
|
||||||
loopMode: loopMode ?? this.loopMode,
|
loopMode: loopMode ?? this.loopMode,
|
||||||
shuffled: shuffled ?? this.shuffled,
|
shuffled: shuffled ?? this.shuffled,
|
||||||
|
collections: collections ?? this.collections,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2854,9 +2858,6 @@ class AudioPlayerStateTableCompanion
|
|||||||
if (playing.present) {
|
if (playing.present) {
|
||||||
map['playing'] = Variable<bool>(playing.value);
|
map['playing'] = Variable<bool>(playing.value);
|
||||||
}
|
}
|
||||||
if (volume.present) {
|
|
||||||
map['volume'] = Variable<double>(volume.value);
|
|
||||||
}
|
|
||||||
if (loopMode.present) {
|
if (loopMode.present) {
|
||||||
map['loop_mode'] = Variable<String>(
|
map['loop_mode'] = Variable<String>(
|
||||||
$AudioPlayerStateTableTable.$converterloopMode.toSql(loopMode.value));
|
$AudioPlayerStateTableTable.$converterloopMode.toSql(loopMode.value));
|
||||||
@ -2864,6 +2865,11 @@ class AudioPlayerStateTableCompanion
|
|||||||
if (shuffled.present) {
|
if (shuffled.present) {
|
||||||
map['shuffled'] = Variable<bool>(shuffled.value);
|
map['shuffled'] = Variable<bool>(shuffled.value);
|
||||||
}
|
}
|
||||||
|
if (collections.present) {
|
||||||
|
map['collections'] = Variable<String>($AudioPlayerStateTableTable
|
||||||
|
.$convertercollections
|
||||||
|
.toSql(collections.value));
|
||||||
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2872,9 +2878,9 @@ class AudioPlayerStateTableCompanion
|
|||||||
return (StringBuffer('AudioPlayerStateTableCompanion(')
|
return (StringBuffer('AudioPlayerStateTableCompanion(')
|
||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
..write('playing: $playing, ')
|
..write('playing: $playing, ')
|
||||||
..write('volume: $volume, ')
|
|
||||||
..write('loopMode: $loopMode, ')
|
..write('loopMode: $loopMode, ')
|
||||||
..write('shuffled: $shuffled')
|
..write('shuffled: $shuffled, ')
|
||||||
|
..write('collections: $collections')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
@ -4591,17 +4597,17 @@ typedef $$AudioPlayerStateTableTableInsertCompanionBuilder
|
|||||||
= AudioPlayerStateTableCompanion Function({
|
= AudioPlayerStateTableCompanion Function({
|
||||||
Value<int> id,
|
Value<int> id,
|
||||||
required bool playing,
|
required bool playing,
|
||||||
required double volume,
|
|
||||||
required PlaylistMode loopMode,
|
required PlaylistMode loopMode,
|
||||||
required bool shuffled,
|
required bool shuffled,
|
||||||
|
required List<String> collections,
|
||||||
});
|
});
|
||||||
typedef $$AudioPlayerStateTableTableUpdateCompanionBuilder
|
typedef $$AudioPlayerStateTableTableUpdateCompanionBuilder
|
||||||
= AudioPlayerStateTableCompanion Function({
|
= AudioPlayerStateTableCompanion Function({
|
||||||
Value<int> id,
|
Value<int> id,
|
||||||
Value<bool> playing,
|
Value<bool> playing,
|
||||||
Value<double> volume,
|
|
||||||
Value<PlaylistMode> loopMode,
|
Value<PlaylistMode> loopMode,
|
||||||
Value<bool> shuffled,
|
Value<bool> shuffled,
|
||||||
|
Value<List<String>> collections,
|
||||||
});
|
});
|
||||||
|
|
||||||
class $$AudioPlayerStateTableTableTableManager extends RootTableManager<
|
class $$AudioPlayerStateTableTableTableManager extends RootTableManager<
|
||||||
@ -4627,30 +4633,30 @@ class $$AudioPlayerStateTableTableTableManager extends RootTableManager<
|
|||||||
getUpdateCompanionBuilder: ({
|
getUpdateCompanionBuilder: ({
|
||||||
Value<int> id = const Value.absent(),
|
Value<int> id = const Value.absent(),
|
||||||
Value<bool> playing = const Value.absent(),
|
Value<bool> playing = const Value.absent(),
|
||||||
Value<double> volume = const Value.absent(),
|
|
||||||
Value<PlaylistMode> loopMode = const Value.absent(),
|
Value<PlaylistMode> loopMode = const Value.absent(),
|
||||||
Value<bool> shuffled = const Value.absent(),
|
Value<bool> shuffled = const Value.absent(),
|
||||||
|
Value<List<String>> collections = const Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
AudioPlayerStateTableCompanion(
|
AudioPlayerStateTableCompanion(
|
||||||
id: id,
|
id: id,
|
||||||
playing: playing,
|
playing: playing,
|
||||||
volume: volume,
|
|
||||||
loopMode: loopMode,
|
loopMode: loopMode,
|
||||||
shuffled: shuffled,
|
shuffled: shuffled,
|
||||||
|
collections: collections,
|
||||||
),
|
),
|
||||||
getInsertCompanionBuilder: ({
|
getInsertCompanionBuilder: ({
|
||||||
Value<int> id = const Value.absent(),
|
Value<int> id = const Value.absent(),
|
||||||
required bool playing,
|
required bool playing,
|
||||||
required double volume,
|
|
||||||
required PlaylistMode loopMode,
|
required PlaylistMode loopMode,
|
||||||
required bool shuffled,
|
required bool shuffled,
|
||||||
|
required List<String> collections,
|
||||||
}) =>
|
}) =>
|
||||||
AudioPlayerStateTableCompanion.insert(
|
AudioPlayerStateTableCompanion.insert(
|
||||||
id: id,
|
id: id,
|
||||||
playing: playing,
|
playing: playing,
|
||||||
volume: volume,
|
|
||||||
loopMode: loopMode,
|
loopMode: loopMode,
|
||||||
shuffled: shuffled,
|
shuffled: shuffled,
|
||||||
|
collections: collections,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -4681,11 +4687,6 @@ class $$AudioPlayerStateTableTableFilterComposer
|
|||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnFilters(column, joinBuilders: joinBuilders));
|
ColumnFilters(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
ColumnFilters<double> get volume => $state.composableBuilder(
|
|
||||||
column: $state.table.volume,
|
|
||||||
builder: (column, joinBuilders) =>
|
|
||||||
ColumnFilters(column, joinBuilders: joinBuilders));
|
|
||||||
|
|
||||||
ColumnWithTypeConverterFilters<PlaylistMode, PlaylistMode, String>
|
ColumnWithTypeConverterFilters<PlaylistMode, PlaylistMode, String>
|
||||||
get loopMode => $state.composableBuilder(
|
get loopMode => $state.composableBuilder(
|
||||||
column: $state.table.loopMode,
|
column: $state.table.loopMode,
|
||||||
@ -4698,6 +4699,13 @@ class $$AudioPlayerStateTableTableFilterComposer
|
|||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnFilters(column, joinBuilders: joinBuilders));
|
ColumnFilters(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
|
ColumnWithTypeConverterFilters<List<String>, List<String>, String>
|
||||||
|
get collections => $state.composableBuilder(
|
||||||
|
column: $state.table.collections,
|
||||||
|
builder: (column, joinBuilders) => ColumnWithTypeConverterFilters(
|
||||||
|
column,
|
||||||
|
joinBuilders: joinBuilders));
|
||||||
|
|
||||||
ComposableFilter playlistTableRefs(
|
ComposableFilter playlistTableRefs(
|
||||||
ComposableFilter Function($$PlaylistTableTableFilterComposer f) f) {
|
ComposableFilter Function($$PlaylistTableTableFilterComposer f) f) {
|
||||||
final $$PlaylistTableTableFilterComposer composer = $state.composerBuilder(
|
final $$PlaylistTableTableFilterComposer composer = $state.composerBuilder(
|
||||||
@ -4725,11 +4733,6 @@ class $$AudioPlayerStateTableTableOrderingComposer
|
|||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnOrderings(column, joinBuilders: joinBuilders));
|
ColumnOrderings(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
ColumnOrderings<double> get volume => $state.composableBuilder(
|
|
||||||
column: $state.table.volume,
|
|
||||||
builder: (column, joinBuilders) =>
|
|
||||||
ColumnOrderings(column, joinBuilders: joinBuilders));
|
|
||||||
|
|
||||||
ColumnOrderings<String> get loopMode => $state.composableBuilder(
|
ColumnOrderings<String> get loopMode => $state.composableBuilder(
|
||||||
column: $state.table.loopMode,
|
column: $state.table.loopMode,
|
||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
@ -4739,6 +4742,11 @@ class $$AudioPlayerStateTableTableOrderingComposer
|
|||||||
column: $state.table.shuffled,
|
column: $state.table.shuffled,
|
||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnOrderings(column, joinBuilders: joinBuilders));
|
ColumnOrderings(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
|
ColumnOrderings<String> get collections => $state.composableBuilder(
|
||||||
|
column: $state.table.collections,
|
||||||
|
builder: (column, joinBuilders) =>
|
||||||
|
ColumnOrderings(column, joinBuilders: joinBuilders));
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef $$PlaylistTableTableInsertCompanionBuilder = PlaylistTableCompanion
|
typedef $$PlaylistTableTableInsertCompanionBuilder = PlaylistTableCompanion
|
||||||
|
@ -3,9 +3,9 @@ part of '../database.dart';
|
|||||||
class AudioPlayerStateTable extends Table {
|
class AudioPlayerStateTable extends Table {
|
||||||
IntColumn get id => integer().autoIncrement()();
|
IntColumn get id => integer().autoIncrement()();
|
||||||
BoolColumn get playing => boolean()();
|
BoolColumn get playing => boolean()();
|
||||||
RealColumn get volume => real()();
|
|
||||||
TextColumn get loopMode => textEnum<PlaylistMode>()();
|
TextColumn get loopMode => textEnum<PlaylistMode>()();
|
||||||
BoolColumn get shuffled => boolean()();
|
BoolColumn get shuffled => boolean()();
|
||||||
|
TextColumn get collections => text().map(const StringListConverter())();
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlaylistTable extends Table {
|
class PlaylistTable extends Table {
|
||||||
|
@ -12,7 +12,7 @@ import 'package:spotube/models/connect/connect.dart';
|
|||||||
import 'package:spotube/pages/album/album.dart';
|
import 'package:spotube/pages/album/album.dart';
|
||||||
import 'package:spotube/provider/connect/connect.dart';
|
import 'package:spotube/provider/connect/connect.dart';
|
||||||
import 'package:spotube/provider/history/history.dart';
|
import 'package:spotube/provider/history/history.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
@ -30,10 +30,10 @@ class AlbumCard extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final playing =
|
final playing =
|
||||||
useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying;
|
useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying;
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
final historyNotifier = ref.read(playbackHistoryProvider.notifier);
|
final historyNotifier = ref.read(playbackHistoryProvider.notifier);
|
||||||
|
|
||||||
bool isPlaylistPlaying = useMemoized(
|
bool isPlaylistPlaying = useMemoized(
|
||||||
@ -59,7 +59,7 @@ class AlbumCard extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 10),
|
margin: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
isPlaying: isPlaylistPlaying,
|
isPlaying: isPlaylistPlaying,
|
||||||
isLoading: (isPlaylistPlaying && playlist.isFetching == true) ||
|
isLoading: (isPlaylistPlaying && playlistNotifier.isFetching()) ||
|
||||||
updating.value,
|
updating.value,
|
||||||
title: album.name!,
|
title: album.name!,
|
||||||
description:
|
description:
|
||||||
|
@ -25,7 +25,7 @@ import 'package:spotube/hooks/utils/use_palette_color.dart';
|
|||||||
import 'package:spotube/models/local_track.dart';
|
import 'package:spotube/models/local_track.dart';
|
||||||
import 'package:spotube/pages/lyrics/lyrics.dart';
|
import 'package:spotube/pages/lyrics/lyrics.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/server/active_sourced_track.dart';
|
import 'package:spotube/provider/server/active_sourced_track.dart';
|
||||||
import 'package:spotube/provider/volume_provider.dart';
|
import 'package:spotube/provider/volume_provider.dart';
|
||||||
import 'package:spotube/services/sourced_track/sources/youtube.dart';
|
import 'package:spotube/services/sourced_track/sources/youtube.dart';
|
||||||
@ -47,7 +47,7 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
final auth = ref.watch(authenticationProvider);
|
final auth = ref.watch(authenticationProvider);
|
||||||
final sourcedCurrentTrack = ref.watch(activeSourcedTrackProvider);
|
final sourcedCurrentTrack = ref.watch(activeSourcedTrackProvider);
|
||||||
final currentActiveTrack =
|
final currentActiveTrack =
|
||||||
ref.watch(proxyPlaylistProvider.select((s) => s.activeTrack));
|
ref.watch(audioPlayerProvider.select((s) => s.activeTrack));
|
||||||
final currentTrack = sourcedCurrentTrack ?? currentActiveTrack;
|
final currentTrack = sourcedCurrentTrack ?? currentActiveTrack;
|
||||||
final isLocalTrack = currentTrack is LocalTrack;
|
final isLocalTrack = currentTrack is LocalTrack;
|
||||||
final mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
@ -309,15 +309,13 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
builder: (context) => Consumer(
|
builder: (context) => Consumer(
|
||||||
builder: (context, ref, _) {
|
builder: (context, ref, _) {
|
||||||
final playlist = ref.watch(
|
final playlist = ref.watch(
|
||||||
proxyPlaylistProvider,
|
audioPlayerProvider,
|
||||||
);
|
|
||||||
final playlistNotifier =
|
|
||||||
ref.read(
|
|
||||||
proxyPlaylistProvider
|
|
||||||
.notifier,
|
|
||||||
);
|
);
|
||||||
|
final playlistNotifier = ref
|
||||||
|
.read(audioPlayerProvider
|
||||||
|
.notifier);
|
||||||
return PlayerQueue
|
return PlayerQueue
|
||||||
.fromProxyPlaylistNotifier(
|
.fromAudioPlayerNotifier(
|
||||||
floating: false,
|
floating: false,
|
||||||
playlist: playlist,
|
playlist: playlist,
|
||||||
notifier: playlistNotifier,
|
notifier: playlistNotifier,
|
||||||
@ -328,8 +326,9 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
: null),
|
: null),
|
||||||
),
|
),
|
||||||
if (auth != null) const SizedBox(width: 10),
|
if (auth.asData?.value != null)
|
||||||
if (auth != null)
|
const SizedBox(width: 10),
|
||||||
|
if (auth.asData?.value != null)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: OutlinedButton.icon(
|
child: OutlinedButton.icon(
|
||||||
label: Text(context.l10n.lyrics),
|
label: Text(context.l10n.lyrics),
|
||||||
|
@ -14,7 +14,7 @@ import 'package:spotube/models/local_track.dart';
|
|||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:spotube/provider/download_manager_provider.dart';
|
import 'package:spotube/provider/download_manager_provider.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/sleep_timer_provider.dart';
|
import 'package:spotube/provider/sleep_timer_provider.dart';
|
||||||
|
|
||||||
class PlayerActions extends HookConsumerWidget {
|
class PlayerActions extends HookConsumerWidget {
|
||||||
@ -33,7 +33,7 @@ class PlayerActions extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final isLocalTrack = playlist.activeTrack is LocalTrack;
|
final isLocalTrack = playlist.activeTrack is LocalTrack;
|
||||||
ref.watch(downloadManagerProvider);
|
ref.watch(downloadManagerProvider);
|
||||||
final downloader = ref.watch(downloadManagerProvider.notifier);
|
final downloader = ref.watch(downloadManagerProvider.notifier);
|
||||||
@ -129,7 +129,9 @@ class PlayerActions extends HookConsumerWidget {
|
|||||||
? () => downloader.addToQueue(playlist.activeTrack!)
|
? () => downloader.addToQueue(playlist.activeTrack!)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
if (playlist.activeTrack != null && !isLocalTrack && auth != null)
|
if (playlist.activeTrack != null &&
|
||||||
|
!isLocalTrack &&
|
||||||
|
auth.asData?.value != null)
|
||||||
TrackHeartButton(track: playlist.activeTrack!),
|
TrackHeartButton(track: playlist.activeTrack!),
|
||||||
AdaptivePopSheetList(
|
AdaptivePopSheetList(
|
||||||
offset: Offset(0, -50 * (sleepTimerEntries.values.length + 2)),
|
offset: Offset(0, -50 * (sleepTimerEntries.values.length + 2)),
|
||||||
|
@ -11,7 +11,7 @@ import 'package:spotube/extensions/context.dart';
|
|||||||
import 'package:spotube/extensions/duration.dart';
|
import 'package:spotube/extensions/duration.dart';
|
||||||
import 'package:spotube/modules/player/use_progress.dart';
|
import 'package:spotube/modules/player/use_progress.dart';
|
||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
class PlayerControls extends HookConsumerWidget {
|
class PlayerControls extends HookConsumerWidget {
|
||||||
@ -43,8 +43,8 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
SeekIntent: SeekAction(),
|
SeekIntent: SeekAction(),
|
||||||
},
|
},
|
||||||
[]);
|
[]);
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
ref.watch(audioPlayerProvider);
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
|
|
||||||
final playing =
|
final playing =
|
||||||
useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying;
|
useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying;
|
||||||
@ -132,7 +132,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
// than total duration. Keeping it resolved
|
// than total duration. Keeping it resolved
|
||||||
value: progress.value.toDouble(),
|
value: progress.value.toDouble(),
|
||||||
secondaryTrackValue: bufferProgress,
|
secondaryTrackValue: bufferProgress,
|
||||||
onChanged: playlist.isFetching == true
|
onChanged: playlistNotifier.isFetching()
|
||||||
? null
|
? null
|
||||||
: (v) {
|
: (v) {
|
||||||
progress.value = v;
|
progress.value = v;
|
||||||
@ -183,7 +183,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
: context.l10n.shuffle_playlist,
|
: context.l10n.shuffle_playlist,
|
||||||
icon: const Icon(SpotubeIcons.shuffle),
|
icon: const Icon(SpotubeIcons.shuffle),
|
||||||
style: shuffled ? activeButtonStyle : buttonStyle,
|
style: shuffled ? activeButtonStyle : buttonStyle,
|
||||||
onPressed: playlist.isFetching == true
|
onPressed: playlistNotifier.isFetching()
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
if (shuffled) {
|
if (shuffled) {
|
||||||
@ -198,15 +198,15 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
tooltip: context.l10n.previous_track,
|
tooltip: context.l10n.previous_track,
|
||||||
icon: const Icon(SpotubeIcons.skipBack),
|
icon: const Icon(SpotubeIcons.skipBack),
|
||||||
style: buttonStyle,
|
style: buttonStyle,
|
||||||
onPressed: playlist.isFetching == true
|
onPressed: playlistNotifier.isFetching()
|
||||||
? null
|
? null
|
||||||
: playlistNotifier.previous,
|
: audioPlayer.skipToPrevious,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: playing
|
tooltip: playing
|
||||||
? context.l10n.pause_playback
|
? context.l10n.pause_playback
|
||||||
: context.l10n.resume_playback,
|
: context.l10n.resume_playback,
|
||||||
icon: playlist.isFetching == true
|
icon: playlistNotifier.isFetching()
|
||||||
? SizedBox(
|
? SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
width: 20,
|
width: 20,
|
||||||
@ -219,7 +219,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
playing ? SpotubeIcons.pause : SpotubeIcons.play,
|
playing ? SpotubeIcons.pause : SpotubeIcons.play,
|
||||||
),
|
),
|
||||||
style: resumePauseStyle,
|
style: resumePauseStyle,
|
||||||
onPressed: playlist.isFetching == true
|
onPressed: playlistNotifier.isFetching()
|
||||||
? null
|
? null
|
||||||
: Actions.handler<PlayPauseIntent>(
|
: Actions.handler<PlayPauseIntent>(
|
||||||
context,
|
context,
|
||||||
@ -230,9 +230,9 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
tooltip: context.l10n.next_track,
|
tooltip: context.l10n.next_track,
|
||||||
icon: const Icon(SpotubeIcons.skipForward),
|
icon: const Icon(SpotubeIcons.skipForward),
|
||||||
style: buttonStyle,
|
style: buttonStyle,
|
||||||
onPressed: playlist.isFetching == true
|
onPressed: playlistNotifier.isFetching()
|
||||||
? null
|
? null
|
||||||
: playlistNotifier.next,
|
: audioPlayer.skipToNext,
|
||||||
),
|
),
|
||||||
StreamBuilder<PlaylistMode>(
|
StreamBuilder<PlaylistMode>(
|
||||||
stream: audioPlayer.loopModeStream,
|
stream: audioPlayer.loopModeStream,
|
||||||
@ -253,7 +253,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
loopMode == PlaylistMode.loop
|
loopMode == PlaylistMode.loop
|
||||||
? activeButtonStyle
|
? activeButtonStyle
|
||||||
: buttonStyle,
|
: buttonStyle,
|
||||||
onPressed: playlist.isFetching == true
|
onPressed: playlistNotifier.isFetching()
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
await audioPlayer.setLoopMode(loopMode);
|
await audioPlayer.setLoopMode(loopMode);
|
||||||
|
@ -11,7 +11,7 @@ import 'package:spotube/collections/spotube_icons.dart';
|
|||||||
import 'package:spotube/collections/intents.dart';
|
import 'package:spotube/collections/intents.dart';
|
||||||
import 'package:spotube/modules/player/use_progress.dart';
|
import 'package:spotube/modules/player/use_progress.dart';
|
||||||
import 'package:spotube/modules/player/player.dart';
|
import 'package:spotube/modules/player/player.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
class PlayerOverlay extends HookConsumerWidget {
|
class PlayerOverlay extends HookConsumerWidget {
|
||||||
@ -24,8 +24,8 @@ class PlayerOverlay extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final canShow = playlist.activeTrack != null;
|
final canShow = playlist.activeTrack != null;
|
||||||
|
|
||||||
final playing =
|
final playing =
|
||||||
@ -127,14 +127,14 @@ class PlayerOverlay extends HookConsumerWidget {
|
|||||||
SpotubeIcons.skipBack,
|
SpotubeIcons.skipBack,
|
||||||
color: textColor,
|
color: textColor,
|
||||||
),
|
),
|
||||||
onPressed: playlist.isFetching
|
onPressed: playlistNotifier.isFetching()
|
||||||
? null
|
? null
|
||||||
: playlistNotifier.previous,
|
: audioPlayer.skipToPrevious,
|
||||||
),
|
),
|
||||||
Consumer(
|
Consumer(
|
||||||
builder: (context, ref, _) {
|
builder: (context, ref, _) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
icon: playlist.isFetching
|
icon: playlistNotifier.isFetching()
|
||||||
? const SizedBox(
|
? const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
width: 20,
|
width: 20,
|
||||||
@ -158,9 +158,9 @@ class PlayerOverlay extends HookConsumerWidget {
|
|||||||
SpotubeIcons.skipForward,
|
SpotubeIcons.skipForward,
|
||||||
color: textColor,
|
color: textColor,
|
||||||
),
|
),
|
||||||
onPressed: playlist.isFetching
|
onPressed: playlistNotifier.isFetching()
|
||||||
? null
|
? null
|
||||||
: playlistNotifier.next,
|
: audioPlayer.skipToNext,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -18,12 +18,12 @@ import 'package:spotube/extensions/artist_simple.dart';
|
|||||||
import 'package:spotube/extensions/constrains.dart';
|
import 'package:spotube/extensions/constrains.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/hooks/controllers/use_auto_scroll_controller.dart';
|
import 'package:spotube/hooks/controllers/use_auto_scroll_controller.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/state.dart';
|
||||||
|
|
||||||
class PlayerQueue extends HookConsumerWidget {
|
class PlayerQueue extends HookConsumerWidget {
|
||||||
final bool floating;
|
final bool floating;
|
||||||
final ProxyPlaylist playlist;
|
final AudioPlayerState playlist;
|
||||||
|
|
||||||
final Future<void> Function(Track track) onJump;
|
final Future<void> Function(Track track) onJump;
|
||||||
final Future<void> Function(String trackId) onRemove;
|
final Future<void> Function(String trackId) onRemove;
|
||||||
@ -40,10 +40,10 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
PlayerQueue.fromProxyPlaylistNotifier({
|
PlayerQueue.fromAudioPlayerNotifier({
|
||||||
this.floating = true,
|
this.floating = true,
|
||||||
required this.playlist,
|
required this.playlist,
|
||||||
required ProxyPlaylistNotifier notifier,
|
required AudioPlayerNotifier notifier,
|
||||||
super.key,
|
super.key,
|
||||||
}) : onJump = notifier.jumpToTrack,
|
}) : onJump = notifier.jumpToTrack,
|
||||||
onRemove = notifier.removeTrack,
|
onRemove = notifier.removeTrack,
|
||||||
@ -93,11 +93,10 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
if (playlist.active == null) return null;
|
if (playlist.activeTrack == null) return null;
|
||||||
|
|
||||||
if (playlist.active! < 0) return;
|
|
||||||
controller.scrollToIndex(
|
controller.scrollToIndex(
|
||||||
playlist.active!,
|
playlist.playlist.index,
|
||||||
preferPosition: AutoScrollPosition.middle,
|
preferPosition: AutoScrollPosition.middle,
|
||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
|
@ -9,7 +9,7 @@ import 'package:spotube/components/links/link_text.dart';
|
|||||||
import 'package:spotube/extensions/artist_simple.dart';
|
import 'package:spotube/extensions/artist_simple.dart';
|
||||||
import 'package:spotube/extensions/constrains.dart';
|
import 'package:spotube/extensions/constrains.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
|
|
||||||
class PlayerTrackDetails extends HookConsumerWidget {
|
class PlayerTrackDetails extends HookConsumerWidget {
|
||||||
@ -21,7 +21,7 @@ class PlayerTrackDetails extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
final playback = ref.watch(proxyPlaylistProvider);
|
final playback = ref.watch(audioPlayerProvider);
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
|
@ -15,7 +15,7 @@ import 'package:spotube/extensions/context.dart';
|
|||||||
import 'package:spotube/extensions/duration.dart';
|
import 'package:spotube/extensions/duration.dart';
|
||||||
import 'package:spotube/hooks/utils/use_debounce.dart';
|
import 'package:spotube/hooks/utils/use_debounce.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/server/active_sourced_track.dart';
|
import 'package:spotube/provider/server/active_sourced_track.dart';
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
|
|
||||||
@ -53,7 +53,8 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
final preferences = ref.watch(userPreferencesProvider);
|
final preferences = ref.watch(userPreferencesProvider);
|
||||||
|
|
||||||
final isSearching = useState(false);
|
final isSearching = useState(false);
|
||||||
@ -129,13 +130,13 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
final siblings = useMemoized(
|
final siblings = useMemoized(
|
||||||
() => playlist.isFetching == false
|
() => playlistNotifier.isFetching()
|
||||||
? [
|
? [
|
||||||
(activeTrack as SourcedTrack).sourceInfo,
|
(activeTrack as SourcedTrack).sourceInfo,
|
||||||
...activeTrack.siblings,
|
...activeTrack.siblings,
|
||||||
]
|
]
|
||||||
: <SourceInfo>[],
|
: <SourceInfo>[],
|
||||||
[playlist.isFetching, activeTrack],
|
[activeTrack],
|
||||||
);
|
);
|
||||||
|
|
||||||
final borderRadius = floating
|
final borderRadius = floating
|
||||||
@ -175,12 +176,12 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
Text(" • ${sourceInfo.artist}"),
|
Text(" • ${sourceInfo.artist}"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
enabled: playlist.isFetching != true,
|
enabled: !playlistNotifier.isFetching(),
|
||||||
selected: playlist.isFetching != true &&
|
selected: !playlistNotifier.isFetching() &&
|
||||||
sourceInfo.id == (activeTrack as SourcedTrack).sourceInfo.id,
|
sourceInfo.id == (activeTrack as SourcedTrack).sourceInfo.id,
|
||||||
selectedTileColor: theme.popupMenuTheme.color,
|
selectedTileColor: theme.popupMenuTheme.color,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (playlist.isFetching == false &&
|
if (!playlistNotifier.isFetching() &&
|
||||||
sourceInfo.id != (activeTrack as SourcedTrack).sourceInfo.id) {
|
sourceInfo.id != (activeTrack as SourcedTrack).sourceInfo.id) {
|
||||||
activeTrackNotifier.swapSibling(sourceInfo);
|
activeTrackNotifier.swapSibling(sourceInfo);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
@ -188,7 +189,7 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[playlist.isFetching, activeTrack, siblings],
|
[activeTrack, siblings],
|
||||||
);
|
);
|
||||||
|
|
||||||
final mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
@ -9,7 +9,7 @@ import 'package:spotube/models/connect/connect.dart';
|
|||||||
import 'package:spotube/pages/playlist/playlist.dart';
|
import 'package:spotube/pages/playlist/playlist.dart';
|
||||||
import 'package:spotube/provider/connect/connect.dart';
|
import 'package:spotube/provider/connect/connect.dart';
|
||||||
import 'package:spotube/provider/history/history.dart';
|
import 'package:spotube/provider/history/history.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
@ -22,8 +22,8 @@ class PlaylistCard extends HookConsumerWidget {
|
|||||||
});
|
});
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final playlistQueue = ref.watch(proxyPlaylistProvider);
|
final playlistQueue = ref.watch(audioPlayerProvider);
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
final historyNotifier = ref.read(playbackHistoryProvider.notifier);
|
final historyNotifier = ref.read(playbackHistoryProvider.notifier);
|
||||||
|
|
||||||
final playing =
|
final playing =
|
||||||
@ -65,8 +65,8 @@ class PlaylistCard extends HookConsumerWidget {
|
|||||||
placeholder: ImagePlaceholder.collection,
|
placeholder: ImagePlaceholder.collection,
|
||||||
),
|
),
|
||||||
isPlaying: isPlaylistPlaying,
|
isPlaying: isPlaylistPlaying,
|
||||||
isLoading:
|
isLoading: (isPlaylistPlaying && playlistNotifier.isFetching()) ||
|
||||||
(isPlaylistPlaying && playlistQueue.isFetching) || updating.value,
|
updating.value,
|
||||||
isOwner: playlist.owner?.id == me.asData?.value.id &&
|
isOwner: playlist.owner?.id == me.asData?.value.id &&
|
||||||
me.asData?.value.id != null,
|
me.asData?.value.id != null,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -19,7 +19,7 @@ import 'package:spotube/hooks/utils/use_brightness_value.dart';
|
|||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
|
|
||||||
import 'package:spotube/provider/volume_provider.dart';
|
import 'package:spotube/provider/volume_provider.dart';
|
||||||
@ -33,7 +33,7 @@ class BottomPlayer extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final auth = ref.watch(authenticationProvider);
|
final auth = ref.watch(authenticationProvider);
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final layoutMode =
|
final layoutMode =
|
||||||
ref.watch(userPreferencesProvider.select((s) => s.layoutMode));
|
ref.watch(userPreferencesProvider.select((s) => s.layoutMode));
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ class BottomPlayer extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
PlayerActions(
|
PlayerActions(
|
||||||
extraActions: [
|
extraActions: [
|
||||||
if (auth != null)
|
if (auth.asData?.value != null)
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: context.l10n.mini_player,
|
tooltip: context.l10n.mini_player,
|
||||||
icon: const Icon(SpotubeIcons.miniPlayer),
|
icon: const Icon(SpotubeIcons.miniPlayer),
|
||||||
|
@ -135,7 +135,7 @@ class ArtistPageHeader extends HookConsumerWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (auth != null)
|
if (auth.asData?.value != null)
|
||||||
Consumer(
|
Consumer(
|
||||||
builder: (context, ref, _) {
|
builder: (context, ref, _) {
|
||||||
final isFollowingQuery = ref
|
final isFollowingQuery = ref
|
||||||
|
@ -9,7 +9,7 @@ import 'package:spotube/components/track_tile/track_tile.dart';
|
|||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/models/connect/connect.dart';
|
import 'package:spotube/models/connect/connect.dart';
|
||||||
import 'package:spotube/provider/connect/connect.dart';
|
import 'package:spotube/provider/connect/connect.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
|
|
||||||
class ArtistPageTopTracks extends HookConsumerWidget {
|
class ArtistPageTopTracks extends HookConsumerWidget {
|
||||||
@ -21,8 +21,8 @@ class ArtistPageTopTracks extends HookConsumerWidget {
|
|||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
|
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
final topTracksQuery = ref.watch(artistTopTracksProvider(artistId));
|
final topTracksQuery = ref.watch(artistTopTracksProvider(artistId));
|
||||||
|
|
||||||
final isPlaylistPlaying = playlist.containsTracks(
|
final isPlaylistPlaying = playlist.containsTracks(
|
||||||
|
@ -17,7 +17,7 @@ import 'package:spotube/extensions/artist_simple.dart';
|
|||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/models/local_track.dart';
|
import 'package:spotube/models/local_track.dart';
|
||||||
import 'package:spotube/provider/local_tracks/local_tracks_provider.dart';
|
import 'package:spotube/provider/local_tracks/local_tracks_provider.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
|
|
||||||
class LocalLibraryPage extends HookConsumerWidget {
|
class LocalLibraryPage extends HookConsumerWidget {
|
||||||
@ -32,8 +32,8 @@ class LocalLibraryPage extends HookConsumerWidget {
|
|||||||
List<LocalTrack> tracks, {
|
List<LocalTrack> tracks, {
|
||||||
LocalTrack? currentTrack,
|
LocalTrack? currentTrack,
|
||||||
}) async {
|
}) async {
|
||||||
final playlist = ref.read(proxyPlaylistProvider);
|
final playlist = ref.read(audioPlayerProvider);
|
||||||
final playback = ref.read(proxyPlaylistProvider.notifier);
|
final playback = ref.read(audioPlayerProvider.notifier);
|
||||||
currentTrack ??= tracks.first;
|
currentTrack ??= tracks.first;
|
||||||
final isPlaylistPlaying = playlist.containsTracks(tracks);
|
final isPlaylistPlaying = playlist.containsTracks(tracks);
|
||||||
if (!isPlaylistPlaying) {
|
if (!isPlaylistPlaying) {
|
||||||
@ -52,7 +52,7 @@ class LocalLibraryPage extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final sortBy = useState<SortBy>(SortBy.none);
|
final sortBy = useState<SortBy>(SortBy.none);
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final trackSnapshot = ref.watch(localTracksProvider);
|
final trackSnapshot = ref.watch(localTracksProvider);
|
||||||
final isPlaylistPlaying = playlist.containsTracks(
|
final isPlaylistPlaying = playlist.containsTracks(
|
||||||
trackSnapshot.asData?.value.values.flattened.toList() ?? []);
|
trackSnapshot.asData?.value.values.flattened.toList() ?? []);
|
||||||
|
@ -11,7 +11,7 @@ import 'package:spotube/components/titlebar/titlebar.dart';
|
|||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/models/spotify/recommendation_seeds.dart';
|
import 'package:spotube/models/spotify/recommendation_seeds.dart';
|
||||||
import 'package:spotube/pages/playlist/playlist.dart';
|
import 'package:spotube/pages/playlist/playlist.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
|
|
||||||
class PlaylistGenerateResultPage extends HookConsumerWidget {
|
class PlaylistGenerateResultPage extends HookConsumerWidget {
|
||||||
@ -28,7 +28,7 @@ class PlaylistGenerateResultPage extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final router = GoRouter.of(context);
|
final router = GoRouter.of(context);
|
||||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
|
|
||||||
final generatedPlaylist = ref.watch(generatePlaylistProvider(state));
|
final generatedPlaylist = ref.watch(generatePlaylistProvider(state));
|
||||||
|
|
||||||
@ -81,9 +81,12 @@ class PlaylistGenerateResultPage extends HookConsumerWidget {
|
|||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
await playlistNotifier.load(
|
await playlistNotifier.load(
|
||||||
generatedPlaylist.asData!.value.where(
|
generatedPlaylist.asData!.value
|
||||||
(e) => selectedTracks.value.contains(e.id!),
|
.where(
|
||||||
),
|
(e) => selectedTracks.value
|
||||||
|
.contains(e.id!),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
autoPlay: true,
|
autoPlay: true,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -18,7 +18,7 @@ import 'package:spotube/hooks/utils/use_palette_color.dart';
|
|||||||
import 'package:spotube/pages/lyrics/plain_lyrics.dart';
|
import 'package:spotube/pages/lyrics/plain_lyrics.dart';
|
||||||
import 'package:spotube/pages/lyrics/synced_lyrics.dart';
|
import 'package:spotube/pages/lyrics/synced_lyrics.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ class LyricsPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
String albumArt = useMemoized(
|
String albumArt = useMemoized(
|
||||||
() => (playlist.activeTrack?.album?.images).asUrlString(
|
() => (playlist.activeTrack?.album?.images).asUrlString(
|
||||||
index: (playlist.activeTrack?.album?.images?.length ?? 1) - 1,
|
index: (playlist.activeTrack?.album?.images?.length ?? 1) - 1,
|
||||||
@ -62,7 +62,7 @@ class LyricsPage extends HookConsumerWidget {
|
|||||||
const Spacer(),
|
const Spacer(),
|
||||||
Consumer(
|
Consumer(
|
||||||
builder: (context, ref, child) {
|
builder: (context, ref, child) {
|
||||||
final playback = ref.watch(proxyPlaylistProvider);
|
final playback = ref.watch(audioPlayerProvider);
|
||||||
final lyric =
|
final lyric =
|
||||||
ref.watch(syncedLyricsProvider(playback.activeTrack));
|
ref.watch(syncedLyricsProvider(playback.activeTrack));
|
||||||
final providerName = lyric.asData?.value.provider;
|
final providerName = lyric.asData?.value.provider;
|
||||||
|
@ -15,7 +15,7 @@ import 'package:spotube/hooks/utils/use_force_update.dart';
|
|||||||
import 'package:spotube/pages/lyrics/plain_lyrics.dart';
|
import 'package:spotube/pages/lyrics/plain_lyrics.dart';
|
||||||
import 'package:spotube/pages/lyrics/synced_lyrics.dart';
|
import 'package:spotube/pages/lyrics/synced_lyrics.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ class MiniLyricsPage extends HookConsumerWidget {
|
|||||||
final update = useForceUpdate();
|
final update = useForceUpdate();
|
||||||
final wasMaximized = useRef<bool>(false);
|
final wasMaximized = useRef<bool>(false);
|
||||||
|
|
||||||
final playlistQueue = ref.watch(proxyPlaylistProvider);
|
final playlistQueue = ref.watch(audioPlayerProvider);
|
||||||
|
|
||||||
final areaActive = useState(false);
|
final areaActive = useState(false);
|
||||||
final hoverMode = useState(true);
|
final hoverMode = useState(true);
|
||||||
@ -230,14 +230,13 @@ class MiniLyricsPage extends HookConsumerWidget {
|
|||||||
builder: (context) {
|
builder: (context) {
|
||||||
return Consumer(builder: (context, ref, _) {
|
return Consumer(builder: (context, ref, _) {
|
||||||
final playlist =
|
final playlist =
|
||||||
ref.watch(proxyPlaylistProvider);
|
ref.watch(audioPlayerProvider);
|
||||||
|
|
||||||
return PlayerQueue
|
return PlayerQueue.fromAudioPlayerNotifier(
|
||||||
.fromProxyPlaylistNotifier(
|
|
||||||
floating: true,
|
floating: true,
|
||||||
playlist: playlist,
|
playlist: playlist,
|
||||||
notifier: ref
|
notifier: ref
|
||||||
.read(proxyPlaylistProvider.notifier),
|
.read(audioPlayerProvider.notifier),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,7 @@ import 'package:spotube/extensions/artist_simple.dart';
|
|||||||
import 'package:spotube/extensions/constrains.dart';
|
import 'package:spotube/extensions/constrains.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
|
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
|
|
||||||
class PlainLyrics extends HookConsumerWidget {
|
class PlainLyrics extends HookConsumerWidget {
|
||||||
@ -27,7 +27,7 @@ class PlainLyrics extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final lyricsQuery = ref.watch(syncedLyricsProvider(playlist.activeTrack));
|
final lyricsQuery = ref.watch(syncedLyricsProvider(playlist.activeTrack));
|
||||||
final mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
final textTheme = Theme.of(context).textTheme;
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
@ -12,7 +12,7 @@ import 'package:spotube/extensions/context.dart';
|
|||||||
import 'package:spotube/hooks/controllers/use_auto_scroll_controller.dart';
|
import 'package:spotube/hooks/controllers/use_auto_scroll_controller.dart';
|
||||||
import 'package:spotube/modules/lyrics/use_synced_lyrics.dart';
|
import 'package:spotube/modules/lyrics/use_synced_lyrics.dart';
|
||||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ class SyncedLyrics extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
|
|
||||||
final mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
final controller = useAutoScrollController();
|
final controller = useAutoScrollController();
|
||||||
@ -54,7 +54,7 @@ class SyncedLyrics extends HookConsumerWidget {
|
|||||||
final textTheme = Theme.of(context).textTheme;
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
ref.listen(
|
ref.listen(
|
||||||
proxyPlaylistProvider.select((s) => s.activeTrack),
|
audioPlayerProvider.select((s) => s.activeTrack),
|
||||||
(previous, next) {
|
(previous, next) {
|
||||||
controller.scrollToIndex(0);
|
controller.scrollToIndex(0);
|
||||||
ref.read(syncedLyricsDelayProvider.notifier).state = 0;
|
ref.read(syncedLyricsDelayProvider.notifier).state = 0;
|
||||||
|
@ -16,7 +16,7 @@ import 'package:spotube/extensions/context.dart';
|
|||||||
import 'package:spotube/hooks/configurators/use_endless_playback.dart';
|
import 'package:spotube/hooks/configurators/use_endless_playback.dart';
|
||||||
import 'package:spotube/pages/home/home.dart';
|
import 'package:spotube/pages/home/home.dart';
|
||||||
import 'package:spotube/provider/download_manager_provider.dart';
|
import 'package:spotube/provider/download_manager_provider.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/server/routes/connect.dart';
|
import 'package:spotube/provider/server/routes/connect.dart';
|
||||||
import 'package:spotube/services/connectivity_adapter.dart';
|
import 'package:spotube/services/connectivity_adapter.dart';
|
||||||
import 'package:spotube/utils/persisted_state_notifier.dart';
|
import 'package:spotube/utils/persisted_state_notifier.dart';
|
||||||
@ -201,11 +201,11 @@ class RootApp extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
child: Consumer(
|
child: Consumer(
|
||||||
builder: (context, ref, _) {
|
builder: (context, ref, _) {
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final playlistNotifier =
|
final playlistNotifier =
|
||||||
ref.read(proxyPlaylistProvider.notifier);
|
ref.read(audioPlayerProvider.notifier);
|
||||||
|
|
||||||
return PlayerQueue.fromProxyPlaylistNotifier(
|
return PlayerQueue.fromAudioPlayerNotifier(
|
||||||
floating: true,
|
floating: true,
|
||||||
playlist: playlist,
|
playlist: playlist,
|
||||||
notifier: playlistNotifier,
|
notifier: playlistNotifier,
|
||||||
|
@ -8,7 +8,7 @@ import 'package:spotube/components/track_tile/track_tile.dart';
|
|||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/models/connect/connect.dart';
|
import 'package:spotube/models/connect/connect.dart';
|
||||||
import 'package:spotube/provider/connect/connect.dart';
|
import 'package:spotube/provider/connect/connect.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
|
|
||||||
class SearchTracksSection extends HookConsumerWidget {
|
class SearchTracksSection extends HookConsumerWidget {
|
||||||
@ -24,8 +24,8 @@ class SearchTracksSection extends HookConsumerWidget {
|
|||||||
ref.watch(searchProvider(SearchType.track).notifier);
|
ref.watch(searchProvider(SearchType.track).notifier);
|
||||||
|
|
||||||
final tracks = searchTrack.asData?.value.items.cast<Track>() ?? [];
|
final tracks = searchTrack.asData?.value.items.cast<Track>() ?? [];
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
|
@ -14,7 +14,7 @@ import 'package:spotube/components/titlebar/titlebar.dart';
|
|||||||
import 'package:spotube/components/track_tile/track_options.dart';
|
import 'package:spotube/components/track_tile/track_options.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
@ -34,8 +34,8 @@ class TrackPage extends HookConsumerWidget {
|
|||||||
final ThemeData(:textTheme, :colorScheme) = Theme.of(context);
|
final ThemeData(:textTheme, :colorScheme) = Theme.of(context);
|
||||||
final mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
|
||||||
final playlist = ref.watch(proxyPlaylistProvider);
|
final playlist = ref.watch(audioPlayerProvider);
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
|
|
||||||
final isActive = playlist.activeTrack?.id == trackId;
|
final isActive = playlist.activeTrack?.id == trackId;
|
||||||
|
|
||||||
|
@ -1,13 +1,22 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:media_kit/media_kit.dart' hide Track;
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
import 'package:spotify/spotify.dart' hide Playlist;
|
import 'package:spotify/spotify.dart' hide Playlist;
|
||||||
|
import 'package:spotube/extensions/track.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
|
import 'package:spotube/models/local_track.dart';
|
||||||
import 'package:spotube/provider/audio_player/state.dart';
|
import 'package:spotube/provider/audio_player/state.dart';
|
||||||
|
import 'package:spotube/provider/blacklist_provider.dart';
|
||||||
import 'package:spotube/provider/database/database.dart';
|
import 'package:spotube/provider/database/database.dart';
|
||||||
|
import 'package:spotube/provider/discord_provider.dart';
|
||||||
|
import 'package:spotube/provider/server/sourced_track.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
||||||
|
BlackListNotifier get _blacklist => ref.read(blacklistProvider.notifier);
|
||||||
|
|
||||||
Future<void> _syncSavedState() async {
|
Future<void> _syncSavedState() async {
|
||||||
final database = ref.read(databaseProvider);
|
final database = ref.read(databaseProvider);
|
||||||
|
|
||||||
@ -18,9 +27,9 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
|||||||
await database.into(database.audioPlayerStateTable).insert(
|
await database.into(database.audioPlayerStateTable).insert(
|
||||||
AudioPlayerStateTableCompanion.insert(
|
AudioPlayerStateTableCompanion.insert(
|
||||||
playing: audioPlayer.isPlaying,
|
playing: audioPlayer.isPlaying,
|
||||||
volume: audioPlayer.volume,
|
|
||||||
loopMode: audioPlayer.loopMode,
|
loopMode: audioPlayer.loopMode,
|
||||||
shuffled: audioPlayer.isShuffled,
|
shuffled: audioPlayer.isShuffled,
|
||||||
|
collections: <String>[],
|
||||||
id: const Value(0),
|
id: const Value(0),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -28,7 +37,6 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
|||||||
playerState =
|
playerState =
|
||||||
await database.select(database.audioPlayerStateTable).getSingle();
|
await database.select(database.audioPlayerStateTable).getSingle();
|
||||||
} else {
|
} else {
|
||||||
await audioPlayer.setVolume(playerState.volume);
|
|
||||||
await audioPlayer.setLoopMode(playerState.loopMode);
|
await audioPlayer.setLoopMode(playerState.loopMode);
|
||||||
await audioPlayer.setShuffle(playerState.shuffled);
|
await audioPlayer.setShuffle(playerState.shuffled);
|
||||||
}
|
}
|
||||||
@ -130,15 +138,6 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
audioPlayer.volumeStream.listen((volume) async {
|
|
||||||
state = state.copyWith(volume: volume);
|
|
||||||
|
|
||||||
await _updatePlayerState(
|
|
||||||
AudioPlayerStateTableCompanion(
|
|
||||||
volume: Value(volume),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
audioPlayer.loopModeStream.listen((loopMode) async {
|
audioPlayer.loopModeStream.listen((loopMode) async {
|
||||||
state = state.copyWith(loopMode: loopMode);
|
state = state.copyWith(loopMode: loopMode);
|
||||||
|
|
||||||
@ -177,49 +176,141 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
|||||||
playing: audioPlayer.isPlaying,
|
playing: audioPlayer.isPlaying,
|
||||||
playlist: audioPlayer.playlist,
|
playlist: audioPlayer.playlist,
|
||||||
shuffled: audioPlayer.isShuffled,
|
shuffled: audioPlayer.isShuffled,
|
||||||
volume: audioPlayer.volume,
|
collections: [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collection related methods
|
||||||
|
Future<void> addCollections(List<String> collectionIds) async {
|
||||||
|
state = state.copyWith(collections: [
|
||||||
|
...state.collections,
|
||||||
|
...collectionIds,
|
||||||
|
]);
|
||||||
|
|
||||||
|
await _updatePlayerState(
|
||||||
|
AudioPlayerStateTableCompanion(
|
||||||
|
collections: Value(state.collections),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addCollection(String collectionId) async {
|
||||||
|
await addCollections([collectionId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removeCollections(List<String> collectionIds) async {
|
||||||
|
state = state.copyWith(
|
||||||
|
collections: state.collections
|
||||||
|
.where((element) => !collectionIds.contains(element))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await _updatePlayerState(
|
||||||
|
AudioPlayerStateTableCompanion(
|
||||||
|
collections: Value(state.collections),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removeCollection(String collectionId) async {
|
||||||
|
await removeCollections([collectionId]);
|
||||||
|
}
|
||||||
|
|
||||||
// Tracks related methods
|
// Tracks related methods
|
||||||
|
|
||||||
|
Future<void> addTracksAtFirst(Iterable<Track> tracks) async {
|
||||||
|
if (state.tracks.length == 1) {
|
||||||
|
return addTracks(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracks = _blacklist.filter(tracks).toList() as List<Track>;
|
||||||
|
|
||||||
|
for (int i = 0; i < tracks.length; i++) {
|
||||||
|
final track = tracks.elementAt(i);
|
||||||
|
|
||||||
|
await audioPlayer.addTrackAt(
|
||||||
|
SpotubeMedia(track),
|
||||||
|
max(state.playlist.index, 0) + i + 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> addTrack(Track track) async {
|
Future<void> addTrack(Track track) async {
|
||||||
|
if (_blacklist.contains(track)) return;
|
||||||
await audioPlayer.addTrack(SpotubeMedia(track));
|
await audioPlayer.addTrack(SpotubeMedia(track));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addTracks(Iterable<Track> tracks) async {
|
Future<void> addTracks(Iterable<Track> tracks) async {
|
||||||
|
tracks = _blacklist.filter(tracks).toList() as List<Track>;
|
||||||
for (final track in tracks) {
|
for (final track in tracks) {
|
||||||
await addTrack(track);
|
await audioPlayer.addTrack(SpotubeMedia(track));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeTrack(Track track) async {
|
Future<void> removeTrack(String trackId) async {
|
||||||
final index = state.tracks.indexWhere((element) => element == track);
|
final index = state.tracks.indexWhere((element) => element.id == trackId);
|
||||||
|
|
||||||
if (index == -1) return;
|
if (index == -1) return;
|
||||||
|
|
||||||
await audioPlayer.removeTrack(index);
|
await audioPlayer.removeTrack(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeTracks(Iterable<Track> tracks) async {
|
Future<void> removeTracks(Iterable<String> trackIds) async {
|
||||||
for (final track in tracks) {
|
for (final trackId in trackIds) {
|
||||||
await removeTrack(track);
|
await removeTrack(trackId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> load(
|
Future<void> load(
|
||||||
List<Track> track, {
|
List<Track> tracks, {
|
||||||
required int initialIndex,
|
int initialIndex = 0,
|
||||||
bool autoPlay = false,
|
bool autoPlay = false,
|
||||||
}) async {
|
}) async {
|
||||||
|
tracks = _blacklist.filter(tracks).toList() as List<Track>;
|
||||||
|
|
||||||
|
// Giving the initial track a boost so MediaKit won't skip
|
||||||
|
// because of timeout
|
||||||
|
final intendedActiveTrack = tracks.elementAt(initialIndex);
|
||||||
|
if (intendedActiveTrack is! LocalTrack) {
|
||||||
|
await ref.read(sourcedTrackProvider(intendedActiveTrack).future);
|
||||||
|
}
|
||||||
|
|
||||||
await audioPlayer.openPlaylist(
|
await audioPlayer.openPlaylist(
|
||||||
track.map((t) => SpotubeMedia(t)).toList(),
|
tracks.asMediaList(),
|
||||||
initialIndex: initialIndex,
|
initialIndex: initialIndex,
|
||||||
autoPlay: autoPlay,
|
autoPlay: autoPlay,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> jumpToTrack(Track track) async {
|
||||||
|
final index =
|
||||||
|
state.tracks.toList().indexWhere((element) => element.id == track.id);
|
||||||
|
if (index == -1) return;
|
||||||
|
await audioPlayer.jumpTo(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> moveTrack(int oldIndex, int newIndex) async {
|
||||||
|
if (oldIndex == newIndex ||
|
||||||
|
newIndex < 0 ||
|
||||||
|
oldIndex < 0 ||
|
||||||
|
newIndex > state.tracks.length - 1 ||
|
||||||
|
oldIndex > state.tracks.length - 1) return;
|
||||||
|
|
||||||
|
await audioPlayer.moveTrack(oldIndex, newIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFetching() {
|
||||||
|
if (state.activeTrack == null) return false;
|
||||||
|
return ref.read(sourcedTrackProvider(state.activeTrack!)).isLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> stop() async {
|
||||||
|
await audioPlayer.stop();
|
||||||
|
ref.read(discordProvider.notifier).clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final audioPlayerProvider = NotifierProvider<AudioPlayerNotifier, AudioPlayerState>(
|
final audioPlayerProvider =
|
||||||
|
NotifierProvider<AudioPlayerNotifier, AudioPlayerState>(
|
||||||
() => AudioPlayerNotifier(),
|
() => AudioPlayerNotifier(),
|
||||||
);
|
);
|
@ -1,19 +1,53 @@
|
|||||||
// ignore_for_file: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:spotube/services/logger/logger.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:palette_generator/palette_generator.dart';
|
import 'package:palette_generator/palette_generator.dart';
|
||||||
import 'package:spotube/components/image/universal_image.dart';
|
import 'package:spotube/components/image/universal_image.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
import 'package:spotube/models/local_track.dart';
|
import 'package:spotube/models/local_track.dart';
|
||||||
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
|
import 'package:spotube/provider/audio_player/state.dart';
|
||||||
|
import 'package:spotube/provider/discord_provider.dart';
|
||||||
|
import 'package:spotube/provider/history/history.dart';
|
||||||
import 'package:spotube/provider/palette_provider.dart';
|
import 'package:spotube/provider/palette_provider.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/skip_segments/skip_segments.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/skip_segments.dart';
|
import 'package:spotube/provider/scrobbler/scrobbler.dart';
|
||||||
import 'package:spotube/provider/server/sourced_track.dart';
|
import 'package:spotube/provider/server/sourced_track.dart';
|
||||||
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
|
import 'package:spotube/services/audio_services/audio_services.dart';
|
||||||
|
import 'package:spotube/services/logger/logger.dart';
|
||||||
|
|
||||||
|
class AudioPlayerStreamListeners {
|
||||||
|
final Ref ref;
|
||||||
|
late final AudioServices notificationService;
|
||||||
|
AudioPlayerStreamListeners(this.ref) {
|
||||||
|
AudioServices.create(ref, ref.read(audioPlayerProvider.notifier)).then(
|
||||||
|
(value) => notificationService = value,
|
||||||
|
);
|
||||||
|
|
||||||
|
final subscriptions = [
|
||||||
|
subscribeToPlaylist(),
|
||||||
|
subscribeToSkipSponsor(),
|
||||||
|
subscribeToScrobbleChanged(),
|
||||||
|
subscribeToPosition(),
|
||||||
|
subscribeToPlayerError(),
|
||||||
|
];
|
||||||
|
|
||||||
|
ref.onDispose(() {
|
||||||
|
for (final subscription in subscriptions) {
|
||||||
|
subscription.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrobblerNotifier get scrobbler => ref.read(scrobblerProvider.notifier);
|
||||||
|
UserPreferences get preferences => ref.read(userPreferencesProvider);
|
||||||
|
Discord get discord => ref.read(discordProvider);
|
||||||
|
AudioPlayerState get audioPlayerState => ref.read(audioPlayerProvider);
|
||||||
|
PlaybackHistoryNotifier get history =>
|
||||||
|
ref.read(playbackHistoryProvider.notifier);
|
||||||
|
|
||||||
extension ProxyPlaylistListeners on ProxyPlaylistNotifier {
|
|
||||||
Future<void> updatePalette() async {
|
Future<void> updatePalette() async {
|
||||||
final palette = ref.read(paletteProvider);
|
final palette = ref.read(paletteProvider);
|
||||||
if (!preferences.albumColorSync) {
|
if (!preferences.albumColorSync) {
|
||||||
@ -21,11 +55,12 @@ extension ProxyPlaylistListeners on ProxyPlaylistNotifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return Future.microtask(() async {
|
return Future.microtask(() async {
|
||||||
if (playlist.activeTrack == null) return;
|
final activeTrack = ref.read(audioPlayerProvider).activeTrack;
|
||||||
|
if (activeTrack == null) return;
|
||||||
|
|
||||||
final palette = await PaletteGenerator.fromImageProvider(
|
final palette = await PaletteGenerator.fromImageProvider(
|
||||||
UniversalImage.imageProvider(
|
UniversalImage.imageProvider(
|
||||||
(playlist.activeTrack?.album?.images).asUrlString(
|
(activeTrack.album?.images).asUrlString(
|
||||||
placeholder: ImagePlaceholder.albumArt,
|
placeholder: ImagePlaceholder.albumArt,
|
||||||
),
|
),
|
||||||
height: 50,
|
height: 50,
|
||||||
@ -38,15 +73,8 @@ extension ProxyPlaylistListeners on ProxyPlaylistNotifier {
|
|||||||
|
|
||||||
StreamSubscription subscribeToPlaylist() {
|
StreamSubscription subscribeToPlaylist() {
|
||||||
return audioPlayer.playlistStream.listen((mpvPlaylist) {
|
return audioPlayer.playlistStream.listen((mpvPlaylist) {
|
||||||
state = playlist.copyWith(
|
notificationService.addTrack(audioPlayerState.activeTrack!);
|
||||||
tracks: mpvPlaylist.medias
|
discord.updatePresence(audioPlayerState.activeTrack!);
|
||||||
.map((media) => SpotubeMedia.fromMedia(media).track)
|
|
||||||
.toSet(),
|
|
||||||
active: mpvPlaylist.index,
|
|
||||||
);
|
|
||||||
|
|
||||||
notificationService.addTrack(playlist.activeTrack!);
|
|
||||||
discord.updatePresence(playlist.activeTrack!);
|
|
||||||
updatePalette();
|
updatePalette();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -72,18 +100,18 @@ extension ProxyPlaylistListeners on ProxyPlaylistNotifier {
|
|||||||
String? lastScrobbled;
|
String? lastScrobbled;
|
||||||
return audioPlayer.positionStream.listen((position) {
|
return audioPlayer.positionStream.listen((position) {
|
||||||
try {
|
try {
|
||||||
final uid = playlist.activeTrack is LocalTrack
|
final uid = audioPlayerState.activeTrack is LocalTrack
|
||||||
? (playlist.activeTrack as LocalTrack).path
|
? (audioPlayerState.activeTrack as LocalTrack).path
|
||||||
: playlist.activeTrack?.id;
|
: audioPlayerState.activeTrack?.id;
|
||||||
|
|
||||||
if (playlist.activeTrack == null ||
|
if (audioPlayerState.activeTrack == null ||
|
||||||
lastScrobbled == uid ||
|
lastScrobbled == uid ||
|
||||||
position.inSeconds < 30) {
|
position.inSeconds < 30) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scrobbler.scrobble(playlist.activeTrack!);
|
scrobbler.scrobble(audioPlayerState.activeTrack!);
|
||||||
history.addTrack(playlist.activeTrack!);
|
history.addTrack(audioPlayerState.activeTrack!);
|
||||||
lastScrobbled = uid;
|
lastScrobbled = uid;
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
AppLogger.reportError(e, stack);
|
AppLogger.reportError(e, stack);
|
||||||
@ -95,9 +123,13 @@ extension ProxyPlaylistListeners on ProxyPlaylistNotifier {
|
|||||||
String lastTrack = ""; // used to prevent multiple calls to the same track
|
String lastTrack = ""; // used to prevent multiple calls to the same track
|
||||||
return audioPlayer.positionStream.listen((event) async {
|
return audioPlayer.positionStream.listen((event) async {
|
||||||
if (event < const Duration(seconds: 3) ||
|
if (event < const Duration(seconds: 3) ||
|
||||||
playlist.active == null ||
|
audioPlayerState.playlist.index == -1 ||
|
||||||
playlist.active == playlist.tracks.length - 1) return;
|
audioPlayerState.playlist.index ==
|
||||||
final nextTrack = playlist.tracks.elementAt(playlist.active! + 1);
|
audioPlayerState.tracks.length - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final nextTrack = audioPlayerState.tracks
|
||||||
|
.elementAt(audioPlayerState.playlist.index + 1);
|
||||||
|
|
||||||
if (lastTrack == nextTrack.id || nextTrack is LocalTrack) return;
|
if (lastTrack == nextTrack.id || nextTrack is LocalTrack) return;
|
||||||
|
|
||||||
@ -113,3 +145,6 @@ extension ProxyPlaylistListeners on ProxyPlaylistNotifier {
|
|||||||
return audioPlayer.errorStream.listen((event) {});
|
return audioPlayer.errorStream.listen((event) {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final audioPlayerStreamListenersProvider =
|
||||||
|
Provider<AudioPlayerStreamListeners>(AudioPlayerStreamListeners.new);
|
@ -4,39 +4,97 @@ import 'package:spotube/services/audio_player/audio_player.dart';
|
|||||||
|
|
||||||
class AudioPlayerState {
|
class AudioPlayerState {
|
||||||
final bool playing;
|
final bool playing;
|
||||||
final double volume;
|
|
||||||
final PlaylistMode loopMode;
|
final PlaylistMode loopMode;
|
||||||
final bool shuffled;
|
final bool shuffled;
|
||||||
final Playlist playlist;
|
final Playlist playlist;
|
||||||
|
|
||||||
final List<Track> tracks;
|
final List<Track> tracks;
|
||||||
|
final List<String> collections;
|
||||||
|
|
||||||
AudioPlayerState({
|
AudioPlayerState({
|
||||||
required this.playing,
|
required this.playing,
|
||||||
required this.volume,
|
|
||||||
required this.loopMode,
|
required this.loopMode,
|
||||||
required this.shuffled,
|
required this.shuffled,
|
||||||
required this.playlist,
|
required this.playlist,
|
||||||
|
required this.collections,
|
||||||
List<Track>? tracks,
|
List<Track>? tracks,
|
||||||
}) : tracks = tracks ??
|
}) : tracks = tracks ??
|
||||||
playlist.medias
|
playlist.medias
|
||||||
.map((media) => SpotubeMedia.fromMedia(media).track)
|
.map((media) => SpotubeMedia.fromMedia(media).track)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
factory AudioPlayerState.fromJson(Map<String, dynamic> json) {
|
||||||
|
return AudioPlayerState(
|
||||||
|
playing: json['playing'],
|
||||||
|
loopMode: PlaylistMode.values.firstWhere(
|
||||||
|
(e) => e.name == json['loopMode'],
|
||||||
|
orElse: () => audioPlayer.loopMode,
|
||||||
|
),
|
||||||
|
shuffled: json['shuffled'],
|
||||||
|
playlist: Playlist(
|
||||||
|
json['playlist']['medias']
|
||||||
|
.map((media) => Media(
|
||||||
|
media['uri'],
|
||||||
|
extras: media['extras'],
|
||||||
|
httpHeaders: media['httpHeaders'],
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
index: json['playlist']['index'],
|
||||||
|
),
|
||||||
|
collections: List<String>.from(json['collections']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'playing': playing,
|
||||||
|
'loopMode': loopMode.name,
|
||||||
|
'shuffled': shuffled,
|
||||||
|
'playlist': {
|
||||||
|
'medias': playlist.medias
|
||||||
|
.map((media) => {
|
||||||
|
'uri': media.uri,
|
||||||
|
'extras': media.extras,
|
||||||
|
'httpHeaders': media.httpHeaders,
|
||||||
|
})
|
||||||
|
.toList(),
|
||||||
|
'index': playlist.index,
|
||||||
|
},
|
||||||
|
'collections': collections,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
AudioPlayerState copyWith({
|
AudioPlayerState copyWith({
|
||||||
bool? playing,
|
bool? playing,
|
||||||
double? volume,
|
|
||||||
PlaylistMode? loopMode,
|
PlaylistMode? loopMode,
|
||||||
bool? shuffled,
|
bool? shuffled,
|
||||||
Playlist? playlist,
|
Playlist? playlist,
|
||||||
|
List<String>? collections,
|
||||||
}) {
|
}) {
|
||||||
return AudioPlayerState(
|
return AudioPlayerState(
|
||||||
playing: playing ?? this.playing,
|
playing: playing ?? this.playing,
|
||||||
volume: volume ?? this.volume,
|
|
||||||
loopMode: loopMode ?? this.loopMode,
|
loopMode: loopMode ?? this.loopMode,
|
||||||
shuffled: shuffled ?? this.shuffled,
|
shuffled: shuffled ?? this.shuffled,
|
||||||
playlist: playlist ?? this.playlist,
|
playlist: playlist ?? this.playlist,
|
||||||
|
collections: collections ?? this.collections,
|
||||||
tracks: playlist == null ? tracks : null,
|
tracks: playlist == null ? tracks : null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Track? get activeTrack {
|
||||||
|
if (playlist.index == -1) return null;
|
||||||
|
return tracks.elementAtOrNull(playlist.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsTrack(Track track) {
|
||||||
|
return tracks.any((t) => t.id == track.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsTracks(List<Track> tracks) {
|
||||||
|
return tracks.every(containsTrack);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsCollection(String collectionId) {
|
||||||
|
return collections.contains(collectionId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:media_kit/media_kit.dart' hide Track;
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
|
import 'package:spotube/provider/audio_player/state.dart';
|
||||||
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/logger/logger.dart';
|
import 'package:spotube/services/logger/logger.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:spotify/spotify.dart' hide Playlist;
|
import 'package:spotify/spotify.dart' hide Playlist;
|
||||||
import 'package:spotube/models/connect/connect.dart';
|
import 'package:spotube/models/connect/connect.dart';
|
||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:spotube/provider/connect/clients.dart';
|
import 'package:spotube/provider/connect/clients.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart';
|
|
||||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||||
import 'package:web_socket_channel/status.dart' as status;
|
import 'package:web_socket_channel/status.dart' as status;
|
||||||
|
|
||||||
@ -31,8 +32,14 @@ final loopModeProvider = StateProvider<PlaylistMode>(
|
|||||||
(ref) => PlaylistMode.none,
|
(ref) => PlaylistMode.none,
|
||||||
);
|
);
|
||||||
|
|
||||||
final queueProvider = StateProvider<ProxyPlaylist>(
|
final queueProvider = StateProvider<AudioPlayerState>(
|
||||||
(ref) => ProxyPlaylist({}),
|
(ref) => AudioPlayerState(
|
||||||
|
playing: audioPlayer.isPlaying,
|
||||||
|
loopMode: audioPlayer.loopMode,
|
||||||
|
shuffled: audioPlayer.isShuffled,
|
||||||
|
playlist: audioPlayer.playlist,
|
||||||
|
collections: [],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final volumeProvider = StateProvider<double>(
|
final volumeProvider = StateProvider<double>(
|
||||||
|
@ -4,7 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/env.dart';
|
import 'package:spotube/collections/env.dart';
|
||||||
import 'package:spotube/extensions/artist_simple.dart';
|
import 'package:spotube/extensions/artist_simple.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ final discordProvider = ChangeNotifierProvider(
|
|||||||
(ref) {
|
(ref) {
|
||||||
final isEnabled =
|
final isEnabled =
|
||||||
ref.watch(userPreferencesProvider.select((s) => s.discordPresence));
|
ref.watch(userPreferencesProvider.select((s) => s.discordPresence));
|
||||||
final playback = ref.read(proxyPlaylistProvider);
|
final playback = ref.read(audioPlayerProvider);
|
||||||
final discord = Discord(isEnabled);
|
final discord = Discord(isEnabled);
|
||||||
|
|
||||||
if (playback.activeTrack != null) {
|
if (playback.activeTrack != null) {
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
|
||||||
import 'package:spotube/models/local_track.dart';
|
|
||||||
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
|
||||||
|
|
||||||
class ProxyPlaylist {
|
|
||||||
final Set<Track> tracks;
|
|
||||||
final Set<String> collections;
|
|
||||||
final int? active;
|
|
||||||
|
|
||||||
ProxyPlaylist(this.tracks, [this.active, this.collections = const {}]);
|
|
||||||
|
|
||||||
factory ProxyPlaylist.fromJson(
|
|
||||||
Map<String, dynamic> json,
|
|
||||||
) {
|
|
||||||
return ProxyPlaylist(
|
|
||||||
List.castFrom<dynamic, Map<String, dynamic>>(
|
|
||||||
json['tracks'] ?? <Map<String, dynamic>>[],
|
|
||||||
).map((t) => _makeAppropriateTrack(t)).toSet(),
|
|
||||||
json['active'] as int?,
|
|
||||||
json['collections'] == null
|
|
||||||
? {}
|
|
||||||
: (json['collections'] as List).toSet().cast<String>(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
factory ProxyPlaylist.fromJsonRaw(Map<String, dynamic> json) => ProxyPlaylist(
|
|
||||||
json['tracks'] == null
|
|
||||||
? <Track>{}
|
|
||||||
: (json['tracks'] as List).map((t) => Track.fromJson(t)).toSet(),
|
|
||||||
json['active'] as int?,
|
|
||||||
json['collections'] == null
|
|
||||||
? {}
|
|
||||||
: (json['collections'] as List).toSet().cast<String>(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Track? get activeTrack =>
|
|
||||||
active == null || active == -1 ? null : tracks.elementAtOrNull(active!);
|
|
||||||
|
|
||||||
bool get isFetching => activeTrack == null && tracks.isNotEmpty;
|
|
||||||
|
|
||||||
bool containsCollection(String collection) {
|
|
||||||
return collections.contains(collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool containsTrack(TrackSimple track) {
|
|
||||||
return tracks.firstWhereOrNull((element) {
|
|
||||||
if (element is LocalTrack && track is LocalTrack) {
|
|
||||||
return element.path == track.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return element.id == track.id;
|
|
||||||
}) !=
|
|
||||||
null;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool containsTracks(Iterable<TrackSimple> tracks) {
|
|
||||||
if (tracks.isEmpty) return false;
|
|
||||||
return tracks.every(containsTrack);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Track _makeAppropriateTrack(Map<String, dynamic> track) {
|
|
||||||
if (track.containsKey("path")) {
|
|
||||||
return LocalTrack.fromJson(track);
|
|
||||||
} else {
|
|
||||||
return Track.fromJson(track);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// To make sure proper instance method is used for JSON serialization
|
|
||||||
/// Otherwise default super.toJson() is used
|
|
||||||
static Map<String, dynamic> _makeAppropriateTrackJson(Track track) {
|
|
||||||
return switch (track) {
|
|
||||||
// ignore: unnecessary_cast
|
|
||||||
LocalTrack() => (track as LocalTrack).toJson(),
|
|
||||||
// ignore: unnecessary_cast
|
|
||||||
SourcedTrack() => (track as SourcedTrack).toJson(),
|
|
||||||
_ => track.toJson(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'tracks': tracks.map(_makeAppropriateTrackJson).toList(),
|
|
||||||
'active': active,
|
|
||||||
'collections': collections.toList(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyPlaylist copyWith({
|
|
||||||
Set<Track>? tracks,
|
|
||||||
int? active,
|
|
||||||
Set<String>? collections,
|
|
||||||
}) {
|
|
||||||
return ProxyPlaylist(
|
|
||||||
tracks ?? this.tracks,
|
|
||||||
active ?? this.active,
|
|
||||||
collections ?? this.collections,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,215 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
|
||||||
import 'package:spotube/extensions/track.dart';
|
|
||||||
import 'package:spotube/models/local_track.dart';
|
|
||||||
|
|
||||||
import 'package:spotube/provider/blacklist_provider.dart';
|
|
||||||
import 'package:spotube/provider/history/history.dart';
|
|
||||||
import 'package:spotube/provider/palette_provider.dart';
|
|
||||||
import 'package:spotube/provider/proxy_playlist/player_listeners.dart';
|
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart';
|
|
||||||
import 'package:spotube/provider/scrobbler/scrobbler.dart';
|
|
||||||
import 'package:spotube/provider/server/sourced_track.dart';
|
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
|
||||||
|
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
|
||||||
import 'package:spotube/services/audio_services/audio_services.dart';
|
|
||||||
import 'package:spotube/provider/discord_provider.dart';
|
|
||||||
|
|
||||||
import 'package:spotube/utils/persisted_state_notifier.dart';
|
|
||||||
|
|
||||||
class ProxyPlaylistNotifier extends PersistedStateNotifier<ProxyPlaylist> {
|
|
||||||
final Ref ref;
|
|
||||||
late final AudioServices notificationService;
|
|
||||||
|
|
||||||
ScrobblerNotifier get scrobbler => ref.read(scrobblerProvider.notifier);
|
|
||||||
UserPreferences get preferences => ref.read(userPreferencesProvider);
|
|
||||||
ProxyPlaylist get playlist => state;
|
|
||||||
BlackListNotifier get blacklist => ref.read(blacklistProvider.notifier);
|
|
||||||
Discord get discord => ref.read(discordProvider);
|
|
||||||
PlaybackHistoryNotifier get history =>
|
|
||||||
ref.read(playbackHistoryProvider.notifier);
|
|
||||||
|
|
||||||
List<StreamSubscription> _subscriptions = [];
|
|
||||||
|
|
||||||
ProxyPlaylistNotifier(this.ref) : super(ProxyPlaylist({}), "playlist") {
|
|
||||||
AudioServices.create(ref, this).then(
|
|
||||||
(value) => notificationService = value,
|
|
||||||
);
|
|
||||||
|
|
||||||
_subscriptions = [
|
|
||||||
// These are subscription methods from player_listeners.dart
|
|
||||||
subscribeToPlaylist(),
|
|
||||||
subscribeToSkipSponsor(),
|
|
||||||
subscribeToPosition(),
|
|
||||||
subscribeToScrobbleChanged(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
// Basic methods for adding or removing tracks to playlist
|
|
||||||
|
|
||||||
Future<void> addTrack(Track track) async {
|
|
||||||
if (blacklist.contains(track)) return;
|
|
||||||
await audioPlayer.addTrack(SpotubeMedia(track));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> addTracks(Iterable<Track> tracks) async {
|
|
||||||
tracks = blacklist.filter(tracks).toList() as List<Track>;
|
|
||||||
for (final track in tracks) {
|
|
||||||
await audioPlayer.addTrack(SpotubeMedia(track));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addCollection(String collectionId) {
|
|
||||||
state = state.copyWith(collections: {
|
|
||||||
...state.collections,
|
|
||||||
collectionId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeCollection(String collectionId) {
|
|
||||||
state = state.copyWith(collections: {
|
|
||||||
...state.collections..remove(collectionId),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> removeTrack(String trackId) async {
|
|
||||||
final trackIndex =
|
|
||||||
state.tracks.toList().indexWhere((element) => element.id == trackId);
|
|
||||||
if (trackIndex == -1) return;
|
|
||||||
await audioPlayer.removeTrack(trackIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> removeTracks(Iterable<String> tracksIds) async {
|
|
||||||
final tracks = state.tracks.map((t) => t.id!).toList();
|
|
||||||
|
|
||||||
for (final track in tracks) {
|
|
||||||
final index = tracks.indexOf(track);
|
|
||||||
if (index == -1) continue;
|
|
||||||
await audioPlayer.removeTrack(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> load(
|
|
||||||
Iterable<Track> tracks, {
|
|
||||||
int initialIndex = 0,
|
|
||||||
bool autoPlay = false,
|
|
||||||
}) async {
|
|
||||||
tracks = blacklist.filter(tracks).toList() as List<Track>;
|
|
||||||
|
|
||||||
state = state.copyWith(collections: {});
|
|
||||||
|
|
||||||
// Giving the initial track a boost so MediaKit won't skip
|
|
||||||
// because of timeout
|
|
||||||
final intendedActiveTrack = tracks.elementAt(initialIndex);
|
|
||||||
if (intendedActiveTrack is! LocalTrack) {
|
|
||||||
await ref.read(sourcedTrackProvider(intendedActiveTrack).future);
|
|
||||||
}
|
|
||||||
|
|
||||||
await audioPlayer.openPlaylist(
|
|
||||||
tracks.asMediaList(),
|
|
||||||
initialIndex: initialIndex,
|
|
||||||
autoPlay: autoPlay,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> jumpTo(int index) async {
|
|
||||||
await audioPlayer.jumpTo(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> jumpToTrack(Track track) async {
|
|
||||||
final index =
|
|
||||||
state.tracks.toList().indexWhere((element) => element.id == track.id);
|
|
||||||
if (index == -1) return;
|
|
||||||
await jumpTo(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> moveTrack(int oldIndex, int newIndex) async {
|
|
||||||
if (oldIndex == newIndex ||
|
|
||||||
newIndex < 0 ||
|
|
||||||
oldIndex < 0 ||
|
|
||||||
newIndex > state.tracks.length - 1 ||
|
|
||||||
oldIndex > state.tracks.length - 1) return;
|
|
||||||
|
|
||||||
await audioPlayer.moveTrack(oldIndex, newIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> addTracksAtFirst(Iterable<Track> tracks) async {
|
|
||||||
if (state.tracks.length == 1) {
|
|
||||||
return addTracks(tracks);
|
|
||||||
}
|
|
||||||
|
|
||||||
tracks = blacklist.filter(tracks).toList() as List<Track>;
|
|
||||||
|
|
||||||
for (int i = 0; i < tracks.length; i++) {
|
|
||||||
final track = tracks.elementAt(i);
|
|
||||||
|
|
||||||
await audioPlayer.addTrackAt(
|
|
||||||
SpotubeMedia(track),
|
|
||||||
(state.active ?? 0) + i + 1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> next() async {
|
|
||||||
await audioPlayer.skipToNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> previous() async {
|
|
||||||
await audioPlayer.skipToPrevious();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> stop() async {
|
|
||||||
state = ProxyPlaylist({});
|
|
||||||
await audioPlayer.stop();
|
|
||||||
discord.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
set state(state) {
|
|
||||||
super.state = state;
|
|
||||||
if (state.tracks.isEmpty && ref.read(paletteProvider) != null) {
|
|
||||||
ref.read(paletteProvider.notifier).state = null;
|
|
||||||
} else {
|
|
||||||
updatePalette();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
onInit() async {
|
|
||||||
if (state.tracks.isEmpty) return null;
|
|
||||||
final oldCollections = state.collections;
|
|
||||||
await load(
|
|
||||||
state.tracks,
|
|
||||||
initialIndex: max(state.active ?? 0, 0),
|
|
||||||
autoPlay: false,
|
|
||||||
);
|
|
||||||
state = state.copyWith(collections: oldCollections);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
FutureOr<ProxyPlaylist> fromJson(Map<String, dynamic> json) {
|
|
||||||
return ProxyPlaylist.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final json = state.toJson();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
for (final subscription in _subscriptions) {
|
|
||||||
subscription.cancel();
|
|
||||||
}
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final proxyPlaylistProvider =
|
|
||||||
StateNotifierProvider<ProxyPlaylistNotifier, ProxyPlaylist>(
|
|
||||||
(ref) => ProxyPlaylistNotifier(ref),
|
|
||||||
);
|
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/sourced_track/models/source_info.dart';
|
import 'package:spotube/services/sourced_track/models/source_info.dart';
|
||||||
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
||||||
@ -28,7 +28,7 @@ class ActiveSourcedTrackNotifier extends Notifier<SourcedTrack?> {
|
|||||||
state = newTrack;
|
state = newTrack;
|
||||||
await audioPlayer.pause();
|
await audioPlayer.pause();
|
||||||
|
|
||||||
final playbackNotifier = ref.read(proxyPlaylistProvider.notifier);
|
final playbackNotifier = ref.read(audioPlayerProvider.notifier);
|
||||||
final oldActiveIndex = audioPlayer.currentIndex;
|
final oldActiveIndex = audioPlayer.currentIndex;
|
||||||
|
|
||||||
await playbackNotifier.addTracksAtFirst([newTrack]);
|
await playbackNotifier.addTracksAtFirst([newTrack]);
|
||||||
|
@ -9,7 +9,7 @@ import 'package:spotify/spotify.dart';
|
|||||||
import 'package:spotube/models/connect/connect.dart';
|
import 'package:spotube/models/connect/connect.dart';
|
||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:spotube/provider/history/history.dart';
|
import 'package:spotube/provider/history/history.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/volume_provider.dart';
|
import 'package:spotube/provider/volume_provider.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/logger/logger.dart';
|
import 'package:spotube/services/logger/logger.dart';
|
||||||
@ -38,8 +38,8 @@ class ServerConnectRoutes {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ProxyPlaylistNotifier get playbackNotifier =>
|
AudioPlayerNotifier get audioPlayerNotifier =>
|
||||||
ref.read(proxyPlaylistProvider.notifier);
|
ref.read(audioPlayerProvider.notifier);
|
||||||
PlaybackHistoryNotifier get historyNotifier =>
|
PlaybackHistoryNotifier get historyNotifier =>
|
||||||
ref.read(playbackHistoryProvider.notifier);
|
ref.read(playbackHistoryProvider.notifier);
|
||||||
Stream<String> get connectClientStream =>
|
Stream<String> get connectClientStream =>
|
||||||
@ -57,7 +57,7 @@ class ServerConnectRoutes {
|
|||||||
_connectClientStreamController.add(origin);
|
_connectClientStreamController.add(origin);
|
||||||
|
|
||||||
ref.listen(
|
ref.listen(
|
||||||
proxyPlaylistProvider,
|
audioPlayerProvider,
|
||||||
(previous, next) {
|
(previous, next) {
|
||||||
channel.sink.addEvent(WebSocketQueueEvent(next));
|
channel.sink.addEvent(WebSocketQueueEvent(next));
|
||||||
},
|
},
|
||||||
@ -67,10 +67,10 @@ class ServerConnectRoutes {
|
|||||||
// because audioPlayer events doesn't fireImmediately
|
// because audioPlayer events doesn't fireImmediately
|
||||||
channel.sink.addEvent(WebSocketPlayingEvent(audioPlayer.isPlaying));
|
channel.sink.addEvent(WebSocketPlayingEvent(audioPlayer.isPlaying));
|
||||||
channel.sink.addEvent(
|
channel.sink.addEvent(
|
||||||
WebSocketPositionEvent(await audioPlayer.position ?? Duration.zero),
|
WebSocketPositionEvent(audioPlayer.position),
|
||||||
);
|
);
|
||||||
channel.sink.addEvent(
|
channel.sink.addEvent(
|
||||||
WebSocketDurationEvent(await audioPlayer.duration ?? Duration.zero),
|
WebSocketDurationEvent(audioPlayer.duration),
|
||||||
);
|
);
|
||||||
channel.sink.addEvent(WebSocketShuffleEvent(audioPlayer.isShuffled));
|
channel.sink.addEvent(WebSocketShuffleEvent(audioPlayer.isShuffled));
|
||||||
channel.sink.addEvent(WebSocketLoopEvent(audioPlayer.loopMode));
|
channel.sink.addEvent(WebSocketLoopEvent(audioPlayer.loopMode));
|
||||||
@ -116,14 +116,14 @@ class ServerConnectRoutes {
|
|||||||
);
|
);
|
||||||
|
|
||||||
event.onLoad((event) async {
|
event.onLoad((event) async {
|
||||||
await playbackNotifier.load(
|
await audioPlayerNotifier.load(
|
||||||
event.data.tracks,
|
event.data.tracks,
|
||||||
autoPlay: true,
|
autoPlay: true,
|
||||||
initialIndex: event.data.initialIndex ?? 0,
|
initialIndex: event.data.initialIndex ?? 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (event.data.collectionId == null) return;
|
if (event.data.collectionId == null) return;
|
||||||
playbackNotifier.addCollection(event.data.collectionId!);
|
audioPlayerNotifier.addCollection(event.data.collectionId!);
|
||||||
if (event.data.collection is AlbumSimple) {
|
if (event.data.collection is AlbumSimple) {
|
||||||
historyNotifier
|
historyNotifier
|
||||||
.addAlbums([event.data.collection as AlbumSimple]);
|
.addAlbums([event.data.collection as AlbumSimple]);
|
||||||
@ -146,15 +146,15 @@ class ServerConnectRoutes {
|
|||||||
});
|
});
|
||||||
|
|
||||||
event.onNext((event) async {
|
event.onNext((event) async {
|
||||||
await playbackNotifier.next();
|
await audioPlayer.skipToNext();
|
||||||
});
|
});
|
||||||
|
|
||||||
event.onPrevious((event) async {
|
event.onPrevious((event) async {
|
||||||
await playbackNotifier.previous();
|
await audioPlayer.skipToPrevious();
|
||||||
});
|
});
|
||||||
|
|
||||||
event.onJump((event) async {
|
event.onJump((event) async {
|
||||||
await playbackNotifier.jumpTo(event.data);
|
await audioPlayer.jumpTo(event.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
event.onSeek((event) async {
|
event.onSeek((event) async {
|
||||||
@ -170,15 +170,15 @@ class ServerConnectRoutes {
|
|||||||
});
|
});
|
||||||
|
|
||||||
event.onAddTrack((event) async {
|
event.onAddTrack((event) async {
|
||||||
await playbackNotifier.addTrack(event.data);
|
await audioPlayerNotifier.addTrack(event.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
event.onRemoveTrack((event) async {
|
event.onRemoveTrack((event) async {
|
||||||
await playbackNotifier.removeTrack(event.data);
|
await audioPlayerNotifier.removeTrack(event.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
event.onReorder((event) async {
|
event.onReorder((event) async {
|
||||||
await playbackNotifier.moveTrack(
|
await audioPlayerNotifier.moveTrack(
|
||||||
event.data.oldIndex,
|
event.data.oldIndex,
|
||||||
event.data.newIndex,
|
event.data.newIndex,
|
||||||
);
|
);
|
||||||
|
@ -2,8 +2,8 @@ import 'package:dio/dio.dart' hide Response;
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shelf/shelf.dart';
|
import 'package:shelf/shelf.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/state.dart';
|
||||||
import 'package:spotube/provider/server/active_sourced_track.dart';
|
import 'package:spotube/provider/server/active_sourced_track.dart';
|
||||||
import 'package:spotube/provider/server/sourced_track.dart';
|
import 'package:spotube/provider/server/sourced_track.dart';
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
@ -12,7 +12,7 @@ import 'package:spotube/services/logger/logger.dart';
|
|||||||
class ServerPlaybackRoutes {
|
class ServerPlaybackRoutes {
|
||||||
final Ref ref;
|
final Ref ref;
|
||||||
UserPreferences get userPreferences => ref.read(userPreferencesProvider);
|
UserPreferences get userPreferences => ref.read(userPreferencesProvider);
|
||||||
ProxyPlaylist get playlist => ref.read(proxyPlaylistProvider);
|
AudioPlayerState get playlist => ref.read(audioPlayerProvider);
|
||||||
final Dio dio;
|
final Dio dio;
|
||||||
|
|
||||||
ServerPlaybackRoutes(this.ref) : dio = Dio();
|
ServerPlaybackRoutes(this.ref) : dio = Dio();
|
||||||
|
@ -2,7 +2,7 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/models/local_track.dart';
|
import 'package:spotube/models/local_track.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
||||||
|
|
||||||
final sourcedTrackProvider =
|
final sourcedTrackProvider =
|
||||||
@ -12,10 +12,9 @@ final sourcedTrackProvider =
|
|||||||
}
|
}
|
||||||
|
|
||||||
ref.listen(
|
ref.listen(
|
||||||
proxyPlaylistProvider,
|
audioPlayerProvider.select((value) => value.tracks),
|
||||||
(old, next) {
|
(old, next) {
|
||||||
if (next.tracks.isEmpty ||
|
if (next.isEmpty || next.none((element) => element.id == track.id)) {
|
||||||
next.tracks.none((element) => element.id == track.id)) {
|
|
||||||
ref.invalidateSelf();
|
ref.invalidateSelf();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:media_kit/media_kit.dart' hide Track;
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
import 'package:tray_manager/tray_manager.dart';
|
import 'package:tray_manager/tray_manager.dart';
|
||||||
@ -19,9 +19,9 @@ final audioPlayerPlaying = StreamProvider<bool>((ref) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
final trayMenuProvider = Provider((ref) {
|
final trayMenuProvider = Provider((ref) {
|
||||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
|
||||||
final isPlaybackPlaying =
|
final isPlaybackPlaying =
|
||||||
ref.watch(proxyPlaylistProvider.select((s) => s.activeTrack != null));
|
ref.watch(audioPlayerProvider.select((s) => s.activeTrack != null));
|
||||||
final isLoopOne =
|
final isLoopOne =
|
||||||
ref.watch(audioPlayerLoopMode).asData?.value == PlaylistMode.single;
|
ref.watch(audioPlayerLoopMode).asData?.value == PlaylistMode.single;
|
||||||
final isShuffled = ref.watch(audioPlayerShuffleMode).asData?.value ?? false;
|
final isShuffled = ref.watch(audioPlayerShuffleMode).asData?.value ?? false;
|
||||||
@ -56,14 +56,14 @@ final trayMenuProvider = Provider((ref) {
|
|||||||
label: "Next",
|
label: "Next",
|
||||||
disabled: !isPlaybackPlaying,
|
disabled: !isPlaybackPlaying,
|
||||||
onClick: (menuItem) {
|
onClick: (menuItem) {
|
||||||
playlistNotifier.next();
|
audioPlayer.skipToNext();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
MenuItem(
|
MenuItem(
|
||||||
label: "Previous",
|
label: "Previous",
|
||||||
disabled: !isPlaybackPlaying,
|
disabled: !isPlaybackPlaying,
|
||||||
onClick: (menuItem) {
|
onClick: (menuItem) {
|
||||||
playlistNotifier.previous();
|
audioPlayer.skipToPrevious();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
MenuItem.submenu(
|
MenuItem.submenu(
|
||||||
|
@ -6,10 +6,9 @@ import 'package:path_provider/path_provider.dart';
|
|||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
|
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
|
||||||
|
import 'package:spotube/provider/audio_player/audio_player_streams.dart';
|
||||||
import 'package:spotube/provider/database/database.dart';
|
import 'package:spotube/provider/database/database.dart';
|
||||||
import 'package:spotube/provider/palette_provider.dart';
|
import 'package:spotube/provider/palette_provider.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/player_listeners.dart';
|
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/sourced_track/enums.dart';
|
import 'package:spotube/services/sourced_track/enums.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
@ -115,7 +114,7 @@ class UserPreferencesNotifier extends Notifier<PreferencesTableData> {
|
|||||||
if (!sync) {
|
if (!sync) {
|
||||||
ref.read(paletteProvider.notifier).state = null;
|
ref.read(paletteProvider.notifier).state = null;
|
||||||
} else {
|
} else {
|
||||||
ref.read(proxyPlaylistProvider.notifier).updatePalette();
|
ref.read(audioPlayerStreamListenersProvider).updatePalette();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/extensions/artist_simple.dart';
|
import 'package:spotube/extensions/artist_simple.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/audio_services/mobile_audio_service.dart';
|
import 'package:spotube/services/audio_services/mobile_audio_service.dart';
|
||||||
import 'package:spotube/services/audio_services/windows_audio_service.dart';
|
import 'package:spotube/services/audio_services/windows_audio_service.dart';
|
||||||
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
||||||
@ -17,7 +17,7 @@ class AudioServices {
|
|||||||
|
|
||||||
static Future<AudioServices> create(
|
static Future<AudioServices> create(
|
||||||
Ref ref,
|
Ref ref,
|
||||||
ProxyPlaylistNotifier playback,
|
AudioPlayerNotifier playback,
|
||||||
) async {
|
) async {
|
||||||
final mobile = kIsMobile || kIsMacOS || kIsLinux
|
final mobile = kIsMobile || kIsMacOS || kIsLinux
|
||||||
? await AudioService.init(
|
? await AudioService.init(
|
||||||
|
@ -2,19 +2,19 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:audio_service/audio_service.dart';
|
import 'package:audio_service/audio_service.dart';
|
||||||
import 'package:audio_session/audio_session.dart';
|
import 'package:audio_session/audio_session.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/state.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:media_kit/media_kit.dart' hide Track;
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
|
|
||||||
class MobileAudioService extends BaseAudioHandler {
|
class MobileAudioService extends BaseAudioHandler {
|
||||||
AudioSession? session;
|
AudioSession? session;
|
||||||
final ProxyPlaylistNotifier playlistNotifier;
|
final AudioPlayerNotifier audioPlayerNotifier;
|
||||||
|
|
||||||
// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
|
// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
|
||||||
ProxyPlaylist get playlist => playlistNotifier.state;
|
AudioPlayerState get playlist => audioPlayerNotifier.state;
|
||||||
|
|
||||||
MobileAudioService(this.playlistNotifier) {
|
MobileAudioService(this.audioPlayerNotifier) {
|
||||||
AudioSession.instance.then((s) {
|
AudioSession.instance.then((s) {
|
||||||
session = s;
|
session = s;
|
||||||
session?.configure(const AudioSessionConfiguration.music());
|
session?.configure(const AudioSessionConfiguration.music());
|
||||||
@ -102,24 +102,24 @@ class MobileAudioService extends BaseAudioHandler {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> stop() async {
|
Future<void> stop() async {
|
||||||
await playlistNotifier.stop();
|
await audioPlayerNotifier.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> skipToNext() async {
|
Future<void> skipToNext() async {
|
||||||
await playlistNotifier.next();
|
await audioPlayer.skipToNext();
|
||||||
await super.skipToNext();
|
await super.skipToNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> skipToPrevious() async {
|
Future<void> skipToPrevious() async {
|
||||||
await playlistNotifier.previous();
|
await audioPlayer.skipToPrevious();
|
||||||
await super.skipToPrevious();
|
await super.skipToPrevious();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onTaskRemoved() async {
|
Future<void> onTaskRemoved() async {
|
||||||
await playlistNotifier.stop();
|
await audioPlayerNotifier.stop();
|
||||||
return super.onTaskRemoved();
|
return super.onTaskRemoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ class MobileAudioService extends BaseAudioHandler {
|
|||||||
PlaylistMode.single => AudioServiceRepeatMode.one,
|
PlaylistMode.single => AudioServiceRepeatMode.one,
|
||||||
_ => AudioServiceRepeatMode.none,
|
_ => AudioServiceRepeatMode.none,
|
||||||
},
|
},
|
||||||
processingState: playlist.isFetching == true
|
processingState: audioPlayer.isBuffering
|
||||||
? AudioProcessingState.loading
|
? AudioProcessingState.loading
|
||||||
: AudioProcessingState.ready,
|
: AudioProcessingState.ready,
|
||||||
);
|
);
|
||||||
|
@ -5,18 +5,18 @@ import 'package:smtc_windows/smtc_windows.dart';
|
|||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/extensions/artist_simple.dart';
|
import 'package:spotube/extensions/artist_simple.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/audio_player/playback_state.dart';
|
import 'package:spotube/services/audio_player/playback_state.dart';
|
||||||
|
|
||||||
class WindowsAudioService {
|
class WindowsAudioService {
|
||||||
final SMTCWindows smtc;
|
final SMTCWindows smtc;
|
||||||
final Ref ref;
|
final Ref ref;
|
||||||
final ProxyPlaylistNotifier playlistNotifier;
|
final AudioPlayerNotifier audioPlayerNotifier;
|
||||||
|
|
||||||
final subscriptions = <StreamSubscription>[];
|
final subscriptions = <StreamSubscription>[];
|
||||||
|
|
||||||
WindowsAudioService(this.ref, this.playlistNotifier)
|
WindowsAudioService(this.ref, this.audioPlayerNotifier)
|
||||||
: smtc = SMTCWindows(enabled: false) {
|
: smtc = SMTCWindows(enabled: false) {
|
||||||
smtc.setPlaybackStatus(PlaybackStatus.Stopped);
|
smtc.setPlaybackStatus(PlaybackStatus.Stopped);
|
||||||
final buttonStream = smtc.buttonPressStream.listen((event) {
|
final buttonStream = smtc.buttonPressStream.listen((event) {
|
||||||
@ -28,13 +28,13 @@ class WindowsAudioService {
|
|||||||
audioPlayer.pause();
|
audioPlayer.pause();
|
||||||
break;
|
break;
|
||||||
case PressedButton.next:
|
case PressedButton.next:
|
||||||
playlistNotifier.next();
|
audioPlayer.skipToNext();
|
||||||
break;
|
break;
|
||||||
case PressedButton.previous:
|
case PressedButton.previous:
|
||||||
playlistNotifier.previous();
|
audioPlayer.skipToPrevious();
|
||||||
break;
|
break;
|
||||||
case PressedButton.stop:
|
case PressedButton.stop:
|
||||||
playlistNotifier.stop();
|
audioPlayerNotifier.stop();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user