queue persistence implemented
This commit is contained in:
parent
9c58244bb4
commit
9e4459bcc3
12 changed files with 489 additions and 124 deletions
|
@ -1,4 +1,4 @@
|
|||
import 'song_model.dart';
|
||||
import 'song.dart';
|
||||
|
||||
class QueueItem {
|
||||
QueueItem(
|
||||
|
@ -7,7 +7,7 @@ class QueueItem {
|
|||
this.type = QueueItemType.standard,
|
||||
});
|
||||
|
||||
final SongModel song;
|
||||
final Song song;
|
||||
final int originalIndex;
|
||||
final QueueItemType type;
|
||||
}
|
|
@ -9,6 +9,7 @@ abstract class MusicDataRepository {
|
|||
Stream<List<Song>> get songStream;
|
||||
Stream<List<Song>> getAlbumSongStream(Album album);
|
||||
Stream<List<Song>> get queueStream;
|
||||
Stream<int> get currentIndexStream;
|
||||
|
||||
Future<Either<Failure, List<Song>>> getSongs();
|
||||
Future<Either<Failure, List<Song>>> getSongsFromAlbum(Album album);
|
||||
|
|
|
@ -18,19 +18,17 @@ class AudioStore extends _AudioStore with _$AudioStore {
|
|||
|
||||
abstract class _AudioStore with Store {
|
||||
_AudioStore(this._audioRepository, this._musicDataRepository) {
|
||||
currentSongStream =
|
||||
_audioRepository.currentSongStream.distinct().asObservable();
|
||||
currentSongStream = _audioRepository.currentSongStream.distinct().asObservable();
|
||||
|
||||
currentPositionStream =
|
||||
_audioRepository.currentPositionStream.asObservable(initialValue: 0);
|
||||
currentPositionStream = _audioRepository.currentPositionStream.asObservable(initialValue: 0);
|
||||
|
||||
queueStream =
|
||||
_musicDataRepository.queueStream.asObservable(initialValue: []);
|
||||
queueStream = _musicDataRepository.queueStream.asObservable();
|
||||
|
||||
queueIndexStream = _audioRepository.queueIndexStream.asObservable();
|
||||
queueIndexStream = _musicDataRepository.currentIndexStream.asObservable();
|
||||
// queueIndexStream = _audioRepository.queueIndexStream.asObservable();
|
||||
|
||||
shuffleModeStream = _audioRepository.shuffleModeStream
|
||||
.asObservable(initialValue: ShuffleMode.none);
|
||||
shuffleModeStream =
|
||||
_audioRepository.shuffleModeStream.asObservable(initialValue: ShuffleMode.none);
|
||||
|
||||
playbackStateStream = _audioRepository.playbackStateStream.asObservable();
|
||||
}
|
||||
|
@ -44,9 +42,14 @@ abstract class _AudioStore with Store {
|
|||
@computed
|
||||
Song get currentSong {
|
||||
print('currentSong!!!');
|
||||
if (queueStream.value != [] && queueIndexStream.status == StreamStatus.active) {
|
||||
final song = queueStream.value[queueIndexStream.value];
|
||||
return song;
|
||||
print(queueStream.value);
|
||||
print(queueIndexStream.value);
|
||||
|
||||
if (queueStream.value != null && queueIndexStream.value != null) {
|
||||
if (queueIndexStream.value < queueStream.value.length) {
|
||||
final song = queueStream.value[queueIndexStream.value];
|
||||
return song;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:logging/logging.dart';
|
|||
import '../../domain/entities/player_state.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';
|
||||
import 'audio_player_contract.dart';
|
||||
import 'stream_constants.dart';
|
||||
|
@ -29,6 +30,15 @@ class MyAudioHandler extends BaseAudioHandler {
|
|||
_audioPlayer.shuffleModeStream.listen((shuffleMode) {
|
||||
customEventSubject.add({SHUFFLE_MODE: shuffleMode});
|
||||
});
|
||||
|
||||
_initAudioPlayer();
|
||||
}
|
||||
|
||||
Future<void> _initAudioPlayer() async {
|
||||
_audioPlayer.loadQueue(
|
||||
queue: await _musicDataSource.queueStream.first,
|
||||
startIndex: await _musicDataSource.currentIndexStream.first,
|
||||
);
|
||||
}
|
||||
|
||||
final AudioPlayer _audioPlayer;
|
||||
|
@ -126,18 +136,20 @@ class MyAudioHandler extends BaseAudioHandler {
|
|||
_audioPlayer.removeQueueIndex(index);
|
||||
}
|
||||
|
||||
void _handleSetQueue(List<SongModel> queue) {
|
||||
void _handleSetQueue(List<QueueItemModel> queue) {
|
||||
print('handleSetQueue');
|
||||
_musicDataSource.setQueue(queue);
|
||||
|
||||
final mediaItems = queue.map((e) => e.toMediaItem()).toList();
|
||||
final mediaItems = queue.map((e) => e.song.toMediaItem()).toList();
|
||||
queueSubject.add(mediaItems);
|
||||
}
|
||||
|
||||
void _handleIndexChange(int index) {
|
||||
_log.info('index: $index');
|
||||
if (index != null) {
|
||||
customEventSubject.add({KEY_INDEX: index});
|
||||
_musicDataSource.setCurrentIndex(index);
|
||||
|
||||
customEventSubject.add({KEY_INDEX: index});
|
||||
playbackStateSubject.add(playbackState.value.copyWith(
|
||||
controls: [
|
||||
MediaControl.skipToPrevious,
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'package:rxdart/rxdart.dart';
|
|||
|
||||
import '../../domain/entities/shuffle_mode.dart';
|
||||
import '../models/player_state_model.dart';
|
||||
import '../models/queue_item.dart';
|
||||
import '../models/queue_item_model.dart';
|
||||
import '../models/song_model.dart';
|
||||
|
||||
abstract class AudioPlayer {
|
||||
|
@ -10,7 +10,7 @@ abstract class AudioPlayer {
|
|||
ValueStream<SongModel> get currentSongStream;
|
||||
ValueStream<PlayerStateModel> get playerStateStream;
|
||||
ValueStream<Duration> get positionStream;
|
||||
ValueStream<List<SongModel>> get queueStream;
|
||||
ValueStream<List<QueueItemModel>> get queueStream;
|
||||
ValueStream<ShuffleMode> get shuffleModeStream;
|
||||
|
||||
Future<void> play();
|
||||
|
@ -20,7 +20,7 @@ abstract class AudioPlayer {
|
|||
Future<void> seekToPrevious();
|
||||
Future<void> dispose();
|
||||
|
||||
Future<void> loadQueue(List<QueueItem> queue);
|
||||
Future<void> loadQueue({List<QueueItemModel> queue, int startIndex});
|
||||
Future<void> addToQueue(SongModel song);
|
||||
Future<void> moveQueueItem(int oldIndex, int newIndex);
|
||||
Future<void> removeQueueIndex(int index);
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import 'package:just_audio/just_audio.dart' as ja;
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
import '../../domain/entities/queue_item.dart';
|
||||
import '../../domain/entities/shuffle_mode.dart';
|
||||
import '../models/player_state_model.dart';
|
||||
import '../models/queue_item.dart';
|
||||
import '../models/queue_item_model.dart';
|
||||
import '../models/song_model.dart';
|
||||
import 'audio_player_contract.dart';
|
||||
import 'queue_generator.dart';
|
||||
|
@ -12,7 +13,7 @@ class AudioPlayerImpl implements AudioPlayer {
|
|||
AudioPlayerImpl(this._audioPlayer, this._queueGenerator) {
|
||||
_audioPlayer.currentIndexStream.listen((event) {
|
||||
_currentIndexSubject.add(event);
|
||||
_currentSongSubject.add(_queueSubject.value[event]);
|
||||
_currentSongSubject.add(_queueSubject.value[event].song);
|
||||
});
|
||||
|
||||
_audioPlayer.positionStream.listen((event) {
|
||||
|
@ -24,7 +25,7 @@ class AudioPlayerImpl implements AudioPlayer {
|
|||
});
|
||||
|
||||
_queueSubject.listen((event) {
|
||||
_currentSongSubject.add(event[_currentIndexSubject.value]);
|
||||
_currentSongSubject.add(event[_currentIndexSubject.value].song);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -33,13 +34,13 @@ class AudioPlayerImpl implements AudioPlayer {
|
|||
final QueueGenerator _queueGenerator;
|
||||
|
||||
List<SongModel> _inputQueue;
|
||||
List<QueueItem> _queue;
|
||||
List<QueueItemModel> _queue;
|
||||
|
||||
final BehaviorSubject<int> _currentIndexSubject = BehaviorSubject();
|
||||
final BehaviorSubject<SongModel> _currentSongSubject = BehaviorSubject();
|
||||
final BehaviorSubject<PlayerStateModel> _playerStateSubject = BehaviorSubject();
|
||||
final BehaviorSubject<Duration> _positionSubject = BehaviorSubject();
|
||||
final BehaviorSubject<List<SongModel>> _queueSubject = BehaviorSubject.seeded([]);
|
||||
final BehaviorSubject<List<QueueItemModel>> _queueSubject = BehaviorSubject();
|
||||
final BehaviorSubject<ShuffleMode> _shuffleModeSubject = BehaviorSubject.seeded(ShuffleMode.none);
|
||||
|
||||
@override
|
||||
|
@ -55,7 +56,7 @@ class AudioPlayerImpl implements AudioPlayer {
|
|||
ValueStream<Duration> get positionStream => _positionSubject.stream;
|
||||
|
||||
@override
|
||||
ValueStream<List<SongModel>> get queueStream => _queueSubject.stream;
|
||||
ValueStream<List<QueueItemModel>> get queueStream => _queueSubject.stream;
|
||||
|
||||
@override
|
||||
ValueStream<ShuffleMode> get shuffleModeStream => _shuffleModeSubject.stream;
|
||||
|
@ -66,9 +67,21 @@ class AudioPlayerImpl implements AudioPlayer {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> loadQueue(List<QueueItem> queue) {
|
||||
// TODO: implement loadQueue
|
||||
throw UnimplementedError();
|
||||
Future<void> loadQueue({List<QueueItemModel> queue, int startIndex = 0}) async {
|
||||
if (queue == null || queue.isEmpty) {
|
||||
return;
|
||||
}
|
||||
_audioSource = _queueGenerator.songModelsToAudioSource([queue[startIndex].song]);
|
||||
await _audioPlayer.load(_audioSource);
|
||||
|
||||
final songModelQueue = queue.map((e) => e.song).toList();
|
||||
_queueSubject.add(queue);
|
||||
|
||||
final completeAudioSource = _queueGenerator.songModelsToAudioSource(songModelQueue);
|
||||
_audioSource.insertAll(0, completeAudioSource.children.sublist(0, startIndex));
|
||||
_audioSource.addAll(
|
||||
completeAudioSource.children.sublist(startIndex + 1, completeAudioSource.length),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -85,15 +98,15 @@ class AudioPlayerImpl implements AudioPlayer {
|
|||
Future<void> playSongList(List<SongModel> songs, int startIndex) async {
|
||||
_inputQueue = songs;
|
||||
|
||||
final firstSong = songs.sublist(startIndex, startIndex + 1);
|
||||
_queueSubject.add(firstSong);
|
||||
_audioSource = _queueGenerator.songModelsToAudioSource(firstSong);
|
||||
final firstSong = songs[startIndex]; // .sublist(startIndex, startIndex + 1);
|
||||
_queueSubject.add([QueueItemModel(firstSong, originalIndex: startIndex)]);
|
||||
_audioSource = _queueGenerator.songModelsToAudioSource([firstSong]);
|
||||
_audioPlayer.play();
|
||||
await _audioPlayer.load(_audioSource, initialIndex: 0);
|
||||
|
||||
_queue = await _queueGenerator.generateQueue(_shuffleModeSubject.value, songs, startIndex);
|
||||
final songModelQueue = _queue.map((e) => e.song).toList();
|
||||
_queueSubject.add(songModelQueue);
|
||||
_queueSubject.add(_queue);
|
||||
|
||||
final int splitIndex = _shuffleModeSubject.value == ShuffleMode.none ? startIndex : 0;
|
||||
final newQueue = _queueGenerator.songModelsToAudioSource(songModelQueue);
|
||||
|
@ -124,23 +137,23 @@ class AudioPlayerImpl implements AudioPlayer {
|
|||
@override
|
||||
Future<void> addToQueue(SongModel song) async {
|
||||
await _audioSource.add(ja.AudioSource.uri(Uri.file(song.path)));
|
||||
_queue.add(QueueItem(song, originalIndex: -1, type: QueueItemType.added));
|
||||
_queueSubject.add(_queue.map((e) => e.song).toList());
|
||||
_queue.add(QueueItemModel(song, originalIndex: -1, type: QueueItemType.added));
|
||||
_queueSubject.add(_queue);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> moveQueueItem(int oldIndex, int newIndex) async {
|
||||
final QueueItem queueItem = _queue.removeAt(oldIndex);
|
||||
final QueueItemModel queueItem = _queue.removeAt(oldIndex);
|
||||
final index = newIndex < oldIndex ? newIndex : newIndex - 1;
|
||||
_queue.insert(index, queueItem);
|
||||
_queueSubject.add(_queue.map((e) => e.song).toList());
|
||||
_queueSubject.add(_queue);
|
||||
await _audioSource.move(oldIndex, index);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeQueueIndex(int index) async {
|
||||
_queue.removeAt(index);
|
||||
_queueSubject.add(_queue.map((e) => e.song).toList());
|
||||
_queueSubject.add(_queue);
|
||||
await _audioSource.removeAt(index);
|
||||
}
|
||||
|
||||
|
@ -154,7 +167,7 @@ class AudioPlayerImpl implements AudioPlayer {
|
|||
_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(songModelQueue);
|
||||
_queueSubject.add(_queue);
|
||||
|
||||
final newQueue = _queueGenerator.songModelsToAudioSource(songModelQueue);
|
||||
_updateQueue(newQueue, currentQueueItem);
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
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.dart';
|
||||
import '../models/queue_item_model.dart';
|
||||
import '../models/song_model.dart';
|
||||
|
||||
class QueueGenerator {
|
||||
|
@ -11,12 +12,12 @@ class QueueGenerator {
|
|||
|
||||
final MusicDataSource _musicDataSource;
|
||||
|
||||
Future<List<QueueItem>> generateQueue(
|
||||
Future<List<QueueItemModel>> generateQueue(
|
||||
ShuffleMode shuffleMode,
|
||||
List<SongModel> songModels,
|
||||
int startIndex,
|
||||
) async {
|
||||
List<QueueItem> queue;
|
||||
List<QueueItemModel> queue;
|
||||
|
||||
switch (shuffleMode) {
|
||||
case ShuffleMode.none:
|
||||
|
@ -44,41 +45,41 @@ class QueueGenerator {
|
|||
);
|
||||
}
|
||||
|
||||
List<QueueItem> _generateNormalQueue(List<SongModel> songs) {
|
||||
return List<QueueItem>.generate(
|
||||
List<QueueItemModel> _generateNormalQueue(List<SongModel> songs) {
|
||||
return List<QueueItemModel>.generate(
|
||||
songs.length,
|
||||
(i) => QueueItem(
|
||||
(i) => QueueItemModel(
|
||||
songs[i],
|
||||
originalIndex: i,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<QueueItem> _generateShuffleQueue(
|
||||
List<QueueItemModel> _generateShuffleQueue(
|
||||
List<SongModel> songs,
|
||||
int startIndex,
|
||||
) {
|
||||
final List<QueueItem> queue = List<QueueItem>.generate(
|
||||
final List<QueueItemModel> queue = List<QueueItemModel>.generate(
|
||||
songs.length,
|
||||
(i) => QueueItem(
|
||||
(i) => QueueItemModel(
|
||||
songs[i],
|
||||
originalIndex: i,
|
||||
),
|
||||
);
|
||||
queue.removeAt(startIndex);
|
||||
queue.shuffle();
|
||||
final first = QueueItem(
|
||||
final first = QueueItemModel(
|
||||
songs[startIndex],
|
||||
originalIndex: startIndex,
|
||||
);
|
||||
return [first] + queue;
|
||||
}
|
||||
|
||||
Future<List<QueueItem>> _generateShufflePlusQueue(
|
||||
Future<List<QueueItemModel>> _generateShufflePlusQueue(
|
||||
List<SongModel> songs,
|
||||
int startIndex,
|
||||
) async {
|
||||
final List<QueueItem> queue = await _getQueueItemWithLinks(
|
||||
final List<QueueItemModel> queue = await _getQueueItemWithLinks(
|
||||
songs[startIndex],
|
||||
startIndex,
|
||||
);
|
||||
|
@ -105,30 +106,30 @@ class QueueGenerator {
|
|||
}
|
||||
|
||||
// TODO: naming things is hard
|
||||
Future<List<QueueItem>> _getQueueItemWithLinks(
|
||||
Future<List<QueueItemModel>> _getQueueItemWithLinks(
|
||||
SongModel song,
|
||||
int index,
|
||||
) async {
|
||||
final List<QueueItem> queueItems = [];
|
||||
final List<QueueItemModel> queueItems = [];
|
||||
|
||||
final predecessors = await _getPredecessors(song);
|
||||
final successors = await _getSuccessors(song);
|
||||
|
||||
for (final p in predecessors) {
|
||||
queueItems.add(QueueItem(
|
||||
queueItems.add(QueueItemModel(
|
||||
p,
|
||||
originalIndex: index,
|
||||
type: QueueItemType.predecessor,
|
||||
));
|
||||
}
|
||||
|
||||
queueItems.add(QueueItem(
|
||||
queueItems.add(QueueItemModel(
|
||||
song,
|
||||
originalIndex: index,
|
||||
));
|
||||
|
||||
for (final p in successors) {
|
||||
queueItems.add(QueueItem(
|
||||
queueItems.add(QueueItemModel(
|
||||
p,
|
||||
originalIndex: index,
|
||||
type: QueueItemType.successor,
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:moor/ffi.dart';
|
||||
import 'package:moor/isolate.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:moor/ffi.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../models/album_model.dart';
|
||||
import '../models/artist_model.dart';
|
||||
import '../models/queue_item_model.dart';
|
||||
import '../models/song_model.dart';
|
||||
import 'music_data_source_contract.dart';
|
||||
|
||||
|
@ -54,18 +55,24 @@ class Songs extends Table {
|
|||
Set<Column> get primaryKey => {path};
|
||||
}
|
||||
|
||||
@DataClassName('QueueEntry')
|
||||
@DataClassName('MoorQueueEntry')
|
||||
class QueueEntries extends Table {
|
||||
IntColumn get index => integer()();
|
||||
TextColumn get path => text()();
|
||||
IntColumn get originalIndex => integer()();
|
||||
IntColumn get type => integer()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {index};
|
||||
}
|
||||
|
||||
@UseMoor(tables: [Artists, Albums, Songs, QueueEntries])
|
||||
class MoorMusicDataSource extends _$MoorMusicDataSource
|
||||
implements MusicDataSource {
|
||||
@DataClassName('PersistentPlayerState')
|
||||
class PlayerState extends Table {
|
||||
IntColumn get index => integer()();
|
||||
}
|
||||
|
||||
@UseMoor(tables: [Artists, Albums, Songs, QueueEntries, PlayerState])
|
||||
class MoorMusicDataSource extends _$MoorMusicDataSource implements MusicDataSource {
|
||||
/// Use MoorMusicDataSource in main isolate only.
|
||||
MoorMusicDataSource() : super(_openConnection());
|
||||
|
||||
|
@ -73,17 +80,15 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
|
|||
MoorMusicDataSource.withQueryExecutor(QueryExecutor e) : super(e);
|
||||
|
||||
/// Used to connect to a database on another isolate.
|
||||
MoorMusicDataSource.connect(DatabaseConnection connection)
|
||||
: super.connect(connection);
|
||||
MoorMusicDataSource.connect(DatabaseConnection connection) : super.connect(connection);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
|
||||
@override
|
||||
Future<List<AlbumModel>> getAlbums() async {
|
||||
return select(albums).get().then((moorAlbumList) => moorAlbumList
|
||||
.map((moorAlbum) => AlbumModel.fromMoorAlbum(moorAlbum))
|
||||
.toList());
|
||||
return select(albums).get().then((moorAlbumList) =>
|
||||
moorAlbumList.map((moorAlbum) => AlbumModel.fromMoorAlbum(moorAlbum)).toList());
|
||||
}
|
||||
|
||||
// TODO: insert can throw exception -> implications?
|
||||
|
@ -94,16 +99,14 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
|
|||
|
||||
@override
|
||||
Stream<List<SongModel>> get songStream {
|
||||
return select(songs).watch().map((moorSongList) => moorSongList
|
||||
.map((moorSong) => SongModel.fromMoorSong(moorSong))
|
||||
.toList());
|
||||
return select(songs).watch().map((moorSongList) =>
|
||||
moorSongList.map((moorSong) => SongModel.fromMoorSong(moorSong)).toList());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<SongModel>> getSongs() {
|
||||
return select(songs).get().then((moorSongList) => moorSongList
|
||||
.map((moorSong) => SongModel.fromMoorSong(moorSong))
|
||||
.toList());
|
||||
return select(songs).get().then((moorSongList) =>
|
||||
moorSongList.map((moorSong) => SongModel.fromMoorSong(moorSong)).toList());
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -115,9 +118,8 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
|
|||
(t) => OrderingTerm(expression: t.trackNumber)
|
||||
]))
|
||||
.watch()
|
||||
.map((moorSongList) => moorSongList
|
||||
.map((moorSong) => SongModel.fromMoorSong(moorSong))
|
||||
.toList());
|
||||
.map((moorSongList) =>
|
||||
moorSongList.map((moorSong) => SongModel.fromMoorSong(moorSong)).toList());
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -129,9 +131,8 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
|
|||
(t) => OrderingTerm(expression: t.trackNumber)
|
||||
]))
|
||||
.get()
|
||||
.then((moorSongList) => moorSongList
|
||||
.map((moorSong) => SongModel.fromMoorSong(moorSong))
|
||||
.toList());
|
||||
.then((moorSongList) =>
|
||||
moorSongList.map((moorSong) => SongModel.fromMoorSong(moorSong)).toList());
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -173,9 +174,8 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
|
|||
|
||||
@override
|
||||
Future<List<ArtistModel>> getArtists() {
|
||||
return select(artists).get().then((moorArtistList) => moorArtistList
|
||||
.map((moorArtist) => ArtistModel.fromMoorArtist(moorArtist))
|
||||
.toList());
|
||||
return select(artists).get().then((moorArtistList) =>
|
||||
moorArtistList.map((moorArtist) => ArtistModel.fromMoorArtist(moorArtist)).toList());
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -209,9 +209,8 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
|
|||
(t) => OrderingTerm(expression: t.trackNumber)
|
||||
]))
|
||||
.get()
|
||||
.then((moorSongList) => moorSongList
|
||||
.map((moorSong) => SongModel.fromMoorSong(moorSong))
|
||||
.toList());
|
||||
.then((moorSongList) =>
|
||||
moorSongList.map((moorSong) => SongModel.fromMoorSong(moorSong)).toList());
|
||||
|
||||
bool current = false;
|
||||
SongModel nextSong;
|
||||
|
@ -233,34 +232,67 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
|
|||
}
|
||||
|
||||
@override
|
||||
Stream<List<SongModel>> get queueStream {
|
||||
final query = (select(queueEntries)
|
||||
..orderBy([(t) => OrderingTerm(expression: t.index)]))
|
||||
Stream<List<SongModel>> get songQueueStream {
|
||||
final query = (select(queueEntries)..orderBy([(t) => OrderingTerm(expression: t.index)]))
|
||||
.join([innerJoin(songs, songs.path.equalsExp(queueEntries.path))]);
|
||||
|
||||
return query.watch().map((rows) {
|
||||
return rows
|
||||
.map((row) => SongModel.fromMoorSong(row.readTable(songs)))
|
||||
.toList();
|
||||
print('rows: ${rows.length}');
|
||||
return rows.map((row) => SongModel.fromMoorSong(row.readTable(songs))).toList();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setQueue(List<SongModel> queue) async {
|
||||
final _queueEntries = <Insertable<QueueEntry>>[];
|
||||
|
||||
Stream<List<QueueItemModel>> get queueStream {
|
||||
final query = (select(queueEntries)..orderBy([(t) => OrderingTerm(expression: t.index)]))
|
||||
.join([innerJoin(songs, songs.path.equalsExp(queueEntries.path))]);
|
||||
|
||||
return query.watch().map((rows) {
|
||||
return rows.map((row) {
|
||||
return QueueItemModel(
|
||||
SongModel.fromMoorSong(row.readTable(songs)),
|
||||
originalIndex: row.readTable(queueEntries).originalIndex,
|
||||
type: row.readTable(queueEntries).type.toQueueItemType(),
|
||||
);
|
||||
}).toList();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setQueue(List<QueueItemModel> queue) async {
|
||||
print('setQueue');
|
||||
final _queueEntries = <Insertable<MoorQueueEntry>>[];
|
||||
|
||||
for (var i = 0; i < queue.length; i++) {
|
||||
_queueEntries.add(QueueEntriesCompanion(index: Value(i), path: Value(queue[i].path)));
|
||||
_queueEntries.add(QueueEntriesCompanion(
|
||||
index: Value(i),
|
||||
path: Value(queue[i].song.path),
|
||||
originalIndex: Value(queue[i].originalIndex),
|
||||
type: Value(queue[i].type.toInt()),
|
||||
));
|
||||
}
|
||||
|
||||
await delete(queueEntries).go();
|
||||
await batch((batch) {
|
||||
batch.insertAll(
|
||||
queueEntries,
|
||||
_queueEntries
|
||||
);
|
||||
batch.insertAll(queueEntries, _queueEntries);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<int> get currentIndexStream {
|
||||
return select(playerState).watchSingle().map((event) => event.index);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setCurrentIndex(int index) async {
|
||||
print('setCurrentIndex: $index');
|
||||
final currentState = await select(playerState).getSingle();
|
||||
if (currentState != null) {
|
||||
update(playerState).write(PlayerStateCompanion(index: Value(index)));
|
||||
} else {
|
||||
into(playerState).insert(PlayerStateCompanion(index: Value(index)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LazyDatabase _openConnection() {
|
||||
|
|
|
@ -1213,18 +1213,28 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> {
|
|||
}
|
||||
}
|
||||
|
||||
class QueueEntry extends DataClass implements Insertable<QueueEntry> {
|
||||
class MoorQueueEntry extends DataClass implements Insertable<MoorQueueEntry> {
|
||||
final int index;
|
||||
final String path;
|
||||
QueueEntry({@required this.index, @required this.path});
|
||||
factory QueueEntry.fromData(Map<String, dynamic> data, GeneratedDatabase db,
|
||||
final int originalIndex;
|
||||
final int type;
|
||||
MoorQueueEntry(
|
||||
{@required this.index,
|
||||
@required this.path,
|
||||
@required this.originalIndex,
|
||||
@required this.type});
|
||||
factory MoorQueueEntry.fromData(
|
||||
Map<String, dynamic> data, GeneratedDatabase db,
|
||||
{String prefix}) {
|
||||
final effectivePrefix = prefix ?? '';
|
||||
final intType = db.typeSystem.forDartType<int>();
|
||||
final stringType = db.typeSystem.forDartType<String>();
|
||||
return QueueEntry(
|
||||
return MoorQueueEntry(
|
||||
index: intType.mapFromDatabaseResponse(data['${effectivePrefix}index']),
|
||||
path: stringType.mapFromDatabaseResponse(data['${effectivePrefix}path']),
|
||||
originalIndex: intType
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}original_index']),
|
||||
type: intType.mapFromDatabaseResponse(data['${effectivePrefix}type']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
|
@ -1236,6 +1246,12 @@ class QueueEntry extends DataClass implements Insertable<QueueEntry> {
|
|||
if (!nullToAbsent || path != null) {
|
||||
map['path'] = Variable<String>(path);
|
||||
}
|
||||
if (!nullToAbsent || originalIndex != null) {
|
||||
map['original_index'] = Variable<int>(originalIndex);
|
||||
}
|
||||
if (!nullToAbsent || type != null) {
|
||||
map['type'] = Variable<int>(type);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
@ -1244,15 +1260,21 @@ class QueueEntry extends DataClass implements Insertable<QueueEntry> {
|
|||
index:
|
||||
index == null && nullToAbsent ? const Value.absent() : Value(index),
|
||||
path: path == null && nullToAbsent ? const Value.absent() : Value(path),
|
||||
originalIndex: originalIndex == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(originalIndex),
|
||||
type: type == null && nullToAbsent ? const Value.absent() : Value(type),
|
||||
);
|
||||
}
|
||||
|
||||
factory QueueEntry.fromJson(Map<String, dynamic> json,
|
||||
factory MoorQueueEntry.fromJson(Map<String, dynamic> json,
|
||||
{ValueSerializer serializer}) {
|
||||
serializer ??= moorRuntimeOptions.defaultSerializer;
|
||||
return QueueEntry(
|
||||
return MoorQueueEntry(
|
||||
index: serializer.fromJson<int>(json['index']),
|
||||
path: serializer.fromJson<String>(json['path']),
|
||||
originalIndex: serializer.fromJson<int>(json['originalIndex']),
|
||||
type: serializer.fromJson<int>(json['type']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
|
@ -1261,57 +1283,86 @@ class QueueEntry extends DataClass implements Insertable<QueueEntry> {
|
|||
return <String, dynamic>{
|
||||
'index': serializer.toJson<int>(index),
|
||||
'path': serializer.toJson<String>(path),
|
||||
'originalIndex': serializer.toJson<int>(originalIndex),
|
||||
'type': serializer.toJson<int>(type),
|
||||
};
|
||||
}
|
||||
|
||||
QueueEntry copyWith({int index, String path}) => QueueEntry(
|
||||
MoorQueueEntry copyWith(
|
||||
{int index, String path, int originalIndex, int type}) =>
|
||||
MoorQueueEntry(
|
||||
index: index ?? this.index,
|
||||
path: path ?? this.path,
|
||||
originalIndex: originalIndex ?? this.originalIndex,
|
||||
type: type ?? this.type,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('QueueEntry(')
|
||||
return (StringBuffer('MoorQueueEntry(')
|
||||
..write('index: $index, ')
|
||||
..write('path: $path')
|
||||
..write('path: $path, ')
|
||||
..write('originalIndex: $originalIndex, ')
|
||||
..write('type: $type')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(index.hashCode, path.hashCode));
|
||||
int get hashCode => $mrjf($mrjc(index.hashCode,
|
||||
$mrjc(path.hashCode, $mrjc(originalIndex.hashCode, type.hashCode))));
|
||||
@override
|
||||
bool operator ==(dynamic other) =>
|
||||
identical(this, other) ||
|
||||
(other is QueueEntry &&
|
||||
(other is MoorQueueEntry &&
|
||||
other.index == this.index &&
|
||||
other.path == this.path);
|
||||
other.path == this.path &&
|
||||
other.originalIndex == this.originalIndex &&
|
||||
other.type == this.type);
|
||||
}
|
||||
|
||||
class QueueEntriesCompanion extends UpdateCompanion<QueueEntry> {
|
||||
class QueueEntriesCompanion extends UpdateCompanion<MoorQueueEntry> {
|
||||
final Value<int> index;
|
||||
final Value<String> path;
|
||||
final Value<int> originalIndex;
|
||||
final Value<int> type;
|
||||
const QueueEntriesCompanion({
|
||||
this.index = const Value.absent(),
|
||||
this.path = const Value.absent(),
|
||||
this.originalIndex = const Value.absent(),
|
||||
this.type = const Value.absent(),
|
||||
});
|
||||
QueueEntriesCompanion.insert({
|
||||
this.index = const Value.absent(),
|
||||
@required String path,
|
||||
}) : path = Value(path);
|
||||
static Insertable<QueueEntry> custom({
|
||||
@required int originalIndex,
|
||||
@required int type,
|
||||
}) : path = Value(path),
|
||||
originalIndex = Value(originalIndex),
|
||||
type = Value(type);
|
||||
static Insertable<MoorQueueEntry> custom({
|
||||
Expression<int> index,
|
||||
Expression<String> path,
|
||||
Expression<int> originalIndex,
|
||||
Expression<int> type,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (index != null) 'index': index,
|
||||
if (path != null) 'path': path,
|
||||
if (originalIndex != null) 'original_index': originalIndex,
|
||||
if (type != null) 'type': type,
|
||||
});
|
||||
}
|
||||
|
||||
QueueEntriesCompanion copyWith({Value<int> index, Value<String> path}) {
|
||||
QueueEntriesCompanion copyWith(
|
||||
{Value<int> index,
|
||||
Value<String> path,
|
||||
Value<int> originalIndex,
|
||||
Value<int> type}) {
|
||||
return QueueEntriesCompanion(
|
||||
index: index ?? this.index,
|
||||
path: path ?? this.path,
|
||||
originalIndex: originalIndex ?? this.originalIndex,
|
||||
type: type ?? this.type,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1324,6 +1375,12 @@ class QueueEntriesCompanion extends UpdateCompanion<QueueEntry> {
|
|||
if (path.present) {
|
||||
map['path'] = Variable<String>(path.value);
|
||||
}
|
||||
if (originalIndex.present) {
|
||||
map['original_index'] = Variable<int>(originalIndex.value);
|
||||
}
|
||||
if (type.present) {
|
||||
map['type'] = Variable<int>(type.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
@ -1331,14 +1388,16 @@ class QueueEntriesCompanion extends UpdateCompanion<QueueEntry> {
|
|||
String toString() {
|
||||
return (StringBuffer('QueueEntriesCompanion(')
|
||||
..write('index: $index, ')
|
||||
..write('path: $path')
|
||||
..write('path: $path, ')
|
||||
..write('originalIndex: $originalIndex, ')
|
||||
..write('type: $type')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class $QueueEntriesTable extends QueueEntries
|
||||
with TableInfo<$QueueEntriesTable, QueueEntry> {
|
||||
with TableInfo<$QueueEntriesTable, MoorQueueEntry> {
|
||||
final GeneratedDatabase _db;
|
||||
final String _alias;
|
||||
$QueueEntriesTable(this._db, [this._alias]);
|
||||
|
@ -1366,8 +1425,34 @@ class $QueueEntriesTable extends QueueEntries
|
|||
);
|
||||
}
|
||||
|
||||
final VerificationMeta _originalIndexMeta =
|
||||
const VerificationMeta('originalIndex');
|
||||
GeneratedIntColumn _originalIndex;
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [index, path];
|
||||
GeneratedIntColumn get originalIndex =>
|
||||
_originalIndex ??= _constructOriginalIndex();
|
||||
GeneratedIntColumn _constructOriginalIndex() {
|
||||
return GeneratedIntColumn(
|
||||
'original_index',
|
||||
$tableName,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
final VerificationMeta _typeMeta = const VerificationMeta('type');
|
||||
GeneratedIntColumn _type;
|
||||
@override
|
||||
GeneratedIntColumn get type => _type ??= _constructType();
|
||||
GeneratedIntColumn _constructType() {
|
||||
return GeneratedIntColumn(
|
||||
'type',
|
||||
$tableName,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [index, path, originalIndex, type];
|
||||
@override
|
||||
$QueueEntriesTable get asDslTable => this;
|
||||
@override
|
||||
|
@ -1375,7 +1460,7 @@ class $QueueEntriesTable extends QueueEntries
|
|||
@override
|
||||
final String actualTableName = 'queue_entries';
|
||||
@override
|
||||
VerificationContext validateIntegrity(Insertable<QueueEntry> instance,
|
||||
VerificationContext validateIntegrity(Insertable<MoorQueueEntry> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
|
@ -1389,15 +1474,29 @@ class $QueueEntriesTable extends QueueEntries
|
|||
} else if (isInserting) {
|
||||
context.missing(_pathMeta);
|
||||
}
|
||||
if (data.containsKey('original_index')) {
|
||||
context.handle(
|
||||
_originalIndexMeta,
|
||||
originalIndex.isAcceptableOrUnknown(
|
||||
data['original_index'], _originalIndexMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_originalIndexMeta);
|
||||
}
|
||||
if (data.containsKey('type')) {
|
||||
context.handle(
|
||||
_typeMeta, type.isAcceptableOrUnknown(data['type'], _typeMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_typeMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<GeneratedColumn> get $primaryKey => {index};
|
||||
@override
|
||||
QueueEntry map(Map<String, dynamic> data, {String tablePrefix}) {
|
||||
MoorQueueEntry map(Map<String, dynamic> data, {String tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
|
||||
return QueueEntry.fromData(data, _db, prefix: effectivePrefix);
|
||||
return MoorQueueEntry.fromData(data, _db, prefix: effectivePrefix);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1406,6 +1505,163 @@ class $QueueEntriesTable extends QueueEntries
|
|||
}
|
||||
}
|
||||
|
||||
class PersistentPlayerState extends DataClass
|
||||
implements Insertable<PersistentPlayerState> {
|
||||
final int index;
|
||||
PersistentPlayerState({@required this.index});
|
||||
factory PersistentPlayerState.fromData(
|
||||
Map<String, dynamic> data, GeneratedDatabase db,
|
||||
{String prefix}) {
|
||||
final effectivePrefix = prefix ?? '';
|
||||
final intType = db.typeSystem.forDartType<int>();
|
||||
return PersistentPlayerState(
|
||||
index: intType.mapFromDatabaseResponse(data['${effectivePrefix}index']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
if (!nullToAbsent || index != null) {
|
||||
map['index'] = Variable<int>(index);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
PlayerStateCompanion toCompanion(bool nullToAbsent) {
|
||||
return PlayerStateCompanion(
|
||||
index:
|
||||
index == null && nullToAbsent ? const Value.absent() : Value(index),
|
||||
);
|
||||
}
|
||||
|
||||
factory PersistentPlayerState.fromJson(Map<String, dynamic> json,
|
||||
{ValueSerializer serializer}) {
|
||||
serializer ??= moorRuntimeOptions.defaultSerializer;
|
||||
return PersistentPlayerState(
|
||||
index: serializer.fromJson<int>(json['index']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({ValueSerializer serializer}) {
|
||||
serializer ??= moorRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'index': serializer.toJson<int>(index),
|
||||
};
|
||||
}
|
||||
|
||||
PersistentPlayerState copyWith({int index}) => PersistentPlayerState(
|
||||
index: index ?? this.index,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('PersistentPlayerState(')
|
||||
..write('index: $index')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf(index.hashCode);
|
||||
@override
|
||||
bool operator ==(dynamic other) =>
|
||||
identical(this, other) ||
|
||||
(other is PersistentPlayerState && other.index == this.index);
|
||||
}
|
||||
|
||||
class PlayerStateCompanion extends UpdateCompanion<PersistentPlayerState> {
|
||||
final Value<int> index;
|
||||
const PlayerStateCompanion({
|
||||
this.index = const Value.absent(),
|
||||
});
|
||||
PlayerStateCompanion.insert({
|
||||
@required int index,
|
||||
}) : index = Value(index);
|
||||
static Insertable<PersistentPlayerState> custom({
|
||||
Expression<int> index,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (index != null) 'index': index,
|
||||
});
|
||||
}
|
||||
|
||||
PlayerStateCompanion copyWith({Value<int> index}) {
|
||||
return PlayerStateCompanion(
|
||||
index: index ?? this.index,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
if (index.present) {
|
||||
map['index'] = Variable<int>(index.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('PlayerStateCompanion(')
|
||||
..write('index: $index')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class $PlayerStateTable extends PlayerState
|
||||
with TableInfo<$PlayerStateTable, PersistentPlayerState> {
|
||||
final GeneratedDatabase _db;
|
||||
final String _alias;
|
||||
$PlayerStateTable(this._db, [this._alias]);
|
||||
final VerificationMeta _indexMeta = const VerificationMeta('index');
|
||||
GeneratedIntColumn _index;
|
||||
@override
|
||||
GeneratedIntColumn get index => _index ??= _constructIndex();
|
||||
GeneratedIntColumn _constructIndex() {
|
||||
return GeneratedIntColumn(
|
||||
'index',
|
||||
$tableName,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [index];
|
||||
@override
|
||||
$PlayerStateTable get asDslTable => this;
|
||||
@override
|
||||
String get $tableName => _alias ?? 'player_state';
|
||||
@override
|
||||
final String actualTableName = 'player_state';
|
||||
@override
|
||||
VerificationContext validateIntegrity(
|
||||
Insertable<PersistentPlayerState> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('index')) {
|
||||
context.handle(
|
||||
_indexMeta, index.isAcceptableOrUnknown(data['index'], _indexMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_indexMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
||||
@override
|
||||
PersistentPlayerState map(Map<String, dynamic> data, {String tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
|
||||
return PersistentPlayerState.fromData(data, _db, prefix: effectivePrefix);
|
||||
}
|
||||
|
||||
@override
|
||||
$PlayerStateTable createAlias(String alias) {
|
||||
return $PlayerStateTable(_db, alias);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _$MoorMusicDataSource extends GeneratedDatabase {
|
||||
_$MoorMusicDataSource(QueryExecutor e)
|
||||
: super(SqlTypeSystem.defaultInstance, e);
|
||||
|
@ -1419,9 +1675,11 @@ abstract class _$MoorMusicDataSource extends GeneratedDatabase {
|
|||
$QueueEntriesTable _queueEntries;
|
||||
$QueueEntriesTable get queueEntries =>
|
||||
_queueEntries ??= $QueueEntriesTable(this);
|
||||
$PlayerStateTable _playerState;
|
||||
$PlayerStateTable get playerState => _playerState ??= $PlayerStateTable(this);
|
||||
@override
|
||||
Iterable<TableInfo> get allTables => allSchemaEntities.whereType<TableInfo>();
|
||||
@override
|
||||
List<DatabaseSchemaEntity> get allSchemaEntities =>
|
||||
[artists, albums, songs, queueEntries];
|
||||
[artists, albums, songs, queueEntries, playerState];
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import '../models/album_model.dart';
|
||||
import '../models/artist_model.dart';
|
||||
import '../models/queue_item_model.dart';
|
||||
import '../models/song_model.dart';
|
||||
|
||||
abstract class MusicDataSource {
|
||||
|
@ -8,8 +9,11 @@ abstract class MusicDataSource {
|
|||
Stream<List<SongModel>> get songStream;
|
||||
Stream<List<SongModel>> getAlbumSongStream(AlbumModel album);
|
||||
|
||||
Future<void> setQueue(List<SongModel> queue);
|
||||
Stream<List<SongModel>> get queueStream;
|
||||
Future<void> setQueue(List<QueueItemModel> queue);
|
||||
Stream<List<SongModel>> get songQueueStream;
|
||||
Stream<List<QueueItemModel>> get queueStream;
|
||||
Future<void> setCurrentIndex(int index);
|
||||
Stream<int> get currentIndexStream;
|
||||
|
||||
/// Insert album into the database. Return the ID of the inserted album.
|
||||
Future<int> insertAlbum(AlbumModel albumModel);
|
||||
|
|
38
lib/system/models/queue_item_model.dart
Normal file
38
lib/system/models/queue_item_model.dart
Normal file
|
@ -0,0 +1,38 @@
|
|||
import '../../domain/entities/queue_item.dart';
|
||||
import 'song_model.dart';
|
||||
|
||||
class QueueItemModel extends QueueItem {
|
||||
QueueItemModel(
|
||||
this.song, {
|
||||
int originalIndex,
|
||||
QueueItemType type = QueueItemType.standard,
|
||||
}) : super(song, originalIndex: originalIndex, type: type);
|
||||
|
||||
@override
|
||||
SongModel song;
|
||||
}
|
||||
|
||||
extension IntToQueueItemType on int {
|
||||
QueueItemType toQueueItemType() {
|
||||
switch(this) {
|
||||
case 1: return QueueItemType.predecessor;
|
||||
case 2: return QueueItemType.successor;
|
||||
case 3: return QueueItemType.added;
|
||||
default: return QueueItemType.standard;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension QueueItemTypeToInt on QueueItemType {
|
||||
int toInt() {
|
||||
switch(this) {
|
||||
case QueueItemType.predecessor:
|
||||
return 1;
|
||||
case QueueItemType.successor:
|
||||
return 2;
|
||||
case QueueItemType.added:
|
||||
return 3;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -135,5 +135,8 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
|
|||
}
|
||||
|
||||
@override
|
||||
Stream<List<Song>> get queueStream => musicDataSource.queueStream;
|
||||
Stream<List<Song>> get queueStream => musicDataSource.songQueueStream;
|
||||
|
||||
@override
|
||||
Stream<int> get currentIndexStream => musicDataSource.currentIndexStream;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue