import 'package:audio_service/audio_service.dart'; import 'package:just_audio/just_audio.dart'; import '../../domain/entities/queue_item.dart'; import '../../domain/entities/shuffle_mode.dart'; import '../datasources/music_data_source_contract.dart'; import '../models/queue_item_model.dart'; import '../models/song_model.dart'; class QueueGenerator { QueueGenerator(this._musicDataSource); final MusicDataSource _musicDataSource; Future> generateQueue( ShuffleMode shuffleMode, List songModels, int startIndex, ) async { List queue; switch (shuffleMode) { case ShuffleMode.none: queue = _generateNormalQueue(songModels); break; case ShuffleMode.standard: queue = _generateShuffleQueue(songModels, startIndex); break; case ShuffleMode.plus: queue = await _generateShufflePlusQueue(songModels, startIndex); } return queue; } ConcatenatingAudioSource mediaItemsToAudioSource(List mediaItems) { return ConcatenatingAudioSource( children: mediaItems.map((MediaItem m) => AudioSource.uri(Uri.file(m.id))).toList(), ); } ConcatenatingAudioSource songModelsToAudioSource(List songModels) { return ConcatenatingAudioSource( children: songModels.map((SongModel m) => AudioSource.uri(Uri.file(m.path))).toList(), ); } List _generateNormalQueue(List songs) { return List.generate( songs.length, (i) => QueueItemModel( songs[i], originalIndex: i, ), ); } List _generateShuffleQueue( List songs, int startIndex, ) { final List queue = List.generate( songs.length, (i) => QueueItemModel( songs[i], originalIndex: i, ), ); queue.removeAt(startIndex); queue.shuffle(); final first = QueueItemModel( 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 SongModel song = songs[index]; queue.addAll(await _getQueueItemWithLinks(song, index)); } return queue; } // TODO: naming things is hard Future> _getQueueItemWithLinks( SongModel song, int index, ) async { final List queueItems = []; final predecessors = await _getPredecessors(song); final successors = await _getSuccessors(song); for (final p in predecessors) { queueItems.add(QueueItemModel( p, originalIndex: index, type: QueueItemType.predecessor, )); } queueItems.add(QueueItemModel( song, originalIndex: index, )); for (final p in successors) { queueItems.add(QueueItemModel( p, originalIndex: index, type: QueueItemType.successor, )); } return queueItems; } Future> _getPredecessors(SongModel song) async { final List songs = []; SongModel currentSong = song; while (currentSong.previous != null) { currentSong = await _musicDataSource.getSongByPath(currentSong.previous); songs.add(currentSong); } return songs.reversed.toList(); } Future> _getSuccessors(SongModel song) async { final List songs = []; SongModel currentSong = song; while (currentSong.next != null) { currentSong = await _musicDataSource.getSongByPath(currentSong.next); songs.add(currentSong); } return songs.toList(); } }