db sync exploration

This commit is contained in:
Moritz Weber 2020-11-15 16:36:50 +01:00
parent c7b15dcf08
commit 3570f6cdbd
18 changed files with 479 additions and 71 deletions

View file

@ -32,5 +32,5 @@ class Song extends Equatable {
final int trackNumber;
@override
List<Object> get props => [title, album, artist];
List<Object> get props => [title, album, artist, blocked];
}

View file

@ -6,6 +6,10 @@ import '../entities/artist.dart';
import '../entities/song.dart';
abstract class MusicDataRepository {
Stream<List<Song>> get songStream;
Stream<List<Song>> getAlbumSongStream(Album album);
Stream<List<Song>> get queueStream;
Future<Either<Failure, List<Song>>> getSongs();
Future<Either<Failure, List<Song>>> getSongsFromAlbum(Album album);
Future<Either<Failure, List<Album>>> getAlbums();

View file

@ -38,6 +38,7 @@ Future<void> setupGetIt() async {
() {
final audioStore = AudioStore(
audioRepository: getIt(),
musicDataRepository: getIt(),
);
return audioStore;
},

View file

@ -68,11 +68,14 @@ class AlbumDetailsPage extends StatelessWidget {
if (index.isEven) {
final songIndex = (index / 2).round();
final Song song = musicDataStore.albumSongs[songIndex];
final Song song =
musicDataStore.albumSongStream.value[songIndex];
return SongListTile(
song: song,
inAlbum: true,
onTap: () => audioStore.playSong(songIndex, musicDataStore.albumSongs),
onTap: () => audioStore.playSong(
songIndex, musicDataStore.albumSongStream.value),
onTapMore: () => _openBottomSheet(song, context),
);
}
return const Divider(
@ -85,11 +88,45 @@ class AlbumDetailsPage extends StatelessWidget {
}
return null;
},
childCount: musicDataStore.albumSongs.length * 2,
childCount: musicDataStore.albumSongStream.value.length * 2,
),
),
],
),
);
}
void _openBottomSheet(Song song, BuildContext context) {
final AudioStore audioStore =
Provider.of<AudioStore>(context, listen: false);
final MusicDataStore musicDataStore =
Provider.of<MusicDataStore>(context, listen: false);
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
child: Column(
children: [
ListTile(
title: const Text('Add to queue'),
onTap: () {
audioStore.addToQueue(song);
Navigator.pop(context);
},
),
ListTile(
title: song.blocked
? const Text('Unblock song')
: const Text('Block song'),
onTap: () {
musicDataStore.setSongBlocked(song, !song.blocked);
Navigator.pop(context);
},
),
],
),
);
});
}
}

View file

@ -29,7 +29,7 @@ class CurrentlyPlayingPage extends StatelessWidget {
Observer(
builder: (BuildContext context) {
_log.info('Observer.build');
final Song song = audioStore.currentSongStream.value;
final Song song = audioStore.currentSong;
return Padding(
padding: const EdgeInsets.only(

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:provider/provider.dart';
import '../../domain/entities/song.dart';
@ -25,34 +26,39 @@ class _SongsPageState extends State<SongsPage>
super.build(context);
return Observer(builder: (_) {
print('SongsPage.build -> Observer.builder');
final bool isFetching = musicDataStore.isFetchingSongs;
if (isFetching) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
CircularProgressIndicator(),
Text('Loading items...'),
],
);
} else {
final List<Song> songs = musicDataStore.songs;
return ListView.separated(
itemCount: songs.length,
itemBuilder: (_, int index) {
final Song song = songs[index];
return SongListTile(
song: song,
inAlbum: false,
onTap: () => audioStore.playSong(index, songs),
onTapMore: () => _openBottomSheet(song),
);
},
separatorBuilder: (BuildContext context, int index) => const Divider(
height: 4.0,
),
);
final songStream = musicDataStore.songStream;
switch (songStream.status) {
case StreamStatus.active:
final List<Song> songs = songStream.value;
return ListView.separated(
itemCount: songs.length,
itemBuilder: (_, int index) {
final Song song = songs[index];
return SongListTile(
song: song,
inAlbum: false,
onTap: () => audioStore.playSong(index, songs),
onTapMore: () => _openBottomSheet(song),
);
},
separatorBuilder: (BuildContext context, int index) =>
const Divider(
height: 4.0,
),
);
case StreamStatus.waiting:
case StreamStatus.done:
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
CircularProgressIndicator(),
Text('Loading items...'),
],
);
}
return Container();
});
}
@ -60,7 +66,10 @@ class _SongsPageState extends State<SongsPage>
bool get wantKeepAlive => true;
void _openBottomSheet(Song song) {
final AudioStore audioStore = Provider.of<AudioStore>(context, listen: false);
final AudioStore audioStore =
Provider.of<AudioStore>(context, listen: false);
final MusicDataStore musicDataStore =
Provider.of<MusicDataStore>(context, listen: false);
showModalBottomSheet(
context: context,
@ -75,6 +84,15 @@ class _SongsPageState extends State<SongsPage>
Navigator.pop(context);
},
),
ListTile(
title: song.blocked
? const Text('Unblock song')
: const Text('Block song'),
onTap: () {
musicDataStore.setSongBlocked(song, !song.blocked);
Navigator.pop(context);
},
),
],
),
);

View file

@ -1,5 +1,6 @@
import 'package:meta/meta.dart';
import 'package:mobx/mobx.dart';
import 'package:mucke/domain/repositories/music_data_repository.dart';
import '../../domain/entities/playback_state.dart';
import '../../domain/entities/shuffle_mode.dart';
@ -9,18 +10,22 @@ import '../../domain/repositories/audio_repository.dart';
part 'audio_store.g.dart';
class AudioStore extends _AudioStore with _$AudioStore {
AudioStore({@required AudioRepository audioRepository})
: super(audioRepository);
AudioStore({
@required AudioRepository audioRepository,
@required MusicDataRepository musicDataRepository,
}) : super(audioRepository, musicDataRepository);
}
abstract class _AudioStore with Store {
_AudioStore(this._audioRepository) {
currentSongStream = _audioRepository.currentSongStream.distinct().asObservable();
_AudioStore(this._audioRepository, this._musicDataRepository) {
currentSongStream =
_audioRepository.currentSongStream.distinct().asObservable();
currentPositionStream =
_audioRepository.currentPositionStream.asObservable(initialValue: 0);
queueStream = _audioRepository.queueStream.asObservable(initialValue: []);
queueStream =
_musicDataRepository.queueStream.asObservable(initialValue: []);
queueIndexStream = _audioRepository.queueIndexStream.asObservable();
@ -31,10 +36,22 @@ abstract class _AudioStore with Store {
}
final AudioRepository _audioRepository;
final MusicDataRepository _musicDataRepository;
@observable
ObservableStream<Song> currentSongStream;
@computed
Song get currentSong {
print('currentSong!!!');
if (queueStream.value != [] && queueIndexStream.status == StreamStatus.active) {
final song = queueStream.value[queueIndexStream.value];
return song;
}
return null;
}
@observable
ObservableStream<PlaybackState> playbackStateStream;

View file

@ -9,6 +9,14 @@ part of 'audio_store.dart';
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
mixin _$AudioStore on _AudioStore, Store {
Computed<Song> _$currentSongComputed;
@override
Song get currentSong =>
(_$currentSongComputed ??= Computed<Song>(() => super.currentSong,
name: '_AudioStore.currentSong'))
.value;
final _$currentSongStreamAtom = Atom(name: '_AudioStore.currentSongStream');
@override
@ -145,7 +153,8 @@ playbackStateStream: ${playbackStateStream},
currentPositionStream: ${currentPositionStream},
queueStream: ${queueStream},
queueIndexStream: ${queueIndexStream},
shuffleModeStream: ${shuffleModeStream}
shuffleModeStream: ${shuffleModeStream},
currentSong: ${currentSong}
''';
}
}

View file

@ -14,12 +14,20 @@ class MusicDataStore extends _MusicDataStore with _$MusicDataStore {
}
abstract class _MusicDataStore with Store {
_MusicDataStore(this._musicDataRepository);
_MusicDataStore(this._musicDataRepository) {
songStream = _musicDataRepository.songStream.asObservable(initialValue: []);
}
final MusicDataRepository _musicDataRepository;
bool _initialized = false;
@observable
ObservableStream<List<Song>> songStream;
@observable
ObservableStream<List<Song>> albumSongStream;
@observable
ObservableList<Artist> artists = <Artist>[].asObservable();
@observable
@ -112,12 +120,14 @@ abstract class _MusicDataStore with Store {
@action
Future<void> fetchSongsFromAlbum(Album album) async {
final result = await _musicDataRepository.getSongsFromAlbum(album);
albumSongs.clear();
result.fold(
(_) => albumSongs = <Song>[].asObservable(),
(songList) => albumSongs.addAll(songList),
);
albumSongStream = _musicDataRepository.getAlbumSongStream(album).asObservable(initialValue: []);
// final result = await _musicDataRepository.getSongsFromAlbum(album);
// albumSongs.clear();
// result.fold(
// (_) => albumSongs = <Song>[].asObservable(),
// (songList) => albumSongs.addAll(songList),
// );
}
Future<void> setSongBlocked(Song song, bool blocked) async {

View file

@ -9,6 +9,36 @@ part of 'music_data_store.dart';
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
mixin _$MusicDataStore on _MusicDataStore, Store {
final _$songStreamAtom = Atom(name: '_MusicDataStore.songStream');
@override
ObservableStream<List<Song>> get songStream {
_$songStreamAtom.reportRead();
return super.songStream;
}
@override
set songStream(ObservableStream<List<Song>> value) {
_$songStreamAtom.reportWrite(value, super.songStream, () {
super.songStream = value;
});
}
final _$albumSongStreamAtom = Atom(name: '_MusicDataStore.albumSongStream');
@override
ObservableStream<List<Song>> get albumSongStream {
_$albumSongStreamAtom.reportRead();
return super.albumSongStream;
}
@override
set albumSongStream(ObservableStream<List<Song>> value) {
_$albumSongStreamAtom.reportWrite(value, super.albumSongStream, () {
super.albumSongStream = value;
});
}
final _$artistsAtom = Atom(name: '_MusicDataStore.artists');
@override
@ -172,6 +202,8 @@ mixin _$MusicDataStore on _MusicDataStore, Store {
@override
String toString() {
return '''
songStream: ${songStream},
albumSongStream: ${albumSongStream},
artists: ${artists},
isFetchingArtists: ${isFetchingArtists},
albums: ${albums},

View file

@ -18,14 +18,14 @@ class CurrentlyPlayingBar extends StatelessWidget {
return Observer(
builder: (BuildContext context) {
if (audioStore.currentSongStream.value != null) {
final Song song = audioStore.currentSongStream.value;
if (audioStore.currentSong != null) {
final Song song = audioStore.currentSong;
return Column(
children: <Widget>[
Container(
child: LinearProgressIndicator(
value: audioStore.currentPositionStream.value / audioStore.currentSongStream.value.duration,
value: audioStore.currentPositionStream.value / audioStore.currentSong.duration,
),
height: 2,
),

View file

@ -17,7 +17,9 @@ class SongCustomizationButtons extends StatelessWidget {
return Observer(
builder: (BuildContext context) {
final Song song = audioStore.currentSongStream.value;
print('building buttons');
final Song song = audioStore.currentSong;
final bool isBlocked = audioStore.currentSong.blocked;
return Row(
children: [
IconButton(
@ -46,10 +48,10 @@ class SongCustomizationButtons extends StatelessWidget {
icon: Icon(
Icons.remove_circle_outline,
size: 20.0,
color: song.blocked ? RASPBERRY : Colors.white70,
color: isBlocked ? RASPBERRY : Colors.white70,
),
onPressed: () =>
musicDataStore.setSongBlocked(song, !song.blocked),
musicDataStore.setSongBlocked(song, !isBlocked),
),
],
mainAxisAlignment: MainAxisAlignment.center,

View file

@ -14,7 +14,7 @@ class TimeProgressIndicator extends StatelessWidget {
return Observer(
builder: (BuildContext context) {
final int duration = audioStore.currentSongStream.value?.duration ?? 1000;
final int duration = audioStore.currentSong?.duration ?? 1000;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),

View file

@ -109,7 +109,7 @@ abstract class AudioPlayerTaskBase extends BackgroundAudioTask with Store {
Future<void> onAddQueueItem(MediaItem mediaItem) async {
await queue.add(AudioSource.uri(Uri.file(mediaItem.id)));
mediaItemQueue.add(mediaItem);
AudioServiceBackground.setQueue(mediaItemQueue);
handleSetQueue(mediaItemQueue);
}
@override
@ -138,6 +138,12 @@ abstract class AudioPlayerTaskBase extends BackgroundAudioTask with Store {
}
}
Future<void> handleSetQueue(List<MediaItem> mediaItemQueue) async {
final songModels = mediaItemQueue.map((e) => SongModel.fromMediaItem(e)).toList();
AudioServiceBackground.setQueue(mediaItemQueue);
moorMusicDataSource.setQueue(songModels);
}
Future<void> init() async {
print('AudioPlayerTask.init');
audioPlayer.playerStateStream.listen((event) => handlePlayerState(event));
@ -174,7 +180,7 @@ abstract class AudioPlayerTaskBase extends BackgroundAudioTask with Store {
mediaItemQueue = playbackContext.map((e) => e.mediaItem).toList();
// FIXME: this does not react correctly when inserted track is currently played
AudioServiceBackground.setQueue(mediaItemQueue);
handleSetQueue(mediaItemQueue);
final newQueue = queueGenerator.mediaItemsToAudioSource(mediaItemQueue);
_updateQueue(newQueue, currentQueueItem);
@ -228,7 +234,7 @@ abstract class AudioPlayerTaskBase extends BackgroundAudioTask with Store {
playbackContext = await queueGenerator.generateQueue(shuffleMode, mediaItems, index);
mediaItemQueue = playbackContext.map((e) => e.mediaItem).toList();
AudioServiceBackground.setQueue(mediaItemQueue);
handleSetQueue(mediaItemQueue);
queue = queueGenerator.mediaItemsToAudioSource(mediaItemQueue);
audioPlayer.play();
final int startIndex = shuffleMode == ShuffleMode.none ? index : 0;
@ -240,13 +246,13 @@ abstract class AudioPlayerTaskBase extends BackgroundAudioTask with Store {
final MediaItem mediaItem = mediaItemQueue.removeAt(oldIndex);
final index = newIndex < oldIndex ? newIndex : newIndex - 1;
mediaItemQueue.insert(index, mediaItem);
AudioServiceBackground.setQueue(mediaItemQueue);
handleSetQueue(mediaItemQueue);
queue.move(oldIndex, index);
}
Future<void> removeQueueItem(int index) async {
mediaItemQueue.removeAt(index);
AudioServiceBackground.setQueue(mediaItemQueue);
handleSetQueue(mediaItemQueue);
queue.removeAt(index);
}

View file

@ -54,7 +54,16 @@ class Songs extends Table {
Set<Column> get primaryKey => {path};
}
@UseMoor(tables: [Artists, Albums, Songs])
@DataClassName('QueueEntry')
class QueueEntries extends Table {
IntColumn get index => integer()();
TextColumn get path => text()();
@override
Set<Column> get primaryKey => {index};
}
@UseMoor(tables: [Artists, Albums, Songs, QueueEntries])
class MoorMusicDataSource extends _$MoorMusicDataSource
implements MusicDataSource {
/// Use MoorMusicDataSource in main isolate only.
@ -83,6 +92,13 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
return await into(albums).insert(albumModel.toAlbumsCompanion());
}
@override
Stream<List<SongModel>> get songStream {
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
@ -90,6 +106,20 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
.toList());
}
@override
Stream<List<SongModel>> getAlbumSongStream(AlbumModel album) {
return (select(songs)
..where((tbl) => tbl.albumId.equals(album.id))
..orderBy([
(t) => OrderingTerm(expression: t.discNumber),
(t) => OrderingTerm(expression: t.trackNumber)
]))
.watch()
.map((moorSongList) => moorSongList
.map((moorSong) => SongModel.fromMoorSong(moorSong))
.toList());
}
@override
Future<List<SongModel>> getSongsFromAlbum(AlbumModel album) {
return (select(songs)
@ -173,15 +203,15 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
Future<void> toggleNextSongLink(SongModel song) async {
if (song.next == null) {
final albumSongs = await (select(songs)
..where((tbl) => tbl.albumId.equals(song.albumId))
..orderBy([
(t) => OrderingTerm(expression: t.discNumber),
(t) => OrderingTerm(expression: t.trackNumber)
]))
.get()
.then((moorSongList) => moorSongList
.map((moorSong) => SongModel.fromMoorSong(moorSong))
.toList());
..where((tbl) => tbl.albumId.equals(song.albumId))
..orderBy([
(t) => OrderingTerm(expression: t.discNumber),
(t) => OrderingTerm(expression: t.trackNumber)
]))
.get()
.then((moorSongList) => moorSongList
.map((moorSong) => SongModel.fromMoorSong(moorSong))
.toList());
bool current = false;
SongModel nextSong;
@ -195,11 +225,41 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
}
}
await (update(songs)..where((tbl) => tbl.path.equals(song.path)))
.write(SongsCompanion(next: Value(nextSong.path)));
.write(SongsCompanion(next: Value(nextSong.path)));
} else {
await (update(songs)..where((tbl) => tbl.path.equals(song.path)))
.write(const SongsCompanion(next: Value(null)));
}
.write(const SongsCompanion(next: Value(null)));
}
}
@override
Stream<List<SongModel>> 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) => SongModel.fromMoorSong(row.readTable(songs)))
.toList();
});
}
@override
Future<void> setQueue(List<SongModel> queue) async {
final _queueEntries = <Insertable<QueueEntry>>[];
for (var i = 0; i < queue.length; i++) {
_queueEntries.add(QueueEntriesCompanion(index: Value(i), path: Value(queue[i].path)));
}
await delete(queueEntries).go();
await batch((batch) {
batch.insertAll(
queueEntries,
_queueEntries
);
});
}
}

View file

@ -1213,6 +1213,199 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> {
}
}
class QueueEntry extends DataClass implements Insertable<QueueEntry> {
final int index;
final String path;
QueueEntry({@required this.index, @required this.path});
factory QueueEntry.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(
index: intType.mapFromDatabaseResponse(data['${effectivePrefix}index']),
path: stringType.mapFromDatabaseResponse(data['${effectivePrefix}path']),
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (!nullToAbsent || index != null) {
map['index'] = Variable<int>(index);
}
if (!nullToAbsent || path != null) {
map['path'] = Variable<String>(path);
}
return map;
}
QueueEntriesCompanion toCompanion(bool nullToAbsent) {
return QueueEntriesCompanion(
index:
index == null && nullToAbsent ? const Value.absent() : Value(index),
path: path == null && nullToAbsent ? const Value.absent() : Value(path),
);
}
factory QueueEntry.fromJson(Map<String, dynamic> json,
{ValueSerializer serializer}) {
serializer ??= moorRuntimeOptions.defaultSerializer;
return QueueEntry(
index: serializer.fromJson<int>(json['index']),
path: serializer.fromJson<String>(json['path']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer serializer}) {
serializer ??= moorRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'index': serializer.toJson<int>(index),
'path': serializer.toJson<String>(path),
};
}
QueueEntry copyWith({int index, String path}) => QueueEntry(
index: index ?? this.index,
path: path ?? this.path,
);
@override
String toString() {
return (StringBuffer('QueueEntry(')
..write('index: $index, ')
..write('path: $path')
..write(')'))
.toString();
}
@override
int get hashCode => $mrjf($mrjc(index.hashCode, path.hashCode));
@override
bool operator ==(dynamic other) =>
identical(this, other) ||
(other is QueueEntry &&
other.index == this.index &&
other.path == this.path);
}
class QueueEntriesCompanion extends UpdateCompanion<QueueEntry> {
final Value<int> index;
final Value<String> path;
const QueueEntriesCompanion({
this.index = const Value.absent(),
this.path = const Value.absent(),
});
QueueEntriesCompanion.insert({
this.index = const Value.absent(),
@required String path,
}) : path = Value(path);
static Insertable<QueueEntry> custom({
Expression<int> index,
Expression<String> path,
}) {
return RawValuesInsertable({
if (index != null) 'index': index,
if (path != null) 'path': path,
});
}
QueueEntriesCompanion copyWith({Value<int> index, Value<String> path}) {
return QueueEntriesCompanion(
index: index ?? this.index,
path: path ?? this.path,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (index.present) {
map['index'] = Variable<int>(index.value);
}
if (path.present) {
map['path'] = Variable<String>(path.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('QueueEntriesCompanion(')
..write('index: $index, ')
..write('path: $path')
..write(')'))
.toString();
}
}
class $QueueEntriesTable extends QueueEntries
with TableInfo<$QueueEntriesTable, QueueEntry> {
final GeneratedDatabase _db;
final String _alias;
$QueueEntriesTable(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,
);
}
final VerificationMeta _pathMeta = const VerificationMeta('path');
GeneratedTextColumn _path;
@override
GeneratedTextColumn get path => _path ??= _constructPath();
GeneratedTextColumn _constructPath() {
return GeneratedTextColumn(
'path',
$tableName,
false,
);
}
@override
List<GeneratedColumn> get $columns => [index, path];
@override
$QueueEntriesTable get asDslTable => this;
@override
String get $tableName => _alias ?? 'queue_entries';
@override
final String actualTableName = 'queue_entries';
@override
VerificationContext validateIntegrity(Insertable<QueueEntry> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('index')) {
context.handle(
_indexMeta, index.isAcceptableOrUnknown(data['index'], _indexMeta));
}
if (data.containsKey('path')) {
context.handle(
_pathMeta, path.isAcceptableOrUnknown(data['path'], _pathMeta));
} else if (isInserting) {
context.missing(_pathMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {index};
@override
QueueEntry map(Map<String, dynamic> data, {String tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
return QueueEntry.fromData(data, _db, prefix: effectivePrefix);
}
@override
$QueueEntriesTable createAlias(String alias) {
return $QueueEntriesTable(_db, alias);
}
}
abstract class _$MoorMusicDataSource extends GeneratedDatabase {
_$MoorMusicDataSource(QueryExecutor e)
: super(SqlTypeSystem.defaultInstance, e);
@ -1223,8 +1416,12 @@ abstract class _$MoorMusicDataSource extends GeneratedDatabase {
$AlbumsTable get albums => _albums ??= $AlbumsTable(this);
$SongsTable _songs;
$SongsTable get songs => _songs ??= $SongsTable(this);
$QueueEntriesTable _queueEntries;
$QueueEntriesTable get queueEntries =>
_queueEntries ??= $QueueEntriesTable(this);
@override
Iterable<TableInfo> get allTables => allSchemaEntities.whereType<TableInfo>();
@override
List<DatabaseSchemaEntity> get allSchemaEntities => [artists, albums, songs];
List<DatabaseSchemaEntity> get allSchemaEntities =>
[artists, albums, songs, queueEntries];
}

View file

@ -5,6 +5,12 @@ import '../models/song_model.dart';
abstract class MusicDataSource {
Future<List<AlbumModel>> getAlbums();
Stream<List<SongModel>> get songStream;
Stream<List<SongModel>> getAlbumSongStream(AlbumModel album);
Future<void> setQueue(List<SongModel> queue);
Stream<List<SongModel>> get queueStream;
/// Insert album into the database. Return the ID of the inserted album.
Future<int> insertAlbum(AlbumModel albumModel);

View file

@ -45,6 +45,12 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
(List<SongModel> songs) => Right<Failure, List<SongModel>>(songs));
}
@override
Stream<List<Song>> get songStream => musicDataSource.songStream;
@override
Stream<List<Song>> getAlbumSongStream(Album album) => musicDataSource.getAlbumSongStream(album as AlbumModel);
@override
Future<Either<Failure, List<Song>>> getSongsFromAlbum(Album album) async {
return musicDataSource.getSongsFromAlbum(album as AlbumModel).then(
@ -127,4 +133,7 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
Future<void> toggleNextSongLink(Song song) async {
musicDataSource.toggleNextSongLink(song as SongModel);
}
@override
Stream<List<Song>> get queueStream => musicDataSource.queueStream;
}