mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
feat: individual shuffle and repeat/loop button of player
This commit is contained in:
parent
e7f3f4eae4
commit
f79223cd41
@ -39,11 +39,12 @@ class UserAlbums extends HookConsumerWidget {
|
||||
final searchText = useState('');
|
||||
|
||||
final albums = useMemoized(() {
|
||||
if (searchText.value.isEmpty) {
|
||||
return albumsQuery.data?.toList() ?? [];
|
||||
}
|
||||
return albumsQuery.data
|
||||
?.map((e) => Tuple2(
|
||||
searchText.value.isEmpty
|
||||
? 100
|
||||
: weightedRatio(e.name!, searchText.value),
|
||||
weightedRatio(e.name!, searchText.value),
|
||||
e,
|
||||
))
|
||||
.sorted((a, b) => b.item1.compareTo(a.item1))
|
||||
|
@ -33,12 +33,15 @@ class UserArtists extends HookConsumerWidget {
|
||||
final searchText = useState('');
|
||||
|
||||
final filteredArtists = useMemoized(() {
|
||||
return artistQuery.pages
|
||||
.expand<Artist>((page) => page?.items ?? const Iterable.empty())
|
||||
final artists = artistQuery.pages
|
||||
.expand<Artist>((page) => page?.items ?? const Iterable.empty());
|
||||
|
||||
if (searchText.value.isEmpty) {
|
||||
return artists.toList();
|
||||
}
|
||||
return artists
|
||||
.map((e) => Tuple2(
|
||||
searchText.value.isEmpty
|
||||
? 100
|
||||
: weightedRatio(e.name!, searchText.value),
|
||||
weightedRatio(e.name!, searchText.value),
|
||||
e,
|
||||
))
|
||||
.sorted((a, b) => b.item1.compareTo(a.item1))
|
||||
|
@ -250,14 +250,15 @@ class UserLocalTracks extends HookConsumerWidget {
|
||||
}, [sortBy.value, tracks]);
|
||||
|
||||
final filteredTracks = useMemoized(() {
|
||||
if (searchText.value.isEmpty) {
|
||||
return sortedTracks;
|
||||
}
|
||||
return sortedTracks
|
||||
.map((e) => Tuple2(
|
||||
searchText.value.isEmpty
|
||||
? 100
|
||||
: weightedRatio(
|
||||
"${e.name} - ${TypeConversionUtils.artists_X_String<Artist>(e.artists ?? [])}",
|
||||
searchText.value,
|
||||
),
|
||||
weightedRatio(
|
||||
"${e.name} - ${TypeConversionUtils.artists_X_String<Artist>(e.artists ?? [])}",
|
||||
searchText.value,
|
||||
),
|
||||
e,
|
||||
))
|
||||
.toList()
|
||||
|
@ -52,20 +52,26 @@ class UserPlaylists extends HookConsumerWidget {
|
||||
likedTracksPlaylist.images = [image];
|
||||
|
||||
final playlists = useMemoized(
|
||||
() => [
|
||||
likedTracksPlaylist,
|
||||
...?playlistsQuery.data,
|
||||
]
|
||||
.map((e) => Tuple2(
|
||||
searchText.value.isEmpty
|
||||
? 100
|
||||
: weightedRatio(e.name!, searchText.value),
|
||||
e,
|
||||
))
|
||||
.sorted((a, b) => b.item1.compareTo(a.item1))
|
||||
.where((e) => e.item1 > 50)
|
||||
.map((e) => e.item2)
|
||||
.toList(),
|
||||
() {
|
||||
if (searchText.value.isEmpty) {
|
||||
return [
|
||||
likedTracksPlaylist,
|
||||
...?playlistsQuery.data,
|
||||
];
|
||||
}
|
||||
return [
|
||||
likedTracksPlaylist,
|
||||
...?playlistsQuery.data,
|
||||
]
|
||||
.map((e) => Tuple2(
|
||||
weightedRatio(e.name!, searchText.value),
|
||||
e,
|
||||
))
|
||||
.sorted((a, b) => b.item1.compareTo(a.item1))
|
||||
.where((e) => e.item1 > 50)
|
||||
.map((e) => e.item2)
|
||||
.toList();
|
||||
},
|
||||
[playlistsQuery.data, searchText.value],
|
||||
);
|
||||
|
||||
|
@ -139,22 +139,20 @@ class PlayerControls extends HookConsumerWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
PlatformIconButton(
|
||||
tooltip: playback.isLoop
|
||||
? "Repeat playlist"
|
||||
: playback.isShuffled
|
||||
? "Loop track"
|
||||
: "Shuffle playlist",
|
||||
tooltip: playback.isShuffled
|
||||
? "Unshuffle playlist"
|
||||
: "Shuffle playlist",
|
||||
icon: Icon(
|
||||
playback.isLoop
|
||||
? Icons.repeat_one_rounded
|
||||
: playback.isShuffled
|
||||
? Icons.shuffle_rounded
|
||||
: Icons.repeat_rounded,
|
||||
Icons.shuffle_rounded,
|
||||
color: playback.isShuffled
|
||||
? PlatformTheme.of(context).primaryColor
|
||||
: null,
|
||||
),
|
||||
onPressed:
|
||||
playback.track == null || playback.playlist == null
|
||||
? null
|
||||
: playback.cyclePlaybackMode,
|
||||
onPressed: playback.playlist == null
|
||||
? null
|
||||
: () {
|
||||
playback.setIsShuffled(!playback.isShuffled);
|
||||
},
|
||||
),
|
||||
PlatformIconButton(
|
||||
tooltip: "Previous track",
|
||||
@ -209,7 +207,22 @@ class PlayerControls extends HookConsumerWidget {
|
||||
}
|
||||
}
|
||||
: null,
|
||||
)
|
||||
),
|
||||
PlatformIconButton(
|
||||
tooltip:
|
||||
!playback.isLoop ? "Loop Track" : "Repeat playlist",
|
||||
icon: Icon(
|
||||
playback.isLoop
|
||||
? Icons.repeat_one_rounded
|
||||
: Icons.repeat_rounded,
|
||||
),
|
||||
onPressed:
|
||||
playback.track == null || playback.playlist == null
|
||||
? null
|
||||
: () {
|
||||
playback.setIsLoop(!playback.isLoop);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5)
|
||||
|
@ -103,14 +103,15 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
||||
final searchText = useState("");
|
||||
|
||||
final filteredTracks = useMemoized(() {
|
||||
if (searchText.value.isEmpty) {
|
||||
return tracksSnapshot.data;
|
||||
}
|
||||
return tracksSnapshot.data
|
||||
?.map((e) => Tuple2(
|
||||
searchText.value.isEmpty
|
||||
? 100
|
||||
: weightedRatio(
|
||||
"${e.name} - ${TypeConversionUtils.artists_X_String<Artist>(e.artists ?? [])}",
|
||||
searchText.value,
|
||||
),
|
||||
weightedRatio(
|
||||
"${e.name} - ${TypeConversionUtils.artists_X_String<Artist>(e.artists ?? [])}",
|
||||
searchText.value,
|
||||
),
|
||||
e,
|
||||
))
|
||||
.toList()
|
||||
|
@ -40,7 +40,7 @@ class CurrentPlaylist {
|
||||
// won't shuffle if already shuffled
|
||||
if (_tempTrack == null) {
|
||||
_tempTrack = [...tracks];
|
||||
tracks.shuffle();
|
||||
tracks = List.from(tracks)..shuffle();
|
||||
if (topTrack != null) {
|
||||
tracks.remove(topTrack);
|
||||
tracks.insert(0, topTrack);
|
||||
|
@ -39,15 +39,10 @@ enum AudioQuality {
|
||||
low,
|
||||
}
|
||||
|
||||
enum PlaybackMode {
|
||||
repeat,
|
||||
shuffle,
|
||||
normal,
|
||||
}
|
||||
|
||||
class Playback extends PersistedChangeNotifier {
|
||||
// player properties
|
||||
PlaybackMode playbackMode;
|
||||
bool isShuffled;
|
||||
bool isLoop;
|
||||
bool isPlaying;
|
||||
Duration currentDuration;
|
||||
double volume;
|
||||
@ -83,7 +78,8 @@ class Playback extends PersistedChangeNotifier {
|
||||
this.mobileAudioService,
|
||||
}) : volume = 1,
|
||||
isPlaying = false,
|
||||
playbackMode = PlaybackMode.normal,
|
||||
isShuffled = false,
|
||||
isLoop = false,
|
||||
currentDuration = Duration.zero,
|
||||
_subscriptions = [],
|
||||
status = PlaybackStatus.idle,
|
||||
@ -287,25 +283,18 @@ class Playback extends PersistedChangeNotifier {
|
||||
isPlaying ? await pause() : await resume();
|
||||
}
|
||||
|
||||
void cyclePlaybackMode() {
|
||||
switch (playbackMode) {
|
||||
case PlaybackMode.normal:
|
||||
playbackMode = PlaybackMode.shuffle;
|
||||
playlist?.shuffle(track);
|
||||
break;
|
||||
case PlaybackMode.shuffle:
|
||||
playbackMode = PlaybackMode.repeat;
|
||||
playlist?.unshuffle();
|
||||
break;
|
||||
case PlaybackMode.repeat:
|
||||
playbackMode = PlaybackMode.normal;
|
||||
break;
|
||||
void setIsShuffled(bool shuffle) {
|
||||
isShuffled = shuffle;
|
||||
if (isShuffled) {
|
||||
playlist?.shuffle(track);
|
||||
} else {
|
||||
playlist?.unshuffle();
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setPlaybackMode(PlaybackMode mode) {
|
||||
playbackMode = mode;
|
||||
void setIsLoop(bool loop) {
|
||||
isLoop = loop;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@ -325,7 +314,8 @@ class Playback extends PersistedChangeNotifier {
|
||||
await player.stop();
|
||||
await player.release();
|
||||
isPlaying = false;
|
||||
playbackMode = PlaybackMode.normal;
|
||||
isShuffled = false;
|
||||
isLoop = false;
|
||||
playlist = null;
|
||||
track = null;
|
||||
status = PlaybackStatus.idle;
|
||||
@ -685,9 +675,6 @@ class Playback extends PersistedChangeNotifier {
|
||||
};
|
||||
}
|
||||
|
||||
bool get isLoop => playbackMode == PlaybackMode.repeat;
|
||||
bool get isShuffled => playbackMode == PlaybackMode.shuffle;
|
||||
bool get isNormal => playbackMode == PlaybackMode.normal;
|
||||
UnmodifiableListView<Video> get siblingYtVideos =>
|
||||
UnmodifiableListView(_siblingYtVideos);
|
||||
}
|
||||
|
@ -249,13 +249,15 @@ class _MprisMediaPlayer2Player extends DBusObject {
|
||||
|
||||
/// Gets value of property org.mpris.MediaPlayer2.Player.LoopStatus
|
||||
Future<DBusMethodResponse> getLoopStatus() async {
|
||||
return DBusMethodSuccessResponse([const DBusString("Playlist")]);
|
||||
return DBusMethodSuccessResponse([
|
||||
playback.isLoop ? const DBusString("Track") : const DBusString("None"),
|
||||
]);
|
||||
}
|
||||
|
||||
/// Sets property org.mpris.MediaPlayer2.Player.LoopStatus
|
||||
Future<DBusMethodResponse> setLoopStatus(String value) async {
|
||||
return DBusMethodErrorResponse.failed(
|
||||
'Set org.mpris.MediaPlayer2.Player.LoopStatus not implemented');
|
||||
playback.setIsLoop(value == "Track");
|
||||
return DBusMethodSuccessResponse();
|
||||
}
|
||||
|
||||
/// Gets value of property org.mpris.MediaPlayer2.Player.Rate
|
||||
@ -275,9 +277,7 @@ class _MprisMediaPlayer2Player extends DBusObject {
|
||||
|
||||
/// Sets property org.mpris.MediaPlayer2.Player.Shuffle
|
||||
Future<DBusMethodResponse> setShuffle(bool value) async {
|
||||
playback.setPlaybackMode(
|
||||
value ? PlaybackMode.shuffle : PlaybackMode.normal,
|
||||
);
|
||||
playback.setIsShuffled(value);
|
||||
return DBusMethodSuccessResponse();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user