Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Playing Directly from Artist Page #493

Merged
merged 6 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions lib/components/ArtistScreen/artist_play_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
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<ArtistPlayButton> createState() => _ArtistPlayButtonState();
}

class _ArtistPlayButtonState extends State<ArtistPlayButton> {
static const _disabledButton = IconButton(
onPressed: null,
icon: Icon(Icons.play_arrow)
);
Future<List<BaseItemDto>?>? artistPlayButtonFuture;

final _jellyfinApiHelper = GetIt.instance<JellyfinApiHelper>();
final _audioServiceHelper = GetIt.instance<AudioServiceHelper>();


@override
Widget build(BuildContext context) {
return ValueListenableBuilder<Box<FinampSettings>>(
valueListenable: FinampSettingsHelper.finampSettingsListener,
builder: (context, box, _) {
final isOffline = box.get("FinampSettings")?.isOffline ?? false;

if (isOffline) {
final downloadsHelper = GetIt.instance<DownloadsHelper>();

final List<BaseItemDto>songs = [];

for (DownloadedSong item in downloadsHelper.downloadedItems) {
if (item.song.albumArtist == widget.artist.name) {
songs.add(item.song);
}
}

// We have to sort by hand in offline mode because a downloadedParent for artists hasn't been implemented
Map<String, List<BaseItemDto>> groupedSongs = {};
for (BaseItemDto song in songs) {
groupedSongs.putIfAbsent((song.albumId ?? 'unknown'), () => []);
groupedSongs[song.albumId]!.add(song);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be done in a single loop? There could be 10k+ downloaded songs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this is a non-issue. The code only iterates through the entire library one. After that, the code uses the songs variable which only contains a list of songs by the artist (so the songs variable will only ever hold at most a few hundred songs). I also just renamed the variable to better reflect that.


groupedSongs.forEach((album, albumSongs) {
albumSongs.sort((a, b) => (a.indexNumber ?? 0).compareTo(b.indexNumber ?? 0));
});

final List<BaseItemDto> sortedSongs = [];
groupedSongs.values.forEach((albumSongs) {
sortedSongs.addAll(albumSongs);
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for this. After sorting, add the albumSongs to sortedSongs right away



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<List<BaseItemDto>?>(
future: artistPlayButtonFuture,
builder: (context, snapshot) {
if (snapshot.hasData){
final List<BaseItemDto> items = snapshot.data!;

return IconButton(
onPressed: () async {
await _audioServiceHelper
.replaceQueueWithItem(itemList: items);
},
icon: const Icon(Icons.play_arrow),
);
} else {
return _disabledButton;
}
},
);
}
},
);
}
}
6 changes: 5 additions & 1 deletion lib/components/MusicScreen/music_screen_tab_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class MusicScreenTabView extends StatefulWidget {
this.sortBy,
this.sortOrder,
this.view,
this.albumArtist,
}) : super(key: key);

final TabContentType tabContentType;
Expand All @@ -37,6 +38,7 @@ class MusicScreenTabView extends StatefulWidget {
final SortBy? sortBy;
final SortOrder? sortOrder;
final BaseItemDto? view;
final String? albumArtist;

@override
State<MusicScreenTabView> createState() => _MusicScreenTabViewState();
Expand Down Expand Up @@ -183,12 +185,14 @@ class _MusicScreenTabViewState extends State<MusicScreenTabView>
.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();
}
Expand Down
4 changes: 4 additions & 0 deletions lib/screens/artist_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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';

class ArtistScreen extends StatelessWidget {
const ArtistScreen({
Expand All @@ -28,6 +29,7 @@ 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),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about adding a shuffle button as well?

if (artist.type != "MusicGenre") FavoriteButton(item: artist),
ArtistDownloadButton(artist: artist)
],
Expand All @@ -36,6 +38,8 @@ class ArtistScreen extends StatelessWidget {
tabContentType: TabContentType.albums,
parentItem: artist,
isFavourite: false,
sortBy: SortBy.premiereDate,
albumArtist: artist.name,
),
bottomNavigationBar: const NowPlayingBar(),
);
Expand Down