mucke/lib/system/repositories/audio_player_repository_impl.dart
2021-12-23 00:34:05 +01:00

277 lines
8.4 KiB
Dart

import 'package:flutter_fimber/flutter_fimber.dart';
import 'package:get_it/get_it.dart';
import 'package:mucke/domain/entities/playable.dart';
import 'package:rxdart/rxdart.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/playback_event.dart';
import '../../domain/entities/queue_item.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../../domain/entities/song.dart';
import '../../domain/modules/dynamic_queue_3.dart';
import '../../domain/repositories/audio_player_repository.dart';
import '../datasources/audio_player_data_source.dart';
import '../models/song_model.dart';
class AudioPlayerRepositoryImpl implements AudioPlayerRepository {
AudioPlayerRepositoryImpl(this._audioPlayerDataSource, this._dynamicQueue) {
_shuffleModeSubject.add(ShuffleMode.none);
_loopModeSubject.add(LoopMode.off);
_excludeBlockedSubject.add(true);
_excludeSkippedSubject.add(true);
_respectSongLinksSubject.add(true);
_audioPlayerDataSource.currentIndexStream.listen(
(index) {
_currentIndexSubject.add(index);
if (!blockIndexUpdate) {
_updateCurrentSong(queueStream.value, index);
}
},
);
_queueSubject.listen((queue) {
if (currentIndexStream.hasValue) {
_updateCurrentSong(queue, currentIndexStream.value);
}
});
}
static final _log = FimberLog('AudioPlayerRepositoryImpl');
final AudioPlayerDataSource _audioPlayerDataSource;
final DynamicQueue2 _dynamicQueue;
final BehaviorSubject<int> _currentIndexSubject = BehaviorSubject();
final BehaviorSubject<Song> _currentSongSubject = BehaviorSubject();
final BehaviorSubject<LoopMode> _loopModeSubject = BehaviorSubject();
final BehaviorSubject<List<Song>> _queueSubject = BehaviorSubject();
final BehaviorSubject<ShuffleMode> _shuffleModeSubject = BehaviorSubject();
final BehaviorSubject<bool> _excludeBlockedSubject = BehaviorSubject();
final BehaviorSubject<bool> _excludeSkippedSubject = BehaviorSubject();
final BehaviorSubject<bool> _respectSongLinksSubject = BehaviorSubject();
// temporarily block song updating via index updates to avoid double updates on shufflemode change
bool blockIndexUpdate = false;
@override
ValueStream<ShuffleMode> get shuffleModeStream => _shuffleModeSubject.stream;
@override
ValueStream<LoopMode> get loopModeStream => _loopModeSubject.stream;
@override
ValueStream<List<Song>> get queueStream => _queueSubject.stream;
@override
ValueStream<int> get currentIndexStream => _currentIndexSubject.stream;
@override
Stream<Song> get currentSongStream => _currentSongSubject.stream.distinct();
@override
Stream<PlaybackEvent> get playbackEventStream => _audioPlayerDataSource.playbackEventStream;
@override
Stream<bool> get playingStream => _audioPlayerDataSource.playingStream;
@override
Stream<Duration> get positionStream => _audioPlayerDataSource.positionStream;
// @override
// ManagedQueueInfo get managedQueueInfo => _dynamicQueue;
@override
Future<void> addToQueue(Song song) async {
_log.d('addToQueue');
_audioPlayerDataSource.addToQueue(song as SongModel);
_dynamicQueue.addToQueue(song);
_queueSubject.add(_dynamicQueue.queue);
}
@override
Future<void> dispose() async {
_audioPlayerDataSource.dispose();
}
@override
Future<void> initQueue(
List<QueueItem> queueItems,
List<Song> originalSongs,
List<Song> addedSongs,
int index,
) async {
// _dynamicQueue.init(
// queueItems,
// originalSongs,
// addedSongs,
// shuffleModeStream.value,
// [],
// [],
// _respectSongLinksSubject.value,
// );
// _queueSubject.add(_dynamicQueue.queue);
// await _audioPlayerDataSource.loadQueue(
// initialIndex: index,
// queue: _dynamicQueue.queue.map((e) => e as SongModel).toList(),
// );
}
@override
Future<void> loadSongs({required List<Song> songs, required int initialIndex, required Playable playable}) async {
final dynamicQueue2 = GetIt.I<DynamicQueue2>();
final shuffleMode = shuffleModeStream.value;
final _initialIndex = await dynamicQueue2.generateQueue(songs, playable, initialIndex, shuffleMode);
_queueSubject.add(dynamicQueue2.queue);
await _audioPlayerDataSource.loadQueue(
initialIndex: _initialIndex,
queue: dynamicQueue2.queue.map((e) => e as SongModel).toList(),
);
}
@override
Future<void> moveQueueItem(int oldIndex, int newIndex) async {
_dynamicQueue.moveQueueItem(oldIndex, newIndex);
final newCurrentIndex = _calcNewCurrentIndexOnMove(
currentIndexStream.value,
oldIndex,
newIndex,
);
_currentIndexSubject.add(newCurrentIndex);
_queueSubject.add(_dynamicQueue.queue);
_audioPlayerDataSource.moveQueueItem(oldIndex, newIndex);
}
@override
Future<void> pause() async {
_audioPlayerDataSource.pause();
}
@override
Future<void> play() async {
_audioPlayerDataSource.play();
}
@override
Future<void> playNext(Song song) async {
_audioPlayerDataSource.playNext(song as SongModel);
_dynamicQueue.insertIntoQueue(song, (currentIndexStream.valueOrNull ?? 0) + 1);
_queueSubject.add(_dynamicQueue.queue);
}
@override
Future<void> removeQueueIndex(int index) async {
_dynamicQueue.removeQueueIndex(index);
final newCurrentIndex = _calcNewCurrentIndexOnRemove(currentIndexStream.value, index);
_currentIndexSubject.add(newCurrentIndex);
_queueSubject.add(_dynamicQueue.queue);
_audioPlayerDataSource.removeQueueIndex(index);
}
@override
Future<bool> seekToNext() async {
return await _audioPlayerDataSource.seekToNext();
}
@override
Future<void> seekToPrevious() async {
await _audioPlayerDataSource.seekToPrevious();
}
@override
Future<void> seekToIndex(int index) async {
await _audioPlayerDataSource.seekToIndex(index);
}
@override
Future<void> setLoopMode(LoopMode loopMode) async {
_loopModeSubject.add(loopMode);
await _audioPlayerDataSource.setLoopMode(loopMode);
}
@override
Future<void> setShuffleMode(ShuffleMode shuffleMode, {bool updateQueue = true}) async {
_shuffleModeSubject.add(shuffleMode);
final dynamicQueue2 = GetIt.I<DynamicQueue2>();
final currentIndex = currentIndexStream.valueOrNull ?? 0;
if (updateQueue) {
final splitIndex = await dynamicQueue2.reshuffleQueue(shuffleMode, currentIndex);
blockIndexUpdate = true;
_audioPlayerDataSource
.replaceQueueAroundIndex(
index: currentIndex,
before: dynamicQueue2.queue.sublist(0, splitIndex).map((e) => e as SongModel).toList(),
after: dynamicQueue2.queue.sublist(splitIndex + 1).map((e) => e as SongModel).toList(),
)
.then((_) {
_queueSubject.add(dynamicQueue2.queue);
});
}
}
@override
Future<void> stop() async {
_audioPlayerDataSource.stop();
}
@override
Future<void> updateSongs(Map<String, Song> songs) async {
final dynamicQueue2 = GetIt.I<DynamicQueue2>();
if (songs.containsKey(_currentSongSubject.valueOrNull?.path)) {
_currentSongSubject.add(songs[_currentSongSubject.value.path]!);
}
if (dynamicQueue2.updateSongs(songs)) {
_queueSubject.add(dynamicQueue2.queue);
}
}
void _updateCurrentSong(List<Song>? queue, int? index) {
if (queue != null && index != null && index < queue.length) {
_log.d('Current song: ${queue[index]}');
_currentSongSubject.add(queue[index]);
}
// idea: unblock index update, once the current song has been updated (via queue update)
blockIndexUpdate = false;
}
/// Calculate the new current index when removing the song at [removeIndex].
int _calcNewCurrentIndexOnRemove(int currentIndex, int removeIndex) {
int result = currentIndex;
if (removeIndex < currentIndex) {
result--;
}
return result;
}
/// Calculate the new current index when moving a song from [oldIndex] to [newIndex].
int _calcNewCurrentIndexOnMove(int currentIndex, int oldIndex, int newIndex) {
int newCurrentIndex = currentIndex;
if (oldIndex == currentIndex) {
newCurrentIndex = newIndex;
} else {
if (oldIndex < currentIndex) {
newCurrentIndex--;
}
if (newIndex < currentIndex) {
newCurrentIndex++;
}
}
return newCurrentIndex;
}
@override
Future<void> seekToPosition(double position) async =>
_audioPlayerDataSource.seekToPosition(position);
}