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 all 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
105 changes: 105 additions & 0 deletions lib/components/ArtistScreen/artist_play_button.dart
Original file line number Diff line number Diff line change
@@ -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<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>artistsSongs = [];

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<String, List<BaseItemDto>> groupedSongs = {};
for (BaseItemDto song in artistsSongs) {
groupedSongs.putIfAbsent((song.albumId ?? 'unknown'), () => []);
groupedSongs[song.albumId]!.add(song);
}

final List<BaseItemDto> 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<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;
}
},
);
}
},
);
}
}
92 changes: 92 additions & 0 deletions lib/components/ArtistScreen/artist_shuffle_button.dart
Original file line number Diff line number Diff line change
@@ -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<ArtistShuffleButton> createState() => _ArtistShuffleButtonState();
}

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

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>artistsSongs = [];

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

return IconButton(
onPressed: () async {
await _audioServiceHelper
.replaceQueueWithItem(itemList: items, shuffle: true);
},
icon: const Icon(Icons.shuffle),
);
} 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
6 changes: 6 additions & 0 deletions lib/screens/artist_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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),
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") ArtistShuffleButton(artist: artist),
if (artist.type != "MusicGenre") FavoriteButton(item: artist),
ArtistDownloadButton(artist: artist)
],
Expand All @@ -36,6 +40,8 @@ class ArtistScreen extends StatelessWidget {
tabContentType: TabContentType.albums,
parentItem: artist,
isFavourite: false,
sortBy: SortBy.premiereDate,
albumArtist: artist.name,
),
bottomNavigationBar: const NowPlayingBar(),
);
Expand Down