Skip to content

Commit

Permalink
Add missing links to album view and bottom view on desktop platform
Browse files Browse the repository at this point in the history
  • Loading branch information
avdept committed Nov 19, 2024
1 parent 039e697 commit d601801
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 81 deletions.
6 changes: 6 additions & 0 deletions lib/src/data/api/jellyfin/jellyfin_api.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:dio/dio.dart';
import 'package:jplayer/src/data/dto/dto.dart';
import 'package:jplayer/src/data/dto/item/item_dto.dart';
import 'package:jplayer/src/data/dto/songs/songs_dto.dart';
import 'package:jplayer/src/data/dto/wrappers/wrappers_dto.dart';
import 'package:retrofit/retrofit.dart';
Expand Down Expand Up @@ -95,6 +96,11 @@ abstract class JellyfinApi {
@Query('IncludeItemTypes') String includeType = 'music',
});

@GET('/Items/{itemId}')
Future<HttpResponse<ItemDTO>> getItem({
@Path('itemId') required String itemId,
});

@POST('/Playlists')
Future<void> createPlaylist(
@Body() Map<String, dynamic> arguments,
Expand Down
28 changes: 28 additions & 0 deletions lib/src/data/api/jellyfin/jellyfin_api.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions lib/src/data/dto/item/item_dto.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:jplayer/src/data/dto/dto.dart';

part 'item_dto.freezed.dart';
part 'item_dto.g.dart';
Expand All @@ -15,6 +16,7 @@ class ItemDTO with _$ItemDTO {
@JsonKey(name: 'RunTimeTicks') required int? durationInTicks,
@JsonKey(name: 'ProductionYear') int? productionYear,
@JsonKey(name: 'AlbumArtist') String? albumArtist,
@Default([]) @JsonKey(name: 'AlbumArtists') List<ArtistDTO> albumArtists,
@Default([]) @JsonKey(name: 'BackdropImageTags') List<String> backgropImageTags,
@Default({}) @JsonKey(name: 'ImageTags') Map<String, String> imageTags,
}) = _ItemDTO;
Expand Down
37 changes: 35 additions & 2 deletions lib/src/data/dto/item/item_dto.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ mixin _$ItemDTO {
int? get productionYear => throw _privateConstructorUsedError;
@JsonKey(name: 'AlbumArtist')
String? get albumArtist => throw _privateConstructorUsedError;
@JsonKey(name: 'AlbumArtists')
List<ArtistDTO> get albumArtists => throw _privateConstructorUsedError;
@JsonKey(name: 'BackdropImageTags')
List<String> get backgropImageTags => throw _privateConstructorUsedError;
@JsonKey(name: 'ImageTags')
Expand All @@ -60,6 +62,7 @@ abstract class $ItemDTOCopyWith<$Res> {
@JsonKey(name: 'RunTimeTicks') int? durationInTicks,
@JsonKey(name: 'ProductionYear') int? productionYear,
@JsonKey(name: 'AlbumArtist') String? albumArtist,
@JsonKey(name: 'AlbumArtists') List<ArtistDTO> albumArtists,
@JsonKey(name: 'BackdropImageTags') List<String> backgropImageTags,
@JsonKey(name: 'ImageTags') Map<String, String> imageTags});
}
Expand All @@ -85,6 +88,7 @@ class _$ItemDTOCopyWithImpl<$Res, $Val extends ItemDTO>
Object? durationInTicks = freezed,
Object? productionYear = freezed,
Object? albumArtist = freezed,
Object? albumArtists = null,
Object? backgropImageTags = null,
Object? imageTags = null,
}) {
Expand Down Expand Up @@ -121,6 +125,10 @@ class _$ItemDTOCopyWithImpl<$Res, $Val extends ItemDTO>
? _value.albumArtist
: albumArtist // ignore: cast_nullable_to_non_nullable
as String?,
albumArtists: null == albumArtists
? _value.albumArtists
: albumArtists // ignore: cast_nullable_to_non_nullable
as List<ArtistDTO>,
backgropImageTags: null == backgropImageTags
? _value.backgropImageTags
: backgropImageTags // ignore: cast_nullable_to_non_nullable
Expand Down Expand Up @@ -149,6 +157,7 @@ abstract class _$$ItemDTOImplCopyWith<$Res> implements $ItemDTOCopyWith<$Res> {
@JsonKey(name: 'RunTimeTicks') int? durationInTicks,
@JsonKey(name: 'ProductionYear') int? productionYear,
@JsonKey(name: 'AlbumArtist') String? albumArtist,
@JsonKey(name: 'AlbumArtists') List<ArtistDTO> albumArtists,
@JsonKey(name: 'BackdropImageTags') List<String> backgropImageTags,
@JsonKey(name: 'ImageTags') Map<String, String> imageTags});
}
Expand All @@ -172,6 +181,7 @@ class __$$ItemDTOImplCopyWithImpl<$Res>
Object? durationInTicks = freezed,
Object? productionYear = freezed,
Object? albumArtist = freezed,
Object? albumArtists = null,
Object? backgropImageTags = null,
Object? imageTags = null,
}) {
Expand Down Expand Up @@ -208,6 +218,10 @@ class __$$ItemDTOImplCopyWithImpl<$Res>
? _value.albumArtist
: albumArtist // ignore: cast_nullable_to_non_nullable
as String?,
albumArtists: null == albumArtists
? _value._albumArtists
: albumArtists // ignore: cast_nullable_to_non_nullable
as List<ArtistDTO>,
backgropImageTags: null == backgropImageTags
? _value._backgropImageTags
: backgropImageTags // ignore: cast_nullable_to_non_nullable
Expand All @@ -232,11 +246,14 @@ class _$ItemDTOImpl extends _ItemDTO {
@JsonKey(name: 'RunTimeTicks') required this.durationInTicks,
@JsonKey(name: 'ProductionYear') this.productionYear,
@JsonKey(name: 'AlbumArtist') this.albumArtist,
@JsonKey(name: 'AlbumArtists')
final List<ArtistDTO> albumArtists = const [],
@JsonKey(name: 'BackdropImageTags')
final List<String> backgropImageTags = const [],
@JsonKey(name: 'ImageTags')
final Map<String, String> imageTags = const {}})
: _backgropImageTags = backgropImageTags,
: _albumArtists = albumArtists,
_backgropImageTags = backgropImageTags,
_imageTags = imageTags,
super._();

Expand Down Expand Up @@ -267,6 +284,15 @@ class _$ItemDTOImpl extends _ItemDTO {
@override
@JsonKey(name: 'AlbumArtist')
final String? albumArtist;
final List<ArtistDTO> _albumArtists;
@override
@JsonKey(name: 'AlbumArtists')
List<ArtistDTO> get albumArtists {
if (_albumArtists is EqualUnmodifiableListView) return _albumArtists;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_albumArtists);
}

final List<String> _backgropImageTags;
@override
@JsonKey(name: 'BackdropImageTags')
Expand All @@ -288,7 +314,7 @@ class _$ItemDTOImpl extends _ItemDTO {

@override
String toString() {
return 'ItemDTO(id: $id, name: $name, serverId: $serverId, type: $type, overview: $overview, durationInTicks: $durationInTicks, productionYear: $productionYear, albumArtist: $albumArtist, backgropImageTags: $backgropImageTags, imageTags: $imageTags)';
return 'ItemDTO(id: $id, name: $name, serverId: $serverId, type: $type, overview: $overview, durationInTicks: $durationInTicks, productionYear: $productionYear, albumArtist: $albumArtist, albumArtists: $albumArtists, backgropImageTags: $backgropImageTags, imageTags: $imageTags)';
}

@override
Expand All @@ -309,6 +335,8 @@ class _$ItemDTOImpl extends _ItemDTO {
other.productionYear == productionYear) &&
(identical(other.albumArtist, albumArtist) ||
other.albumArtist == albumArtist) &&
const DeepCollectionEquality()
.equals(other._albumArtists, _albumArtists) &&
const DeepCollectionEquality()
.equals(other._backgropImageTags, _backgropImageTags) &&
const DeepCollectionEquality()
Expand All @@ -327,6 +355,7 @@ class _$ItemDTOImpl extends _ItemDTO {
durationInTicks,
productionYear,
albumArtist,
const DeepCollectionEquality().hash(_albumArtists),
const DeepCollectionEquality().hash(_backgropImageTags),
const DeepCollectionEquality().hash(_imageTags));

Expand Down Expand Up @@ -354,6 +383,7 @@ abstract class _ItemDTO extends ItemDTO {
@JsonKey(name: 'RunTimeTicks') required final int? durationInTicks,
@JsonKey(name: 'ProductionYear') final int? productionYear,
@JsonKey(name: 'AlbumArtist') final String? albumArtist,
@JsonKey(name: 'AlbumArtists') final List<ArtistDTO> albumArtists,
@JsonKey(name: 'BackdropImageTags') final List<String> backgropImageTags,
@JsonKey(name: 'ImageTags')
final Map<String, String> imageTags}) = _$ItemDTOImpl;
Expand Down Expand Up @@ -386,6 +416,9 @@ abstract class _ItemDTO extends ItemDTO {
@JsonKey(name: 'AlbumArtist')
String? get albumArtist;
@override
@JsonKey(name: 'AlbumArtists')
List<ArtistDTO> get albumArtists;
@override
@JsonKey(name: 'BackdropImageTags')
List<String> get backgropImageTags;
@override
Expand Down
5 changes: 5 additions & 0 deletions lib/src/data/dto/item/item_dto.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 15 additions & 33 deletions lib/src/domain/providers/playback_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ class PlaybackNotifier extends StateNotifier<PlaybackState> {
PlaybackNotifier(
this._ref,
this._audioPlayer,
) : super(PlaybackState(
status: PlaybackStatus.stopped,
position: Duration.zero,
cacheProgress: Duration.zero)) {
) : super(PlaybackState(status: PlaybackStatus.stopped, position: Duration.zero, cacheProgress: Duration.zero)) {
// Listen for song completion
_audioPlayer.positionStream.listen((position) {
state = PlaybackState(
Expand All @@ -48,24 +45,18 @@ class PlaybackNotifier extends StateNotifier<PlaybackState> {

_audioPlayer.playerStateStream.listen((playerState) {
if (playerState.processingState == ProcessingState.completed) {
state = PlaybackState(
status: PlaybackStatus.stopped,
position: Duration.zero,
cacheProgress: Duration.zero,
totalDuration: Duration.zero);
print(_audioPlayer.sequenceState?.currentIndex);
print('PlayerState: Completed');
_audioPlayer.stop();
_audioPlayer.setAudioSource(_audioPlayer.audioSource!, initialIndex: 0);
state = PlaybackState(status: PlaybackStatus.stopped, position: Duration.zero, cacheProgress: Duration.zero, totalDuration: Duration.zero);
_audioPlayer
..stop()
..setAudioSource(_audioPlayer.audioSource!, initialIndex: 0);
}
// Handle other player states as needed
});
}
final AudioPlayer _audioPlayer;
final StateNotifierProviderRef<PlaybackNotifier, PlaybackState> _ref;

Future<void> play(
SongDTO playSong, List<SongDTO> songs, ItemDTO album) async {
Future<void> play(SongDTO playSong, List<SongDTO> songs, ItemDTO album) async {
try {
final domainUri = Uri.parse(_ref.read(baseUrlProvider)!);

Expand All @@ -85,8 +76,7 @@ class PlaybackNotifier extends StateNotifier<PlaybackState> {
'TranscodingProtocol': 'http',
'TranscodingContainer': 'm4a',
'AudioCodec': 'm4a',
'Container':
'mp3,aac,m4a|aac,m4b|aac,flac,alac,m4a|alac,m4b|alac,wav,m4a,aiff,aif',
'Container': 'mp3,aac,m4a|aac,m4b|aac,flac,alac,m4a|alac,m4b|alac,wav,m4a,aiff,aif',
},
),
tag: MediaItem(
Expand All @@ -101,19 +91,16 @@ class PlaybackNotifier extends StateNotifier<PlaybackState> {
displaySubtitle: song.albumName,
displayDescription: song.albumArtist,
artUri: song.imageTags['Primary'] != null
? Uri.parse(_ref.read(imageProvider).imagePath(
tagId: song.imageTags['Primary']!, id: song.id))
? Uri.parse(_ref.read(imageProvider).imagePath(tagId: song.imageTags['Primary']!, id: song.id))
: album.imageTags['Primary'] != null
? Uri.parse(_ref.read(imageProvider).imagePath(
tagId: album.imageTags['Primary']!, id: album.id))
? Uri.parse(_ref.read(imageProvider).imagePath(tagId: album.imageTags['Primary']!, id: album.id))
: null,
),
),
],
);

await _audioPlayer.setAudioSource(playlist,
initialIndex: songs.indexOf(playSong), preload: false);
await _audioPlayer.setAudioSource(playlist, initialIndex: songs.indexOf(playSong), preload: false);
unawaited(_audioPlayer.play());
state = PlaybackState(
status: PlaybackStatus.playing,
Expand Down Expand Up @@ -165,13 +152,11 @@ class PlaybackNotifier extends StateNotifier<PlaybackState> {
}

Future<void> resume() async {
if ((state.status == PlaybackStatus.stopped) &&
state.totalDuration?.inSeconds == 0) {
if ((state.status == PlaybackStatus.stopped) && state.totalDuration?.inSeconds == 0) {
final queue = _ref.read(audioQueueProvider.notifier);
// Case when song has finished but user clicks on play(resume) button. In this case we want to restart playback from first song.
if (queue.state.songs.isNotEmpty) {
await play(
queue.state.songs.first, queue.state.songs, queue.state.album!);
await play(queue.state.songs.first, queue.state.songs, queue.state.album!);
}

return;
Expand Down Expand Up @@ -229,14 +214,12 @@ class PlaybackNotifier extends StateNotifier<PlaybackState> {

if (currentSong == null) return;

final currentIndex =
songs.indexOf(songs.firstWhere((element) => element == currentSong));
final currentIndex = songs.indexOf(songs.firstWhere((element) => element == currentSong));
if (currentIndex != -1 && currentIndex + 1 < songs.length) {
// There's a next song in the queue
final nextSong = songs[currentIndex + 1];
_ref.read(audioQueueProvider.notifier).setCurrentSong(nextSong);
unawaited(play(
nextSong, songs, queueState.album!)); // Start playing the next song
unawaited(play(nextSong, songs, queueState.album!)); // Start playing the next song
} else {
await _ref.read(playerProvider).stop();
state = PlaybackState(
Expand All @@ -257,5 +240,4 @@ class PlaybackNotifier extends StateNotifier<PlaybackState> {
}
}

final playbackProvider = StateNotifierProvider<PlaybackNotifier, PlaybackState>(
(ref) => PlaybackNotifier(ref, ref.read(playerProvider)));
final playbackProvider = StateNotifierProvider<PlaybackNotifier, PlaybackState>((ref) => PlaybackNotifier(ref, ref.read(playerProvider)));
24 changes: 23 additions & 1 deletion lib/src/presentation/pages/album_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:jplayer/resources/j_player_icons.dart';
import 'package:jplayer/src/config/routes.dart';
import 'package:jplayer/src/data/dto/item/item_dto.dart';
import 'package:jplayer/src/data/dto/songs/songs_dto.dart';
import 'package:jplayer/src/data/providers/jellyfin_api_provider.dart';
Expand All @@ -12,6 +14,7 @@ import 'package:jplayer/src/domain/providers/current_user_provider.dart';
import 'package:jplayer/src/domain/providers/playback_provider.dart';
import 'package:jplayer/src/domain/providers/playlists_provider.dart';
import 'package:jplayer/src/presentation/utils/utils.dart';
import 'package:jplayer/src/presentation/widgets/clickable_widget.dart';
import 'package:jplayer/src/presentation/widgets/random_queue_button.dart';
import 'package:jplayer/src/presentation/widgets/widgets.dart';
import 'package:jplayer/src/providers/base_url_provider.dart';
Expand Down Expand Up @@ -357,7 +360,26 @@ class _AlbumPageState extends ConsumerState<AlbumPage> {
),
],
),
Text(widget.album.albumArtist ?? ''),
// Text(widget.album.albumArtist ?? ''),
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Row(
children: widget.album.albumArtists.map((a) {
return ClickableWidget(
onPressed: () async {
final item = await ref.read(jellyfinApiProvider).getItem(itemId: a.id);
if (!context.mounted) return;
context.go(
'${Routes.listen}${Routes.artist}',
extra: {'artist': item.data},
);
},
textStyle: const TextStyle(fontSize: 15, fontWeight: FontWeight.w300),
child: Text(a.name),
);
}).toList(),
),
),
Row(
children: [
_albumDetails(
Expand Down
Loading

0 comments on commit d601801

Please sign in to comment.