Skip to content

Commit

Permalink
Merge pull request #493 from BobcatNoah/main
Browse files Browse the repository at this point in the history
Playing Directly from Artist Page
  • Loading branch information
jmshrv authored Oct 2, 2023
2 parents 0b38574 + 141f029 commit 028b251
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 1 deletion.
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),
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

0 comments on commit 028b251

Please sign in to comment.