import 'package:flutter_fimber/flutter_fimber.dart'; import '../entities/queue_item.dart'; import '../entities/shuffle_mode.dart'; import '../entities/song.dart'; import '../repositories/music_data_repository.dart'; class ManagedQueue { ManagedQueue(this._musicDataRepository); static final _log = FimberLog('ManagedQueue'); List get queue => _queue.map((e) => e.song).toList(); List get queueItemList => _queue; final MusicDataInfoRepository _musicDataRepository; // die brauch ich aktuell für den originalIndex für neue Songs List _originalSongList = []; List _addedSongs = []; // this resembles the queue in AudioPlayer // QueueItems are needed to determine the original position of the current song List _queue; List _filteredQueueItems = []; 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 a song is removed from the queue, it should not pop up again when reshuffling if (queueItem.type == QueueItemType.added) { _addedSongs.removeAt(queueItem.originalIndex); } else if (queueItem.type == QueueItemType.standard) { _originalSongList.removeAt(queueItem.originalIndex); } for (int i = 0; i < queueItemList.length; i++) { if (queueItemList[i].type == queueItem.type && queueItemList[i].originalIndex > queueItem.originalIndex) { queueItemList[i] = QueueItem( queueItemList[i].song, originalIndex: queueItemList[i].originalIndex - 1, type: queueItemList[i].type, ); } } _queue.removeAt(index); } // so this is the motivation behind all the original/added songs and queueitems: // to regenerate the original queue, we need the the original song list // and the position of the current song in this original song list 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; } return await _generateQueue(shuffleMode, songs, originalIndex); } /// Generate a queue from [songs] according to [shuffleMode]. Future generateQueue( ShuffleMode shuffleMode, List songs, int startIndex, ) async { _originalSongList = songs; _addedSongs = []; return 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, startIndex); case ShuffleMode.standard: return _generateShuffleQueue(songs, startIndex); case ShuffleMode.plus: return await _generateShufflePlusQueue(songs, startIndex); } } int _generateNormalQueue(List songs, int startIndex) { _queue = List.generate( songs.length, (i) => QueueItem( songs[i], originalIndex: i, ), ); return startIndex; } int _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, ); _queue = [first] + queue; return 0; } Future _generateShufflePlusQueue( List songs, int startIndex, ) async { final List queue = await _getQueueItemWithLinks( songs[startIndex], startIndex, ); // determine the start index in cases where the "first song" has predecessors int newStartIndex = 0; for (final qi in queue) { if (qi.song.path == songs[startIndex].path) { break; } else { newStartIndex++; } } final List indices = []; final List filteredIndices = []; // filter mediaitem list for (var i = 0; i < songs.length; i++) { if (i != startIndex && !songs[i].blocked) { indices.addAll(List.generate(songs[i].likeCount + 1, (_) => i)); } else if (i != startIndex) { filteredIndices.add(i); } } indices.shuffle(); for (final index in indices) { final Song song = songs[index]; queue.addAll(await _getQueueItemWithLinks(song, index)); } final List filteredQueue = []; for (final index in filteredIndices) { filteredQueue.add(QueueItem(songs[index], originalIndex: index)); } _filteredQueueItems = filteredQueue; _queue = queue; return newStartIndex; } // 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(); } List _calculateOriginalSongList(List queueItemList) { final Map original = {}; final Map added = {}; for (final qi in queueItemList) { if (qi.type == QueueItemType.standard) { original[qi.originalIndex] = qi; } else if (qi.type == QueueItemType.added) { added[qi.originalIndex] = qi; } } for (final qi in _filteredQueueItems) { if (qi.type == QueueItemType.standard) { original[qi.originalIndex] = qi; } else if (qi.type == QueueItemType.added) { original[qi.originalIndex] = qi; } } final List originalList = original.values.toList(); final List addedList = added.values.toList(); originalList.sort((a, b) => a.originalIndex.compareTo(b.originalIndex)); addedList.sort((a, b) => a.originalIndex.compareTo(b.originalIndex)); return originalList.map((e) => e.song).toList() + addedList.map((e) => e.song).toList(); } }