db sync exploration
This commit is contained in:
parent
c7b15dcf08
commit
3570f6cdbd
18 changed files with 479 additions and 71 deletions
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -38,6 +38,7 @@ Future<void> setupGetIt() async {
|
|||
() {
|
||||
final audioStore = AudioStore(
|
||||
audioRepository: getIt(),
|
||||
musicDataRepository: getIt(),
|
||||
);
|
||||
return audioStore;
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,18 +26,12 @@ 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;
|
||||
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) {
|
||||
|
@ -48,11 +43,22 @@ class _SongsPageState extends State<SongsPage>
|
|||
onTapMore: () => _openBottomSheet(song),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) => const Divider(
|
||||
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);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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}
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
@ -201,6 +231,36 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
|
|||
.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
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
LazyDatabase _openConnection() {
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue