diff --git a/lib/domain/repositories/music_data_repository.dart b/lib/domain/repositories/music_data_repository.dart index 33ed718..8e4b923 100644 --- a/lib/domain/repositories/music_data_repository.dart +++ b/lib/domain/repositories/music_data_repository.dart @@ -3,17 +3,12 @@ import 'package:dartz/dartz.dart'; import '../../core/error/failures.dart'; import '../entities/album.dart'; import '../entities/artist.dart'; -import '../entities/loop_mode.dart'; import '../entities/song.dart'; abstract class MusicDataRepository { Stream> get songStream; Stream> getAlbumSongStream(Album album); - Stream> get queueStream; - Stream get currentIndexStream; - Stream get loopModeStream; - Future>> getSongs(); Future>> getSongsFromAlbum(Album album); Future>> getAlbums(); @@ -22,4 +17,4 @@ abstract class MusicDataRepository { Future setSongBlocked(Song song, bool blocked); Future toggleNextSongLink(Song song); -} \ No newline at end of file +} diff --git a/lib/domain/repositories/persistent_player_state_repository.dart b/lib/domain/repositories/persistent_player_state_repository.dart new file mode 100644 index 0000000..535edba --- /dev/null +++ b/lib/domain/repositories/persistent_player_state_repository.dart @@ -0,0 +1,8 @@ +import '../entities/loop_mode.dart'; +import '../entities/song.dart'; + +abstract class PlayerStateRepository { + Stream> get queueStream; + Stream get currentIndexStream; + Stream get loopModeStream; +} diff --git a/lib/injection_container.dart b/lib/injection_container.dart index 3ba60cd..3029885 100644 --- a/lib/injection_container.dart +++ b/lib/injection_container.dart @@ -6,6 +6,7 @@ import 'package:just_audio/just_audio.dart' as ja; import 'domain/repositories/audio_repository.dart'; import 'domain/repositories/music_data_repository.dart'; +import 'domain/repositories/persistent_player_state_repository.dart'; import 'presentation/state/audio_store.dart'; import 'presentation/state/music_data_store.dart'; import 'presentation/state/navigation_store.dart'; @@ -19,8 +20,10 @@ import 'system/datasources/local_music_fetcher.dart'; import 'system/datasources/local_music_fetcher_contract.dart'; import 'system/datasources/moor_music_data_source.dart'; import 'system/datasources/music_data_source_contract.dart'; +import 'system/datasources/player_state_data_source.dart'; import 'system/repositories/audio_repository_impl.dart'; import 'system/repositories/music_data_repository_impl.dart'; +import 'system/repositories/persistent_player_state_repository_impl.dart'; final GetIt getIt = GetIt.instance; @@ -41,7 +44,7 @@ Future setupGetIt() async { () { final audioStore = AudioStore( audioRepository: getIt(), - musicDataRepository: getIt(), + persistentPlayerStateRepository: getIt(), ); return audioStore; }, @@ -65,10 +68,14 @@ Future setupGetIt() async { getIt(), ), ); + getIt.registerLazySingleton( + () => PlayerStateRepositoryImpl(getIt()), + ); // data sources final MoorMusicDataSource moorMusicDataSource = MoorMusicDataSource(); getIt.registerLazySingleton(() => moorMusicDataSource); + getIt.registerLazySingleton(() => moorMusicDataSource.playerStateDao); getIt.registerLazySingleton( () => LocalMusicFetcherImpl( getIt(), @@ -84,7 +91,7 @@ Future setupGetIt() async { getIt.registerLazySingleton(() => audioPlayer); final _audioHandler = await AudioService.init( - builder: () => MyAudioHandler(getIt(), getIt()), + builder: () => MyAudioHandler(getIt(), getIt(), getIt()), config: AudioServiceConfig( androidNotificationChannelName: 'mucke', androidEnableQueue: true, diff --git a/lib/presentation/state/audio_store.dart b/lib/presentation/state/audio_store.dart index d49fb06..cadf743 100644 --- a/lib/presentation/state/audio_store.dart +++ b/lib/presentation/state/audio_store.dart @@ -6,38 +6,39 @@ import '../../domain/entities/playback_state.dart'; import '../../domain/entities/shuffle_mode.dart'; import '../../domain/entities/song.dart'; import '../../domain/repositories/audio_repository.dart'; -import '../../domain/repositories/music_data_repository.dart'; +import '../../domain/repositories/persistent_player_state_repository.dart'; part 'audio_store.g.dart'; class AudioStore extends _AudioStore with _$AudioStore { AudioStore({ @required AudioRepository audioRepository, - @required MusicDataRepository musicDataRepository, - }) : super(audioRepository, musicDataRepository); + @required PlayerStateRepository persistentPlayerStateRepository, + }) : super(audioRepository, persistentPlayerStateRepository); } abstract class _AudioStore with Store { - _AudioStore(this._audioRepository, this._musicDataRepository) { + _AudioStore( + this._audioRepository, this._persistentPlayerStateRepository) { currentSongStream = _audioRepository.currentSongStream.distinct().asObservable(); currentPositionStream = _audioRepository.currentPositionStream.asObservable(initialValue: 0); - queueStream = _musicDataRepository.queueStream.asObservable(); + queueStream = _persistentPlayerStateRepository.queueStream.asObservable(); - queueIndexStream = _musicDataRepository.currentIndexStream.asObservable(); + queueIndexStream = _persistentPlayerStateRepository.currentIndexStream.asObservable(); // queueIndexStream = _audioRepository.queueIndexStream.asObservable(); shuffleModeStream = _audioRepository.shuffleModeStream.asObservable(initialValue: ShuffleMode.none); - loopModeStream = _musicDataRepository.loopModeStream.asObservable(); + loopModeStream = _persistentPlayerStateRepository.loopModeStream.asObservable(); playbackStateStream = _audioRepository.playbackStateStream.asObservable(); } final AudioRepository _audioRepository; - final MusicDataRepository _musicDataRepository; + final PlayerStateRepository _persistentPlayerStateRepository; @observable ObservableStream currentSongStream; @@ -48,7 +49,7 @@ abstract class _AudioStore with Store { print(queueStream.value); print(queueIndexStream.value); - if (queueStream.value != null && queueIndexStream.value != null) { + if (queueStream.value != null && queueIndexStream.value != null) { if (queueIndexStream.value < queueStream.value.length) { final song = queueStream.value[queueIndexStream.value]; return song; diff --git a/lib/system/audio/audio_handler.dart b/lib/system/audio/audio_handler.dart index e8f0205..386ea07 100644 --- a/lib/system/audio/audio_handler.dart +++ b/lib/system/audio/audio_handler.dart @@ -7,6 +7,7 @@ import '../../domain/entities/loop_mode.dart'; import '../../domain/entities/playback_event.dart'; import '../../domain/entities/shuffle_mode.dart'; import '../datasources/music_data_source_contract.dart'; +import '../datasources/player_state_data_source.dart'; import '../models/playback_event_model.dart'; import '../models/queue_item_model.dart'; import '../models/song_model.dart'; @@ -14,7 +15,7 @@ import 'audio_player_contract.dart'; import 'stream_constants.dart'; class MyAudioHandler extends BaseAudioHandler { - MyAudioHandler(this._musicDataSource, this._audioPlayer) { + MyAudioHandler(this._musicDataSource, this._audioPlayer, this._playerStateDataSource) { _audioPlayer.queueStream.listen((event) { _handleSetQueue(event); }); @@ -28,36 +29,37 @@ class MyAudioHandler extends BaseAudioHandler { }); _audioPlayer.shuffleModeStream.listen((shuffleMode) { - _musicDataSource.setShuffleMode(shuffleMode); + _playerStateDataSource.setShuffleMode(shuffleMode); customEventSubject.add({SHUFFLE_MODE: shuffleMode}); }); _audioPlayer.loopModeStream.listen((event) { - _musicDataSource.setLoopMode(event); + _playerStateDataSource.setLoopMode(event); }); _initAudioPlayer(); } Future _initAudioPlayer() async { - if (_musicDataSource.loopModeStream != null) { - _audioPlayer.setLoopMode(await _musicDataSource.loopModeStream.first); + if (_playerStateDataSource.loopModeStream != null) { + _audioPlayer.setLoopMode(await _playerStateDataSource.loopModeStream.first); } - if (_musicDataSource.shuffleModeStream != null) { - _audioPlayer.setShuffleMode(await _musicDataSource.shuffleModeStream.first, false); + if (_playerStateDataSource.shuffleModeStream != null) { + _audioPlayer.setShuffleMode(await _playerStateDataSource.shuffleModeStream.first, false); } - if (_musicDataSource.queueStream != null && _musicDataSource.currentIndexStream != null) { + if (_playerStateDataSource.queueStream != null && _playerStateDataSource.currentIndexStream != null) { _audioPlayer.loadQueue( - queue: await _musicDataSource.queueStream.first, - startIndex: await _musicDataSource.currentIndexStream.first, + queue: await _playerStateDataSource.queueStream.first, + startIndex: await _playerStateDataSource.currentIndexStream.first, ); } } final AudioPlayer _audioPlayer; final MusicDataSource _musicDataSource; + final PlayerStateDataSource _playerStateDataSource; static final _log = Logger('AudioHandler'); @@ -158,7 +160,7 @@ class MyAudioHandler extends BaseAudioHandler { } void _handleSetQueue(List queue) { - _musicDataSource.setQueue(queue); + _playerStateDataSource.setQueue(queue); final mediaItems = queue.map((e) => e.song.toMediaItem()).toList(); queueSubject.add(mediaItems); @@ -166,7 +168,7 @@ class MyAudioHandler extends BaseAudioHandler { void _handlePlaybackEvent(PlaybackEventModel pe) { if (pe.index != null) { - _musicDataSource.setCurrentIndex(pe.index); + _playerStateDataSource.setCurrentIndex(pe.index); customEventSubject.add({KEY_INDEX: pe.index}); } diff --git a/lib/system/datasources/moor/player_state_dao.dart b/lib/system/datasources/moor/player_state_dao.dart new file mode 100644 index 0000000..f713d79 --- /dev/null +++ b/lib/system/datasources/moor/player_state_dao.dart @@ -0,0 +1,107 @@ +import 'package:moor/moor.dart'; + +import '../../../domain/entities/loop_mode.dart'; +import '../../../domain/entities/shuffle_mode.dart'; +import '../../models/loop_mode_model.dart'; +import '../../models/queue_item_model.dart'; +import '../../models/shuffle_mode_model.dart'; +import '../../models/song_model.dart'; +import '../moor_music_data_source.dart'; +import '../player_state_data_source.dart'; + +part 'player_state_dao.g.dart'; + +@UseDao(tables: [Songs, QueueEntries, PlayerState]) +class PlayerStateDao extends DatabaseAccessor with _$PlayerStateDaoMixin implements PlayerStateDataSource { + PlayerStateDao(MoorMusicDataSource db) : super(db); + + @override + Stream> get songQueueStream { + final query = (select(queueEntries)..orderBy([(t) => OrderingTerm(expression: t.index)])) + .join([innerJoin(songs, songs.path.equalsExp(queueEntries.path))]); + + return query.watch().map((rows) { + return rows.map((row) => SongModel.fromMoorSong(row.readTable(songs))).toList(); + }); + } + + @override + Stream> get queueStream { + final query = (select(queueEntries)..orderBy([(t) => OrderingTerm(expression: t.index)])) + .join([innerJoin(songs, songs.path.equalsExp(queueEntries.path))]); + + return query.watch().map((rows) { + return rows.map((row) { + return QueueItemModel( + SongModel.fromMoorSong(row.readTable(songs)), + originalIndex: row.readTable(queueEntries).originalIndex, + type: row.readTable(queueEntries).type.toQueueItemType(), + ); + }).toList(); + }); + } + + @override + Future setQueue(List queue) async { + final _queueEntries = >[]; + + for (var i = 0; i < queue.length; i++) { + _queueEntries.add(QueueEntriesCompanion( + index: Value(i), + path: Value(queue[i].song.path), + originalIndex: Value(queue[i].originalIndex), + type: Value(queue[i].type.toInt()), + )); + } + + await delete(queueEntries).go(); + await batch((batch) { + batch.insertAll(queueEntries, _queueEntries); + }); + } + + @override + Stream get currentIndexStream { + return select(playerState).watchSingle().map((event) => event.index); + } + + @override + Future setCurrentIndex(int index) async { + final currentState = await select(playerState).getSingle(); + if (currentState != null) { + update(playerState).write(PlayerStateCompanion(index: Value(index))); + } else { + into(playerState).insert(PlayerStateCompanion(index: Value(index))); + } + } + + @override + Stream get loopModeStream { + return select(playerState).watchSingle().map((event) => event.loopMode.toLoopMode()); + } + + @override + Future setLoopMode(LoopMode loopMode) async { + final currentState = await select(playerState).getSingle(); + if (currentState != null) { + update(playerState).write(PlayerStateCompanion(loopMode: Value(loopMode.toInt()))); + } else { + into(playerState).insert(PlayerStateCompanion(loopMode: Value(loopMode.toInt()))); + } + } + + @override + Future setShuffleMode(ShuffleMode shuffleMode) async { + final currentState = await select(playerState).getSingle(); + if (currentState != null) { + update(playerState).write(PlayerStateCompanion(shuffleMode: Value(shuffleMode.toInt()))); + } else { + into(playerState).insert(PlayerStateCompanion(shuffleMode: Value(shuffleMode.toInt()))); + } + } + + @override + Stream get shuffleModeStream { + return select(playerState).watchSingle().map((event) => event.shuffleMode.toShuffleMode()); + } +} \ No newline at end of file diff --git a/lib/system/datasources/moor/player_state_dao.g.dart b/lib/system/datasources/moor/player_state_dao.g.dart new file mode 100644 index 0000000..2aaac67 --- /dev/null +++ b/lib/system/datasources/moor/player_state_dao.g.dart @@ -0,0 +1,13 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'player_state_dao.dart'; + +// ************************************************************************** +// DaoGenerator +// ************************************************************************** + +mixin _$PlayerStateDaoMixin on DatabaseAccessor { + $SongsTable get songs => attachedDatabase.songs; + $QueueEntriesTable get queueEntries => attachedDatabase.queueEntries; + $PlayerStateTable get playerState => attachedDatabase.playerState; +} diff --git a/lib/system/datasources/moor_music_data_source.dart b/lib/system/datasources/moor_music_data_source.dart index 79b6f6a..56443f1 100644 --- a/lib/system/datasources/moor_music_data_source.dart +++ b/lib/system/datasources/moor_music_data_source.dart @@ -7,14 +7,10 @@ import 'package:moor/moor.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; -import '../../domain/entities/loop_mode.dart'; -import '../../domain/entities/shuffle_mode.dart'; import '../models/album_model.dart'; import '../models/artist_model.dart'; -import '../models/loop_mode_model.dart'; -import '../models/queue_item_model.dart'; -import '../models/shuffle_mode_model.dart'; import '../models/song_model.dart'; +import 'moor/player_state_dao.dart'; import 'music_data_source_contract.dart'; part 'moor_music_data_source.g.dart'; @@ -77,7 +73,7 @@ class PlayerState extends Table { IntColumn get loopMode => integer().withDefault(const Constant(0))(); } -@UseMoor(tables: [Artists, Albums, Songs, QueueEntries, PlayerState]) +@UseMoor(tables: [Artists, Albums, Songs, QueueEntries, PlayerState], daos: [PlayerStateDao]) class MoorMusicDataSource extends _$MoorMusicDataSource implements MusicDataSource { /// Use MoorMusicDataSource in main isolate only. MoorMusicDataSource() : super(_openConnection()); @@ -236,96 +232,6 @@ class MoorMusicDataSource extends _$MoorMusicDataSource implements MusicDataSour .write(const SongsCompanion(next: Value(null))); } } - - @override - Stream> get songQueueStream { - final query = (select(queueEntries)..orderBy([(t) => OrderingTerm(expression: t.index)])) - .join([innerJoin(songs, songs.path.equalsExp(queueEntries.path))]); - - return query.watch().map((rows) { - return rows.map((row) => SongModel.fromMoorSong(row.readTable(songs))).toList(); - }); - } - - @override - Stream> get queueStream { - final query = (select(queueEntries)..orderBy([(t) => OrderingTerm(expression: t.index)])) - .join([innerJoin(songs, songs.path.equalsExp(queueEntries.path))]); - - return query.watch().map((rows) { - return rows.map((row) { - return QueueItemModel( - SongModel.fromMoorSong(row.readTable(songs)), - originalIndex: row.readTable(queueEntries).originalIndex, - type: row.readTable(queueEntries).type.toQueueItemType(), - ); - }).toList(); - }); - } - - @override - Future setQueue(List queue) async { - final _queueEntries = >[]; - - for (var i = 0; i < queue.length; i++) { - _queueEntries.add(QueueEntriesCompanion( - index: Value(i), - path: Value(queue[i].song.path), - originalIndex: Value(queue[i].originalIndex), - type: Value(queue[i].type.toInt()), - )); - } - - await delete(queueEntries).go(); - await batch((batch) { - batch.insertAll(queueEntries, _queueEntries); - }); - } - - @override - Stream get currentIndexStream { - return select(playerState).watchSingle().map((event) => event.index); - } - - @override - Future setCurrentIndex(int index) async { - final currentState = await select(playerState).getSingle(); - if (currentState != null) { - update(playerState).write(PlayerStateCompanion(index: Value(index))); - } else { - into(playerState).insert(PlayerStateCompanion(index: Value(index))); - } - } - - @override - Stream get loopModeStream { - return select(playerState).watchSingle().map((event) => event.loopMode.toLoopMode()); - } - - @override - Future setLoopMode(LoopMode loopMode) async { - final currentState = await select(playerState).getSingle(); - if (currentState != null) { - update(playerState).write(PlayerStateCompanion(loopMode: Value(loopMode.toInt()))); - } else { - into(playerState).insert(PlayerStateCompanion(loopMode: Value(loopMode.toInt()))); - } - } - - @override - Future setShuffleMode(ShuffleMode shuffleMode) async { - final currentState = await select(playerState).getSingle(); - if (currentState != null) { - update(playerState).write(PlayerStateCompanion(shuffleMode: Value(shuffleMode.toInt()))); - } else { - into(playerState).insert(PlayerStateCompanion(shuffleMode: Value(shuffleMode.toInt()))); - } - } - - @override - Stream get shuffleModeStream { - return select(playerState).watchSingle().map((event) => event.shuffleMode.toShuffleMode()); - } } LazyDatabase _openConnection() { diff --git a/lib/system/datasources/moor_music_data_source.g.dart b/lib/system/datasources/moor_music_data_source.g.dart index 97c2c73..7e9ca39 100644 --- a/lib/system/datasources/moor_music_data_source.g.dart +++ b/lib/system/datasources/moor_music_data_source.g.dart @@ -1762,6 +1762,9 @@ abstract class _$MoorMusicDataSource extends GeneratedDatabase { _queueEntries ??= $QueueEntriesTable(this); $PlayerStateTable _playerState; $PlayerStateTable get playerState => _playerState ??= $PlayerStateTable(this); + PlayerStateDao _playerStateDao; + PlayerStateDao get playerStateDao => + _playerStateDao ??= PlayerStateDao(this as MoorMusicDataSource); @override Iterable get allTables => allSchemaEntities.whereType(); @override diff --git a/lib/system/datasources/music_data_source_contract.dart b/lib/system/datasources/music_data_source_contract.dart index 44288b9..f4f07a8 100644 --- a/lib/system/datasources/music_data_source_contract.dart +++ b/lib/system/datasources/music_data_source_contract.dart @@ -1,8 +1,5 @@ -import '../../domain/entities/loop_mode.dart'; -import '../../domain/entities/shuffle_mode.dart'; import '../models/album_model.dart'; import '../models/artist_model.dart'; -import '../models/queue_item_model.dart'; import '../models/song_model.dart'; abstract class MusicDataSource { @@ -11,16 +8,6 @@ abstract class MusicDataSource { Stream> get songStream; Stream> getAlbumSongStream(AlbumModel album); - Future setQueue(List queue); - Stream> get songQueueStream; - Stream> get queueStream; - Future setCurrentIndex(int index); - Stream get currentIndexStream; - Future setShuffleMode(ShuffleMode shuffleMode); - Stream get shuffleModeStream; - Future setLoopMode(LoopMode loopMode); - Stream get loopModeStream; - /// Insert album into the database. Return the ID of the inserted album. Future insertAlbum(AlbumModel albumModel); diff --git a/lib/system/datasources/player_state_data_source.dart b/lib/system/datasources/player_state_data_source.dart new file mode 100644 index 0000000..d0672bc --- /dev/null +++ b/lib/system/datasources/player_state_data_source.dart @@ -0,0 +1,19 @@ +import '../../domain/entities/loop_mode.dart'; +import '../../domain/entities/shuffle_mode.dart'; +import '../models/queue_item_model.dart'; +import '../models/song_model.dart'; + +abstract class PlayerStateDataSource { + Future setQueue(List queue); + Stream> get songQueueStream; + Stream> get queueStream; + + Future setCurrentIndex(int index); + Stream get currentIndexStream; + + Future setShuffleMode(ShuffleMode shuffleMode); + Stream get shuffleModeStream; + + Future setLoopMode(LoopMode loopMode); + Stream get loopModeStream; +} diff --git a/lib/system/repositories/music_data_repository_impl.dart b/lib/system/repositories/music_data_repository_impl.dart index 5928cd3..0d2937a 100644 --- a/lib/system/repositories/music_data_repository_impl.dart +++ b/lib/system/repositories/music_data_repository_impl.dart @@ -8,7 +8,6 @@ import 'package:path_provider/path_provider.dart'; import '../../core/error/failures.dart'; import '../../domain/entities/album.dart'; import '../../domain/entities/artist.dart'; -import '../../domain/entities/loop_mode.dart'; import '../../domain/entities/song.dart'; import '../../domain/repositories/music_data_repository.dart'; import '../datasources/local_music_fetcher_contract.dart'; @@ -134,13 +133,4 @@ class MusicDataRepositoryImpl implements MusicDataRepository { Future toggleNextSongLink(Song song) async { musicDataSource.toggleNextSongLink(song as SongModel); } - - @override - Stream> get queueStream => musicDataSource.songQueueStream; - - @override - Stream get currentIndexStream => musicDataSource.currentIndexStream; - - @override - Stream get loopModeStream => musicDataSource.loopModeStream; } diff --git a/lib/system/repositories/persistent_player_state_repository_impl.dart b/lib/system/repositories/persistent_player_state_repository_impl.dart new file mode 100644 index 0000000..02a814f --- /dev/null +++ b/lib/system/repositories/persistent_player_state_repository_impl.dart @@ -0,0 +1,19 @@ +import '../../domain/entities/loop_mode.dart'; +import '../../domain/entities/song.dart'; +import '../../domain/repositories/persistent_player_state_repository.dart'; +import '../datasources/player_state_data_source.dart'; + +class PlayerStateRepositoryImpl implements PlayerStateRepository { + PlayerStateRepositoryImpl(this._playerStateDataSource); + + final PlayerStateDataSource _playerStateDataSource; + + @override + Stream get currentIndexStream => _playerStateDataSource.currentIndexStream; + + @override + Stream get loopModeStream => _playerStateDataSource.loopModeStream; + + @override + Stream> get queueStream => _playerStateDataSource.songQueueStream; +}