smart list playback

This commit is contained in:
Moritz Weber 2021-09-06 21:00:36 +02:00
parent 33223d0069
commit ebc4381216
9 changed files with 140 additions and 68 deletions

View file

@ -1,7 +1,6 @@
import 'package:mucke/domain/entities/smart_list.dart';
import '../entities/album.dart'; import '../entities/album.dart';
import '../entities/artist.dart'; import '../entities/artist.dart';
import '../entities/smart_list.dart';
import '../entities/song.dart'; import '../entities/song.dart';
abstract class MusicDataInfoRepository { abstract class MusicDataInfoRepository {

View file

@ -0,0 +1,20 @@
import '../entities/smart_list.dart';
import '../repositories/music_data_repository.dart';
import 'play_songs.dart';
class PlaySmartList {
PlaySmartList(
this._musicDataRepository,
this._playSongs,
);
final PlaySongs _playSongs;
final MusicDataRepository _musicDataRepository;
Future<void> call(SmartList smartList) async {
final songs = await _musicDataRepository.getSmartListSongStream(smartList).first;
_playSongs(songs: songs, initialIndex: 0);
}
}

View file

@ -26,6 +26,7 @@ import 'domain/usecases/play.dart';
import 'domain/usecases/play_album.dart'; import 'domain/usecases/play_album.dart';
import 'domain/usecases/play_artist.dart'; import 'domain/usecases/play_artist.dart';
import 'domain/usecases/play_next.dart'; import 'domain/usecases/play_next.dart';
import 'domain/usecases/play_smart_list.dart';
import 'domain/usecases/play_songs.dart'; import 'domain/usecases/play_songs.dart';
import 'domain/usecases/remove_queue_index.dart'; import 'domain/usecases/remove_queue_index.dart';
import 'domain/usecases/reset_like_count.dart'; import 'domain/usecases/reset_like_count.dart';
@ -92,6 +93,7 @@ Future<void> setupGetIt() async {
playAlbum: getIt(), playAlbum: getIt(),
playArtist: getIt(), playArtist: getIt(),
playNext: getIt(), playNext: getIt(),
playSmartList: getIt(),
playSongs: getIt(), playSongs: getIt(),
removeQueueIndex: getIt(), removeQueueIndex: getIt(),
seekToIndex: getIt(), seekToIndex: getIt(),
@ -120,10 +122,12 @@ Future<void> setupGetIt() async {
() => SettingsStore(settingsRepository: getIt()), () => SettingsStore(settingsRepository: getIt()),
); );
getIt.registerFactoryParam<SmartListFormStore, SmartList, void>( getIt.registerFactoryParam<SmartListFormStore, SmartList, void>(
(SmartList? smartList, _) => SmartListFormStore(settingsRepository: getIt(), smartList: smartList), (SmartList? smartList, _) =>
SmartListFormStore(settingsRepository: getIt(), smartList: smartList),
); );
getIt.registerFactoryParam<SmartListPageStore, SmartList, void>( getIt.registerFactoryParam<SmartListPageStore, SmartList, void>(
(SmartList? smartList, _) => SmartListPageStore(smartList: smartList!, musicDataInfoRepository: getIt()), (SmartList? smartList, _) =>
SmartListPageStore(smartList: smartList!, musicDataInfoRepository: getIt()),
); );
// use cases // use cases
@ -183,6 +187,12 @@ Future<void> setupGetIt() async {
getIt(), getIt(),
), ),
); );
getIt.registerLazySingleton<PlaySmartList>(
() => PlaySmartList(
getIt(),
getIt(),
),
);
getIt.registerLazySingleton<PlayNext>( getIt.registerLazySingleton<PlayNext>(
() => PlayNext( () => PlayNext(
getIt(), getIt(),

View file

@ -161,7 +161,7 @@ class CurrentlyPlayingPage extends StatelessWidget {
final song = audioStore.currentSongStream.value; final song = audioStore.currentSongStream.value;
if (song == null) if (song == null)
return; return;
// EXPLORATORY // TODO: EXPLORATORY
final albums = await musicDataStore.albumStream.first; final albums = await musicDataStore.albumStream.first;
final album = albums.singleWhere((a) => a.title == song.album); final album = albums.singleWhere((a) => a.title == song.album);

View file

@ -42,6 +42,7 @@ class _HomePageState extends State<HomePage> {
slivers: [ slivers: [
SliverList( SliverList(
delegate: SliverChildListDelegate([ delegate: SliverChildListDelegate([
const SizedBox(height: 12.0),
const Padding( const Padding(
padding: EdgeInsets.symmetric(horizontal: HORIZONTAL_PADDING), padding: EdgeInsets.symmetric(horizontal: HORIZONTAL_PADDING),
child: Highlight(), child: Highlight(),

View file

@ -5,6 +5,7 @@ import '../../domain/entities/artist.dart';
import '../../domain/entities/loop_mode.dart'; import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/shuffle_mode.dart'; import '../../domain/entities/shuffle_mode.dart';
import '../../domain/entities/song.dart'; import '../../domain/entities/song.dart';
import '../../domain/entities/smart_list.dart';
import '../../domain/repositories/audio_player_repository.dart'; import '../../domain/repositories/audio_player_repository.dart';
import '../../domain/usecases/add_to_queue.dart'; import '../../domain/usecases/add_to_queue.dart';
import '../../domain/usecases/move_queue_item.dart'; import '../../domain/usecases/move_queue_item.dart';
@ -13,6 +14,7 @@ import '../../domain/usecases/play.dart';
import '../../domain/usecases/play_album.dart'; import '../../domain/usecases/play_album.dart';
import '../../domain/usecases/play_artist.dart'; import '../../domain/usecases/play_artist.dart';
import '../../domain/usecases/play_next.dart'; import '../../domain/usecases/play_next.dart';
import '../../domain/usecases/play_smart_list.dart';
import '../../domain/usecases/play_songs.dart'; import '../../domain/usecases/play_songs.dart';
import '../../domain/usecases/remove_queue_index.dart'; import '../../domain/usecases/remove_queue_index.dart';
import '../../domain/usecases/seek_to_index.dart'; import '../../domain/usecases/seek_to_index.dart';
@ -34,6 +36,7 @@ class AudioStore extends _AudioStore with _$AudioStore {
required PlayArtist playArtist, required PlayArtist playArtist,
required PlayNext playNext, required PlayNext playNext,
required PlaySongs playSongs, required PlaySongs playSongs,
required PlaySmartList playSmartList,
required RemoveQueueIndex removeQueueIndex, required RemoveQueueIndex removeQueueIndex,
required SeekToIndex seekToIndex, required SeekToIndex seekToIndex,
required SeekToNext seekToNext, required SeekToNext seekToNext,
@ -52,6 +55,7 @@ class AudioStore extends _AudioStore with _$AudioStore {
playAlbum, playAlbum,
playArtist, playArtist,
playNext, playNext,
playSmartList,
removeQueueIndex, removeQueueIndex,
seekToIndex, seekToIndex,
seekToNext, seekToNext,
@ -73,6 +77,7 @@ abstract class _AudioStore with Store {
this._playAlbum, this._playAlbum,
this._playArtist, this._playArtist,
this._playNext, this._playNext,
this._playSmartList,
this._removeQueueIndex, this._removeQueueIndex,
this._seekToIndex, this._seekToIndex,
this._seekToNext, this._seekToNext,
@ -90,6 +95,7 @@ abstract class _AudioStore with Store {
final Play _play; final Play _play;
final PlayAlbum _playAlbum; final PlayAlbum _playAlbum;
final PlayArtist _playArtist; final PlayArtist _playArtist;
final PlaySmartList _playSmartList;
final PlayNext _playNext; final PlayNext _playNext;
final PlaySongs _playSongs; final PlaySongs _playSongs;
final RemoveQueueIndex _removeQueueIndex; final RemoveQueueIndex _removeQueueIndex;
@ -186,6 +192,10 @@ abstract class _AudioStore with Store {
_playAlbum(album); _playAlbum(album);
} }
Future<void> playSmartList(SmartList smartList) async {
_playSmartList(smartList);
}
Future<void> shuffleArtist(Artist artist) async { Future<void> shuffleArtist(Artist artist) async {
_playArtist(artist); _playArtist(artist);
} }

View file

@ -32,69 +32,64 @@ class Highlight extends StatelessWidget {
), ),
child: Container( child: Container(
color: Colors.transparent, color: Colors.transparent,
child: Column( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Row( Expanded(
crossAxisAlignment: CrossAxisAlignment.center, flex: 10,
children: [ child: AspectRatio(
Expanded( aspectRatio: 1,
flex: 10, child: Container(
child: AspectRatio( clipBehavior: Clip.antiAlias,
aspectRatio: 1, decoration: BoxDecoration(
child: Container( borderRadius: BorderRadius.circular(1.0),
clipBehavior: Clip.antiAlias, ),
decoration: BoxDecoration( child: Image(
borderRadius: BorderRadius.circular(1.0), image: getAlbumImage(album.albumArtPath),
), fit: BoxFit.cover,
child: Image( ),
image: getAlbumImage(album.albumArtPath), ),
fit: BoxFit.cover, ),
), ),
Expanded(
flex: 23,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Album of the Day'.toUpperCase(),
style: TEXT_SMALL_HEADLINE,
), ),
), Container(height: 6.0),
), Text(
Expanded( album.title,
flex: 23, style: Theme.of(context).textTheme.headline4,
child: Padding( maxLines: 2,
padding: const EdgeInsets.symmetric(horizontal: 8.0), overflow: TextOverflow.ellipsis,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Album of the Day'.toUpperCase(),
style: TEXT_SMALL_HEADLINE,
),
Container(height: 6.0),
Text(
album.title,
style: Theme.of(context).textTheme.headline4,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
Text(
album.artist,
style: TEXT_SMALL_SUBTITLE,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
), ),
), Text(
album.artist,
style: TEXT_SMALL_SUBTITLE,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
), ),
IconButton( ),
padding: EdgeInsets.zero, ),
icon: const Icon( IconButton(
Icons.play_circle_fill_rounded, padding: EdgeInsets.zero,
size: 48.0, icon: const Icon(
), Icons.play_circle_fill_rounded,
iconSize: 48.0, size: 48.0,
onPressed: () => audioStore.playAlbum(album), ),
), iconSize: 48.0,
], onPressed: () => audioStore.playAlbum(album),
) ),
], ],
), ),
), ),

View file

@ -4,14 +4,17 @@ import 'package:get_it/get_it.dart';
import '../../domain/entities/smart_list.dart'; import '../../domain/entities/smart_list.dart';
import '../pages/smart_list_page.dart'; import '../pages/smart_list_page.dart';
import '../state/audio_store.dart';
import '../state/navigation_store.dart'; import '../state/navigation_store.dart';
import '../state/settings_store.dart'; import '../state/settings_store.dart';
import '../theming.dart';
class SmartLists extends StatelessWidget { class SmartLists extends StatelessWidget {
const SmartLists({Key? key}) : super(key: key); const SmartLists({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final AudioStore audioStore = GetIt.I<AudioStore>();
final NavigationStore navStore = GetIt.I<NavigationStore>(); final NavigationStore navStore = GetIt.I<NavigationStore>();
final SettingsStore settingsStore = GetIt.I<SettingsStore>(); final SettingsStore settingsStore = GetIt.I<SettingsStore>();
@ -22,14 +25,48 @@ class SmartLists extends StatelessWidget {
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(_, int index) { (_, int index) {
final SmartList smartList = smartLists[index]; final SmartList smartList = smartLists[index];
return ListTile( return GestureDetector(
title: Text(smartList.name),
onTap: () => navStore.push( onTap: () => navStore.push(
context, context,
MaterialPageRoute<Widget>( MaterialPageRoute<Widget>(
builder: (context) => SmartListPage(smartList: smartList), builder: (context) => SmartListPage(smartList: smartList),
), ),
), ),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: HORIZONTAL_PADDING,
vertical: 6.0,
),
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4.0), color: Colors.white10),
child: Padding(
padding: const EdgeInsets.only(left: HORIZONTAL_PADDING, top: 4.0, bottom: 4.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Text(
smartList.name,
style: Theme.of(context).textTheme.headline4,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8.0),
IconButton(
padding: EdgeInsets.zero,
icon: const Icon(
Icons.play_circle_fill_rounded,
size: 40.0,
),
iconSize: 48.0,
onPressed: () => audioStore.playSmartList(smartList),
),
],
),
),
),
),
); );
}, },
childCount: smartLists.length, childCount: smartLists.length,

View file

@ -1,19 +1,19 @@
import 'dart:math'; import 'dart:math';
import 'package:fimber/fimber.dart'; import 'package:fimber/fimber.dart';
import 'package:mucke/domain/entities/smart_list.dart';
import 'package:mucke/system/models/smart_list_model.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:string_similarity/string_similarity.dart'; import 'package:string_similarity/string_similarity.dart';
import '../../domain/entities/album.dart'; import '../../domain/entities/album.dart';
import '../../domain/entities/artist.dart'; import '../../domain/entities/artist.dart';
import '../../domain/entities/smart_list.dart';
import '../../domain/entities/song.dart'; import '../../domain/entities/song.dart';
import '../../domain/repositories/music_data_repository.dart'; import '../../domain/repositories/music_data_repository.dart';
import '../datasources/local_music_fetcher.dart'; import '../datasources/local_music_fetcher.dart';
import '../datasources/music_data_source_contract.dart'; import '../datasources/music_data_source_contract.dart';
import '../models/album_model.dart'; import '../models/album_model.dart';
import '../models/artist_model.dart'; import '../models/artist_model.dart';
import '../models/smart_list_model.dart';
import '../models/song_model.dart'; import '../models/song_model.dart';
class MusicDataRepositoryImpl implements MusicDataRepository { class MusicDataRepositoryImpl implements MusicDataRepository {