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 _currentIndexSubject = BehaviorSubject(); final BehaviorSubject _currentSongSubject = BehaviorSubject(); final BehaviorSubject _loopModeSubject = BehaviorSubject(); final BehaviorSubject> _queueSubject = BehaviorSubject(); final BehaviorSubject _shuffleModeSubject = BehaviorSubject(); final BehaviorSubject _excludeBlockedSubject = BehaviorSubject(); final BehaviorSubject _excludeSkippedSubject = BehaviorSubject(); final BehaviorSubject _respectSongLinksSubject = BehaviorSubject(); // temporarily block song updating via index updates to avoid double updates on shufflemode change bool blockIndexUpdate = false; @override ValueStream get shuffleModeStream => _shuffleModeSubject.stream; @override ValueStream get loopModeStream => _loopModeSubject.stream; @override ValueStream> get queueStream => _queueSubject.stream; @override ValueStream get currentIndexStream => _currentIndexSubject.stream; @override Stream get currentSongStream => _currentSongSubject.stream.distinct(); @override Stream get playbackEventStream => _audioPlayerDataSource.playbackEventStream; @override Stream get playingStream => _audioPlayerDataSource.playingStream; @override Stream get positionStream => _audioPlayerDataSource.positionStream; // @override // ManagedQueueInfo get managedQueueInfo => _dynamicQueue; @override Future addToQueue(Song song) async { _log.d('addToQueue'); _audioPlayerDataSource.addToQueue(song as SongModel); _dynamicQueue.addToQueue(song); _queueSubject.add(_dynamicQueue.queue); } @override Future dispose() async { _audioPlayerDataSource.dispose(); } @override Future initQueue( List queueItems, List originalSongs, List 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 loadSongs({required List songs, required int initialIndex, required Playable playable}) async { final dynamicQueue2 = GetIt.I(); 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 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 pause() async { _audioPlayerDataSource.pause(); } @override Future play() async { _audioPlayerDataSource.play(); } @override Future playNext(Song song) async { _audioPlayerDataSource.playNext(song as SongModel); _dynamicQueue.insertIntoQueue(song, (currentIndexStream.valueOrNull ?? 0) + 1); _queueSubject.add(_dynamicQueue.queue); } @override Future 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 seekToNext() async { return await _audioPlayerDataSource.seekToNext(); } @override Future seekToPrevious() async { await _audioPlayerDataSource.seekToPrevious(); } @override Future seekToIndex(int index) async { await _audioPlayerDataSource.seekToIndex(index); } @override Future setLoopMode(LoopMode loopMode) async { _loopModeSubject.add(loopMode); await _audioPlayerDataSource.setLoopMode(loopMode); } @override Future setShuffleMode(ShuffleMode shuffleMode, {bool updateQueue = true}) async { _shuffleModeSubject.add(shuffleMode); final dynamicQueue2 = GetIt.I(); 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 stop() async { _audioPlayerDataSource.stop(); } @override Future updateSongs(Map songs) async { final dynamicQueue2 = GetIt.I(); if (songs.containsKey(_currentSongSubject.valueOrNull?.path)) { _currentSongSubject.add(songs[_currentSongSubject.value.path]!); } if (dynamicQueue2.updateSongs(songs)) { _queueSubject.add(dynamicQueue2.queue); } } void _updateCurrentSong(List? 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 seekToPosition(double position) async => _audioPlayerDataSource.seekToPosition(position); }