import '../entities/queue_item.dart'; import '../entities/shuffle_mode.dart'; import '../entities/song.dart'; import '../repositories/music_data_repository.dart'; class QueueManagerModule { QueueManagerModule(this._musicDataRepository); List get queue => _queue; final MusicDataInfoRepository _musicDataRepository; List _originalSongList = []; List _addedSongs = []; // this resembles the queue in AudioPlayer List _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 reshuffleQueue( ShuffleMode shuffleMode, int currentIndex, ) async { final songs = _originalSongList.cast() + _addedSongs; final currentQueueItem = _queue[currentIndex]; int originalIndex = currentQueueItem.originalIndex; if (currentQueueItem.type == QueueItemType.added) { originalIndex += _originalSongList.length; } _queue = await _generateQueue(shuffleMode, songs, originalIndex); } Future setQueue( ShuffleMode shuffleMode, List songs, int startIndex, ) async { _originalSongList = songs; _addedSongs = []; _queue = await _generateQueue(shuffleMode, songs, startIndex); } // ignore: missing_return Future> _generateQueue( ShuffleMode shuffleMode, List songs, int startIndex, ) async { switch (shuffleMode) { case ShuffleMode.none: return _generateNormalQueue(songs); case ShuffleMode.standard: return _generateShuffleQueue(songs, startIndex); case ShuffleMode.plus: return await _generateShufflePlusQueue(songs, startIndex); } } List _generateNormalQueue(List songs) { return List.generate( songs.length, (i) => QueueItem( songs[i], originalIndex: i, ), ); } List _generateShuffleQueue( List songs, int startIndex, ) { final List queue = List.generate( songs.length, (i) => QueueItem( songs[i], originalIndex: i, ), ); queue.removeAt(startIndex); queue.shuffle(); final first = QueueItem( songs[startIndex], originalIndex: startIndex, ); return [first] + queue; } Future> _generateShufflePlusQueue( List songs, int startIndex, ) async { final List queue = await _getQueueItemWithLinks( songs[startIndex], startIndex, ); final List indices = []; // filter mediaitem list // TODO: multiply higher rated songs for (var i = 0; i < songs.length; i++) { if (i != startIndex && !songs[i].blocked) { indices.add(i); } } indices.shuffle(); for (var i = 0; i < indices.length; i++) { final int index = indices[i]; final Song song = songs[index]; queue.addAll(await _getQueueItemWithLinks(song, index)); } return queue; } // TODO: naming things is hard Future> _getQueueItemWithLinks( Song song, int index, ) async { final List queueItems = []; final predecessors = await _getPredecessors(song); final successors = await _getSuccessors(song); for (final p in predecessors) { queueItems.add(QueueItem( p, originalIndex: index, type: QueueItemType.predecessor, )); } queueItems.add(QueueItem( song, originalIndex: index, )); for (final p in successors) { queueItems.add(QueueItem( p, originalIndex: index, type: QueueItemType.successor, )); } return queueItems; } Future> _getPredecessors(Song song) async { final List songs = []; Song currentSong = song; while (currentSong.previous != null) { currentSong = await _musicDataRepository.getSongByPath(currentSong.previous); songs.add(currentSong); } return songs.reversed.toList(); } Future> _getSuccessors(Song song) async { final List songs = []; Song currentSong = song; while (currentSong.next != null) { currentSong = await _musicDataRepository.getSongByPath(currentSong.next); songs.add(currentSong); } return songs.toList(); } }