diff --git a/lib/components/ArtistScreen/artist_play_button.dart b/lib/components/ArtistScreen/artist_play_button.dart new file mode 100644 index 000000000..374b44708 --- /dev/null +++ b/lib/components/ArtistScreen/artist_play_button.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; +import 'package:hive/hive.dart'; + + +import '../../models/jellyfin_models.dart'; +import '../../models/finamp_models.dart'; +import '../../services/jellyfin_api_helper.dart'; +import '../../services/audio_service_helper.dart'; +import '../../services/finamp_settings_helper.dart'; +import '../../services/downloads_helper.dart'; + +class ArtistPlayButton extends StatefulWidget { + const ArtistPlayButton({ + Key? key, + required this.artist, + }) : super(key: key); + + final BaseItemDto artist; + + + @override + State createState() => _ArtistPlayButtonState(); +} + +class _ArtistPlayButtonState extends State { + static const _disabledButton = IconButton( + onPressed: null, + icon: Icon(Icons.play_arrow) + ); + Future?>? artistPlayButtonFuture; + + final _jellyfinApiHelper = GetIt.instance(); + final _audioServiceHelper = GetIt.instance(); + + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder>( + valueListenable: FinampSettingsHelper.finampSettingsListener, + builder: (context, box, _) { + final isOffline = box.get("FinampSettings")?.isOffline ?? false; + + if (isOffline) { + final downloadsHelper = GetIt.instance(); + + final ListartistsSongs = []; + + for (DownloadedSong item in downloadsHelper.downloadedItems) { + if (item.song.albumArtist == widget.artist.name) { + artistsSongs.add(item.song); + } + } + + // We have to sort by hand in offline mode because a downloadedParent for artists hasn't been implemented + Map> groupedSongs = {}; + for (BaseItemDto song in artistsSongs) { + groupedSongs.putIfAbsent((song.albumId ?? 'unknown'), () => []); + groupedSongs[song.albumId]!.add(song); + } + + final List sortedSongs = []; + groupedSongs.forEach((album, albumSongs) { + albumSongs.sort((a, b) => (a.indexNumber ?? 0).compareTo(b.indexNumber ?? 0)); + sortedSongs.addAll(albumSongs); + }); + + return IconButton( + onPressed: () async { + await _audioServiceHelper + .replaceQueueWithItem(itemList: sortedSongs); + }, + icon: const Icon(Icons.play_arrow), + ); + } else { + artistPlayButtonFuture ??= _jellyfinApiHelper.getItems( + parentItem: widget.artist, + includeItemTypes: "Audio", + sortBy: 'PremiereDate,Album,SortName', + isGenres: false, + ); + + return FutureBuilder?>( + future: artistPlayButtonFuture, + builder: (context, snapshot) { + if (snapshot.hasData){ + final List items = snapshot.data!; + + return IconButton( + onPressed: () async { + await _audioServiceHelper + .replaceQueueWithItem(itemList: items); + }, + icon: const Icon(Icons.play_arrow), + ); + } else { + return _disabledButton; + } + }, + ); + } + }, + ); + } +} \ No newline at end of file diff --git a/lib/components/ArtistScreen/artist_shuffle_button.dart b/lib/components/ArtistScreen/artist_shuffle_button.dart new file mode 100644 index 000000000..da86da726 --- /dev/null +++ b/lib/components/ArtistScreen/artist_shuffle_button.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; +import 'package:hive/hive.dart'; + + +import '../../models/jellyfin_models.dart'; +import '../../models/finamp_models.dart'; +import '../../services/jellyfin_api_helper.dart'; +import '../../services/audio_service_helper.dart'; +import '../../services/finamp_settings_helper.dart'; +import '../../services/downloads_helper.dart'; + +class ArtistShuffleButton extends StatefulWidget { + const ArtistShuffleButton({ + Key? key, + required this.artist, + }) : super(key: key); + + final BaseItemDto artist; + + + @override + State createState() => _ArtistShuffleButtonState(); +} + +class _ArtistShuffleButtonState extends State { + static const _disabledButton = IconButton( + onPressed: null, + icon: Icon(Icons.play_arrow) + ); + Future?>? artistShuffleButtonFuture; + + final _jellyfinApiHelper = GetIt.instance(); + final _audioServiceHelper = GetIt.instance(); + + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder>( + valueListenable: FinampSettingsHelper.finampSettingsListener, + builder: (context, box, _) { + final isOffline = box.get("FinampSettings")?.isOffline ?? false; + + if (isOffline) { + final downloadsHelper = GetIt.instance(); + + final ListartistsSongs = []; + + for (DownloadedSong item in downloadsHelper.downloadedItems) { + if (item.song.albumArtist == widget.artist.name) { + artistsSongs.add(item.song); + } + } + + return IconButton( + onPressed: () async { + await _audioServiceHelper + .replaceQueueWithItem(itemList: artistsSongs, shuffle: true); + }, + icon: const Icon(Icons.shuffle), + ); + } else { + artistShuffleButtonFuture ??= _jellyfinApiHelper.getItems( + parentItem: widget.artist, + includeItemTypes: "Audio", + sortBy: 'PremiereDate,Album,SortName', + isGenres: false, + ); + + return FutureBuilder?>( + future: artistShuffleButtonFuture, + builder: (context, snapshot) { + if (snapshot.hasData){ + final List items = snapshot.data!; + + return IconButton( + onPressed: () async { + await _audioServiceHelper + .replaceQueueWithItem(itemList: items, shuffle: true); + }, + icon: const Icon(Icons.shuffle), + ); + } else { + return _disabledButton; + } + }, + ); + } + }, + ); + } +} \ No newline at end of file diff --git a/lib/components/MusicScreen/music_screen_tab_view.dart b/lib/components/MusicScreen/music_screen_tab_view.dart index 77cc87ddd..6a6f02bf8 100644 --- a/lib/components/MusicScreen/music_screen_tab_view.dart +++ b/lib/components/MusicScreen/music_screen_tab_view.dart @@ -28,6 +28,7 @@ class MusicScreenTabView extends StatefulWidget { this.sortBy, this.sortOrder, this.view, + this.albumArtist, }) : super(key: key); final TabContentType tabContentType; @@ -37,6 +38,7 @@ class MusicScreenTabView extends StatefulWidget { final SortBy? sortBy; final SortOrder? sortOrder; final BaseItemDto? view; + final String? albumArtist; @override State createState() => _MusicScreenTabViewState(); @@ -183,12 +185,14 @@ class _MusicScreenTabViewState extends State .map((e) => e.song) .toList(); } else { + String? albumArtist = widget.albumArtist; offlineSortedItems = downloadsHelper.downloadedParents .where((element) => element.item.type == _includeItemTypes(widget.tabContentType) && element.viewId == - _finampUserHelper.currentUser!.currentViewId) + _finampUserHelper.currentUser!.currentViewId && + (albumArtist == null || element.item.albumArtist?.toLowerCase() == albumArtist.toLowerCase())) .map((e) => e.item) .toList(); } diff --git a/lib/screens/artist_screen.dart b/lib/screens/artist_screen.dart index 49a22a066..e25b7fc2a 100644 --- a/lib/screens/artist_screen.dart +++ b/lib/screens/artist_screen.dart @@ -6,6 +6,8 @@ import '../components/ArtistScreen/artist_download_button.dart'; import '../components/MusicScreen/music_screen_tab_view.dart'; import '../components/now_playing_bar.dart'; import '../components/favourite_button.dart'; +import '../components/ArtistScreen/artist_play_button.dart'; +import '../components/ArtistScreen/artist_shuffle_button.dart'; class ArtistScreen extends StatelessWidget { const ArtistScreen({ @@ -28,6 +30,8 @@ class ArtistScreen extends StatelessWidget { title: Text(artist.name ?? "Unknown Name"), actions: [ // this screen is also used for genres, which can't be favorited + if (artist.type != "MusicGenre") ArtistPlayButton(artist: artist), + if (artist.type != "MusicGenre") ArtistShuffleButton(artist: artist), if (artist.type != "MusicGenre") FavoriteButton(item: artist), ArtistDownloadButton(artist: artist) ], @@ -36,6 +40,8 @@ class ArtistScreen extends StatelessWidget { tabContentType: TabContentType.albums, parentItem: artist, isFavourite: false, + sortBy: SortBy.premiereDate, + albumArtist: artist.name, ), bottomNavigationBar: const NowPlayingBar(), );