new PlayerStateRepository

This commit is contained in:
Moritz Weber 2020-12-21 11:50:46 +01:00
parent ac143220e6
commit c9cdef237a
13 changed files with 205 additions and 148 deletions

View file

@ -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<List<Song>> get songStream;
Stream<List<Song>> getAlbumSongStream(Album album);
Stream<List<Song>> get queueStream;
Stream<int> get currentIndexStream;
Stream<LoopMode> get loopModeStream;
Future<Either<Failure, List<Song>>> getSongs();
Future<Either<Failure, List<Song>>> getSongsFromAlbum(Album album);
Future<Either<Failure, List<Album>>> getAlbums();
@ -22,4 +17,4 @@ abstract class MusicDataRepository {
Future<void> setSongBlocked(Song song, bool blocked);
Future<void> toggleNextSongLink(Song song);
}
}

View file

@ -0,0 +1,8 @@
import '../entities/loop_mode.dart';
import '../entities/song.dart';
abstract class PlayerStateRepository {
Stream<List<Song>> get queueStream;
Stream<int> get currentIndexStream;
Stream<LoopMode> get loopModeStream;
}

View file

@ -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<void> setupGetIt() async {
() {
final audioStore = AudioStore(
audioRepository: getIt(),
musicDataRepository: getIt(),
persistentPlayerStateRepository: getIt(),
);
return audioStore;
},
@ -65,10 +68,14 @@ Future<void> setupGetIt() async {
getIt(),
),
);
getIt.registerLazySingleton<PlayerStateRepository>(
() => PlayerStateRepositoryImpl(getIt()),
);
// data sources
final MoorMusicDataSource moorMusicDataSource = MoorMusicDataSource();
getIt.registerLazySingleton<MusicDataSource>(() => moorMusicDataSource);
getIt.registerLazySingleton<PlayerStateDataSource>(() => moorMusicDataSource.playerStateDao);
getIt.registerLazySingleton<LocalMusicFetcher>(
() => LocalMusicFetcherImpl(
getIt(),
@ -84,7 +91,7 @@ Future<void> setupGetIt() async {
getIt.registerLazySingleton<AudioPlayer>(() => audioPlayer);
final _audioHandler = await AudioService.init(
builder: () => MyAudioHandler(getIt(), getIt()),
builder: () => MyAudioHandler(getIt(), getIt(), getIt()),
config: AudioServiceConfig(
androidNotificationChannelName: 'mucke',
androidEnableQueue: true,

View file

@ -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<Song> 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;

View file

@ -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<void> _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<QueueItemModel> 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});
}

View file

@ -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<MoorMusicDataSource> with _$PlayerStateDaoMixin implements PlayerStateDataSource {
PlayerStateDao(MoorMusicDataSource db) : super(db);
@override
Stream<List<SongModel>> 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<List<QueueItemModel>> 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<void> setQueue(List<QueueItemModel> queue) async {
final _queueEntries = <Insertable<MoorQueueEntry>>[];
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<int> get currentIndexStream {
return select(playerState).watchSingle().map((event) => event.index);
}
@override
Future<void> 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<LoopMode> get loopModeStream {
return select(playerState).watchSingle().map((event) => event.loopMode.toLoopMode());
}
@override
Future<void> 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<void> 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<ShuffleMode> get shuffleModeStream {
return select(playerState).watchSingle().map((event) => event.shuffleMode.toShuffleMode());
}
}

View file

@ -0,0 +1,13 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'player_state_dao.dart';
// **************************************************************************
// DaoGenerator
// **************************************************************************
mixin _$PlayerStateDaoMixin on DatabaseAccessor<MoorMusicDataSource> {
$SongsTable get songs => attachedDatabase.songs;
$QueueEntriesTable get queueEntries => attachedDatabase.queueEntries;
$PlayerStateTable get playerState => attachedDatabase.playerState;
}

View file

@ -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<List<SongModel>> 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<List<QueueItemModel>> 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<void> setQueue(List<QueueItemModel> queue) async {
final _queueEntries = <Insertable<MoorQueueEntry>>[];
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<int> get currentIndexStream {
return select(playerState).watchSingle().map((event) => event.index);
}
@override
Future<void> 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<LoopMode> get loopModeStream {
return select(playerState).watchSingle().map((event) => event.loopMode.toLoopMode());
}
@override
Future<void> 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<void> 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<ShuffleMode> get shuffleModeStream {
return select(playerState).watchSingle().map((event) => event.shuffleMode.toShuffleMode());
}
}
LazyDatabase _openConnection() {

View file

@ -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<TableInfo> get allTables => allSchemaEntities.whereType<TableInfo>();
@override

View file

@ -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<List<SongModel>> get songStream;
Stream<List<SongModel>> getAlbumSongStream(AlbumModel album);
Future<void> setQueue(List<QueueItemModel> queue);
Stream<List<SongModel>> get songQueueStream;
Stream<List<QueueItemModel>> get queueStream;
Future<void> setCurrentIndex(int index);
Stream<int> get currentIndexStream;
Future<void> setShuffleMode(ShuffleMode shuffleMode);
Stream<ShuffleMode> get shuffleModeStream;
Future<void> setLoopMode(LoopMode loopMode);
Stream<LoopMode> get loopModeStream;
/// Insert album into the database. Return the ID of the inserted album.
Future<int> insertAlbum(AlbumModel albumModel);

View file

@ -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<void> setQueue(List<QueueItemModel> queue);
Stream<List<SongModel>> get songQueueStream;
Stream<List<QueueItemModel>> get queueStream;
Future<void> setCurrentIndex(int index);
Stream<int> get currentIndexStream;
Future<void> setShuffleMode(ShuffleMode shuffleMode);
Stream<ShuffleMode> get shuffleModeStream;
Future<void> setLoopMode(LoopMode loopMode);
Stream<LoopMode> get loopModeStream;
}

View file

@ -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<void> toggleNextSongLink(Song song) async {
musicDataSource.toggleNextSongLink(song as SongModel);
}
@override
Stream<List<Song>> get queueStream => musicDataSource.songQueueStream;
@override
Stream<int> get currentIndexStream => musicDataSource.currentIndexStream;
@override
Stream<LoopMode> get loopModeStream => musicDataSource.loopModeStream;
}

View file

@ -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<int> get currentIndexStream => _playerStateDataSource.currentIndexStream;
@override
Stream<LoopMode> get loopModeStream => _playerStateDataSource.loopModeStream;
@override
Stream<List<Song>> get queueStream => _playerStateDataSource.songQueueStream;
}