queue and audiosource stuff

This commit is contained in:
Moritz Weber 2021-03-06 19:09:19 +01:00
parent 39e4111037
commit 5c6a8a0a6d
19 changed files with 629 additions and 221 deletions

View file

@ -3,30 +3,104 @@ import '../entities/shuffle_mode.dart';
import '../entities/song.dart'; import '../entities/song.dart';
import '../repositories/music_data_repository.dart'; import '../repositories/music_data_repository.dart';
class QueueGenerationModule { class QueueManagerModule {
QueueGenerationModule(this._musicDataRepository); QueueManagerModule(this._musicDataRepository);
List<QueueItem> get queue => _queue;
final MusicDataInfoRepository _musicDataRepository; final MusicDataInfoRepository _musicDataRepository;
Future<List<QueueItem>> generateQueue( List<Song> _originalSongList = [];
List<Song> _addedSongs = [];
// this resembles the queue in AudioPlayer
List<QueueItem> _queue;
void addToQueue(Song song) {
_addedSongs.add(song);
final queueItem = QueueItem(
song,
originalIndex: _addedSongs.length - 1,
type: QueueItemType.added,
);
_queue.add(queueItem);
}
void insertIntoQueue(Song song, int index) {
_addedSongs.add(song);
final queueItem = QueueItem(
song,
originalIndex: _addedSongs.length - 1,
type: QueueItemType.added,
);
_queue.insert(index, queueItem);
}
void moveQueueItem(int oldIndex, int newIndex) {
final queueItem = _queue.removeAt(oldIndex);
_queue.insert(newIndex, queueItem);
}
void removeQueueIndex(int index) {
final queueItem = _queue[index];
if (queueItem.type == QueueItemType.added) {
_addedSongs.removeAt(queueItem.originalIndex);
} else if (queueItem.type == QueueItemType.standard) {
_originalSongList.removeAt(queueItem.originalIndex);
}
for (int i = 0; i < queue.length; i++) {
if (queue[i].type == queueItem.type && queue[i].originalIndex > queueItem.originalIndex) {
queue[i] = QueueItem(
queue[i].song,
originalIndex: queue[i].originalIndex - 1,
type: queue[i].type,
);
}
}
_queue.removeAt(index);
}
Future<void> reshuffleQueue(
ShuffleMode shuffleMode,
int currentIndex,
) async {
final songs = _originalSongList.cast<Song>() + _addedSongs;
final currentQueueItem = _queue[currentIndex];
int originalIndex = currentQueueItem.originalIndex;
if (currentQueueItem.type == QueueItemType.added) {
originalIndex += _originalSongList.length;
}
_queue = await _generateQueue(shuffleMode, songs, originalIndex);
}
Future<void> setQueue(
ShuffleMode shuffleMode, ShuffleMode shuffleMode,
List<Song> songs, List<Song> songs,
int startIndex, int startIndex,
) async { ) async {
List<QueueItem> queue; _originalSongList = songs;
_addedSongs = [];
_queue = await _generateQueue(shuffleMode, songs, startIndex);
}
// ignore: missing_return
Future<List<QueueItem>> _generateQueue(
ShuffleMode shuffleMode,
List<Song> songs,
int startIndex,
) async {
switch (shuffleMode) { switch (shuffleMode) {
case ShuffleMode.none: case ShuffleMode.none:
queue = _generateNormalQueue(songs); return _generateNormalQueue(songs);
break;
case ShuffleMode.standard: case ShuffleMode.standard:
queue = _generateShuffleQueue(songs, startIndex); return _generateShuffleQueue(songs, startIndex);
break;
case ShuffleMode.plus: case ShuffleMode.plus:
queue = await _generateShufflePlusQueue(songs, startIndex); return await _generateShufflePlusQueue(songs, startIndex);
} }
return queue;
} }
List<QueueItem> _generateNormalQueue(List<Song> songs) { List<QueueItem> _generateNormalQueue(List<Song> songs) {

View file

@ -3,7 +3,6 @@ import 'package:rxdart/rxdart.dart';
import '../entities/event.dart'; import '../entities/event.dart';
import '../entities/loop_mode.dart'; import '../entities/loop_mode.dart';
import '../entities/playback_event.dart'; import '../entities/playback_event.dart';
import '../entities/queue_item.dart';
import '../entities/shuffle_mode.dart'; import '../entities/shuffle_mode.dart';
import '../entities/song.dart'; import '../entities/song.dart';
@ -12,8 +11,7 @@ abstract class AudioPlayerInfoRepository {
ValueStream<ShuffleMode> get shuffleModeStream; ValueStream<ShuffleMode> get shuffleModeStream;
ValueStream<LoopMode> get loopModeStream; ValueStream<LoopMode> get loopModeStream;
ValueStream<List<Song>> get songListStream; ValueStream<List<Song>> get queueStream;
ValueStream<List<QueueItem>> get queueStream;
ValueStream<int> get currentIndexStream; ValueStream<int> get currentIndexStream;
Stream<Song> get currentSongStream; Stream<Song> get currentSongStream;
@ -28,14 +26,16 @@ abstract class AudioPlayerRepository extends AudioPlayerInfoRepository {
Future<void> stop(); Future<void> stop();
Future<bool> seekToNext(); Future<bool> seekToNext();
Future<void> seekToPrevious(); Future<void> seekToPrevious();
Future<void> seekToIndex(int index);
Future<void> dispose(); Future<void> dispose();
Future<void> playSong(Song song); Future<void> playSong(Song song);
Future<void> loadQueue({List<QueueItem> queue, int initialIndex}); Future<void> loadQueue({List<Song> queue, int initialIndex});
Future<void> addToQueue(Song song); Future<void> addToQueue(Song song);
Future<void> playNext(Song song);
Future<void> moveQueueItem(int oldIndex, int newIndex); Future<void> moveQueueItem(int oldIndex, int newIndex);
Future<void> removeQueueIndex(int index); Future<void> removeQueueIndex(int index);
Future<void> setIndex(int index); Future<void> replaceQueueAroundIndex({List<Song> before, List<Song> after, int index});
/// Set the ShuffleMode. Does not affect playback/queue. /// Set the ShuffleMode. Does not affect playback/queue.
Future<void> setShuffleMode(ShuffleMode shuffleMode); Future<void> setShuffleMode(ShuffleMode shuffleMode);

View file

@ -0,0 +1,30 @@
import '../entities/song.dart';
import '../modules/queue_manager.dart';
import '../repositories/audio_player_repository.dart';
import '../repositories/persistent_player_state_repository.dart';
import '../repositories/platform_integration_repository.dart';
class AddToQueue {
AddToQueue(
this._audioPlayerRepository,
this._platformIntegrationRepository,
this._playerStateRepository,
this._queueManagerModule,
);
final AudioPlayerRepository _audioPlayerRepository;
final PlatformIntegrationRepository _platformIntegrationRepository;
final PlayerStateRepository _playerStateRepository;
final QueueManagerModule _queueManagerModule;
Future<void> call(Song song) async {
_queueManagerModule.addToQueue(song);
await _audioPlayerRepository.addToQueue(song);
final songList = _audioPlayerRepository.queueStream.value;
print(songList.length);
_platformIntegrationRepository.setQueue(songList);
}
}

View file

@ -0,0 +1,28 @@
import '../modules/queue_manager.dart';
import '../repositories/audio_player_repository.dart';
import '../repositories/persistent_player_state_repository.dart';
import '../repositories/platform_integration_repository.dart';
class MoveQueueItem {
MoveQueueItem(
this._audioPlayerRepository,
this._platformIntegrationRepository,
this._playerStateRepository,
this._queueManagerModule,
);
final AudioPlayerRepository _audioPlayerRepository;
final PlatformIntegrationRepository _platformIntegrationRepository;
final PlayerStateRepository _playerStateRepository;
final QueueManagerModule _queueManagerModule;
Future<void> call(int oldIndex, int newIndex) async {
_queueManagerModule.moveQueueItem(oldIndex, newIndex);
await _audioPlayerRepository.moveQueueItem(oldIndex, newIndex);
final songList = _audioPlayerRepository.queueStream.value;
_platformIntegrationRepository.setQueue(songList);
}
}

View file

@ -0,0 +1,31 @@
import '../entities/song.dart';
import '../modules/queue_manager.dart';
import '../repositories/audio_player_repository.dart';
import '../repositories/persistent_player_state_repository.dart';
import '../repositories/platform_integration_repository.dart';
class PlayNext {
PlayNext(
this._audioPlayerRepository,
this._platformIntegrationRepository,
this._playerStateRepository,
this._queueManagerModule,
);
final AudioPlayerRepository _audioPlayerRepository;
final PlatformIntegrationRepository _platformIntegrationRepository;
final PlayerStateRepository _playerStateRepository;
final QueueManagerModule _queueManagerModule;
Future<void> call(Song song) async {
final currentIndex = _audioPlayerRepository.currentIndexStream.value;
_queueManagerModule.insertIntoQueue(song, currentIndex + 1);
await _audioPlayerRepository.playNext(song);
final songList = _audioPlayerRepository.queueStream.value;
print(songList.length);
_platformIntegrationRepository.setQueue(songList);
}
}

View file

@ -1,5 +1,6 @@
import '../entities/shuffle_mode.dart';
import '../entities/song.dart'; import '../entities/song.dart';
import '../modules/queue_generator.dart'; import '../modules/queue_manager.dart';
import '../repositories/audio_player_repository.dart'; import '../repositories/audio_player_repository.dart';
import '../repositories/persistent_player_state_repository.dart'; import '../repositories/persistent_player_state_repository.dart';
import '../repositories/platform_integration_repository.dart'; import '../repositories/platform_integration_repository.dart';
@ -16,27 +17,32 @@ class PlaySongs {
final PlatformIntegrationRepository _platformIntegrationRepository; final PlatformIntegrationRepository _platformIntegrationRepository;
final PlayerStateRepository _playerStateRepository; final PlayerStateRepository _playerStateRepository;
final QueueGenerationModule _queueGenerationModule; final QueueManagerModule _queueGenerationModule;
Future<void> call({List<Song> songs, int initialIndex}) async { Future<void> call({List<Song> songs, int initialIndex}) async {
if (0 <= initialIndex && initialIndex < songs.length) { if (0 <= initialIndex && initialIndex < songs.length) {
// _audioPlayerRepository.playSong(songs[initialIndex]); // _audioPlayerRepository.playSong(songs[initialIndex]);
final queueItems = await _queueGenerationModule.generateQueue( final shuffleMode = _audioPlayerRepository.shuffleModeStream.value;
_audioPlayerRepository.shuffleModeStream.value,
await _queueGenerationModule.setQueue(
shuffleMode,
songs, songs,
initialIndex, initialIndex,
); );
final queueItems = _queueGenerationModule.queue;
final songList = queueItems.map((e) => e.song).toList();
await _audioPlayerRepository.loadQueue( await _audioPlayerRepository.loadQueue(
initialIndex: initialIndex, initialIndex: shuffleMode == ShuffleMode.none ? initialIndex : 0,
queue: queueItems, queue: songList,
); );
_audioPlayerRepository.play(); _audioPlayerRepository.play();
_platformIntegrationRepository.setCurrentSong(songs[initialIndex]); _platformIntegrationRepository.setCurrentSong(songs[initialIndex]);
// _platformIntegrationRepository.play(); // _platformIntegrationRepository.play();
_platformIntegrationRepository.setQueue(queueItems.map((e) => e.song).toList()); _platformIntegrationRepository.setQueue(songList);
} }
} }
} }

View file

@ -0,0 +1,29 @@
import '../modules/queue_manager.dart';
import '../repositories/audio_player_repository.dart';
import '../repositories/persistent_player_state_repository.dart';
import '../repositories/platform_integration_repository.dart';
class RemoveQueueIndex {
RemoveQueueIndex(
this._audioPlayerRepository,
this._platformIntegrationRepository,
this._playerStateRepository,
this._queueManagerModule,
);
final AudioPlayerRepository _audioPlayerRepository;
final PlatformIntegrationRepository _platformIntegrationRepository;
final PlayerStateRepository _playerStateRepository;
final QueueManagerModule _queueManagerModule;
Future<void> call(int index) async {
_queueManagerModule.removeQueueIndex(index);
await _audioPlayerRepository.removeQueueIndex(index);
final songList = _audioPlayerRepository.queueStream.value;
print(songList.length);
_platformIntegrationRepository.setQueue(songList);
}
}

View file

@ -0,0 +1,11 @@
import '../repositories/audio_player_repository.dart';
class SeekToIndex {
SeekToIndex(this._audioPlayerRepository);
final AudioPlayerRepository _audioPlayerRepository;
Future<void> call(int index) async {
await _audioPlayerRepository.seekToIndex(index);
}
}

View file

@ -1,8 +1,5 @@
import 'package:mucke/domain/entities/queue_item.dart';
import '../entities/shuffle_mode.dart'; import '../entities/shuffle_mode.dart';
import '../entities/song.dart'; import '../modules/queue_manager.dart';
import '../modules/queue_generator.dart';
import '../repositories/audio_player_repository.dart'; import '../repositories/audio_player_repository.dart';
import '../repositories/persistent_player_state_repository.dart'; import '../repositories/persistent_player_state_repository.dart';
import '../repositories/platform_integration_repository.dart'; import '../repositories/platform_integration_repository.dart';
@ -12,53 +9,32 @@ class SetShuffleMode {
this._audioPlayerRepository, this._audioPlayerRepository,
this._platformIntegrationRepository, this._platformIntegrationRepository,
this._playerStateRepository, this._playerStateRepository,
this._queueGenerationModule, this._queueManagerModule,
); );
final AudioPlayerRepository _audioPlayerRepository; final AudioPlayerRepository _audioPlayerRepository;
final PlatformIntegrationRepository _platformIntegrationRepository; final PlatformIntegrationRepository _platformIntegrationRepository;
final PlayerStateRepository _playerStateRepository; final PlayerStateRepository _playerStateRepository;
final QueueGenerationModule _queueGenerationModule; final QueueManagerModule _queueManagerModule;
Future<void> call(ShuffleMode shuffleMode) async { Future<void> call(ShuffleMode shuffleMode) async {
// _audioPlayerRepository.playSong(songs[initialIndex]);
_audioPlayerRepository.setShuffleMode(shuffleMode); _audioPlayerRepository.setShuffleMode(shuffleMode);
final queue = _audioPlayerRepository.queueStream.value;
final currentIndex = _audioPlayerRepository.currentIndexStream.value; final currentIndex = _audioPlayerRepository.currentIndexStream.value;
final originalIndex = _queueManagerModule.queue[currentIndex].originalIndex;
final QueueItem currentQueueItem = queue[currentIndex]; await _queueManagerModule.reshuffleQueue(shuffleMode, currentIndex);
final int index = currentQueueItem.originalIndex; final queue = _queueManagerModule.queue;
// WAS LETZTE GEDANKE? final songList = queue.map((e) => e.song).toList();
// _inputQueue ist die originale Song Liste aus playSongs o.ä. BEVOR der QueueGenerator drüber läuft final splitIndex = shuffleMode == ShuffleMode.none ? originalIndex : 0;
_audioPlayerRepository.replaceQueueAroundIndex(
index: currentIndex,
before: songList.sublist(0, splitIndex),
after: songList.sublist(splitIndex + 1),
);
// _queue = await _queueGenerator.generateQueue(shuffleMode, _inputQueue, index); _platformIntegrationRepository.setQueue(songList);
// // TODO: maybe refactor _queue to a subject and listen for changes
// final songModelQueue = _queue.map((e) => e.song).toList();
// _queueSubject.add(_queue);
// final newQueue = _songModelsToAudioSource(songModelQueue);
// _updateQueue(newQueue, currentQueueItem);
// final queueItems = await _queueGenerationModule.generateQueue(
// shuffleMode,
// songs,
// initialIndex,
// );
// final songList = queueItems.map((e) => e.song).toList();
// await _audioPlayerRepository.loadQueue(
// initialIndex: initialIndex,
// queue: songList,
// );
// _audioPlayerRepository.play();
// _platformIntegrationRepository.setCurrentSong(songs[initialIndex]);
// // _platformIntegrationRepository.play();
// _platformIntegrationRepository.setQueue(songList);
} }
} }

View file

@ -2,7 +2,7 @@ import 'dart:math';
import '../entities/shuffle_mode.dart'; import '../entities/shuffle_mode.dart';
import '../entities/song.dart'; import '../entities/song.dart';
import '../modules/queue_generator.dart'; import '../modules/queue_manager.dart';
import '../repositories/audio_player_repository.dart'; import '../repositories/audio_player_repository.dart';
import '../repositories/music_data_repository.dart'; import '../repositories/music_data_repository.dart';
import '../repositories/persistent_player_state_repository.dart'; import '../repositories/persistent_player_state_repository.dart';
@ -24,7 +24,7 @@ class ShuffleAll {
final PlatformIntegrationRepository _platformIntegrationRepository; final PlatformIntegrationRepository _platformIntegrationRepository;
final PlayerStateRepository _playerStateRepository; final PlayerStateRepository _playerStateRepository;
final QueueGenerationModule _queueGenerationModule; final QueueManagerModule _queueGenerationModule;
Future<void> call() async { Future<void> call() async {
final List<Song> songs = await _musicDataRepository.songStream.first; final List<Song> songs = await _musicDataRepository.songStream.first;
@ -33,20 +33,23 @@ class ShuffleAll {
_audioPlayerRepository.setShuffleMode(SHUFFLE_MODE); _audioPlayerRepository.setShuffleMode(SHUFFLE_MODE);
final queueItems = await _queueGenerationModule.generateQueue( await _queueGenerationModule.setQueue(
SHUFFLE_MODE, SHUFFLE_MODE,
songs, songs,
index, index,
); );
final queueItems = _queueGenerationModule.queue;
final songList = queueItems.map((e) => e.song).toList();
await _audioPlayerRepository.loadQueue( await _audioPlayerRepository.loadQueue(
initialIndex: 0, initialIndex: 0,
queue: queueItems, queue: songList,
); );
_audioPlayerRepository.play(); _audioPlayerRepository.play();
_platformIntegrationRepository.setCurrentSong(songs[index]); _platformIntegrationRepository.setCurrentSong(songs[index]);
// _platformIntegrationRepository.play(); // _platformIntegrationRepository.play();
_platformIntegrationRepository.setQueue(queueItems.map((e) => e.song).toList()); _platformIntegrationRepository.setQueue(songList);
} }
} }

View file

@ -6,21 +6,27 @@ import 'package:just_audio/just_audio.dart';
import 'domain/actors/audio_player_actor.dart'; import 'domain/actors/audio_player_actor.dart';
import 'domain/actors/platform_integration_actor.dart'; import 'domain/actors/platform_integration_actor.dart';
import 'domain/modules/queue_generator.dart'; import 'domain/modules/queue_manager.dart';
import 'domain/repositories/audio_player_repository.dart'; import 'domain/repositories/audio_player_repository.dart';
import 'domain/repositories/music_data_modifier_repository.dart'; import 'domain/repositories/music_data_modifier_repository.dart';
import 'domain/repositories/music_data_repository.dart'; import 'domain/repositories/music_data_repository.dart';
import 'domain/repositories/persistent_player_state_repository.dart'; import 'domain/repositories/persistent_player_state_repository.dart';
import 'domain/repositories/platform_integration_repository.dart'; import 'domain/repositories/platform_integration_repository.dart';
import 'domain/repositories/settings_repository.dart'; import 'domain/repositories/settings_repository.dart';
import 'domain/usecases/add_to_queue.dart';
import 'domain/usecases/handle_playback_state.dart'; import 'domain/usecases/handle_playback_state.dart';
import 'domain/usecases/move_queue_item.dart';
import 'domain/usecases/pause.dart'; import 'domain/usecases/pause.dart';
import 'domain/usecases/play.dart'; import 'domain/usecases/play.dart';
import 'domain/usecases/play_next.dart';
import 'domain/usecases/play_songs.dart'; import 'domain/usecases/play_songs.dart';
import 'domain/usecases/remove_queue_index.dart';
import 'domain/usecases/seek_to_index.dart';
import 'domain/usecases/seek_to_next.dart'; import 'domain/usecases/seek_to_next.dart';
import 'domain/usecases/seek_to_previous.dart'; import 'domain/usecases/seek_to_previous.dart';
import 'domain/usecases/set_current_song.dart'; import 'domain/usecases/set_current_song.dart';
import 'domain/usecases/set_loop_mode.dart'; import 'domain/usecases/set_loop_mode.dart';
import 'domain/usecases/set_shuffle_mode.dart';
import 'domain/usecases/shuffle_all.dart'; import 'domain/usecases/shuffle_all.dart';
import 'domain/usecases/update_database.dart'; import 'domain/usecases/update_database.dart';
import 'presentation/state/audio_store.dart'; import 'presentation/state/audio_store.dart';
@ -64,12 +70,18 @@ Future<void> setupGetIt() async {
() { () {
final audioStore = AudioStore( final audioStore = AudioStore(
audioPlayerInfoRepository: getIt(), audioPlayerInfoRepository: getIt(),
addToQueue: getIt(),
moveQueueItem: getIt(),
pause: getIt(), pause: getIt(),
play: getIt(), play: getIt(),
playNext: getIt(),
playSongs: getIt(), playSongs: getIt(),
removeQueueIndex: getIt(),
seekToIndex: getIt(),
seekToNext: getIt(), seekToNext: getIt(),
seekToPrevious: getIt(), seekToPrevious: getIt(),
setLoopMode: getIt(), setLoopMode: getIt(),
setShuffleMode: getIt(),
shuffleAll: getIt(), shuffleAll: getIt(),
); );
return audioStore; return audioStore;
@ -83,11 +95,27 @@ Future<void> setupGetIt() async {
); );
// use cases // use cases
getIt.registerLazySingleton<AddToQueue>(
() => AddToQueue(
getIt(),
getIt(),
getIt(),
getIt(),
),
);
getIt.registerLazySingleton<HandlePlaybackEvent>( getIt.registerLazySingleton<HandlePlaybackEvent>(
() => HandlePlaybackEvent( () => HandlePlaybackEvent(
getIt(), getIt(),
), ),
); );
getIt.registerLazySingleton<MoveQueueItem>(
() => MoveQueueItem(
getIt(),
getIt(),
getIt(),
getIt(),
),
);
getIt.registerLazySingleton<Pause>( getIt.registerLazySingleton<Pause>(
() => Pause( () => Pause(
getIt(), getIt(),
@ -98,6 +126,14 @@ Future<void> setupGetIt() async {
getIt(), getIt(),
), ),
); );
getIt.registerLazySingleton<PlayNext>(
() => PlayNext(
getIt(),
getIt(),
getIt(),
getIt(),
),
);
getIt.registerLazySingleton<PlaySongs>( getIt.registerLazySingleton<PlaySongs>(
() => PlaySongs( () => PlaySongs(
getIt(), getIt(),
@ -106,6 +142,19 @@ Future<void> setupGetIt() async {
getIt(), getIt(),
), ),
); );
getIt.registerLazySingleton<RemoveQueueIndex>(
() => RemoveQueueIndex(
getIt(),
getIt(),
getIt(),
getIt(),
),
);
getIt.registerLazySingleton<SeekToIndex>(
() => SeekToIndex(
getIt(),
),
);
getIt.registerLazySingleton<SeekToNext>( getIt.registerLazySingleton<SeekToNext>(
() => SeekToNext( () => SeekToNext(
getIt(), getIt(),
@ -126,6 +175,14 @@ Future<void> setupGetIt() async {
getIt(), getIt(),
), ),
); );
getIt.registerLazySingleton<SetShuffleMode>(
() => SetShuffleMode(
getIt(),
getIt(),
getIt(),
getIt(),
),
);
getIt.registerLazySingleton<ShuffleAll>( getIt.registerLazySingleton<ShuffleAll>(
() => ShuffleAll( () => ShuffleAll(
getIt(), getIt(),
@ -142,8 +199,8 @@ Future<void> setupGetIt() async {
); );
// modules // modules
getIt.registerLazySingleton<QueueGenerationModule>( getIt.registerLazySingleton<QueueManagerModule>(
() => QueueGenerationModule( () => QueueManagerModule(
getIt(), getIt(),
), ),
); );

View file

@ -108,6 +108,13 @@ class AlbumDetailsPage extends StatelessWidget {
return Container( return Container(
child: Column( child: Column(
children: [ children: [
ListTile(
title: const Text('Play next'),
onTap: () {
audioStore.playNext(song);
Navigator.pop(context);
},
),
ListTile( ListTile(
title: const Text('Add to queue'), title: const Text('Add to queue'),
onTap: () { onTap: () {

View file

@ -47,7 +47,7 @@ class QueuePage extends StatelessWidget {
subtitle: '${song.artist}', subtitle: '${song.artist}',
albumArtPath: song.albumArtPath, albumArtPath: song.albumArtPath,
highlight: index == activeIndex, highlight: index == activeIndex,
onTap: () => audioStore.setIndex(index), onTap: () => audioStore.seekToIndex(index),
), ),
onDismissed: (direction) { onDismissed: (direction) {
audioStore.removeQueueIndex(index); audioStore.removeQueueIndex(index);

View file

@ -77,6 +77,13 @@ class _SongsPageState extends State<SongsPage>
return Container( return Container(
child: Column( child: Column(
children: [ children: [
ListTile(
title: const Text('Play next'),
onTap: () {
audioStore.playNext(song);
Navigator.pop(context);
},
),
ListTile( ListTile(
title: const Text('Add to queue'), title: const Text('Add to queue'),
onTap: () { onTap: () {

View file

@ -7,45 +7,77 @@ 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/repositories/audio_player_repository.dart'; import '../../domain/repositories/audio_player_repository.dart';
import '../../domain/usecases/add_to_queue.dart';
import '../../domain/usecases/move_queue_item.dart';
import '../../domain/usecases/pause.dart'; import '../../domain/usecases/pause.dart';
import '../../domain/usecases/play.dart'; import '../../domain/usecases/play.dart';
import '../../domain/usecases/play_next.dart';
import '../../domain/usecases/play_songs.dart'; import '../../domain/usecases/play_songs.dart';
import '../../domain/usecases/remove_queue_index.dart';
import '../../domain/usecases/seek_to_index.dart';
import '../../domain/usecases/seek_to_next.dart'; import '../../domain/usecases/seek_to_next.dart';
import '../../domain/usecases/seek_to_previous.dart'; import '../../domain/usecases/seek_to_previous.dart';
import '../../domain/usecases/set_loop_mode.dart'; import '../../domain/usecases/set_loop_mode.dart';
import '../../domain/usecases/set_shuffle_mode.dart';
import '../../domain/usecases/shuffle_all.dart'; import '../../domain/usecases/shuffle_all.dart';
part 'audio_store.g.dart'; part 'audio_store.g.dart';
class AudioStore extends _AudioStore with _$AudioStore { class AudioStore extends _AudioStore with _$AudioStore {
AudioStore({ AudioStore({
@required AddToQueue addToQueue,
@required MoveQueueItem moveQueueItem,
@required Pause pause, @required Pause pause,
@required Play play, @required Play play,
@required PlayNext playNext,
@required PlaySongs playSongs, @required PlaySongs playSongs,
@required RemoveQueueIndex removeQueueIndex,
@required SeekToIndex seekToIndex,
@required SeekToNext seekToNext, @required SeekToNext seekToNext,
@required SeekToPrevious seekToPrevious, @required SeekToPrevious seekToPrevious,
@required SetLoopMode setLoopMode, @required SetLoopMode setLoopMode,
@required SetShuffleMode setShuffleMode,
@required ShuffleAll shuffleAll, @required ShuffleAll shuffleAll,
@required AudioPlayerInfoRepository audioPlayerInfoRepository, @required AudioPlayerInfoRepository audioPlayerInfoRepository,
}) : super(playSongs, audioPlayerInfoRepository, pause, play, seekToNext, seekToPrevious, }) : super(
setLoopMode, shuffleAll); addToQueue,
moveQueueItem,
playSongs,
audioPlayerInfoRepository,
pause,
play,
playNext,
removeQueueIndex,
seekToIndex,
seekToNext,
seekToPrevious,
setLoopMode,
setShuffleMode,
shuffleAll,
);
} }
abstract class _AudioStore with Store { abstract class _AudioStore with Store {
_AudioStore( _AudioStore(
this._addToQueue,
this._moveQueueItem,
this._playSongs, this._playSongs,
this._audioPlayerInfoRepository, this._audioPlayerInfoRepository,
this._pause, this._pause,
this._play, this._play,
this._playNext,
this._removeQueueIndex,
this._seekToIndex,
this._seekToNext, this._seekToNext,
this._seekToPrevious, this._seekToPrevious,
this._setLoopMode, this._setLoopMode,
this._setShuffleMode,
this._shuffleAll, this._shuffleAll,
) { ) {
currentPositionStream = _audioPlayerInfoRepository.positionStream currentPositionStream = _audioPlayerInfoRepository.positionStream
.asObservable(initialValue: const Duration(seconds: 0)); .asObservable(initialValue: const Duration(seconds: 0));
queueStream = _audioPlayerInfoRepository.songListStream.asObservable(); queueStream = _audioPlayerInfoRepository.queueStream.asObservable();
queueIndexStream = _audioPlayerInfoRepository.currentIndexStream.asObservable(); queueIndexStream = _audioPlayerInfoRepository.currentIndexStream.asObservable();
@ -60,12 +92,18 @@ abstract class _AudioStore with Store {
final AudioPlayerInfoRepository _audioPlayerInfoRepository; final AudioPlayerInfoRepository _audioPlayerInfoRepository;
final AddToQueue _addToQueue;
final MoveQueueItem _moveQueueItem;
final Pause _pause; final Pause _pause;
final Play _play; final Play _play;
final PlayNext _playNext;
final PlaySongs _playSongs; final PlaySongs _playSongs;
final RemoveQueueIndex _removeQueueIndex;
final SeekToIndex _seekToIndex;
final SeekToNext _seekToNext; final SeekToNext _seekToNext;
final SeekToPrevious _seekToPrevious; final SeekToPrevious _seekToPrevious;
final SetLoopMode _setLoopMode; final SetLoopMode _setLoopMode;
final SetShuffleMode _setShuffleMode;
final ShuffleAll _shuffleAll; final ShuffleAll _shuffleAll;
@observable @observable
@ -77,6 +115,8 @@ abstract class _AudioStore with Store {
@observable @observable
ObservableStream<Duration> currentPositionStream; ObservableStream<Duration> currentPositionStream;
// beware that this only triggers reactions when a new list (new reference) is set
// doesn't work if the same reference is added to BehaviorSubject
@observable @observable
ObservableStream<List<Song>> queueStream; ObservableStream<List<Song>> queueStream;
@ -109,12 +149,12 @@ abstract class _AudioStore with Store {
_seekToPrevious(); _seekToPrevious();
} }
Future<void> setIndex(int index) async { Future<void> seekToIndex(int index) async {
// _audioInterface.setIndex(index); _seekToIndex(index);
} }
Future<void> setShuffleMode(ShuffleMode shuffleMode) async { Future<void> setShuffleMode(ShuffleMode shuffleMode) async {
// _audioInterface.setShuffleMode(shuffleMode); _setShuffleMode(shuffleMode);
} }
Future<void> setLoopMode(LoopMode loopMode) async { Future<void> setLoopMode(LoopMode loopMode) async {
@ -126,15 +166,19 @@ abstract class _AudioStore with Store {
} }
Future<void> addToQueue(Song song) async { Future<void> addToQueue(Song song) async {
// _audioInterface.addToQueue(song); _addToQueue(song);
}
Future<void> playNext(Song song) async {
_playNext(song);
} }
Future<void> moveQueueItem(int oldIndex, int newIndex) async { Future<void> moveQueueItem(int oldIndex, int newIndex) async {
// _audioInterface.moveQueueItem(oldIndex, newIndex); _moveQueueItem(oldIndex, newIndex);
} }
Future<void> removeQueueIndex(int index) async { Future<void> removeQueueIndex(int index) async {
// _audioInterface.removeQueueIndex(index); _removeQueueIndex(index);
} }
Future<void> playAlbum(Album album) async { Future<void> playAlbum(Album album) async {

View file

@ -18,8 +18,6 @@ class AlbumArt extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2.0), borderRadius: BorderRadius.circular(2.0),
boxShadow: const [ boxShadow: const [
BoxShadow(color: Colors.black45, blurRadius: 1, offset: Offset(0, 1)),
BoxShadow(color: Colors.black38, blurRadius: 5, offset: Offset(0, 1)),
BoxShadow(color: Colors.black26, blurRadius: 8, offset: Offset(0, 1)), BoxShadow(color: Colors.black26, blurRadius: 8, offset: Offset(0, 1)),
], ],
), ),
@ -33,22 +31,6 @@ class AlbumArt extends StatelessWidget {
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
), ),
Positioned(
bottom: 0,
left: 0,
right: 0,
height: 150,
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0x00000000), Color(0xBB000000)],
stops: [0.0, 1.0],
),
),
),
),
Positioned( Positioned(
bottom: 0, bottom: 0,
left: 0, left: 0,

View file

@ -6,7 +6,7 @@ import '../models/song_model.dart';
abstract class AudioPlayerDataSource { abstract class AudioPlayerDataSource {
ValueStream<int> get currentIndexStream; ValueStream<int> get currentIndexStream;
ValueStream<SongModel> get currentSongStream; // ValueStream<SongModel> get currentSongStream;
Stream<PlaybackEventModel> get playbackEventStream; Stream<PlaybackEventModel> get playbackEventStream;
ValueStream<bool> get playingStream; ValueStream<bool> get playingStream;
ValueStream<Duration> get positionStream; ValueStream<Duration> get positionStream;
@ -21,10 +21,10 @@ abstract class AudioPlayerDataSource {
Future<void> loadQueue({List<SongModel> queue, int initialIndex}); Future<void> loadQueue({List<SongModel> queue, int initialIndex});
Future<void> addToQueue(SongModel song); Future<void> addToQueue(SongModel song);
Future<void> moveQueueItem(int oldIndex, int newIndex); Future<void> moveQueueItem(int oldIndex, int newIndex);
Future<void> playNext(SongModel song);
Future<void> removeQueueIndex(int index); Future<void> removeQueueIndex(int index);
Future<void> setIndex(int index); Future<void> replaceQueueAroundIndex({List<SongModel> before, List<SongModel> after, int index});
Future<void> seekToIndex(int index);
Future<void> setLoopMode(LoopMode loopMode); Future<void> setLoopMode(LoopMode loopMode);
Future<void> playSongList(List<SongModel> songs, int startIndex);
} }

View file

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:just_audio/just_audio.dart' as ja; import 'package:just_audio/just_audio.dart' as ja;
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
@ -8,24 +10,25 @@ import '../models/playback_event_model.dart';
import '../models/song_model.dart'; import '../models/song_model.dart';
import 'audio_player_data_source.dart'; import 'audio_player_data_source.dart';
/// beide fälle (start > ende und ende <= start) beim initialen laden schon behandeln
/// offset berechnung passt -> damit sollte die index ausgabe auch stimmen
/// vielleicht lässt sich allgemeiner fall mit modulo rechnung finden: loadIndex = (li+-1) % length
/// die beiden richtungen (skip next/prev) verhalten sich in den beiden fällen genau gegensätzlich
const int LOAD_INTERVAL = 2;
class AudioPlayerDataSourceImpl implements AudioPlayerDataSource { class AudioPlayerDataSourceImpl implements AudioPlayerDataSource {
AudioPlayerDataSourceImpl(this._audioPlayer) { AudioPlayerDataSourceImpl(this._audioPlayer) {
_audioPlayer.currentIndexStream.listen((event) { _audioPlayer.currentIndexStream.listen((index) async {
_log.info('currentIndex: $event'); _log.info('currentIndexSteam.listen: $index');
_currentIndexSubject.add(event); if (!await _updateLoadedQueue(index)) {
if (_queue != null && event != null && event < _queue.length) { _updateCurrentIndex(index);
_currentSongSubject.add(_queue[event]);
} }
}); });
_audioPlayer.playingStream.listen((event) { _audioPlayer.playingStream.listen((event) => _playingSubject.add(event));
_log.info('playing: $event');
_playingSubject.add(event);
});
_audioPlayer.positionStream.listen((event) { _audioPlayer.positionStream.listen((event) => _positionSubject.add(event));
_positionSubject.add(event);
});
_playbackEventModelStream = Rx.combineLatest2<ja.PlaybackEvent, bool, PlaybackEventModel>( _playbackEventModelStream = Rx.combineLatest2<ja.PlaybackEvent, bool, PlaybackEventModel>(
_audioPlayer.playbackEventStream, _audioPlayer.playbackEventStream,
@ -40,29 +43,43 @@ class AudioPlayerDataSourceImpl implements AudioPlayerDataSource {
static final _log = Logger('AudioPlayer'); static final _log = Logger('AudioPlayer');
final BehaviorSubject<int> _currentIndexSubject = BehaviorSubject(); final BehaviorSubject<int> _currentIndexSubject = BehaviorSubject();
final BehaviorSubject<SongModel> _currentSongSubject = BehaviorSubject();
final BehaviorSubject<PlaybackEventModel> _playbackEventSubject = BehaviorSubject(); final BehaviorSubject<PlaybackEventModel> _playbackEventSubject = BehaviorSubject();
final BehaviorSubject<bool> _playingSubject = BehaviorSubject(); final BehaviorSubject<bool> _playingSubject = BehaviorSubject();
final BehaviorSubject<Duration> _positionSubject = BehaviorSubject(); final BehaviorSubject<Duration> _positionSubject = BehaviorSubject();
Stream<PlaybackEventModel> _playbackEventModelStream; Stream<PlaybackEventModel> _playbackEventModelStream;
List<SongModel> _queue; List<SongModel> _queue;
int _loadStartIndex;
int _loadEndIndex;
void _setQueue(List<SongModel> queue, {int index}) { set loadStartIndex(int i) {
_queue = queue; _loadStartIndex = i;
if (index != null) { _log.info('loadStartIndex <- $i');
_currentSongSubject.add(_queue[index]); // _updateCurrentIndex(_audioPlayer.currentIndex);
} else if (_currentIndexSubject.value != null) { }
_currentSongSubject.add(_queue[_currentIndexSubject.value]);
int get loadStartIndex => _loadStartIndex;
set loadEndIndex(int i) {
_loadEndIndex = i;
_log.info('loadEndIndex <- $i');
// _updateCurrentIndex(_audioPlayer.currentIndex);
}
int get loadEndIndex => _loadEndIndex;
int get loadOffset {
if (loadStartIndex != null && loadEndIndex != null) {
final offset = loadStartIndex < loadEndIndex ? loadStartIndex : loadStartIndex - loadEndIndex;
print('offset: $offset');
return offset;
} }
return null;
} }
@override @override
ValueStream<int> get currentIndexStream => _currentIndexSubject.stream; ValueStream<int> get currentIndexStream => _currentIndexSubject.stream;
@override
ValueStream<SongModel> get currentSongStream => _currentSongSubject.stream;
@override @override
Stream<PlaybackEventModel> get playbackEventStream => _playbackEventModelStream; Stream<PlaybackEventModel> get playbackEventStream => _playbackEventModelStream;
@ -75,7 +92,6 @@ class AudioPlayerDataSourceImpl implements AudioPlayerDataSource {
@override @override
Future<void> dispose() async { Future<void> dispose() async {
await _currentIndexSubject.close(); await _currentIndexSubject.close();
await _currentSongSubject.close();
await _playbackEventSubject.close(); await _playbackEventSubject.close();
await _positionSubject.close(); await _positionSubject.close();
await _audioPlayer.dispose(); await _audioPlayer.dispose();
@ -86,12 +102,18 @@ class AudioPlayerDataSourceImpl implements AudioPlayerDataSource {
if (queue == null || initialIndex == null || initialIndex >= queue.length) { if (queue == null || initialIndex == null || initialIndex >= queue.length) {
return; return;
} }
_setQueue(queue, index: initialIndex); _queue = queue;
// final smallQueue = queue.sublist(max(initialIndex - 10, 0), min(initialIndex + 140, queue.length)); loadStartIndex = max(initialIndex - LOAD_INTERVAL, 0);
loadEndIndex = min(initialIndex + LOAD_INTERVAL + 1, queue.length);
_audioSource = _songModelsToAudioSource(queue); final smallQueue = queue.sublist(loadStartIndex, loadEndIndex);
_audioPlayer.setAudioSource(_audioSource, initialIndex: initialIndex);
_audioSource = _songModelsToAudioSource(smallQueue);
// _loadStartIndex.add(loadStartIndex);
// _loadEndIndex.add(loadEndIndex);
_audioPlayer.setAudioSource(_audioSource, initialIndex: initialIndex - loadStartIndex);
} }
@override @override
@ -104,26 +126,6 @@ class AudioPlayerDataSourceImpl implements AudioPlayerDataSource {
_audioPlayer.play(); _audioPlayer.play();
} }
@override
Future<void> playSongList(List<SongModel> songs, int startIndex) async {
// _inputQueue = songs;
// final firstSong = songs[startIndex];
// _queueSubject.add([QueueItemModel(firstSong, originalIndex: startIndex)]);
// _audioSource = _songModelsToAudioSource([firstSong]);
// await _audioPlayer.setAudioSource(_audioSource);
// _audioPlayer.play();
// _queue = await _queueGenerator.generateQueue(_shuffleModeSubject.value, songs, startIndex);
// final songModelQueue = _queue.map((e) => e.song).toList();
// _queueSubject.add(_queue);
// final int splitIndex = _shuffleModeSubject.value == ShuffleMode.none ? startIndex : 0;
// final newQueue = _songModelsToAudioSource(songModelQueue);
// await _audioSource.insertAll(0, newQueue.children.sublist(0, splitIndex));
// _audioSource.addAll(newQueue.children.sublist(splitIndex + 1, newQueue.length));
}
@override @override
Future<bool> seekToNext() async { Future<bool> seekToNext() async {
final result = _audioPlayer.hasNext; final result = _audioPlayer.hasNext;
@ -141,7 +143,7 @@ class AudioPlayerDataSourceImpl implements AudioPlayerDataSource {
} }
@override @override
Future<void> setIndex(int index) async { Future<void> seekToIndex(int index) async {
await _audioPlayer.seek(const Duration(seconds: 0), index: index); await _audioPlayer.seek(const Duration(seconds: 0), index: index);
} }
@ -153,43 +155,43 @@ class AudioPlayerDataSourceImpl implements AudioPlayerDataSource {
@override @override
Future<void> addToQueue(SongModel song) async { Future<void> addToQueue(SongModel song) async {
await _audioSource.add(ja.AudioSource.uri(Uri.file(song.path))); await _audioSource.add(ja.AudioSource.uri(Uri.file(song.path)));
// _queue.add(QueueItemModel(song, originalIndex: -1, type: QueueItemType.added)); _queue.add(song);
// _queueSubject.add(_queue);
} }
@override @override
Future<void> moveQueueItem(int oldIndex, int newIndex) async { Future<void> moveQueueItem(int oldIndex, int newIndex) async {
// final QueueItemModel queueItem = _queue.removeAt(oldIndex);
// _queue.insert(newIndex, queueItem);
// _queueSubject.add(_queue);
await _audioSource.move(oldIndex, newIndex); await _audioSource.move(oldIndex, newIndex);
final song = _queue.removeAt(oldIndex);
_queue.insert(newIndex, song);
}
@override
Future<void> playNext(SongModel song) async {
final index = currentIndexStream.value + 1;
await _audioSource.insert(index, ja.AudioSource.uri(Uri.file(song.path)));
_queue.insert(index, song);
} }
@override @override
Future<void> removeQueueIndex(int index) async { Future<void> removeQueueIndex(int index) async {
// _queue.removeAt(index);
// _queueSubject.add(_queue);
await _audioSource.removeAt(index); await _audioSource.removeAt(index);
_queue.removeAt(index);
} }
// @override @override
// Future<void> setShuffleMode(ShuffleMode shuffleMode, bool updateQueue) async { Future<void> replaceQueueAroundIndex(
// _log.info('setShuffleMode: $shuffleMode'); {List<SongModel> before, List<SongModel> after, int index}) async {
// if (shuffleMode == null) return; _queue = before + [_queue[index]] + after;
// _shuffleModeSubject.add(shuffleMode);
// if (updateQueue) { final _before = _songModelsToAudioSource(before);
// final QueueItem currentQueueItem = _queue[_currentIndexSubject.value]; final _after = _songModelsToAudioSource(after);
// final int index = currentQueueItem.originalIndex;
// _queue = await _queueGenerator.generateQueue(shuffleMode, _inputQueue, index);
// // TODO: maybe refactor _queue to a subject and listen for changes
// final songModelQueue = _queue.map((e) => e.song).toList();
// _queueSubject.add(_queue);
// final newQueue = _songModelsToAudioSource(songModelQueue); await _audioSource.removeRange(0, index);
// _updateQueue(newQueue, currentQueueItem); await _audioSource.removeRange(1, _audioSource.length);
// }
// } await _audioSource.insertAll(0, _before.children);
await _audioSource.addAll(_after.children);
}
@override @override
Future<void> setLoopMode(LoopMode loopMode) async { Future<void> setLoopMode(LoopMode loopMode) async {
@ -203,32 +205,117 @@ class AudioPlayerDataSourceImpl implements AudioPlayerDataSource {
); );
} }
// Future<void> _updateQueue( /// extend the loaded audiosource, when seeking to previous/next
// ja.ConcatenatingAudioSource newQueue, QueueItem currentQueueItem) async { Future<bool> _updateLoadedQueue(int newIndex) async {
// final int index = currentQueueItem.originalIndex; _log.info('updateLoadedQueue: $newIndex');
_log.info('[$loadStartIndex, $loadEndIndex]');
// _audioSource.removeRange(0, _currentIndexSubject.value); if (loadStartIndex == null || loadEndIndex == null || newIndex == null) return false;
// _audioSource.removeRange(1, _audioSource.length);
// if (_shuffleModeSubject.value == ShuffleMode.none) { if (loadStartIndex == loadEndIndex || (loadStartIndex == 0 && loadEndIndex == _queue.length))
// switch (currentQueueItem.type) { return false;
// case QueueItemType.added:
// case QueueItemType.standard: if (loadStartIndex < loadEndIndex) {
// await _audioSource.insertAll(0, newQueue.children.sublist(0, index)); _log.info('base case');
// _audioSource.addAll(newQueue.children.sublist(index + 1)); return await _updateLoadedQueueBaseCase(newIndex);
// break; } else {
// case QueueItemType.predecessor: _log.info('inverted case');
// await _audioSource.insertAll(0, newQueue.children.sublist(0, index)); return await _updateLoadedQueueInverted(newIndex);
// _audioSource.addAll(newQueue.children.sublist(index)); }
// break; }
// case QueueItemType.successor:
// await _audioSource.insertAll(0, newQueue.children.sublist(0, index + 1)); Future<bool> _updateLoadedQueueBaseCase(int newIndex) async {
// _audioSource.addAll(newQueue.children.sublist(index + 1)); if (newIndex < LOAD_INTERVAL) {
// break; // nearing the start of the loaded songs
// } if (loadStartIndex > 0) {
// _currentIndexSubject.add(index); // load the song previous to the already loaded songs
// } else { _log.info('loadStartIndex--');
// _audioSource.addAll(newQueue.children.sublist(1)); loadStartIndex--;
// } await _audioSource.insert(0, ja.AudioSource.uri(Uri.file(_queue[loadStartIndex].path)));
// } return true;
} else if (loadEndIndex < _queue.length) {
// load the last song, if it isn't already loaded
_log.info('loadStartIndex = ${_queue.length - 1}');
loadStartIndex = _queue.length - 1;
await _audioSource.add(ja.AudioSource.uri(Uri.file(_queue.last.path)));
return false;
}
} else if (newIndex > _audioSource.length - LOAD_INTERVAL - 1) {
// need to load next song
if (loadEndIndex < _queue.length) {
// we ARE NOT at the end of the queue -> load next song
_log.info('loadEndIndex++');
loadEndIndex++;
await _audioSource.add(ja.AudioSource.uri(Uri.file(_queue[loadEndIndex - 1].path)));
return false;
} else if (loadStartIndex > 0) {
// we ARE at the end of the queue AND the first song has not been loaded yet
// -> load first song
_log.info('loadEndIndex = 1');
loadEndIndex = 1;
await _audioSource.insert(0, ja.AudioSource.uri(Uri.file(_queue[0].path)));
return true;
}
}
return false;
}
Future<bool> _updateLoadedQueueInverted(int newIndex) async {
final rightOfLoadEnd = newIndex >= loadEndIndex;
int leftBorder = newIndex - LOAD_INTERVAL;
if (newIndex < loadEndIndex) {
leftBorder += _audioSource.length;
}
int rightBorder = newIndex + LOAD_INTERVAL;
if (newIndex > loadEndIndex) {
rightBorder -= _audioSource.length;
}
if (leftBorder < loadEndIndex) {
// nearing the start of the loaded songs
// load the song previous to the already loaded songs
_log.info('inv: loadStartIndex--');
loadStartIndex--;
await _audioSource.insert(
loadEndIndex, ja.AudioSource.uri(Uri.file(_queue[loadStartIndex].path)));
return rightOfLoadEnd;
} else if (rightBorder >= loadEndIndex) {
// need to load next song
// we ARE NOT at the end of the queue -> load next song
_log.info('inv: loadEndIndex++');
loadEndIndex++;
await _audioSource.insert(
loadEndIndex - 1, ja.AudioSource.uri(Uri.file(_queue[loadEndIndex - 1].path)));
return rightOfLoadEnd;
}
return false;
}
void _updateCurrentIndex(int apIndex) {
if (loadStartIndex == null || loadEndIndex == null) {
_currentIndexSubject.add(apIndex);
return;
}
int result;
if (_audioSource != null && _audioSource.length == _queue.length) {
_log.info('EVERYTHING LOADED');
result = apIndex;
} else if (loadStartIndex < loadEndIndex) {
// base case
result = apIndex != null ? (apIndex + (loadStartIndex ?? 0)) : null;
} else {
// inverted case
if (apIndex < loadEndIndex) {
result = apIndex;
} else {
result = apIndex + (loadStartIndex - loadEndIndex);
}
}
_currentIndexSubject.add(result);
_log.info('updateCurrentIndex: $result');
}
} }

View file

@ -2,7 +2,6 @@ import 'package:rxdart/rxdart.dart';
import '../../domain/entities/loop_mode.dart'; import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/playback_event.dart'; import '../../domain/entities/playback_event.dart';
import '../../domain/entities/queue_item.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/repositories/audio_player_repository.dart'; import '../../domain/repositories/audio_player_repository.dart';
@ -13,15 +12,22 @@ class AudioPlayerRepositoryImpl implements AudioPlayerRepository {
AudioPlayerRepositoryImpl(this._audioPlayerDataSource) { AudioPlayerRepositoryImpl(this._audioPlayerDataSource) {
_shuffleModeSubject.add(ShuffleMode.none); _shuffleModeSubject.add(ShuffleMode.none);
_loopModeSubject.add(LoopMode.off); _loopModeSubject.add(LoopMode.off);
_audioPlayerDataSource.currentIndexStream.listen(
(index) {
print('CURRENT INDEX: $index');
_updateCurrentSong(queueStream.value, index);
},
);
_queueSubject.listen((queue) => _updateCurrentSong(queue, currentIndexStream.value));
} }
final AudioPlayerDataSource _audioPlayerDataSource; final AudioPlayerDataSource _audioPlayerDataSource;
// final BehaviorSubject<int> _currentIndexSubject = BehaviorSubject(); // final BehaviorSubject<int> _currentIndexSubject = BehaviorSubject();
// final BehaviorSubject<Song> _currentSongSubject = BehaviorSubject<Song>(); final BehaviorSubject<Song> _currentSongSubject = BehaviorSubject<Song>();
final BehaviorSubject<LoopMode> _loopModeSubject = BehaviorSubject(); final BehaviorSubject<LoopMode> _loopModeSubject = BehaviorSubject();
final BehaviorSubject<List<Song>> _songListSubject = BehaviorSubject(); final BehaviorSubject<List<Song>> _queueSubject = BehaviorSubject();
final BehaviorSubject<List<QueueItem>> _queueSubject = BehaviorSubject();
final BehaviorSubject<ShuffleMode> _shuffleModeSubject = BehaviorSubject(); final BehaviorSubject<ShuffleMode> _shuffleModeSubject = BehaviorSubject();
@override @override
@ -34,16 +40,13 @@ class AudioPlayerRepositoryImpl implements AudioPlayerRepository {
ValueStream<LoopMode> get loopModeStream => _loopModeSubject.stream; ValueStream<LoopMode> get loopModeStream => _loopModeSubject.stream;
@override @override
ValueStream<List<Song>> get songListStream => _songListSubject.stream; ValueStream<List<Song>> get queueStream => _queueSubject.stream;
@override
ValueStream<List<QueueItem>> get queueStream => _queueSubject.stream;
@override @override
ValueStream<int> get currentIndexStream => _audioPlayerDataSource.currentIndexStream; ValueStream<int> get currentIndexStream => _audioPlayerDataSource.currentIndexStream;
@override @override
Stream<Song> get currentSongStream => _audioPlayerDataSource.currentSongStream; Stream<Song> get currentSongStream => _currentSongSubject.stream;
@override @override
Stream<PlaybackEvent> get playbackEventStream => _audioPlayerDataSource.playbackEventStream; Stream<PlaybackEvent> get playbackEventStream => _audioPlayerDataSource.playbackEventStream;
@ -55,9 +58,9 @@ class AudioPlayerRepositoryImpl implements AudioPlayerRepository {
Stream<Duration> get positionStream => _audioPlayerDataSource.positionStream; Stream<Duration> get positionStream => _audioPlayerDataSource.positionStream;
@override @override
Future<void> addToQueue(Song song) { Future<void> addToQueue(Song song) async {
// TODO: implement addToQueue _audioPlayerDataSource.addToQueue(song as SongModel);
throw UnimplementedError(); _queueSubject.add(_queueSubject.value + [song]);
} }
@override @override
@ -66,10 +69,9 @@ class AudioPlayerRepositoryImpl implements AudioPlayerRepository {
} }
@override @override
Future<void> loadQueue({List<QueueItem> queue, int initialIndex}) async { Future<void> loadQueue({List<Song> queue, int initialIndex}) async {
// _currentSongSubject.add(queue[initialIndex]); // _currentSongSubject.add(queue[initialIndex]);
_queueSubject.add(queue); _queueSubject.add(queue);
_songListSubject.add(queue.map((e) => e.song).toList());
// _currentIndexSubject.add(initialIndex); // _currentIndexSubject.add(initialIndex);
await _audioPlayerDataSource.loadQueue( await _audioPlayerDataSource.loadQueue(
@ -79,9 +81,15 @@ class AudioPlayerRepositoryImpl implements AudioPlayerRepository {
} }
@override @override
Future<void> moveQueueItem(int oldIndex, int newIndex) { Future<void> moveQueueItem(int oldIndex, int newIndex) async {
// TODO: implement moveQueueItem final _songList = _queueSubject.value.toList();
throw UnimplementedError();
_audioPlayerDataSource.moveQueueItem(oldIndex, newIndex);
final song = _songList.removeAt(oldIndex);
_songList.insert(newIndex, song);
_queueSubject.add(_songList);
} }
@override @override
@ -104,9 +112,32 @@ class AudioPlayerRepositoryImpl implements AudioPlayerRepository {
} }
@override @override
Future<void> removeQueueIndex(int index) { Future<void> playNext(Song song) async {
// TODO: implement removeQueueIndex final index = currentIndexStream.value + 1;
throw UnimplementedError(); final _songList = _queueSubject.value;
_audioPlayerDataSource.playNext(song as SongModel);
_queueSubject.add(_songList.sublist(0, index) + [song] + _songList.sublist(index));
}
@override
Future<void> removeQueueIndex(int index) async {
final _songList = _queueSubject.value;
_audioPlayerDataSource.removeQueueIndex(index);
_queueSubject.add(_songList.sublist(0, index) + _songList.sublist(index + 1));
}
@override
Future<void> replaceQueueAroundIndex({List<Song> before, List<Song> after, int index}) async {
_queueSubject.add(before + [_queueSubject.value[index]] + after);
await _audioPlayerDataSource.replaceQueueAroundIndex(
before: before.map((e) => e as SongModel).toList(),
after: after.map((e) => e as SongModel).toList(),
index: index,
);
} }
@override @override
@ -120,9 +151,8 @@ class AudioPlayerRepositoryImpl implements AudioPlayerRepository {
} }
@override @override
Future<void> setIndex(int index) { Future<void> seekToIndex(int index) async {
// TODO: implement setIndex await _audioPlayerDataSource.seekToIndex(index);
throw UnimplementedError();
} }
@override @override
@ -141,4 +171,10 @@ class AudioPlayerRepositoryImpl implements AudioPlayerRepository {
// TODO: implement stop // TODO: implement stop
throw UnimplementedError(); throw UnimplementedError();
} }
void _updateCurrentSong(List<Song> queue, int index) {
if (queue != null && index != null && index < queue.length) {
_currentSongSubject.add(queue[index]);
}
}
} }